Copy wininet to branch
[reactos.git] / reactos / subsys / system / dhcp / dispatch.c
1 /* $OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david Exp $ */
2
3 /*
4 * Copyright 2004 Henning Brauer <henning@openbsd.org>
5 * Copyright (c) 1995, 1996, 1997, 1998, 1999
6 * The Internet Software Consortium. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of The Internet Software Consortium nor the names
18 * of its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
22 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * This software has been written for the Internet Software Consortium
36 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
37 * Enterprises. To learn more about the Internet Software Consortium,
38 * see ``http://www.vix.com/isc''. To learn more about Vixie
39 * Enterprises, see ``http://www.vix.com''.
40 */
41
42 #include "rosdhcp.h"
43 #include "dhcpd.h"
44 //#include <sys/ioctl.h>
45
46 //#include <net/if_media.h>
47 //#include <ifaddrs.h>
48 //#include <poll.h>
49
50 struct protocol *protocols = NULL;
51 struct timeout *timeouts = NULL;
52 static struct timeout *free_timeouts = NULL;
53 static int interfaces_invalidated = FALSE;
54 void (*bootp_packet_handler)(struct interface_info *,
55 struct dhcp_packet *, int, unsigned int,
56 struct iaddr, struct hardware *);
57
58 static int interface_status(struct interface_info *ifinfo);
59
60 /*
61 * Use getifaddrs() to get a list of all the attached interfaces. For
62 * each interface that's of type INET and not the loopback interface,
63 * register that interface with the network I/O software, figure out
64 * what subnet it's on, and add it to the list of interfaces.
65 */
66 #if 0
67 void
68 discover_interfaces(struct interface_info *iface)
69 {
70 struct ifaddrs *ifap, *ifa;
71 struct sockaddr_in foo;
72 struct ifreq *tif;
73
74 if (getifaddrs(&ifap) != 0)
75 error("getifaddrs failed");
76
77 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
78 if ((ifa->ifa_flags & IFF_LOOPBACK) ||
79 (ifa->ifa_flags & IFF_POINTOPOINT) ||
80 (!(ifa->ifa_flags & IFF_UP)))
81 continue;
82
83
84 if (strcmp(iface->name, ifa->ifa_name))
85 continue;
86
87 /*
88 * If we have the capability, extract link information
89 * and record it in a linked list.
90 */
91 if (ifa->ifa_addr->sa_family == AF_LINK) {
92 struct sockaddr_dl *foo =
93 (struct sockaddr_dl *)ifa->ifa_addr;
94
95 iface->index = foo->sdl_index;
96 iface->hw_address.hlen = foo->sdl_alen;
97 iface->hw_address.htype = HTYPE_ETHER; /* XXX */
98 memcpy(iface->hw_address.haddr,
99 LLADDR(foo), foo->sdl_alen);
100 } else if (ifa->ifa_addr->sa_family == AF_INET) {
101 struct iaddr addr;
102
103 memcpy(&foo, ifa->ifa_addr, sizeof(foo));
104 if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK))
105 continue;
106 if (!iface->ifp) {
107 int len = IFNAMSIZ + ifa->ifa_addr->sa_len;
108 if ((tif = malloc(len)) == NULL)
109 error("no space to remember ifp");
110 strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
111 memcpy(&tif->ifr_addr, ifa->ifa_addr,
112 ifa->ifa_addr->sa_len);
113 iface->ifp = tif;
114 iface->primary_address = foo.sin_addr;
115 }
116 addr.len = 4;
117 memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);
118 }
119 }
120
121 if (!iface->ifp)
122 error("%s: not found", iface->name);
123
124 /* Register the interface... */
125 if_register_receive(iface);
126 if_register_send(iface);
127 add_protocol(iface->name, iface->rfdesc, got_one, iface);
128 freeifaddrs(ifap);
129 }
130 #else
131 void
132 discover_interfaces(struct interface_info *iface)
133 {
134 NTSTATUS Status;
135 ULONG dim;
136 char TmpName [IFNAMSIZ];
137
138 PIP_ADAPTER_INFO pAdapterInfo;
139 PIP_ADAPTER_INFO pAdapter = NULL;
140
141 pAdapterInfo = malloc(sizeof(IP_ADAPTER_INFO));
142 dim = sizeof(IP_ADAPTER_INFO);
143
144 if (GetAdaptersInfo( pAdapterInfo, &dim) != ERROR_SUCCESS) {
145 free(pAdapterInfo);
146 pAdapterInfo = (IP_ADAPTER_INFO *) malloc (dim);
147 }
148
149 if ((Status = GetAdaptersInfo( pAdapterInfo, &dim)) != NO_ERROR) {
150 note("Error %x", Status);
151 free (pAdapterInfo);
152 return;
153 }
154
155 for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) {
156 /* we only do ethernet */
157 note("found: %s %x\n", pAdapter->AdapterName, pAdapter->Address);
158 if (pAdapter->Type != MIB_IF_TYPE_ETHERNET) {
159 continue;
160 }
161 note ("found ethernet\n");
162
163 iface->hw_address.hlen = pAdapter->AddressLength;
164 iface->hw_address.htype = HTYPE_ETHER;
165 memcpy (&iface->hw_address.haddr[0],
166 pAdapter->Address, iface->hw_address.hlen);
167
168 if (pAdapter->IpAddressList.IpAddress.String)
169 iface->primary_address.S_un.S_addr = inet_addr(pAdapter->IpAddressList.IpAddress.String);
170
171 }
172 if (iface) {
173 if_register_receive(iface);
174 if_register_send(iface);
175 add_protocol(iface->name, iface->rfdesc, got_one, iface);
176 }
177 free (pAdapterInfo);
178 }
179 #endif
180
181 void
182 reinitialize_interfaces(void)
183 {
184 interfaces_invalidated = 1;
185 }
186
187 /*
188 * Wait for packets to come in using poll(). When a packet comes in,
189 * call receive_packet to receive the packet and possibly strip hardware
190 * addressing information from it, and then call through the
191 * bootp_packet_handler hook to try to do something with it.
192 */
193 void
194 dispatch(void)
195 {
196 int count, i, to_msec, nfds = 0;
197 struct protocol *l;
198 fd_set fds;
199 time_t howlong;
200 struct timeval timeval;
201
202 for (l = protocols; l; l = l->next)
203 nfds++;
204
205 FD_ZERO(&fds);
206
207 // fds = malloc(nfds * sizeof(struct pollfd));
208 // if (fds == NULL)
209 // error("Can't allocate poll structures.");
210
211 do {
212 DH_DbgPrint(MID_TRACE,("Cycling dispatch()\n"));
213 /*
214 * Call any expired timeouts, and then if there's still
215 * a timeout registered, time out the select call then.
216 */
217 another:
218 if (timeouts) {
219 DH_DbgPrint(MID_TRACE,("Some timeouts are available\n"));
220
221 struct timeout *t;
222
223 if (timeouts->when <= cur_time) {
224 DH_DbgPrint(MID_TRACE,("Calling timeout %x %p %x\n",
225 timeouts->when,
226 timeouts->func,
227 timeouts->what));
228 t = timeouts;
229 timeouts = timeouts->next;
230 (*(t->func))(t->what);
231 t->next = free_timeouts;
232 free_timeouts = t;
233 goto another;
234 }
235
236 /*
237 * Figure timeout in milliseconds, and check for
238 * potential overflow, so we can cram into an
239 * int for poll, while not polling with a
240 * negative timeout and blocking indefinitely.
241 */
242 howlong = timeouts->when - cur_time;
243 if (howlong > INT_MAX / 1000)
244 howlong = INT_MAX / 1000;
245 to_msec = howlong * 1000;
246 } else
247 to_msec = -1;
248
249 /* Set up the descriptors to be polled. */
250 for (i = 0, l = protocols; l; l = l->next) {
251 struct interface_info *ip = l->local;
252
253 if (ip && (l->handler != got_one || !ip->dead)) {
254 DH_DbgPrint(MID_TRACE,("l->fd %d\n", l->fd));
255
256 FD_SET(l->fd, &fds);
257 // fds[i].fd = l->fd;
258 // fds[i].events = POLLIN;
259 // fds[i].revents = 0;
260 i++;
261 }
262 }
263
264 if (i == 0)
265 error("No live interfaces to poll on - exiting.");
266
267 /* Wait for a packet or a timeout... XXX */
268 timeval.tv_sec = to_msec / 1000;
269 timeval.tv_usec = (to_msec % 1000) * 1000;
270 DH_DbgPrint(MID_TRACE,("select(%d,%d.%03d) =>\n",
271 nfds,timeval.tv_sec,timeval.tv_usec/1000));
272 count = select(nfds, &fds, NULL, NULL, &timeval);
273 DH_DbgPrint(MID_TRACE,(" => %d\n", count));
274
275 /* Not likely to be transitory... */
276 if (count == SOCKET_ERROR) {
277 if (errno == EAGAIN || errno == EINTR) {
278 time(&cur_time);
279 continue;
280 } else
281 error("poll: %m");
282 }
283
284 /* Get the current time... */
285 time(&cur_time);
286
287 i = 0;
288 for (l = protocols; l; l = l->next) {
289 struct interface_info *ip;
290 ip = l->local;
291 if (!FD_ISSET(l->fd, &fds)) {
292 //.revents & (POLLIN | POLLHUP))) {
293 // fds[i].revents = 0;
294 if (ip && (l->handler != got_one ||
295 !ip->dead)) {
296 DH_DbgPrint(MID_TRACE,("Handling %x\n", l));
297 (*(l->handler))(l);
298 if (interfaces_invalidated)
299 break;
300 }
301 i++;
302 }
303 interfaces_invalidated = 0;
304 }
305 DH_DbgPrint(MID_TRACE,("Done\n"));
306 } while (1);
307 }
308
309 void
310 got_one(struct protocol *l)
311 {
312 struct sockaddr_in from;
313 struct hardware hfrom;
314 struct iaddr ifrom;
315 ssize_t result;
316 union {
317 /*
318 * Packet input buffer. Must be as large as largest
319 * possible MTU.
320 */
321 unsigned char packbuf[4095];
322 struct dhcp_packet packet;
323 } u;
324 struct interface_info *ip = l->local;
325
326 if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
327 &hfrom)) == -1) {
328 warning("receive_packet failed on %s: %s", ip->name,
329 strerror(errno));
330 ip->errors++;
331 if ((!interface_status(ip)) ||
332 (ip->noifmedia && ip->errors > 20)) {
333 /* our interface has gone away. */
334 warning("Interface %s no longer appears valid.",
335 ip->name);
336 ip->dead = 1;
337 interfaces_invalidated = 1;
338 close(l->fd);
339 remove_protocol(l);
340 free(ip);
341 }
342 return;
343 }
344 if (result == 0)
345 return;
346
347 if (bootp_packet_handler) {
348 ifrom.len = 4;
349 memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
350
351 (*bootp_packet_handler)(ip, &u.packet, result,
352 from.sin_port, ifrom, &hfrom);
353 }
354 }
355
356 #if 0
357 int
358 interface_status(struct interface_info *ifinfo)
359 {
360 char *ifname = ifinfo->name;
361 int ifsock = ifinfo->rfdesc;
362 struct ifreq ifr;
363 struct ifmediareq ifmr;
364
365 /* get interface flags */
366 memset(&ifr, 0, sizeof(ifr));
367 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
368 if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
369 syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
370 goto inactive;
371 }
372
373 /*
374 * if one of UP and RUNNING flags is dropped,
375 * the interface is not active.
376 */
377 if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
378 goto inactive;
379
380 /* Next, check carrier on the interface, if possible */
381 if (ifinfo->noifmedia)
382 goto active;
383 memset(&ifmr, 0, sizeof(ifmr));
384 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
385 if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
386 if (errno != EINVAL) {
387 syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
388 ifname);
389
390 ifinfo->noifmedia = 1;
391 goto active;
392 }
393 /*
394 * EINVAL (or ENOTTY) simply means that the interface
395 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
396 */
397 ifinfo->noifmedia = 1;
398 goto active;
399 }
400 if (ifmr.ifm_status & IFM_AVALID) {
401 switch (ifmr.ifm_active & IFM_NMASK) {
402 case IFM_ETHER:
403 if (ifmr.ifm_status & IFM_ACTIVE)
404 goto active;
405 else
406 goto inactive;
407 break;
408 default:
409 goto inactive;
410 }
411 }
412 inactive:
413 return (0);
414 active:
415 return (1);
416 }
417 #else
418 int
419 interface_status(struct interface_info *ifinfo)
420 {
421 return (1);
422 }
423 #endif
424
425 void
426 add_timeout(time_t when, void (*where)(void *), void *what)
427 {
428 struct timeout *t, *q;
429
430 DH_DbgPrint(MID_TRACE,("Adding timeout %x %p %x\n", when, where, what));
431 /* See if this timeout supersedes an existing timeout. */
432 t = NULL;
433 for (q = timeouts; q; q = q->next) {
434 if (q->func == where && q->what == what) {
435 if (t)
436 t->next = q->next;
437 else
438 timeouts = q->next;
439 break;
440 }
441 t = q;
442 }
443
444 /* If we didn't supersede a timeout, allocate a timeout
445 structure now. */
446 if (!q) {
447 if (free_timeouts) {
448 q = free_timeouts;
449 free_timeouts = q->next;
450 q->func = where;
451 q->what = what;
452 } else {
453 q = malloc(sizeof(struct timeout));
454 if (!q)
455 error("Can't allocate timeout structure!");
456 q->func = where;
457 q->what = what;
458 }
459 }
460
461 q->when = when;
462
463 /* Now sort this timeout into the timeout list. */
464
465 /* Beginning of list? */
466 if (!timeouts || timeouts->when > q->when) {
467 q->next = timeouts;
468 timeouts = q;
469 return;
470 }
471
472 /* Middle of list? */
473 for (t = timeouts; t->next; t = t->next) {
474 if (t->next->when > q->when) {
475 q->next = t->next;
476 t->next = q;
477 return;
478 }
479 }
480
481 /* End of list. */
482 t->next = q;
483 q->next = NULL;
484 }
485
486 void
487 cancel_timeout(void (*where)(void *), void *what)
488 {
489 struct timeout *t, *q;
490
491 /* Look for this timeout on the list, and unlink it if we find it. */
492 t = NULL;
493 for (q = timeouts; q; q = q->next) {
494 if (q->func == where && q->what == what) {
495 if (t)
496 t->next = q->next;
497 else
498 timeouts = q->next;
499 break;
500 }
501 t = q;
502 }
503
504 /* If we found the timeout, put it on the free list. */
505 if (q) {
506 q->next = free_timeouts;
507 free_timeouts = q;
508 }
509 }
510
511 /* Add a protocol to the list of protocols... */
512 void
513 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
514 void *local)
515 {
516 struct protocol *p;
517
518 p = malloc(sizeof(*p));
519 if (!p)
520 error("can't allocate protocol struct for %s", name);
521
522 p->fd = fd;
523 p->handler = handler;
524 p->local = local;
525 p->next = protocols;
526 protocols = p;
527 }
528
529 void
530 remove_protocol(struct protocol *proto)
531 {
532 struct protocol *p, *next, *prev;
533
534 prev = NULL;
535 for (p = protocols; p; p = next) {
536 next = p->next;
537 if (p == proto) {
538 if (prev)
539 prev->next = p->next;
540 else
541 protocols = p->next;
542 free(p);
543 }
544 }
545 }
546
547 int
548 interface_link_status(char *ifname)
549 {
550 #if 0
551 struct ifmediareq ifmr;
552 int sock;
553
554 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
555 error("Can't create socket");
556
557 memset(&ifmr, 0, sizeof(ifmr));
558 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
559 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
560 /* EINVAL -> link state unknown. treat as active */
561 if (errno != EINVAL)
562 syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
563 ifname);
564 close(sock);
565 return (1);
566 }
567 close(sock);
568
569 if (ifmr.ifm_status & IFM_AVALID) {
570 if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) {
571 if (ifmr.ifm_status & IFM_ACTIVE)
572 return (1);
573 else
574 return (0);
575 }
576 }
577 #endif
578 return (1);
579 }