1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26 #include "private-lib-system-dhcpclient.h"
27
28 typedef enum {
29 LDHC_INIT_REBOOT,
30 LDHC_REBOOTING, /* jitterwait */
31 LDHC_INIT, /* issue DHCPDISCOVER */
32 LDHC_SELECTING,
33 LDHC_REQUESTING,
34 LDHC_REBINDING,
35 LDHC_BOUND,
36 LDHC_RENEWING
37 } lws_dhcpc_state_t;
38
39 enum {
40 LWSDHCPDISCOVER = 1,
41 LWSDHCPOFFER,
42 LWSDHCPREQUEST,
43 LWSDHCPDECLINE,
44 LWSDHCPACK,
45 LWSDHCPNACK,
46 LWSDHCPRELEASE,
47
48 IPV4_PROPOSED = 0,
49 IPV4_SERVER,
50 IPV4_ROUTER,
51 IPV4_SUBNET_MASK,
52 IPV4_BROADCAST,
53 IPV4_TIME_SERVER,
54 IPV4_DNS_SRV_1,
55 IPV4_DNS_SRV_2,
56 IPV4_DNS_SRV_3,
57 IPV4_DNS_SRV_4,
58 IPV4_LEASE_SECS,
59 IPV4_REBINDING_SECS,
60 IPV4_RENEWAL_SECS,
61
62 _IPV4_COUNT,
63
64 LWSDHCPOPT_PAD = 0,
65 LWSDHCPOPT_SUBNET_MASK = 1,
66 LWSDHCPOPT_TIME_OFFSET = 2,
67 LWSDHCPOPT_ROUTER = 3,
68 LWSDHCPOPT_TIME_SERVER = 4,
69 LWSDHCPOPT_NAME_SERVER = 5,
70 LWSDHCPOPT_DNSERVER = 6,
71 LWSDHCPOPT_LOG_SERVER = 7,
72 LWSDHCPOPT_COOKIE_SERVER = 8,
73 LWSDHCPOPT_LPR_SERVER = 9,
74 LWSDHCPOPT_IMPRESS_SERVER = 10,
75 LWSDHCPOPT_RESLOC_SERVER = 11,
76 LWSDHCPOPT_HOST_NAME = 12,
77 LWSDHCPOPT_BOOTFILE_SIZE = 13,
78 LWSDHCPOPT_MERIT_DUMP_FILE = 14,
79 LWSDHCPOPT_DOMAIN_NAME = 15,
80 LWSDHCPOPT_SWAP_SERVER = 16,
81 LWSDHCPOPT_ROOT_PATH = 17,
82 LWSDHCPOPT_EXTENSIONS_PATH = 18,
83 LWSDHCPOPT_BROADCAST_ADS = 28,
84
85 LWSDHCPOPT_REQUESTED_ADS = 50,
86 LWSDHCPOPT_LEASE_TIME = 51,
87 LWSDHCPOPT_OPTION_OVERLOAD = 52,
88 LWSDHCPOPT_MESSAGE_TYPE = 53,
89 LWSDHCPOPT_SERVER_ID = 54,
90 LWSDHCPOPT_PARAM_REQ_LIST = 55,
91 LWSDHCPOPT_MESSAGE = 56,
92 LWSDHCPOPT_MAX_DHCP_MSG_SIZE = 57,
93 LWSDHCPOPT_RENEWAL_TIME = 58, /* AKA T1 */
94 LWSDHCPOPT_REBINDING_TIME = 59, /* AKA T2 */
95 LWSDHCPOPT_VENDOR_CLASS_ID = 60,
96 LWSDHCPOPT_CLIENT_ID = 61,
97
98 LWSDHCPOPT_END_OPTIONS = 255
99 };
100
101 typedef struct lws_dhcpc_req {
102 lws_dll2_t list;
103 char domain[64];
104 struct lws_context *context;
105 lws_sorted_usec_list_t sul_conn;
106 lws_sorted_usec_list_t sul_write;
107 dhcpc_cb_t cb; /* cb on completion / failure */
108 void *opaque; /* ignored by lws, give to cb */
109
110 /* these are separated so we can close the bcast one asynchronously */
111 struct lws *wsi_raw; /* for broadcast */
112 lws_dhcpc_state_t state;
113
114 uint32_t ipv4[_IPV4_COUNT];
115
116 uint16_t retry_count_conn;
117 uint16_t retry_count_write;
118 uint8_t mac[6];
119 uint8_t xid[4];
120 uint8_t af; /* address family */
121 } lws_dhcpc_req_t;
122 /* interface name is overallocated here */
123
124 static const uint32_t botable2[] = { 1500, 1750, 5000 /* in case dog slow */ };
125 static const lws_retry_bo_t bo2 = {
126 botable2, LWS_ARRAY_SIZE(botable2), LWS_RETRY_CONCEAL_ALWAYS, 0, 0, 20 };
127
128 static const uint8_t rawdisc[] = {
129 0x45, 0x00, 0, 0, 0, 0, 0x40, 0, 0x2e, IPPROTO_UDP,
130 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff,
131 0, 68, 0, 67, 0, 0, 0, 0
132 };
133
134 #define LDHC_OP_BOOTREQUEST 1
135 #define LDHC_OP_BOOTREPLY 2
136
137 /*
138 * IPv4... max total 576
139 *
140 * 0 1 2 3
141 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
142 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
143 * | op (1) | htype (1) | hlen (1) | hops (1) |
144 * +---------------+---------------+---------------+---------------+
145 * | +04 xid (4) |
146 * +-------------------------------+-------------------------------+
147 * | +08 secs (2) | +0a flags (2) |
148 * +-------------------------------+-------------------------------+
149 * | +0C ciaddr (4) client IP |
150 * +---------------------------------------------------------------+
151 * | +10 yiaddr (4) your IP |
152 * +---------------------------------------------------------------+
153 * | +14 siaddr (4) server IP |
154 * +---------------------------------------------------------------+
155 * | +18 giaddr (4) gateway IP |
156 * +---------------------------------------------------------------+
157 * | |
158 * | +1C chaddr (16) client HWADDR |
159 * +---------------------------------------------------------------+
160 * | |
161 * | +2C sname (64) |
162 * +---------------------------------------------------------------+
163 * | |
164 * | +6C file (128) |
165 * +---------------------------------------------------------------+
166 * | |
167 * | +EC options (variable) |
168 * +---------------------------------------------------------------+
169 */
170
171 static const char *dhcp_entry_names[] = {
172 "proposed ip",
173 "dhcp server",
174 "router",
175 "subnet mask",
176 "broadcast",
177 "time server",
178 "dns1",
179 "dns2",
180 "dns3",
181 "dns4",
182 "lease secs",
183 "rebinding secs",
184 "renewal secs",
185 };
186
187 static void
lws_dhcpc_retry_conn(struct lws_sorted_usec_list * sul)188 lws_dhcpc_retry_conn(struct lws_sorted_usec_list *sul)
189 {
190 lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_conn);
191
192 if (r->wsi_raw || !lws_dll2_is_detached(&r->sul_conn.list))
193 return;
194
195 /* create the UDP socket aimed at the server */
196
197 r->retry_count_write = 0;
198 r->wsi_raw = lws_create_adopt_udp(r->context->vhost_system, "0.0.0.0",
199 68, LWS_CAUDP_PF_PACKET |
200 LWS_CAUDP_BROADCAST,
201 "lws-dhcpclient", (const char *)&r[1],
202 NULL, NULL, &bo2);
203 lwsl_debug("%s: created wsi_raw: %p\n", __func__, r->wsi_raw);
204 if (!r->wsi_raw) {
205 lwsl_err("%s: unable to create udp skt\n", __func__);
206
207 lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2,
208 lws_dhcpc_retry_conn,
209 &r->retry_count_conn);
210
211 return;
212 }
213
214 /* force the network if up */
215 lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 0);
216 lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 1);
217
218 r->wsi_raw->user_space = r;
219 r->wsi_raw->user_space_externally_allocated = 1;
220
221 lws_get_random(r->wsi_raw->context, r->xid, 4);
222 }
223
224 static void
lws_dhcpc_retry_write(struct lws_sorted_usec_list * sul)225 lws_dhcpc_retry_write(struct lws_sorted_usec_list *sul)
226 {
227 lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_write);
228
229 lwsl_debug("%s\n", __func__);
230
231 if (r && r->wsi_raw)
232 lws_callback_on_writable(r->wsi_raw);
233 }
234
235 #if 0
236 static int
237 lws_sys_dhcpc_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *l,
238 int current, int target)
239 {
240 lws_dhcpc_req_t *r = lws_container_of(l, lws_dhcpc_req_t, notify_link);
241
242 if (target != LWS_SYSTATE_TIME_VALID || v->set_time)
243 return 0;
244
245 /* it's trying to do it ever since the protocol / vhost was set up */
246
247 return 1;
248 }
249 #endif
250
251 static int
lws_dhcpc_prep(uint8_t * start,int bufsiz,lws_dhcpc_req_t * r,int op)252 lws_dhcpc_prep(uint8_t *start, int bufsiz, lws_dhcpc_req_t *r, int op)
253 {
254 uint8_t *p = start;
255
256 memset(start, 0, bufsiz);
257
258 *p++ = 1;
259 *p++ = 1;
260 *p++ = 6; /* sizeof ethernet MAC */
261
262 memcpy(p + 1, r->xid, 4);
263
264 // p[7] = 0x80; /* broadcast flag */
265
266 p += 0x1c - 3;
267
268 if (lws_plat_ifname_to_hwaddr(r->wsi_raw->desc.sockfd,
269 (const char *)&r[1], r->mac, 6) < 0)
270 return -1;
271
272 memcpy(p, r->mac, 6);
273
274 p += 16 + 64 + 128;
275
276 *p++ = 0x63; /* RFC2132 Magic Cookie indicates start of options */
277 *p++ = 0x82;
278 *p++ = 0x53;
279 *p++ = 0x63;
280
281 *p++ = LWSDHCPOPT_MESSAGE_TYPE;
282 *p++ = 1; /* length */
283 *p++ = op;
284
285 switch (op) {
286 case LWSDHCPDISCOVER:
287 *p++ = LWSDHCPOPT_PARAM_REQ_LIST;
288 *p++ = 4; /* length */
289 *p++ = 1; /* subnet mask */
290 *p++ = 3; /* router */
291 *p++ = 15; /* domain name */
292 *p++ = 6; /* DNServer */
293 break;
294 case LWSDHCPREQUEST:
295 *p++ = LWSDHCPOPT_REQUESTED_ADS;
296 *p++ = 4; /* length */
297 lws_ser_wu32be(p, r->ipv4[IPV4_PROPOSED]);
298 p += 4;
299 *p++ = LWSDHCPOPT_SERVER_ID;
300 *p++ = 4; /* length */
301 lws_ser_wu32be(p, r->ipv4[IPV4_SERVER]);
302 p += 4;
303 break;
304 }
305
306 *p++ = LWSDHCPOPT_END_OPTIONS;
307
308 return lws_ptr_diff(p, start);
309 }
310
311 static int
callback_dhcpc(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)312 callback_dhcpc(struct lws *wsi, enum lws_callback_reasons reason, void *user,
313 void *in, size_t len)
314 {
315 lws_dhcpc_req_t *r = (lws_dhcpc_req_t *)user;
316 uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE, *end;
317 int n, m;
318
319 switch (reason) {
320
321 case LWS_CALLBACK_RAW_ADOPT:
322 lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__);
323 lws_callback_on_writable(wsi);
324 break;
325
326 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
327 lwsl_err("%s: udp conn failed\n", __func__);
328
329 /* fallthru */
330 case LWS_CALLBACK_RAW_CLOSE:
331 lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__);
332 if (!r)
333 break;
334 r->wsi_raw = NULL;
335 lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
336 LWS_SET_TIMER_USEC_CANCEL);
337 if (r->state != LDHC_BOUND) {
338 r->state = LDHC_INIT;
339 lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2,
340 lws_dhcpc_retry_conn,
341 &r->retry_count_conn);
342 }
343 break;
344
345 case LWS_CALLBACK_RAW_RX:
346
347 switch (r->state) {
348 case LDHC_INIT: /* expect DHCPOFFER */
349 case LDHC_REQUESTING: /* expect DHCPACK */
350 /*
351 * We should check carefully if we like what we were
352 * sent... anything can spam us with crafted replies
353 */
354 if (len < 0x100)
355 break;
356
357 p = (uint8_t *)in + 28; /* skip to UDP payload */
358 if (p[0] != 2 || p[1] != 1 || p[2] != 6)
359 break;
360
361 if (memcmp(&p[4], r->xid, 4)) /* must be our xid */
362 break;
363
364 if (memcmp(&p[0x1c], r->mac, 6)) /* our netif mac? */
365 break;
366
367 /* the DHCP magic cookie must be in place */
368 if (lws_ser_ru32be(&p[0xec]) != 0x63825363)
369 break;
370
371 r->ipv4[IPV4_PROPOSED] = lws_ser_ru32be(&p[0x10]);
372 r->ipv4[IPV4_SERVER] = lws_ser_ru32be(&p[0x14]);
373
374 /* it looks legit so far... look at the options */
375
376 end = (uint8_t *)in + len;
377 p += 0xec + 4;
378 while (p < end) {
379 uint8_t c = *p++;
380 uint8_t l = 0;
381
382 if (c && c != 0xff) {
383 /* pad 0 and EOT 0xff have no length */
384 l = *p++;
385 if (!l) {
386 lwsl_err("%s: zero length\n",
387 __func__);
388 goto broken;
389 }
390 if (p + l > end) {
391 /* ...nice try... */
392 lwsl_err("%s: bad len\n",
393 __func__);
394 goto broken;
395 }
396 }
397
398 if (c == 0xff) /* end of options */
399 break;
400
401 m = 0;
402 switch (c) {
403 case LWSDHCPOPT_SUBNET_MASK:
404 n = IPV4_SUBNET_MASK;
405 goto get_ipv4;
406
407 case LWSDHCPOPT_ROUTER:
408 n = IPV4_ROUTER;
409 goto get_ipv4;
410
411 case LWSDHCPOPT_TIME_SERVER:
412 n = IPV4_TIME_SERVER;
413 goto get_ipv4;
414
415 case LWSDHCPOPT_BROADCAST_ADS:
416 n = IPV4_BROADCAST;
417 goto get_ipv4;
418
419 case LWSDHCPOPT_LEASE_TIME:
420 n = IPV4_LEASE_SECS;
421 goto get_ipv4;
422
423 case LWSDHCPOPT_RENEWAL_TIME: /* AKA T1 */
424 n = IPV4_RENEWAL_SECS;
425 goto get_ipv4;
426
427 case LWSDHCPOPT_REBINDING_TIME: /* AKA T2 */
428 n = IPV4_REBINDING_SECS;
429 goto get_ipv4;
430
431 case LWSDHCPOPT_DNSERVER:
432 if (l & 3)
433 break;
434 m = IPV4_DNS_SRV_1;
435 while (l && m - IPV4_DNS_SRV_1 < 4) {
436 r->ipv4[m++] = lws_ser_ru32be(p);
437 l -= 4;
438 p += 4;
439 }
440 break;
441 case LWSDHCPOPT_DOMAIN_NAME:
442 m = l;
443 if (m > (int)sizeof(r->domain) - 1)
444 m = sizeof(r->domain) - 1;
445 memcpy(r->domain, p, m);
446 r->domain[m] = '\0';
447 break;
448
449 case LWSDHCPOPT_MESSAGE_TYPE:
450 /*
451 * Confirm this is the right message
452 * for the state of the negotiation
453 */
454 if (r->state == LDHC_INIT &&
455 *p != LWSDHCPOFFER)
456 goto broken;
457 if (r->state == LDHC_REQUESTING &&
458 *p != LWSDHCPACK)
459 goto broken;
460 break;
461
462 default:
463 break;
464 }
465
466 p += l;
467 continue;
468 get_ipv4:
469 if (l >= 4)
470 r->ipv4[n] = lws_ser_ru32be(p);
471 p += l;
472 continue;
473 broken:
474 memset(r->ipv4, 0, sizeof(r->ipv4));
475 break;
476 }
477
478 #if defined(_DEBUG)
479 /* dump what we have parsed out */
480
481 for (n = 0; n < (int)LWS_ARRAY_SIZE(dhcp_entry_names); n++)
482 if (n >= IPV4_LEASE_SECS)
483 lwsl_info("%s: %s: %ds\n", __func__,
484 dhcp_entry_names[n],
485 r->ipv4[n]);
486 else {
487 m = ntohl(r->ipv4[n]);
488 lws_write_numeric_address((uint8_t *)&m,
489 4,(char *)pkt, 20);
490 lwsl_info("%s: %s: %s\n", __func__,
491 dhcp_entry_names[n],
492 pkt);
493 }
494 #endif
495
496 /*
497 * Having seen everything in there... do we really feel
498 * we could use it? Everything critical is there?
499 */
500
501 if (!r->ipv4[IPV4_PROPOSED] ||
502 !r->ipv4[IPV4_SERVER] ||
503 !r->ipv4[IPV4_ROUTER] ||
504 !r->ipv4[IPV4_SUBNET_MASK] ||
505 !r->ipv4[IPV4_LEASE_SECS] ||
506 !r->ipv4[IPV4_DNS_SRV_1]) {
507 memset(r->ipv4, 0, sizeof(r->ipv4));
508 break;
509 }
510
511 /*
512 * Network layout has to be internally consistent...
513 * DHCP server has to be reachable by broadcast and
514 * default route has to be on same subnet
515 */
516
517 if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) !=
518 (r->ipv4[IPV4_SERVER] & r->ipv4[IPV4_SUBNET_MASK]))
519 break;
520
521 if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) !=
522 (r->ipv4[IPV4_ROUTER] & r->ipv4[IPV4_SUBNET_MASK]))
523 break;
524
525 if (r->state == LDHC_INIT) {
526 lwsl_info("%s: moving to REQ\n", __func__);
527 r->state = LDHC_REQUESTING;
528 lws_callback_on_writable(r->wsi_raw);
529 break;
530 }
531
532 /*
533 * that's it... commit to the configuration
534 */
535
536 /* set up our network interface as offered */
537
538 if (lws_plat_ifconfig_ip((const char *)&r[1],
539 r->wsi_raw->desc.sockfd,
540 (uint8_t *)&r->ipv4[IPV4_PROPOSED],
541 (uint8_t *)&r->ipv4[IPV4_SUBNET_MASK],
542 (uint8_t *)&r->ipv4[IPV4_ROUTER])) {
543 /*
544 * Problem setting the IP... maybe something
545 * transient like racing with NetworkManager?
546 * Since the sul retries are still around it
547 * will retry
548 */
549 return -1;
550 }
551
552 /* clear timeouts related to the broadcast socket */
553
554 lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
555 LWS_SET_TIMER_USEC_CANCEL);
556 lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
557 LWS_SET_TIMER_USEC_CANCEL);
558
559 lwsl_notice("%s: DHCP configured %s\n", __func__,
560 (const char *)&r[1]);
561 r->state = LDHC_BOUND;
562
563 lws_state_transition_steps(&wsi->context->mgr_system,
564 LWS_SYSTATE_OPERATIONAL);
565
566 r->cb(r->opaque, r->af,
567 (uint8_t *)&r->ipv4[IPV4_PROPOSED], 4);
568
569 r->wsi_raw = NULL;
570 return -1; /* close the broadcast wsi */
571 default:
572 break;
573 }
574
575 break;
576
577 case LWS_CALLBACK_RAW_WRITEABLE:
578
579 if (!r)
580 break;
581
582 /*
583 * UDP is not reliable, it can be locally dropped, or dropped
584 * by any intermediary or the remote peer. So even though we
585 * will do the write in a moment, we schedule another request
586 * for rewrite according to the wsi retry policy.
587 *
588 * If the result came before, we'll cancel it in the close flow.
589 *
590 * If we have already reached the end of our concealed retries
591 * in the policy, just close without another write.
592 */
593 if (lws_dll2_is_detached(&r->sul_write.list) &&
594 lws_retry_sul_schedule_retry_wsi(wsi, &r->sul_write,
595 lws_dhcpc_retry_write,
596 &r->retry_count_write)) {
597 /* we have reached the end of our concealed retries */
598 lwsl_warn("%s: concealed retries done, failing\n",
599 __func__);
600 goto retry_conn;
601 }
602
603 switch (r->state) {
604 case LDHC_INIT:
605 n = LWSDHCPDISCOVER;
606 goto bcast;
607
608 case LDHC_REQUESTING:
609 n = LWSDHCPREQUEST;
610
611 /* fallthru */
612 bcast:
613 n = lws_dhcpc_prep(p + 28, sizeof(pkt) - LWS_PRE - 28,
614 r, n);
615 if (n < 0) {
616 lwsl_err("%s: failed to prep\n", __func__);
617 break;
618 }
619
620 m = lws_plat_rawudp_broadcast(p, rawdisc,
621 LWS_ARRAY_SIZE(rawdisc),
622 n + 28,
623 r->wsi_raw->desc.sockfd,
624 (const char *)&r[1]);
625 if (m < 0)
626 lwsl_err("%s: Failed to write dhcp client req: "
627 "%d %d, errno %d\n", __func__,
628 n, m, LWS_ERRNO);
629 break;
630 default:
631 break;
632 }
633
634 return 0;
635
636 retry_conn:
637 lws_retry_sul_schedule(wsi->context, 0, &r->sul_conn, &bo2,
638 lws_dhcpc_retry_conn,
639 &r->retry_count_conn);
640
641 return -1;
642
643 default:
644 break;
645 }
646
647 return 0;
648
649 #if 0
650 cancel_conn_timer:
651 lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
652 LWS_SET_TIMER_USEC_CANCEL);
653
654 return 0;
655 #endif
656 }
657
658 struct lws_protocols lws_system_protocol_dhcpc =
659 { "lws-dhcpclient", callback_dhcpc, 0, 128, };
660
661 static void
lws_dhcpc_destroy(lws_dhcpc_req_t ** pr)662 lws_dhcpc_destroy(lws_dhcpc_req_t **pr)
663 {
664 lws_dhcpc_req_t *r = *pr;
665
666 lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
667 LWS_SET_TIMER_USEC_CANCEL);
668 lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
669 LWS_SET_TIMER_USEC_CANCEL);
670 if (r->wsi_raw)
671 lws_set_timeout(r->wsi_raw, 1, LWS_TO_KILL_ASYNC);
672
673 lws_dll2_remove(&r->list);
674
675 lws_free_set_NULL(r);
676 }
677
678 int
lws_dhcpc_status(struct lws_context * context,lws_sockaddr46 * sa46)679 lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46)
680 {
681 lws_dhcpc_req_t *r;
682
683 lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) {
684 r = (lws_dhcpc_req_t *)p;
685
686 if (r->state == LDHC_BOUND) {
687 if (sa46) {
688 memset(sa46, 0, sizeof(*sa46));
689 sa46->sa4.sin_family = AF_INET;
690 sa46->sa4.sin_addr.s_addr = r->ipv4[IPV4_DNS_SRV_1];
691 }
692 return 1;
693 }
694
695 } lws_end_foreach_dll(p);
696
697 return 0;
698 }
699
700 static lws_dhcpc_req_t *
lws_dhcpc_find(struct lws_context * context,const char * iface,int af)701 lws_dhcpc_find(struct lws_context *context, const char *iface, int af)
702 {
703 lws_dhcpc_req_t *r;
704
705 /* see if we are already looking after this af / iface combination */
706
707 lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) {
708 r = (lws_dhcpc_req_t *)p;
709
710 if (!strcmp((const char *)&r[1], iface) && af == r->af)
711 return r; /* yes... */
712
713 } lws_end_foreach_dll(p);
714
715 return NULL;
716 }
717
718 /*
719 * Create a persistent dhcp client entry for network interface "iface" and AF
720 * type "af"
721 */
722
723 int
lws_dhcpc_request(struct lws_context * context,const char * iface,int af,dhcpc_cb_t cb,void * opaque)724 lws_dhcpc_request(struct lws_context *context, const char *iface, int af,
725 dhcpc_cb_t cb, void *opaque)
726 {
727 lws_dhcpc_req_t *r = lws_dhcpc_find(context, iface, af);
728 int n;
729
730 /* see if we are already looking after this af / iface combination */
731
732 if (r)
733 return 0;
734
735 /* nope... let's create a request object as he asks */
736
737 n = strlen(iface);
738 r = lws_zalloc(sizeof(*r) + n + 1, __func__);
739 if (!r)
740 return 1;
741
742 memcpy(&r[1], iface, n + 1);
743 r->af = af;
744 r->cb = cb;
745 r->opaque = opaque;
746 r->context = context;
747 r->state = LDHC_INIT;
748
749 lws_dll2_add_head(&r->list, &context->dhcpc_owner); /* add him to list */
750
751 lws_dhcpc_retry_conn(&r->sul_conn);
752
753 return 0;
754 }
755
756 /*
757 * Destroy every DHCP client object related to interface "iface"
758 */
759
760 static int
_remove_if(struct lws_dll2 * d,void * opaque)761 _remove_if(struct lws_dll2 *d, void *opaque)
762 {
763 lws_dhcpc_req_t *r = lws_container_of(d, lws_dhcpc_req_t, list);
764
765 if (!opaque || !strcmp((const char *)&r[1], (const char *)opaque))
766 lws_dhcpc_destroy(&r);
767
768 return 0;
769 }
770
771 int
lws_dhcpc_remove(struct lws_context * context,const char * iface)772 lws_dhcpc_remove(struct lws_context *context, const char *iface)
773 {
774 lws_dll2_foreach_safe(&context->dhcpc_owner, (void *)iface, _remove_if);
775
776 return 0;
777 }
778