OpenOCD
discovery_tcp.c
Go to the documentation of this file.
1 /*
2  * This file is part of the libjaylink project.
3  *
4  * Copyright (C) 2015-2017 Marc Schink <jaylink-dev@marcschink.de>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <string.h>
23 #include <ctype.h>
24 #ifdef _WIN32
25 #include <winsock2.h>
26 #else
27 #include <sys/select.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #endif
33 
34 #include "libjaylink.h"
35 #include "libjaylink-internal.h"
36 
45 #define ADV_MESSAGE_SIZE 128
46 
48 #define DISC_PORT 19020
49 
51 #define DISC_MESSAGE_SIZE 64
52 
54 #define DISC_TIMEOUT 20
55 
57 static bool compare_devices(const void *a, const void *b)
58 {
59  const struct jaylink_device *dev;
60  const struct jaylink_device *new_dev;
61 
62  dev = a;
63  new_dev = b;
64 
65  if (dev->iface != JAYLINK_HIF_TCP)
66  return false;
67 
68  if (memcmp(dev->ipv4_address, new_dev->ipv4_address,
69  sizeof(dev->ipv4_address)) != 0)
70  return false;
71 
72  if (dev->serial_number != new_dev->serial_number)
73  return false;
74 
75  if (memcmp(dev->mac_address, new_dev->mac_address,
76  sizeof(dev->mac_address)) != 0)
77  return false;
78 
79  if (strcmp(dev->product_name, new_dev->product_name) != 0)
80  return false;
81 
82  if (strcmp(dev->nickname, new_dev->nickname) != 0)
83  return false;
84 
85  if (dev->hw_version.type != new_dev->hw_version.type)
86  return false;
87 
88  if (dev->hw_version.major != new_dev->hw_version.major)
89  return false;
90 
91  if (dev->hw_version.minor != new_dev->hw_version.minor)
92  return false;
93 
94  if (dev->hw_version.revision != new_dev->hw_version.revision)
95  return false;
96 
97  return true;
98 }
99 
100 static struct jaylink_device *find_device(struct list *list,
101  const struct jaylink_device *dev)
102 {
103  struct list *item;
104 
105  item = list_find_custom(list, &compare_devices, dev);
106 
107  if (item)
108  return item->data;
109 
110  return NULL;
111 }
112 
113 static bool parse_adv_message(struct jaylink_device *dev,
114  const uint8_t *buffer)
115 {
116  struct in_addr in;
117  uint32_t tmp;
118 
119  if (memcmp(buffer, "Found", 5) != 0)
120  return false;
121 
122  /*
123  * Use inet_ntoa() instead of inet_ntop() because the latter requires
124  * at least Windows Vista.
125  */
126  memcpy(&in, buffer + 16, 4);
127  memcpy(dev->ipv4_address, inet_ntoa(in), sizeof(dev->ipv4_address));
128 
129  memcpy(dev->mac_address, buffer + 32, sizeof(dev->mac_address));
130  dev->has_mac_address = true;
131 
132  dev->serial_number = buffer_get_u32(buffer, 48);
133  dev->has_serial_number = true;
134 
135  tmp = buffer_get_u32(buffer, 52);
136  dev->hw_version.type = (tmp / 1000000) % 100;
137  dev->hw_version.major = (tmp / 10000) % 100;
138  dev->hw_version.minor = (tmp / 100) % 100;
139  dev->hw_version.revision = tmp % 100;
140  dev->has_hw_version = true;
141 
142  memcpy(dev->product_name, buffer + 64, sizeof(dev->product_name));
144  dev->has_product_name = isprint((unsigned char)dev->product_name[0]);
145 
146  memcpy(dev->nickname, buffer + 96, sizeof(dev->nickname));
147  dev->nickname[JAYLINK_NICKNAME_MAX_LENGTH - 1] = '\0';
148  dev->has_nickname = isprint((unsigned char)dev->nickname[0]);
149 
150  return true;
151 }
152 
154  struct sockaddr_in *addr, const uint8_t *buffer)
155 {
156  struct jaylink_device tmp;
157  struct jaylink_device *dev;
158 
159  /*
160  * Use inet_ntoa() instead of inet_ntop() because the latter requires
161  * at least Windows Vista.
162  */
163  log_dbg(ctx, "Received advertisement message (IPv4 address = %s).",
164  inet_ntoa(addr->sin_addr));
165 
166  if (!parse_adv_message(&tmp, buffer)) {
167  log_dbg(ctx, "Received invalid advertisement message.");
168  return NULL;
169  }
170 
171  log_dbg(ctx, "Found device (IPv4 address = %s).", tmp.ipv4_address);
172  log_dbg(ctx, "Device: MAC address = %02x:%02x:%02x:%02x:%02x:%02x.",
173  tmp.mac_address[0], tmp.mac_address[1], tmp.mac_address[2],
174  tmp.mac_address[3], tmp.mac_address[4], tmp.mac_address[5]);
175  log_dbg(ctx, "Device: Serial number = %u.", tmp.serial_number);
176 
177  if (tmp.has_product_name)
178  log_dbg(ctx, "Device: Product = %s.", tmp.product_name);
179 
180  if (tmp.has_nickname)
181  log_dbg(ctx, "Device: Nickname = %s.", tmp.nickname);
182 
183  dev = find_device(ctx->discovered_devs, &tmp);
184 
185  if (dev) {
186  log_dbg(ctx, "Ignoring already discovered device.");
187  return NULL;
188  }
189 
190  dev = find_device(ctx->devs, &tmp);
191 
192  if (dev) {
193  log_dbg(ctx, "Using existing device instance.");
194  return jaylink_ref_device(dev);
195  }
196 
197  log_dbg(ctx, "Allocating new device instance.");
198 
199  dev = device_allocate(ctx);
200 
201  if (!dev) {
202  log_warn(ctx, "Device instance malloc failed.");
203  return NULL;
204  }
205 
206  dev->iface = JAYLINK_HIF_TCP;
207 
208  dev->serial_number = tmp.serial_number;
210 
211  memcpy(dev->ipv4_address, tmp.ipv4_address, sizeof(dev->ipv4_address));
212 
213  memcpy(dev->mac_address, tmp.mac_address, sizeof(dev->mac_address));
214  dev->has_mac_address = tmp.has_mac_address;
215 
216  memcpy(dev->product_name, tmp.product_name, sizeof(dev->product_name));
218 
219  memcpy(dev->nickname, tmp.nickname, sizeof(dev->nickname));
220  dev->has_nickname = tmp.has_nickname;
221 
222  dev->hw_version = tmp.hw_version;
223  dev->has_hw_version = tmp.has_hw_version;
224 
225  return dev;
226 }
227 
230 {
231  int ret;
232  int sock;
233  int opt_value;
234  fd_set rfds;
235  struct sockaddr_in addr;
236  size_t addr_length;
237  struct timeval timeout;
238  uint8_t buf[ADV_MESSAGE_SIZE];
239  struct jaylink_device *dev;
240  size_t length;
241  size_t num_devs;
242 
243  sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
244 
245  if (sock < 0) {
246  log_err(ctx, "Failed to create discovery socket.");
247  return JAYLINK_ERR;
248  }
249 
250  opt_value = true;
251 
252  if (!socket_set_option(sock, SOL_SOCKET, SO_BROADCAST, &opt_value,
253  sizeof(opt_value))) {
254  log_err(ctx, "Failed to enable broadcast option for discovery "
255  "socket.");
256  socket_close(sock);
257  return JAYLINK_ERR;
258  }
259 
260  memset(&addr, 0, sizeof(struct sockaddr_in));
261  addr.sin_family = AF_INET;
262  addr.sin_port = htons(DISC_PORT);
263  addr.sin_addr.s_addr = INADDR_ANY;
264 
265  if (!socket_bind(sock, (struct sockaddr *)&addr,
266  sizeof(struct sockaddr_in))) {
267  log_err(ctx, "Failed to bind discovery socket.");
268  socket_close(sock);
269  return JAYLINK_ERR;
270  }
271 
272  addr.sin_family = AF_INET;
273  addr.sin_port = htons(DISC_PORT);
274  addr.sin_addr.s_addr = INADDR_BROADCAST;
275 
276  memset(buf, 0, DISC_MESSAGE_SIZE);
277  memcpy(buf, "Discover", 8);
278 
279  log_dbg(ctx, "Sending discovery message.");
280 
281  length = DISC_MESSAGE_SIZE;
282 
283  if (!socket_sendto(sock, (char *)buf, &length, 0,
284  (const struct sockaddr *)&addr, sizeof(addr))) {
285  log_err(ctx, "Failed to send discovery message.");
286  socket_close(sock);
287  return JAYLINK_ERR_IO;
288  }
289 
290  if (length < DISC_MESSAGE_SIZE) {
291  log_err(ctx, "Only sent %zu bytes of discovery message.",
292  length);
293  socket_close(sock);
294  return JAYLINK_ERR_IO;
295  }
296 
297  timeout.tv_sec = DISC_TIMEOUT / 1000;
298  timeout.tv_usec = (DISC_TIMEOUT % 1000) * 1000;
299 
300  num_devs = 0;
301 
302  while (true) {
303  FD_ZERO(&rfds);
304  FD_SET(sock, &rfds);
305 
306  ret = select(sock + 1, &rfds, NULL, NULL, &timeout);
307 
308  if (ret <= 0)
309  break;
310 
311  if (!FD_ISSET(sock, &rfds))
312  continue;
313 
314  length = ADV_MESSAGE_SIZE;
315  addr_length = sizeof(struct sockaddr_in);
316 
317  if (!socket_recvfrom(sock, buf, &length, 0,
318  (struct sockaddr *)&addr, &addr_length)) {
319  log_warn(ctx, "Failed to receive advertisement "
320  "message.");
321  continue;
322  }
323 
324  /*
325  * Filter out messages with an invalid size. This includes the
326  * broadcast message we sent before.
327  */
328  if (length != ADV_MESSAGE_SIZE)
329  continue;
330 
331  dev = probe_device(ctx, &addr, buf);
332 
333  if (dev) {
335  ctx->discovered_devs, dev);
336  num_devs++;
337  }
338  }
339 
340  socket_close(sock);
341 
342  if (ret < 0) {
343  log_err(ctx, "select() failed.");
344  return JAYLINK_ERR;
345  }
346 
347  log_dbg(ctx, "Found %zu TCP/IP device(s).", num_devs);
348 
349  return JAYLINK_OK;
350 }
static struct jaylink_device * find_device(struct list *list, const struct jaylink_device *dev)
long tv_sec
Definition: replacements.h:56
static struct jaylink_device * probe_device(struct jaylink_context *ctx, struct sockaddr_in *addr, const uint8_t *buffer)
JAYLINK_PRIV uint32_t buffer_get_u32(const uint8_t *buffer, size_t offset)
Read a 32-bit unsigned integer value from a buffer.
Definition: buffer.c:122
JAYLINK_API struct jaylink_device * jaylink_ref_device(struct jaylink_device *dev)
Increment the reference count of a device.
Definition: device.c:518
long tv_usec
Definition: replacements.h:57
static bool parse_adv_message(struct jaylink_device *dev, const uint8_t *buffer)
static bool compare_devices(const void *a, const void *b)
Definition: discovery_tcp.c:57
uint32_t addr
Definition: nuttx.c:76
#define NULL
Definition: usb.h:27