69c7e36a72aa92d8b81df4f28f0cda4dcb2ac214
[oweals/nmrpflash.git] / ethsock.c
1 #include <sys/types.h>
2 #include <stdbool.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <pcap.h>
7 #include "ethsock.h"
8 #include "nmrpd.h"
9
10 #if defined(NMRPFLASH_WINDOWS)
11 #include <windows.h>
12 #define NMRPFLASH_ALIAS_PREFIX "net"
13 #else
14 #include <ifaddrs.h>
15 #if defined(NMRPFLASH_LINUX)
16 #include <linux/if_packet.h>
17 #elif defined (NMRPFLASH_OSX)
18 #include <net/if_dl.h>
19 #endif
20 #endif
21
22 #ifndef MIN
23 #define MIN(a, b) ((a) < (b) ? (a) : (b))
24 #endif
25
26
27 struct ethsock
28 {
29         pcap_t *pcap;
30 #ifndef NMRPFLASH_WINDOWS
31         struct timeval timeout;
32         int fd;
33 #else
34         DWORD timeout;
35         HANDLE handle;
36 #endif
37         uint8_t hwaddr[6];
38 };
39
40 static int x_pcap_findalldevs(pcap_if_t **devs)
41 {
42         char errbuf[PCAP_ERRBUF_SIZE];
43         if (pcap_findalldevs(devs, errbuf) != 0) {
44                 fprintf(stderr, "%s.\n", errbuf);
45                 return -1;
46         }
47
48         return 0;
49 }
50
51 #ifndef NMRPFLASH_WINDOWS
52 static bool get_hwaddr(uint8_t *hwaddr, const char *intf)
53 {
54         struct ifaddrs *ifas, *ifa;
55         void *src;
56         bool found;
57
58         if (getifaddrs(&ifas) != 0) {
59                 perror("getifaddrs");
60                 return false;
61         }
62
63         found = false;
64
65         for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
66                 if (!strcmp(ifa->ifa_name, intf)) {
67 #ifdef NMRPFLASH_LINUX
68                         if (ifa->ifa_addr->sa_family != AF_PACKET) {
69                                 continue;
70                         }
71                         src = ((struct sockaddr_ll*)ifa->ifa_addr)->sll_addr;
72 #else
73                         if (ifa->ifa_addr->sa_family != AF_LINK) {
74                                 continue;
75                         }
76                         src = LLADDR((struct sockaddr_dl*)ifa->ifa_addr);
77 #endif
78                         memcpy(hwaddr, src, 6);
79                         found = true;
80                         break;
81                 }
82         }
83
84         freeifaddrs(ifas);
85         return found;
86 }
87 #else
88
89 static void win_perror2(const char *msg, int err)
90 {
91         fprintf(stderr, "%s: error %d\n", msg, err);
92 }
93
94 static bool get_hwaddr(uint8_t *hwaddr, const char *intf)
95 {
96         PIP_ADAPTER_INFO adapters, adapter;
97         DWORD ret;
98         ULONG i, bufLen = 0;
99         bool found = false;
100
101         if ((ret = GetAdaptersInfo(NULL, &bufLen)) != ERROR_BUFFER_OVERFLOW) {
102                 win_perror2("GetAdaptersInfo", ret);
103                 return false;
104         }
105
106         adapters = malloc(bufLen);
107         if (!adapters) {
108                 perror("malloc");
109                 return false;
110         }
111
112         if ((ret = GetAdaptersInfo(adapters, &bufLen) == NO_ERROR)) {
113                 for (adapter = adapters; adapter; adapter = adapter->Next) {
114                         if (adapter->Type != MIB_IF_TYPE_ETHERNET) {
115                                 continue;
116                         }
117
118 #ifndef NMRPFLASH_WINDOWS
119                         if (!strcmp(intf, adapter->AdapterName))
120 #else
121                         /* Interface names from WinPcap are "\Device\NPF_{GUID}", while
122                          * AdapterName from GetAdaptersInfo is just "{GUID}".*/
123                         if (strstr(intf, adapter->AdapterName))
124 #endif
125                         {
126                                 if (adapter->AddressLength == 6) {
127                                         for (i = 0; i != 6; ++i) {
128                                                 hwaddr[i] = adapter->Address[i];
129                                         }
130
131                                         found = true;
132                                         break;
133                                 }
134                         }
135                 }
136         } else {
137                 win_perror2("GetAdaptersInfo", ret);
138         }
139
140         free(adapters);
141         return found;
142 }
143
144 static const char *intf_alias_to_wpcap(const char *intf)
145 {
146         static char buf[128];
147         pcap_if_t *devs, *dev;
148         unsigned i = 0, dev_num = 0;
149
150         if (intf[0] == '\\') {
151                 return intf;
152         } else if (sscanf(intf, NMRPFLASH_ALIAS_PREFIX "%u", &dev_num) != 1) {
153                 fprintf(stderr, "Invalid interface alias.\n");
154                 return NULL;
155         }
156
157         if (x_pcap_findalldevs(&devs) != 0) {
158                 return NULL;
159         }
160
161         for (dev = devs; dev; dev = dev->next) {
162                 if (i == dev_num) {
163                         printf(NMRPFLASH_ALIAS_PREFIX "%u: %s\n", i, dev->name);
164                         strncpy(buf, dev->name, sizeof(buf) - 1);
165                         buf[sizeof(buf) - 1] = '\0';
166                         break;
167                 }
168         }
169
170         pcap_freealldevs(devs);
171
172         if (!dev) {
173                 fprintf(stderr, "Interface alias not found.\n");
174                 return NULL;
175         }
176
177         return buf;
178 }
179
180 static const char *intf_get_pretty_name(const char *intf)
181 {
182         static char buf[512];
183         char *guid;
184         HKEY hkey;
185         LONG err;
186         DWORD len;
187
188         guid = strstr(intf, "NPF_{");
189         if (!guid) {
190                 return NULL;
191         }
192
193         guid += 4;
194
195         snprintf(buf, sizeof(buf),
196                         "System\\CurrentControlSet\\Control\\Network\\"
197                         "{4D36E972-E325-11CE-BFC1-08002BE10318}\\"
198                         "%s\\Connection", guid);
199         err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &hkey);
200         if (err != ERROR_SUCCESS) {
201                 win_perror2("RegOpenKeyExA", err);
202                 return NULL;
203         }
204
205         len = sizeof(buf);
206         err = RegQueryValueExA(hkey, "Name", NULL, NULL, (LPBYTE)buf, &len);
207         if (err == ERROR_SUCCESS) {
208                 intf = buf;
209         } else {
210                 win_perror2("RegQueryValueExA", err);
211                 intf = NULL;
212         }
213
214         RegCloseKey(hkey);
215         return intf;
216 }
217 #endif
218
219
220 inline uint8_t *ethsock_get_hwaddr(struct ethsock *sock)
221 {
222         return sock->hwaddr;
223 }
224
225 struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
226 {
227         char buf[PCAP_ERRBUF_SIZE];
228         struct bpf_program fp;
229         struct ethsock *sock;
230         int err;
231
232         sock = malloc(sizeof(struct ethsock));
233         if (!sock) {
234                 perror("malloc");
235                 return NULL;
236         }
237
238 #ifdef NMRPFLASH_WINDOWS
239         intf = intf_alias_to_wpcap(intf);
240         if (!intf) {
241                 return NULL;
242         }
243 #endif
244
245         buf[0] = '\0';
246
247         sock->pcap = pcap_open_live(intf, BUFSIZ, 1, 1, buf);
248         if (!sock->pcap) {
249                 fprintf(stderr, "%s.\n", buf);
250                 goto cleanup_malloc;
251         }
252
253         if (*buf) {
254                 fprintf(stderr, "Warning: %s.\n", buf);
255         }
256
257         if (pcap_datalink(sock->pcap) != DLT_EN10MB) {
258                 fprintf(stderr, "%s is not an ethernet interface.\n",
259                                 intf);
260                 goto cleanup_pcap;
261         }
262
263         if (!get_hwaddr(sock->hwaddr, intf)) {
264                 fprintf(stderr, "Failed to get MAC address of interface.\n");
265                 goto cleanup_malloc;
266         }
267
268 #ifndef NMRPFLASH_WINDOWS
269         sock->fd = pcap_get_selectable_fd(sock->pcap);
270         if (sock->fd == -1) {
271                 fprintf(stderr, "No selectable file descriptor available.\n");
272                 goto cleanup_pcap;
273         }
274 #else
275         sock->handle = pcap_getevent(sock->pcap);
276         if (!sock->handle) {
277                 fprintf(stderr, "No event handle available.\n");
278                 goto cleanup_pcap;
279         }
280 #endif
281
282         snprintf(buf, sizeof(buf), "ether proto %04x", protocol);
283         err = pcap_compile(sock->pcap, &fp, buf, 0, 0);
284         if (err) {
285                 pcap_perror(sock->pcap, "pcap_compile");
286                 goto cleanup_pcap;
287         }
288
289         if ((err = pcap_setfilter(sock->pcap, &fp))) {
290                 pcap_perror(sock->pcap, "pcap_setfilter");
291                 goto cleanup_pcap;
292         }
293
294         return sock;
295
296 cleanup_pcap:
297         pcap_close(sock->pcap);
298 cleanup_malloc:
299         free(sock);
300         return NULL;
301 }
302
303 ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len)
304 {
305         struct pcap_pkthdr* hdr;
306         const u_char *capbuf;
307         int status;
308 #ifndef NMRPFLASH_WINDOWS
309         fd_set fds;
310 #else
311         DWORD ret;
312 #endif
313
314 #ifndef NMRPFLASH_WINDOWS
315         if (sock->timeout.tv_sec || sock->timeout.tv_usec) {
316                 FD_ZERO(&fds);
317                 FD_SET(sock->fd, &fds);
318
319                 status = select(sock->fd + 1, &fds, NULL, NULL, &sock->timeout);
320                 if (status == -1) {
321                         perror("select");
322                         return -1;
323                 } else if (status == 0) {
324                         return 0;
325                 }
326         }
327 #else
328         if (sock->timeout) {
329                 ret = WaitForSingleObject(sock->handle, sock->timeout);
330                 if (ret == WAIT_TIMEOUT) {
331                         return 0;
332                 } else if (ret != WAIT_OBJECT_0) {
333                         fprintf(stderr, "WaitForSingleObject: returned %d\n", (int)ret);
334                         return -1;
335                 }
336         }
337 #endif
338
339         status = pcap_next_ex(sock->pcap, &hdr, &capbuf);
340         switch (status) {
341                 case 1:
342                         memcpy(buf, capbuf, MIN(len, hdr->caplen));
343                         return hdr->caplen;
344                 case 0:
345                         return 0;
346                 case -1:
347                         pcap_perror(sock->pcap, "pcap_next_ex");
348                         return -1;
349                 default:
350                         fprintf(stderr, "pcap_next_ex: returned %d.\n", status);
351                         return -1;
352         }
353 }
354
355 int ethsock_send(struct ethsock *sock, void *buf, size_t len)
356 {
357 #ifdef NMRPFLASH_WINDOWS
358         if (pcap_sendpacket(sock->pcap, buf, len) == 0) {
359                 return 0;
360         } else {
361                 pcap_perror(sock->pcap, "pcap_sendpacket");
362                 return -1;
363         }
364 #else
365         if (pcap_inject(sock->pcap, buf, len) == len) {
366                 return 0;
367         } else {
368                 pcap_perror(sock->pcap, "pcap_inject");
369                 return -1;
370         }
371 #endif
372 }
373
374 int ethsock_close(struct ethsock *sock)
375 {
376         pcap_close(sock->pcap);
377         free(sock);
378         return 0;
379 }
380
381 int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
382 {
383 #ifndef NMRPFLASH_WINDOWS
384         sock->timeout.tv_sec = msec / 1000;
385         sock->timeout.tv_usec = (msec % 1000) * 1000;
386 #else
387         sock->timeout = msec;
388 #endif
389         return 0;
390 }
391
392 static bool is_ethernet(const char *intf)
393 {
394         pcap_t *pcap;
395         char errbuf[PCAP_ERRBUF_SIZE];
396         bool ret = false;
397
398         if ((pcap = pcap_create(intf, errbuf))) {
399                 if (pcap_activate(pcap) == 0) {
400                         ret = (pcap_datalink(pcap) == DLT_EN10MB);
401                 }
402                 pcap_close(pcap);
403         }
404
405         return ret;
406 }
407
408 int ethsock_list_all(void)
409 {
410         pcap_if_t *devs, *dev;
411         pcap_addr_t *addr;
412         uint8_t hwaddr[6];
413         unsigned dev_num = 0;
414 #ifdef NMRPFLASH_WINDOWS
415         const char *pretty;
416 #endif
417
418         if (x_pcap_findalldevs(&devs) != 0) {
419                 return -1;
420         }
421
422         memset(hwaddr, 0, 6);
423
424         for (dev = devs; dev; dev = dev->next) {
425                 if (!is_ethernet(dev->name)) {
426                         if (verbosity > 1) {
427                                 printf("%s  (not an ethernet device)\n",
428                                                 dev->name);
429                         }
430                         continue;
431                 }
432
433                 if (!get_hwaddr(hwaddr, dev->name)) {
434                         if (verbosity > 1) {
435                                 printf("%s  (failed to get hardware address)\n",
436                                                 dev->name);
437                         }
438                         continue;
439                 }
440
441 #ifndef NMRPFLASH_WINDOWS
442                 printf("%s", dev->name);
443 #else
444                 printf(NMRPFLASH_ALIAS_PREFIX "%u", dev_num);
445 #endif
446
447                 for (addr = dev->addresses; addr; addr = addr->next) {
448                         if (addr->addr->sa_family == AF_INET) {
449                                 printf("  %15s",
450                                                 inet_ntoa(((struct sockaddr_in*)addr->addr)->sin_addr));
451                                 break;
452                         }
453                 }
454
455                 if (!addr) {
456                         printf("                 ");
457                 }
458
459                 printf("  %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0], hwaddr[1],
460                                 hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
461
462 #ifdef NMRPFLASH_WINDOWS
463                 if (verbosity) {
464                         printf("  %s", dev->name);
465                 }
466
467                 pretty = intf_get_pretty_name(dev->name);
468                 if (pretty) {
469                         printf("  (%s)", pretty);
470                 } else if (dev->description) {
471                         printf("  (%s)", dev->description);
472                 }
473
474 #endif
475                 printf("\n");
476                 ++dev_num;
477         }
478
479         if (!dev_num) {
480                 printf("No suitable network interfaces found.\n");
481         }
482
483         return 0;
484 }