xref: /reactos/base/services/dhcpcsvc/dhcp/dispatch.c (revision fb5d5ecd)
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 
44 //#include <sys/ioctl.h>
45 
46 //#include <net/if_media.h>
47 //#include <ifaddrs.h>
48 //#include <poll.h>
49 
50 extern SOCKET DhcpSocket;
51 HANDLE AdapterStateChangedEvent = NULL;
52 struct protocol *protocols = NULL;
53 struct timeout *timeouts = NULL;
54 static struct timeout *free_timeouts = NULL;
55 void (*bootp_packet_handler)(struct interface_info *,
56                              struct dhcp_packet *, int, unsigned int,
57                              struct iaddr, struct hardware *);
58 
59 /*
60  * Wait for packets to come in using poll().  When a packet comes in,
61  * call receive_packet to receive the packet and possibly strip hardware
62  * addressing information from it, and then call through the
63  * bootp_packet_handler hook to try to do something with it.
64  */
65 void
66 dispatch(HANDLE hStopEvent)
67 {
68     int count, to_msec;
69     struct protocol *l;
70     time_t howlong, cur_time;
71     HANDLE Events[3];
72     int EventCount = 2;
73 
74     Events[0] = StartAdapterDiscovery();
75     if (!Events[0])
76          return;
77 
78     AdapterStateChangedEvent = Events[0];
79 
80     Events[1] = hStopEvent;
81     Events[2] = WSA_INVALID_EVENT;
82 
83     ApiLock();
84 
85     do {
86         /*
87          * Call any expired timeouts, and then if there's still
88          * a timeout registered, time out the select call then.
89          */
90         time(&cur_time);
91 
92         if (timeouts)
93         {
94             struct timeout *t;
95 
96             if (timeouts->when <= cur_time) {
97                 t = timeouts;
98                 timeouts = timeouts->next;
99                 (*(t->func))(t->what);
100                 t->next = free_timeouts;
101                 free_timeouts = t;
102                 continue;
103             }
104 
105             /*
106              * Figure timeout in milliseconds, and check for
107              * potential overflow, so we can cram into an
108              * int for poll, while not polling with a
109              * negative timeout and blocking indefinitely.
110              */
111             howlong = timeouts->when - cur_time;
112             if (howlong > INT_MAX / 1000)
113                 howlong = INT_MAX / 1000;
114             to_msec = howlong * 1000;
115         }
116         else
117         {
118             to_msec = INFINITE;
119         }
120 
121         if (Events[2] == WSA_INVALID_EVENT && DhcpSocket != INVALID_SOCKET)
122         {
123             Events[2] = WSACreateEvent();
124             if (Events[2] != WSA_INVALID_EVENT)
125             {
126                 count = WSAEventSelect(DhcpSocket, Events[2], FD_READ | FD_CLOSE);
127                 if (count != NO_ERROR)
128                 {
129                     WSACloseEvent(Events[2]);
130                     Events[2] = WSA_INVALID_EVENT;
131                 }
132                 else
133                 {
134                     EventCount = 3;
135                 }
136             }
137         }
138         else if (Events[2] != WSA_INVALID_EVENT && DhcpSocket == INVALID_SOCKET)
139         {
140             WSACloseEvent(Events[2]);
141             Events[2] = WSA_INVALID_EVENT;
142 
143             EventCount = 2;
144         }
145 
146         ApiUnlock();
147         count = WaitForMultipleObjects(EventCount,
148                                        Events,
149                                        FALSE,
150                                        to_msec);
151         ApiLock();
152         if (count == WAIT_OBJECT_0)
153         {
154             /* Adapter state change */
155             continue;
156         }
157         else if (count == WAIT_OBJECT_0 + 1)
158         {
159             /* Stop event signalled */
160             break;
161         }
162         else if (count == WAIT_OBJECT_0 + 2)
163         {
164             /* Packet received */
165 
166             /* WSA events are manual reset events */
167             WSAResetEvent(Events[2]);
168         }
169         else
170         {
171             /* Timeout */
172             continue;
173         }
174 
175         for (l = protocols; l; l = l->next) {
176             struct interface_info *ip;
177             ip = l->local;
178             if (ip && (l->handler != got_one ||
179                         !ip->dead)) {
180                 DH_DbgPrint(MID_TRACE,("Handling %x\n", l));
181                 (*(l->handler))(l);
182             }
183         }
184     } while (1);
185 
186     AdapterStateChangedEvent = NULL;
187     CloseHandle(Events[0]);
188     CloseHandle(Events[1]);
189     WSACloseEvent(Events[2]);
190 
191     ApiUnlock();
192 }
193 
194 void
195 got_one(struct protocol *l)
196 {
197     struct sockaddr_in from;
198     struct hardware hfrom;
199     struct iaddr ifrom;
200     ssize_t result;
201     union {
202         /*
203          * Packet input buffer.  Must be as large as largest
204          * possible MTU.
205          */
206         unsigned char packbuf[4095];
207         struct dhcp_packet packet;
208     } u;
209     struct interface_info *ip = l->local;
210     PDHCP_ADAPTER adapter;
211 
212     if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
213                                  &hfrom)) == -1) {
214         warning("receive_packet failed on %s: %d", ip->name,
215                 WSAGetLastError());
216         ip->errors++;
217         if (ip->errors > 20) {
218             /* our interface has gone away. */
219             warning("Interface %s no longer appears valid.",
220                     ip->name);
221             ip->dead = 1;
222             closesocket(l->fd);
223             remove_protocol(l);
224             adapter = AdapterFindInfo(ip);
225             if (adapter) {
226                 RemoveEntryList(&adapter->ListEntry);
227                 free(adapter);
228             }
229         }
230         return;
231     }
232     if (result == 0)
233         return;
234 
235     if (bootp_packet_handler) {
236         ifrom.len = 4;
237         memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
238 
239 
240         adapter = AdapterFindByHardwareAddress(u.packet.chaddr,
241                                                u.packet.hlen);
242 
243         if (!adapter) {
244             warning("Discarding packet with a non-matching target physical address\n");
245             return;
246         }
247 
248         (*bootp_packet_handler)(&adapter->DhclientInfo, &u.packet, result,
249                                 from.sin_port, ifrom, &hfrom);
250     }
251 }
252 
253 void
254 add_timeout(time_t when, void (*where)(void *), void *what)
255 {
256     struct timeout *t, *q;
257 
258     DH_DbgPrint(MID_TRACE,("Adding timeout %x %p %x\n", when, where, what));
259     /* See if this timeout supersedes an existing timeout. */
260     t = NULL;
261     for (q = timeouts; q; q = q->next) {
262         if (q->func == where && q->what == what) {
263             if (t)
264                 t->next = q->next;
265             else
266                 timeouts = q->next;
267             break;
268         }
269         t = q;
270     }
271 
272     /* If we didn't supersede a timeout, allocate a timeout
273        structure now. */
274     if (!q) {
275         if (free_timeouts) {
276             q = free_timeouts;
277             free_timeouts = q->next;
278             q->func = where;
279             q->what = what;
280         } else {
281             q = malloc(sizeof(struct timeout));
282             if (!q) {
283                 error("Can't allocate timeout structure!");
284                 return;
285             }
286             q->func = where;
287             q->what = what;
288         }
289     }
290 
291     q->when = when;
292 
293     /* Now sort this timeout into the timeout list. */
294 
295     /* Beginning of list? */
296     if (!timeouts || timeouts->when > q->when) {
297         q->next = timeouts;
298         timeouts = q;
299         return;
300     }
301 
302     /* Middle of list? */
303     for (t = timeouts; t->next; t = t->next) {
304         if (t->next->when > q->when) {
305             q->next = t->next;
306             t->next = q;
307             return;
308         }
309     }
310 
311     /* End of list. */
312     t->next = q;
313     q->next = NULL;
314 }
315 
316 void
317 cancel_timeout(void (*where)(void *), void *what)
318 {
319     struct timeout *t, *q;
320 
321     /* Look for this timeout on the list, and unlink it if we find it. */
322     t = NULL;
323     for (q = timeouts; q; q = q->next) {
324         if (q->func == where && q->what == what) {
325             if (t)
326                 t->next = q->next;
327             else
328                 timeouts = q->next;
329             break;
330         }
331         t = q;
332     }
333 
334     /* If we found the timeout, put it on the free list. */
335     if (q) {
336         q->next = free_timeouts;
337         free_timeouts = q;
338     }
339 }
340 
341 /* Add a protocol to the list of protocols... */
342 void
343 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
344              void *local)
345 {
346     struct protocol *p;
347 
348     p = malloc(sizeof(*p));
349     if (!p)
350         error("can't allocate protocol struct for %s", name);
351 
352     p->fd = fd;
353     p->handler = handler;
354     p->local = local;
355     p->next = protocols;
356     protocols = p;
357 }
358 
359 void
360 remove_protocol(struct protocol *proto)
361 {
362     struct protocol *p, *next, *prev;
363     struct interface_info *ip = proto->local;
364     struct timeout *t, *q, *u;
365 
366     t = NULL;
367     q = timeouts;
368     while (q != NULL)
369     {
370         /* Remove all timeouts for this protocol */
371         if (q->what == ip)
372         {
373             /* Unlink the timeout from previous */
374             if (t)
375                 t->next = q->next;
376             else
377                 timeouts = q->next;
378 
379             /* Advance to the next timeout */
380             u = q->next;
381 
382             /* Add it to the free list */
383             q->next = free_timeouts;
384             free_timeouts = q;
385         }
386         else
387         {
388             /* Advance to the next timeout */
389             u = q->next;
390 
391             /* Update the previous pointer */
392             t = q;
393         }
394 
395         /* Advance */
396         q = u;
397     }
398 
399     prev = NULL;
400     for (p = protocols; p; p = next) {
401         next = p->next;
402         if (p == proto) {
403             if (prev)
404                 prev->next = p->next;
405             else
406                 protocols = p->next;
407             free(p);
408         }
409     }
410 }
411 
412 struct protocol *
413 find_protocol_by_adapter(struct interface_info *info)
414 {
415     struct protocol *p;
416 
417     for( p = protocols; p; p = p->next ) {
418         if( p->local == (void *)info ) return p;
419     }
420 
421     return NULL;
422 }
423 
424 int
425 interface_link_status(char *ifname)
426 {
427     return (1);
428 }
429