Fix FreeBSD issues
[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 <sys/ioctl.h>
14 #include <ifaddrs.h>
15 #include <unistd.h>
16 #include <net/if.h>
17 #include <pcap.h>
18 #if defined(NMRPFLASH_LINUX)
19 #define NMRPFLASH_AF_PACKET AF_PACKET
20 #include <linux/if_packet.h>
21 #else
22 #define NMRPFLASH_AF_PACKET AF_LINK
23 #include <net/if_types.h>
24 #endif
25 #endif
26
27 struct ethsock
28 {
29         const char *intf;
30         pcap_t *pcap;
31 #ifndef NMRPFLASH_WINDOWS
32         int fd;
33 #else
34         HANDLE handle;
35         DWORD index;
36 #endif
37         unsigned timeout;
38         uint8_t hwaddr[6];
39 };
40
41 struct ethsock_arp_undo
42 {
43         uint32_t ipaddr;
44         uint8_t hwaddr[6];
45 };
46
47 struct ethsock_ip_undo
48 {
49 #ifndef NMRPFLASH_WINDOWS
50         uint32_t ip[2];
51 #else
52         ULONG context;
53 #endif
54 };
55
56 const char *mac_to_str(uint8_t *mac)
57 {
58         static char buf[18];
59         snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
60                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
61         return buf;
62 }
63
64 static int x_pcap_findalldevs(pcap_if_t **devs)
65 {
66         char errbuf[PCAP_ERRBUF_SIZE];
67         if (pcap_findalldevs(devs, errbuf) != 0) {
68                 fprintf(stderr, "%s.\n", errbuf);
69                 return -1;
70         }
71
72         return 0;
73 }
74
75 #ifndef NMRPFLASH_WINDOWS
76 static inline bool sockaddr_get_hwaddr(struct sockaddr *sa, uint8_t *hwaddr)
77 {
78         void *src;
79
80         if (sa->sa_family != NMRPFLASH_AF_PACKET) {
81                 return false;
82         }
83
84 #ifndef NMRPFLASH_LINUX
85         if (((struct sockaddr_dl*)sa)->sdl_type != IFT_ETHER) {
86                 return false;
87         }
88         src = LLADDR((struct sockaddr_dl*)sa);
89 #else
90         src = ((struct sockaddr_ll*)sa)->sll_addr;
91 #endif
92
93         memcpy(hwaddr, src, 6);
94         return true;
95 }
96
97 static bool get_intf_info(const char *intf, uint8_t *hwaddr, void *dummy)
98 {
99         struct ifaddrs *ifas, *ifa;
100         bool found;
101
102         if (getifaddrs(&ifas) != 0) {
103                 perror("getifaddrs");
104                 return false;
105         }
106
107         found = false;
108
109         for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
110                 if (!strcmp(ifa->ifa_name, intf)) {
111                         if (sockaddr_get_hwaddr(ifa->ifa_addr, hwaddr)) {
112                                 found = true;
113                                 break;
114                         }
115                 }
116         }
117
118         freeifaddrs(ifas);
119         return found;
120 }
121
122 #else
123
124 void win_perror2(const char *msg, DWORD err)
125 {
126         char *buf = NULL;
127         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
128                         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
129                         NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
130                         (LPTSTR)&buf, 0, NULL);
131
132         if (buf) {
133                 /* FormatMessageA terminates buf with CRLF! */
134                 fprintf(stderr, "%s: %s", msg, buf);
135                 LocalFree(buf);
136         } else {
137                 fprintf(stderr, "%s: error %d\n", msg, (int)err);
138         }
139 }
140
141 static bool get_intf_info(const char *intf, uint8_t *hwaddr, DWORD *index)
142 {
143         PIP_ADAPTER_INFO adapters, adapter;
144         DWORD ret;
145         ULONG i, bufLen = 0;
146         bool found = false;
147
148         if ((ret = GetAdaptersInfo(NULL, &bufLen)) != ERROR_BUFFER_OVERFLOW) {
149                 win_perror2("GetAdaptersInfo", ret);
150                 return false;
151         }
152
153         adapters = malloc(bufLen);
154         if (!adapters) {
155                 perror("malloc");
156                 return false;
157         }
158
159         if ((ret = GetAdaptersInfo(adapters, &bufLen) == NO_ERROR)) {
160                 for (adapter = adapters; adapter; adapter = adapter->Next) {
161                         if (adapter->Type != MIB_IF_TYPE_ETHERNET && adapter->Type != IF_TYPE_IEEE80211) {
162                                 continue;
163                         }
164
165                         /* Interface names from WinPcap are "\Device\NPF_{GUID}", while
166                          * AdapterName from GetAdaptersInfo is just "{GUID}".*/
167                         if (strstr(intf, adapter->AdapterName)) {
168                                 if (adapter->AddressLength == 6) {
169                                         memcpy(hwaddr, adapter->Address, 6);
170                                         if (index) {
171                                                 *index = adapter->Index;
172                                         }
173                                         found = true;
174                                         break;
175                                 }
176                         }
177                 }
178         } else {
179                 win_perror2("GetAdaptersInfo", ret);
180         }
181
182         free(adapters);
183         return found;
184 }
185
186 static const char *intf_alias_to_wpcap(const char *intf)
187 {
188         static char buf[128];
189         pcap_if_t *devs, *dev;
190         unsigned i = 0, dev_num = 0;
191
192         if (intf[0] == '\\') {
193                 return intf;
194         } else if (sscanf(intf, NMRPFLASH_NETALIAS_PREFIX "%u", &dev_num) != 1) {
195                 fprintf(stderr, "Invalid interface alias.\n");
196                 return NULL;
197         }
198
199         if (x_pcap_findalldevs(&devs) != 0) {
200                 return NULL;
201         }
202
203         for (dev = devs; dev; dev = dev->next, ++i) {
204                 if (i == dev_num) {
205                         if (verbosity) {
206                                 printf("%s%u: %s\n", NMRPFLASH_NETALIAS_PREFIX, i, dev->name);
207                         }
208                         strncpy(buf, dev->name, sizeof(buf) - 1);
209                         buf[sizeof(buf) - 1] = '\0';
210                         break;
211                 }
212         }
213
214         pcap_freealldevs(devs);
215
216         if (!dev) {
217                 fprintf(stderr, "Interface alias not found.\n");
218                 return NULL;
219         }
220
221         return buf;
222 }
223
224 static const char *intf_get_pretty_name(const char *intf)
225 {
226         static char buf[512];
227         char *guid;
228         HKEY hkey;
229         LONG err;
230         DWORD len;
231
232         guid = strstr(intf, "NPF_{");
233         if (!guid) {
234                 return NULL;
235         }
236
237         guid += 4;
238
239         snprintf(buf, sizeof(buf),
240                         "System\\CurrentControlSet\\Control\\Network\\"
241                         "{4D36E972-E325-11CE-BFC1-08002BE10318}\\"
242                         "%s\\Connection", guid);
243         err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &hkey);
244         if (err != ERROR_SUCCESS) {
245                 if (verbosity > 1) {
246                         win_perror2("RegOpenKeyExA", err);
247                 }
248                 return NULL;
249         }
250
251         len = sizeof(buf);
252         err = RegQueryValueExA(hkey, "Name", NULL, NULL, (LPBYTE)buf, &len);
253         if (err == ERROR_SUCCESS) {
254                 intf = buf;
255         } else {
256                 if (verbosity > 1) {
257                         win_perror2("RegQueryValueExA", err);
258                 }
259                 intf = NULL;
260         }
261
262         RegCloseKey(hkey);
263         return intf;
264 }
265 #endif
266
267 inline uint8_t *ethsock_get_hwaddr(struct ethsock *sock)
268 {
269         return sock->hwaddr;
270 }
271
272 struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
273 {
274         char buf[PCAP_ERRBUF_SIZE];
275         struct bpf_program fp;
276         struct ethsock *sock;
277         int err;
278
279         sock = malloc(sizeof(struct ethsock));
280         if (!sock) {
281                 perror("malloc");
282                 return NULL;
283         }
284
285 #ifdef NMRPFLASH_WINDOWS
286         intf = intf_alias_to_wpcap(intf);
287         if (!intf) {
288                 return NULL;
289         }
290 #endif
291
292         buf[0] = '\0';
293
294         sock->intf = intf;
295         sock->pcap = pcap_open_live(sock->intf, BUFSIZ, 1, 1, buf);
296         if (!sock->pcap) {
297                 fprintf(stderr, "%s.\n", buf);
298                 goto cleanup_malloc;
299         }
300
301         if (*buf) {
302                 fprintf(stderr, "Warning: %s.\n", buf);
303         }
304
305         if (pcap_datalink(sock->pcap) != DLT_EN10MB) {
306                 fprintf(stderr, "%s is not an ethernet interface.\n",
307                                 intf);
308                 goto cleanup_pcap;
309         }
310
311 #ifndef NMRPFLASH_WINDOWS
312         err = !get_intf_info(intf, sock->hwaddr, NULL);
313 #else
314         err = !get_intf_info(intf, sock->hwaddr, &sock->index);
315 #endif
316         if (err) {
317                 fprintf(stderr, "Failed to get interface info.\n");
318                 goto cleanup_malloc;
319         }
320
321 #ifndef NMRPFLASH_WINDOWS
322         sock->fd = pcap_get_selectable_fd(sock->pcap);
323         if (sock->fd == -1) {
324                 pcap_perror(sock->pcap, "pcap_get_selectable_fd");
325                 goto cleanup_pcap;
326         }
327 #else
328         sock->handle = pcap_getevent(sock->pcap);
329         if (!sock->handle) {
330                 pcap_perror(sock->pcap, "pcap_getevent");
331                 goto cleanup_pcap;
332         }
333
334         err = pcap_setmintocopy(sock->pcap, 1);
335         if (err) {
336                 pcap_perror(sock->pcap, "pcap_setmintocopy");
337                 goto cleanup_pcap;
338         }
339 #endif
340
341         snprintf(buf, sizeof(buf), "ether proto 0x%04x and not ether src %s",
342                         protocol, mac_to_str(sock->hwaddr));
343
344         err = pcap_compile(sock->pcap, &fp, buf, 0, 0);
345         if (err) {
346                 pcap_perror(sock->pcap, "pcap_compile");
347                 goto cleanup_pcap;
348         }
349
350         err = pcap_setfilter(sock->pcap, &fp);
351         pcap_freecode(&fp);
352
353         if (err) {
354                 pcap_perror(sock->pcap, "pcap_setfilter");
355                 goto cleanup_pcap;
356         }
357
358         return sock;
359
360 cleanup_pcap:
361         pcap_close(sock->pcap);
362 cleanup_malloc:
363         free(sock);
364         return NULL;
365 }
366
367 int select_fd(int fd, unsigned timeout)
368 {
369         struct timeval tv;
370         int status;
371         fd_set fds;
372
373         FD_ZERO(&fds);
374         FD_SET(fd, &fds);
375
376         tv.tv_sec = timeout / 1000;
377         tv.tv_usec = 1000 * (timeout % 1000);
378
379         status = select(fd + 1, &fds, NULL, NULL, &tv);
380         if (status < 0) {
381                 sock_perror("select");
382         }
383
384         return status;
385 }
386
387 ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len)
388 {
389         struct pcap_pkthdr* hdr;
390         const u_char *capbuf;
391         int status;
392 #ifdef NMRPFLASH_WINDOWS
393         DWORD ret;
394
395         if (sock->timeout) {
396                 ret = WaitForSingleObject(sock->handle, sock->timeout);
397                 if (ret == WAIT_TIMEOUT) {
398                         return 0;
399                 } else if (ret != WAIT_OBJECT_0) {
400                         win_perror2("WaitForSingleObject", ret);
401                         return -1;
402                 }
403         }
404 #else
405         if (sock->timeout) {
406                 status = select_fd(sock->fd, sock->timeout);
407                 if (status < 0) {
408                         return -1;
409                 } else if (status == 0) {
410                         return 0;
411                 }
412         }
413 #endif
414
415         status = pcap_next_ex(sock->pcap, &hdr, &capbuf);
416         switch (status) {
417                 case 1:
418                         memcpy(buf, capbuf, MIN(len, hdr->caplen));
419                         return hdr->caplen;
420                 case 0:
421                         return 0;
422                 case -1:
423                         pcap_perror(sock->pcap, "pcap_next_ex");
424                         return -1;
425                 default:
426                         fprintf(stderr, "pcap_next_ex: returned %d.\n", status);
427                         return -1;
428         }
429 }
430
431 int ethsock_send(struct ethsock *sock, void *buf, size_t len)
432 {
433 #ifdef NMRPFLASH_WINDOWS
434         if (pcap_sendpacket(sock->pcap, buf, len) == 0) {
435                 return 0;
436         } else {
437                 pcap_perror(sock->pcap, "pcap_sendpacket");
438                 return -1;
439         }
440 #else
441         if (pcap_inject(sock->pcap, buf, len) == len) {
442                 return 0;
443         } else {
444                 pcap_perror(sock->pcap, "pcap_inject");
445                 return -1;
446         }
447 #endif
448 }
449
450 int ethsock_close(struct ethsock *sock)
451 {
452         pcap_close(sock->pcap);
453         free(sock);
454         return 0;
455 }
456
457 inline int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
458 {
459         sock->timeout = msec;
460         return 0;
461 }
462
463 #ifndef NMRPFLASH_WINDOWS
464 int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, uint32_t ipaddr, struct ethsock_arp_undo **undo)
465 {
466         return 0;
467 }
468
469 int ethsock_arp_del(struct ethsock *sock, struct ethsock_arp_undo **undo)
470 {
471         return 0;
472 }
473 #else
474 static int ethsock_arp(struct ethsock *sock, uint8_t *hwaddr, uint32_t ipaddr, struct ethsock_arp_undo **undo)
475 {
476         DWORD ret;
477         MIB_IPNETROW arp = {
478                 .dwIndex = sock->index,
479                 .dwPhysAddrLen = 6,
480                 .dwAddr = ipaddr,
481                 .dwType = MIB_IPNET_TYPE_STATIC
482         };
483
484         memcpy(arp.bPhysAddr, hwaddr, 6);
485
486         if (undo) {
487                 ret = CreateIpNetEntry(&arp);
488                 if (ret != NO_ERROR) {
489                         win_perror2("CreateIpNetEntry", ret);
490                         return -1;
491                 }
492
493                 *undo = malloc(sizeof(struct ethsock_arp_undo));
494                 if (!*undo) {
495                         perror("malloc");
496                         return -1;
497                 }
498
499                 (*undo)->ipaddr = ipaddr;
500                 memcpy((*undo)->hwaddr, hwaddr, 6);
501         } else {
502                 DeleteIpNetEntry(&arp);
503         }
504
505         return 0;
506 }
507
508 int ethsock_arp_add(struct ethsock *sock, uint8_t *hwaddr, uint32_t ipaddr, struct ethsock_arp_undo **undo)
509 {
510         ethsock_arp(sock, hwaddr, ipaddr, NULL);
511         return undo ? ethsock_arp(sock, hwaddr, ipaddr, undo) : -1;
512 }
513
514 int ethsock_arp_del(struct ethsock *sock, struct ethsock_arp_undo **undo)
515 {
516         if (!*undo) {
517                 return 0;
518         }
519
520         int ret = ethsock_arp(sock, (*undo)->hwaddr, (*undo)->ipaddr, NULL);
521         free(*undo);
522         *undo = NULL;
523         return ret;
524 }
525 #endif
526
527 static bool get_hwaddr_from_pcap(const pcap_if_t *dev, uint8_t *hwaddr)
528 {
529 #ifndef NMRPFLASH_WINDOWS
530         pcap_addr_t *addr;
531         int i;
532
533         for (addr = dev->addresses; addr; addr = addr->next) {
534                 if (verbosity > 1) {
535                         printf("%s: sa_family=%d, sa_data={ ", dev->name,
536                                         addr->addr->sa_family);
537                         for (i = 0; i != sizeof(addr->addr->sa_data); ++i) {
538                                 printf("%02x ", addr->addr->sa_data[i] & 0xff);
539                         }
540                         printf("}\n");
541                 }
542
543                 if (sockaddr_get_hwaddr(addr->addr, hwaddr)) {
544                         return true;
545                 }
546         }
547 #endif
548
549         return get_intf_info(dev->name, hwaddr, NULL);
550 }
551
552 int ethsock_list_all(void)
553 {
554         pcap_if_t *devs, *dev;
555         pcap_addr_t *addr;
556         uint8_t hwaddr[6];
557         unsigned dev_num = 0, dev_ok = 0;
558 #ifdef NMRPFLASH_WINDOWS
559         const char *pretty;
560 #endif
561
562         if (x_pcap_findalldevs(&devs) != 0) {
563                 return -1;
564         }
565
566         memset(hwaddr, 0, 6);
567
568         for (dev = devs; dev; dev = dev->next, ++dev_num) {
569                 if (dev->flags & PCAP_IF_LOOPBACK) {
570                         if (verbosity) {
571                                 printf("%-15s  (loopback device)\n", dev->name);
572                         }
573                         continue;
574                 }
575
576                 if (!get_hwaddr_from_pcap(dev, hwaddr)) {
577                         if (verbosity) {
578                                 printf("%-15s  (not an ethernet device)\n",
579                                                 dev->name);
580                         }
581                         continue;
582                 }
583
584 #ifndef NMRPFLASH_WINDOWS
585                 printf("%-15s", dev->name);
586 #else
587                 /* Call this here so *_perror() calls don't happen within a line */
588                 pretty = intf_get_pretty_name(dev->name);
589
590                 if (!verbosity) {
591                         printf("%s%u", NMRPFLASH_NETALIAS_PREFIX, dev_num);
592                 } else {
593                         printf("%s", dev->name);
594                 }
595 #endif
596
597                 for (addr = dev->addresses; addr; addr = addr->next) {
598                         if (addr->addr->sa_family == AF_INET) {
599                                 printf("  %-15s",
600                                                 inet_ntoa(((struct sockaddr_in*)addr->addr)->sin_addr));
601                                 break;
602                         }
603                 }
604
605                 if (!addr) {
606                         printf("  %-15s", "0.0.0.0");
607                 }
608
609                 printf("  %s", mac_to_str(hwaddr));
610
611 #ifdef NMRPFLASH_WINDOWS
612                 if (pretty) {
613                         printf("  (%s)", pretty);
614                 } else if (dev->description) {
615                         printf("  (%s)", dev->description);
616                 }
617
618 #endif
619                 printf("\n");
620                 ++dev_ok;
621         }
622
623         if (!dev_ok) {
624                 printf("No suitable network interfaces found.\n");
625         }
626
627         return 0;
628 }
629
630 int ethsock_for_each_ip(struct ethsock *sock, ethsock_ip_callback_t callback,
631                 void *arg)
632 {
633         struct ethsock_ip_callback_args args;
634         pcap_if_t *devs, *dev;
635         pcap_addr_t *addr;
636         int status = 0;
637
638         if (x_pcap_findalldevs(&devs) != 0) {
639                 return -1;
640         }
641
642         args.arg = arg;
643
644         for (dev = devs; dev; dev = dev->next) {
645                 if (strcmp(sock->intf, dev->name)) {
646                         continue;
647                 }
648
649                 for (addr = dev->addresses; addr; addr = addr->next) {
650                         if (addr->addr->sa_family == AF_INET) {
651                                 args.ipaddr = &((struct sockaddr_in*)addr->addr)->sin_addr;
652                                 args.ipmask = &((struct sockaddr_in*)addr->netmask)->sin_addr;
653
654                                 status = callback(&args);
655                                 if (status <= 0) {
656                                         break;
657                                 }
658                         }
659                 }
660
661                 break;
662         }
663
664         pcap_freealldevs(devs);
665
666         return status <= 0 ? status : 0;
667 }
668
669 static inline void set_addr(void *p, uint32_t addr)
670 {
671         struct sockaddr_in* sin = p;
672         sin->sin_family = AF_INET;
673         sin->sin_addr.s_addr = addr;
674 #ifdef NMRPFLASH_BSD
675         ((struct sockaddr*)p)->sa_len = sizeof(struct sockaddr_in);
676 #endif
677 }
678
679 #ifndef NMRPFLASH_WINDOWS
680 static bool set_interface_up(int fd, const char *intf, bool up)
681 {
682         struct ifreq ifr;
683         strncpy(ifr.ifr_name, intf, IFNAMSIZ);
684
685         if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
686                 perror("ioctl(SIOCGIFFLAGS)");
687                 return false;
688         }
689
690         if (!up) {
691                 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
692         } else {
693                 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
694         }
695
696         if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
697                 perror("ioctl(SIOCSIFFLAGS)");
698                 return false;
699         }
700
701         return true;
702 }
703
704 #endif
705
706 int ethsock_ip_add(struct ethsock *sock, uint32_t ipaddr, uint32_t ipmask, struct ethsock_ip_undo **undo)
707 {
708         if (undo && !(*undo = malloc(sizeof(struct ethsock_ip_undo)))) {
709                 perror("malloc");
710                 return -1;
711         }
712
713         int fd = socket(AF_INET, SOCK_DGRAM, 0);
714         if (!fd) {
715                 sock_perror("socket");
716                 return -1;
717         }
718
719         int ret = -1;
720 #ifndef NMRPFLASH_WINDOWS
721         // XXX: undo is non-zero only if we're adding an IP
722         bool add = undo;
723 #ifdef NMRPFLASH_LINUX
724         struct ifreq ifr;
725         strncpy(ifr.ifr_name, sock->intf, IFNAMSIZ);
726         // FIXME: automatically determine the next free alias
727         strcat(ifr.ifr_name, ":42");
728
729         if (add) {
730                 set_addr(&ifr.ifr_addr, ipaddr);
731                 if (ioctl(fd, SIOCSIFADDR, &ifr) != 0) {
732                         perror("ioctl(SIOSIFADDR)");
733                         goto out;
734                 }
735
736                 set_addr(&ifr.ifr_netmask, ipmask);
737                 if (ioctl(fd, SIOCSIFNETMASK, &ifr) != 0) {
738                         perror("ioctl(SIOCSIFNETMASK)");
739                         goto out;
740                 }
741
742                 (*undo)->ip[0] = ipaddr;
743                 (*undo)->ip[1] = ipmask;
744         }
745
746         if (!set_interface_up(fd, ifr.ifr_name, add ? true : false)) {
747                 goto out;
748         }
749 #else // NMRPFLASH_OSX (or any other BSD)
750         struct ifaliasreq ifra;
751         memset(&ifra, 0, sizeof(ifra));
752         strncpy(ifra.ifra_name, sock->intf, IFNAMSIZ);
753
754         set_addr(&ifra.ifra_addr, ipaddr);
755         set_addr(&ifra.ifra_mask, ipmask);
756         //set_addr(&ifra.ifra_broadaddr, (ipaddr & ipmask) | ~ipmask);
757
758         if (ioctl(fd, add ? SIOCAIFADDR : SIOCDIFADDR, &ifra) != 0) {
759                 perror(add ? "ioctl(SIOCAIFADDR)" : "ioctl(SIOCDIFADDR)");
760                 goto out;
761         }
762
763         if (add) {
764                 (*undo)->ip[0] = ipaddr;
765                 (*undo)->ip[1] = ipmask;
766                 set_interface_up(fd, ifra.ifra_name, true);
767         }
768
769 #endif
770 #else // NMRPFLASH_WINDOWS
771         struct sockaddr_in sin;
772         ULONG instance;
773
774         (*undo)->context = 0;
775
776         DWORD err = AddIPAddress(ipaddr, ipmask, sock->index, &(*undo)->context, &instance);
777         if (err != NO_ERROR && err != ERROR_DUP_DOMAINNAME && err != ERROR_OBJECT_ALREADY_EXISTS) {
778                 win_perror2("AddIPAddress", err);
779                 goto out;
780         }
781
782         set_addr(&sin, ipaddr);
783         time_t beg = time_monotonic();
784
785         /* Wait until the new IP has actually been added */
786
787         while (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
788                 if ((time_monotonic() - beg) >= 5) {
789                         fprintf(stderr, "Failed to bind after 5 seconds: ");
790                         sock_perror("bind");
791                         DeleteIPAddress((*undo)->context);
792                         goto out;
793                 }
794         }
795 #endif
796         ret = 0;
797
798 out:
799 #ifndef NMRPFLASH_WINDOWS
800         close(fd);
801 #else
802         closesocket(fd);
803 #endif
804         return ret;
805 }
806
807 int ethsock_ip_del(struct ethsock *sock, struct ethsock_ip_undo **undo)
808 {
809         if (!*undo) {
810                 return 0;
811         }
812
813         int ret;
814
815 #ifndef NMRPFLASH_WINDOWS
816         if ((*undo)->ip[0] != INADDR_NONE) {
817                 ret = ethsock_ip_add(sock, (*undo)->ip[0], (*undo)->ip[1], NULL);
818         } else {
819                 ret = 0;
820         }
821 #else
822         DeleteIPAddress((*undo)->context);
823         ret = 0;
824 #endif
825
826         free(*undo);
827         *undo = NULL;
828         return ret;
829 }