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