* Sync to trunk HEAD (r53298).
[reactos.git] / dll / win32 / dhcpcsvc / 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
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 void (*bootp_packet_handler)(struct interface_info *,
54 struct dhcp_packet *, int, unsigned int,
55 struct iaddr, struct hardware *);
56
57 /*
58 * Wait for packets to come in using poll(). When a packet comes in,
59 * call receive_packet to receive the packet and possibly strip hardware
60 * addressing information from it, and then call through the
61 * bootp_packet_handler hook to try to do something with it.
62 */
63 void
64 dispatch(void)
65 {
66 int count, to_msec, err;
67 struct protocol *l;
68 fd_set fds;
69 time_t howlong, cur_time;
70 struct timeval timeval;
71 HANDLE AdapterStateChangedEvent;
72
73 AdapterStateChangedEvent = StartAdapterDiscovery();
74 if (!AdapterStateChangedEvent)
75 return;
76
77 ApiLock();
78
79 do {
80 /*
81 * Call any expired timeouts, and then if there's still
82 * a timeout registered, time out the select call then.
83 */
84 time(&cur_time);
85
86 if (timeouts) {
87 struct timeout *t;
88
89 if (timeouts->when <= cur_time) {
90 t = timeouts;
91 timeouts = timeouts->next;
92 (*(t->func))(t->what);
93 t->next = free_timeouts;
94 free_timeouts = t;
95 continue;
96 }
97
98 /*
99 * Figure timeout in milliseconds, and check for
100 * potential overflow, so we can cram into an
101 * int for poll, while not polling with a
102 * negative timeout and blocking indefinitely.
103 */
104 howlong = timeouts->when - cur_time;
105 if (howlong > INT_MAX / 1000)
106 howlong = INT_MAX / 1000;
107 to_msec = howlong * 1000;
108
109 /* Set up the descriptors to be polled. */
110 FD_ZERO(&fds);
111
112 for (l = protocols; l; l = l->next)
113 FD_SET(l->fd, &fds);
114
115 /* Wait for a packet or a timeout... XXX */
116 timeval.tv_sec = to_msec / 1000;
117 timeval.tv_usec = to_msec % 1000;
118
119 ApiUnlock();
120
121 count = select(0, &fds, NULL, NULL, &timeval);
122
123 ApiLock();
124 }
125 else
126 {
127 ApiUnlock();
128 WaitForSingleObject(AdapterStateChangedEvent, INFINITE);
129 ApiLock();
130
131 continue;
132 }
133
134 DH_DbgPrint(MID_TRACE,("Select: %d\n", count));
135
136 /* Not likely to be transitory... */
137 if (count == SOCKET_ERROR) {
138 err = WSAGetLastError();
139 error("poll: %d", err);
140 break;
141 }
142
143 for (l = protocols; l; l = l->next) {
144 struct interface_info *ip;
145 ip = l->local;
146 if (FD_ISSET(l->fd, &fds)) {
147 if (ip && (l->handler != got_one ||
148 !ip->dead)) {
149 DH_DbgPrint(MID_TRACE,("Handling %x\n", l));
150 (*(l->handler))(l);
151 }
152 }
153 }
154 } while (1);
155
156 CloseHandle(AdapterStateChangedEvent);
157
158 ApiUnlock();
159 }
160
161 void
162 got_one(struct protocol *l)
163 {
164 struct sockaddr_in from;
165 struct hardware hfrom;
166 struct iaddr ifrom;
167 ssize_t result;
168 union {
169 /*
170 * Packet input buffer. Must be as large as largest
171 * possible MTU.
172 */
173 unsigned char packbuf[4095];
174 struct dhcp_packet packet;
175 } u;
176 struct interface_info *ip = l->local;
177 PDHCP_ADAPTER adapter;
178
179 if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
180 &hfrom)) == -1) {
181 warning("receive_packet failed on %s: %d", ip->name,
182 WSAGetLastError());
183 ip->errors++;
184 if (ip->errors > 20) {
185 /* our interface has gone away. */
186 warning("Interface %s no longer appears valid.",
187 ip->name);
188 ip->dead = 1;
189 closesocket(l->fd);
190 remove_protocol(l);
191 adapter = AdapterFindInfo(ip);
192 if (adapter) {
193 RemoveEntryList(&adapter->ListEntry);
194 free(adapter);
195 }
196 }
197 return;
198 }
199 if (result == 0)
200 return;
201
202 if (bootp_packet_handler) {
203 ifrom.len = 4;
204 memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
205
206
207 adapter = AdapterFindByHardwareAddress(u.packet.chaddr,
208 u.packet.hlen);
209
210 if (!adapter) {
211 warning("Discarding packet with a non-matching target physical address\n");
212 return;
213 }
214
215 (*bootp_packet_handler)(&adapter->DhclientInfo, &u.packet, result,
216 from.sin_port, ifrom, &hfrom);
217 }
218 }
219
220 void
221 add_timeout(time_t when, void (*where)(void *), void *what)
222 {
223 struct timeout *t, *q;
224
225 DH_DbgPrint(MID_TRACE,("Adding timeout %x %p %x\n", when, where, what));
226 /* See if this timeout supersedes an existing timeout. */
227 t = NULL;
228 for (q = timeouts; q; q = q->next) {
229 if (q->func == where && q->what == what) {
230 if (t)
231 t->next = q->next;
232 else
233 timeouts = q->next;
234 break;
235 }
236 t = q;
237 }
238
239 /* If we didn't supersede a timeout, allocate a timeout
240 structure now. */
241 if (!q) {
242 if (free_timeouts) {
243 q = free_timeouts;
244 free_timeouts = q->next;
245 q->func = where;
246 q->what = what;
247 } else {
248 q = malloc(sizeof(struct timeout));
249 if (!q) {
250 error("Can't allocate timeout structure!");
251 return;
252 }
253 q->func = where;
254 q->what = what;
255 }
256 }
257
258 q->when = when;
259
260 /* Now sort this timeout into the timeout list. */
261
262 /* Beginning of list? */
263 if (!timeouts || timeouts->when > q->when) {
264 q->next = timeouts;
265 timeouts = q;
266 return;
267 }
268
269 /* Middle of list? */
270 for (t = timeouts; t->next; t = t->next) {
271 if (t->next->when > q->when) {
272 q->next = t->next;
273 t->next = q;
274 return;
275 }
276 }
277
278 /* End of list. */
279 t->next = q;
280 q->next = NULL;
281 }
282
283 void
284 cancel_timeout(void (*where)(void *), void *what)
285 {
286 struct timeout *t, *q;
287
288 /* Look for this timeout on the list, and unlink it if we find it. */
289 t = NULL;
290 for (q = timeouts; q; q = q->next) {
291 if (q->func == where && q->what == what) {
292 if (t)
293 t->next = q->next;
294 else
295 timeouts = q->next;
296 break;
297 }
298 t = q;
299 }
300
301 /* If we found the timeout, put it on the free list. */
302 if (q) {
303 q->next = free_timeouts;
304 free_timeouts = q;
305 }
306 }
307
308 /* Add a protocol to the list of protocols... */
309 void
310 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
311 void *local)
312 {
313 struct protocol *p;
314
315 p = malloc(sizeof(*p));
316 if (!p)
317 error("can't allocate protocol struct for %s", name);
318
319 p->fd = fd;
320 p->handler = handler;
321 p->local = local;
322 p->next = protocols;
323 protocols = p;
324 }
325
326 void
327 remove_protocol(struct protocol *proto)
328 {
329 struct protocol *p, *next, *prev;
330
331 prev = NULL;
332 for (p = protocols; p; p = next) {
333 next = p->next;
334 if (p == proto) {
335 if (prev)
336 prev->next = p->next;
337 else
338 protocols = p->next;
339 free(p);
340 }
341 }
342 }
343
344 struct protocol *
345 find_protocol_by_adapter(struct interface_info *info)
346 {
347 struct protocol *p;
348
349 for( p = protocols; p; p = p->next ) {
350 if( p->local == (void *)info ) return p;
351 }
352
353 return NULL;
354 }
355
356 int
357 interface_link_status(char *ifname)
358 {
359 return (1);
360 }