Watt-32 tcp/ip  2.2 dev-rel.10
pcdhcp.c
Go to the documentation of this file.
1 
8 /* Copyright (c) 1997-2007 Gisle Vanem <gvanem@yahoo.no>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  * must display the following acknowledgement:
20  * This product includes software developed by Gisle Vanem
21  * Bergen, Norway.
22  *
23  * THIS SOFTWARE IS PROVIDED BY ME (Gisle Vanem) AND CONTRIBUTORS ``AS IS''
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL I OR CONTRIBUTORS BE LIABLE FOR ANY
27  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 /* \version 0.5: Oct 28, 1996 :
36  * G. Vanem - implemented from RFC1541 with help from pcbootp.c.
37  *
38  * \version 0.6: May 18, 1997 :
39  * G. Vanem - added RFC2131 DHCPINFORM message.
40  *
41  * \version 0.7: Apr 25, 2002 :
42  * G. Vanem - added RFC3004 User Class option.
43  *
44  * \version 0.8: Jul 24, 2002 :
45  * G. Vanem - Rewitten as a non-blocking FSM.
46  * - Removed DHCPINFORM message.
47  *
48  * \version 0.9: Nov 08, 2002 :
49  * G. Vanem - added handling of option 60 (Vendor Class)
50  *
51  * \version 0.91: Feb 21, 2003 :
52  * Greg Bredthauer found some bugs in DHCP_request(); DHCP_USER_CLASS
53  * and DHCP_CLASS_ID tags had 2 bytes of zeros in them.
54  * Added sending DHCP_CLIENT_ID in DHCP_request() since some DHCP-servers
55  * doesn't reply without it.
56  *
57  * \version 0.92: Jul 17, 2003:
58  * Riccardo De Agostini contibuted changes for application hooking of
59  * transient configuration; dhcp_set_config_func() etc.
60  *
61  * \version 0.93: Jul 2, 2004:
62  * Fixed logic around reading transient config.
63  *
64  * \version 0.94: Oct 22, 2007:
65  * Fixes for Linux dhcp servers: parse dhcp options in ACK msgs.
66  * Changed DHCP_do_boot() to handle renew and rebind as well as init.
67  * Renegotiate lease if past the renew or rebind times.
68  */
69 
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <time.h>
74 #include <io.h>
75 #include <ctype.h>
76 #include <errno.h>
77 
78 #include "wattcp.h"
79 #include "strings.h"
80 #include "language.h"
81 #include "misc.h"
82 #include "run.h"
83 #include "timer.h"
84 #include "pcdns.h"
85 #include "netaddr.h"
86 #include "bsdname.h"
87 #include "ip4_out.h"
88 #include "syslog2.h"
89 #include "sock_ini.h"
90 #include "printk.h"
91 #include "tftp.h"
92 #include "pctcp.h"
93 #include "pcsed.h"
94 #include "pcarp.h"
95 #include "pcqueue.h"
96 #include "pcdbug.h"
97 #include "pcpkt.h"
98 #include "pcconfig.h"
99 #include "pcbootp.h"
100 #include "pcdhcp.h"
101 
102 #if defined(USE_DHCP)
103 
104 #define BROADCAST_FLAG intel16 (0x8000) /* network order (LSB=1) */
105 
106 /* This was 284, but D-Link 614+ router ACKs are too small
107  */
108 #define DHCP_MIN_SIZE 280
109 
110 BOOL DHCP_did_gratuitous_arp = FALSE;
111 
112 static WattDHCPConfigFunc config_func = NULL;
113 
114 static DWORD exchange_id;
115 static DWORD router, nameserver;
116 static DWORD dhcp_server = 0;
117 static DWORD dhcp_renewal = 0;
118 static DWORD dhcp_rebind = 0;
119 static DWORD dhcp_iplease = 0;
120 static DWORD suggest_lease = 0;
121 static DWORD old_ip_addr = 0;
122 
123 static BOOL bcast_flag = TRUE;
124 static BOOL got_offer = FALSE;
125 static BOOL configured = FALSE;
126 static BOOL arp_check_ip = FALSE;
127 static BOOL cfg_read = FALSE;
128 static BOOL cfg_saved = FALSE;
129 
130 static int dhcp_timeout = 10;
131 static int max_retries = 3;
132 static int discover_loops = 0;
133 
134 static struct dhcp dhcp_in, dhcp_out;
135 
136 static void (*DHCP_state) (int) = NULL;
137 static time_t renewal_timeout;
138 static time_t rebind_timeout;
139 static time_t lease_timeout;
140 static DWORD send_timeout;
141 static BOOL trace_on = FALSE;
142 
143 static char config_file [MAX_VALUELEN+1] = "";
144 static sock_type *sock = NULL;
145 
146 /* Default list of DHCP request values
147  */
148 static const BYTE default_request_list[] = {
149  DHCP_OPT_SUBNET_MASK,
150  DHCP_OPT_ROUTERS_ON_SNET,
151  DHCP_OPT_DNS_SRV,
152  DHCP_OPT_COOKIE_SRV,
153  DHCP_OPT_LPR_SRV,
154  DHCP_OPT_HOST_NAME,
155  DHCP_OPT_DOMAIN_NAME,
156  DHCP_OPT_IP_DEFAULT_TTL,
157  DHCP_OPT_IF_MTU,
158  DHCP_OPT_ARP_CACHE_TIMEOUT,
159  DHCP_OPT_ETHERNET_ENCAPSULATION,
160  DHCP_OPT_TCP_DEFAULT_TTL,
161 #if defined(USE_BSD_API)
162  DHCP_OPT_LOG_SRV, /* links in syslog2.c */
163  DHCP_OPT_NBIOS_NAME_SRV, /* SMB-lib needs these */
164  DHCP_OPT_NBIOS_NODE_TYPE,
165  DHCP_OPT_NBIOS_SCOPE,
166 #endif
167 #if defined(USE_TFTP)
168  DHCP_OPT_TFTP_SERVER,
169  DHCP_OPT_BOOT_FILENAME
170 #endif
171  };
172 
173 static struct DHCP_list request_list = {
174  (BYTE*) &default_request_list,
175  sizeof (default_request_list)
176  };
177 
178 static struct DHCP_list extra_options = { NULL, 0 };
179 static struct DHCP_list user_class = { NULL, 0 };
180 static struct DHCP_list vend_class = { (BYTE*)"Watt-32", 7 };
181 
182 static void DHCP_state_INIT (int event);
183 static void DHCP_state_BOUND (int event);
184 static void DHCP_state_RENEWING (int event);
185 static void DHCP_state_REQUESTING (int event);
186 static void DHCP_state_SELECTING (int event);
187 static void DHCP_state_REBINDING (int event);
188 
189 static void dhcp_fsm (void);
190 static void dhcp_options_add (const BYTE *opt, unsigned max);
191 static void dhcp_set_timers (void);
192 static void change_ip_addr (void);
193 static BYTE *put_request_list (BYTE *opt, int filled);
194 static int write_config (void);
195 static void erase_config (void);
196 
197 #define DHCP_SEND(end) sock_fastwrite (sock, (const BYTE*)&dhcp_out, /* structure to send */ \
198  end - (BYTE*)&dhcp_out) /* length of structue */
199 
200 #if defined(USE_DEBUG)
201  #define TRACE(x) do { if (trace_on) (*_printf) x; } while (0)
202  #define INET_NTOA(x) _inet_ntoa (NULL, x)
203 #else
204  #define TRACE(x) ((void)0)
205  #define INET_NTOA(x) ((void)0)
206 #endif
207 
208 #if defined(USE_DEBUG)
209 
212 static const char *state_name (void)
213 {
214  return (DHCP_state == DHCP_state_INIT ? "INIT" :
215  DHCP_state == DHCP_state_BOUND ? "BOUND" :
216  DHCP_state == DHCP_state_RENEWING ? "RENEWING" :
217  DHCP_state == DHCP_state_REQUESTING ? "REQUESTING" :
218  DHCP_state == DHCP_state_SELECTING ? "SELECTING" :
219  DHCP_state == DHCP_state_REBINDING ? "REBINDING" : "??");
220 }
221 
225 static const char *period (DWORD sec)
226 {
227  static char buf[20];
228  DWORD hours = sec / 3600UL;
229 
230  if (sec < 60UL)
231  sprintf (buf, "%lus", sec);
232  else if (sec < 3600UL)
233  sprintf (buf, "%lu:%02lu", sec/60, sec % 60);
234  else sprintf (buf, "%lu:%02lu:%02lu", hours, sec/60-hours*60, sec % 60);
235  return (buf);
236 }
237 #endif
238 
242 static BYTE *make_boot_header (void)
243 {
244  DWORD my_ip = 0UL;
245 
246  if (DHCP_state == DHCP_state_BOUND ||
247  DHCP_state == DHCP_state_RENEWING ||
248  DHCP_state == DHCP_state_REBINDING)
249  my_ip = intel (my_ip_addr);
250 
251  memset (&dhcp_out, 0, sizeof(dhcp_out));
252 
253  _eth_get_hwtype (&dhcp_out.dh_htype, &dhcp_out.dh_hlen);
254 
255  dhcp_out.dh_op = BOOTP_REQUEST;
256  dhcp_out.dh_xid = exchange_id;
257  dhcp_out.dh_secs = 0;
258  dhcp_out.dh_flags = bcast_flag ? BROADCAST_FLAG : 0;
259  dhcp_out.dh_yiaddr = 0;
260  dhcp_out.dh_ciaddr = my_ip;
261  dhcp_out.dh_giaddr = 0;
262  memcpy (dhcp_out.dh_chaddr, _eth_addr, _eth_mac_len);
263  *(DWORD*) &dhcp_out.dh_opt[0] = DHCP_MAGIC_COOKIE;
264  return (&dhcp_out.dh_opt[4]);
265 }
266 
270 static BYTE *put_hardware_opt (BYTE *opt)
271 {
272  BYTE hw_type, hw_len;
273 
274  _eth_get_hwtype (&hw_type, &hw_len);
275  *opt++ = DHCP_OPT_CLIENT_ID;
276  *opt++ = hw_len + 1;
277  *opt++ = hw_type;
278  memcpy (opt, _eth_addr, hw_len);
279  opt += hw_len;
280  return (opt);
281 }
282 
286 static int DHCP_discover (void)
287 {
288  BYTE *opt, *start;
289 
290  exchange_id = set_timeout (0); /* random exchange ID */
291  opt = start = make_boot_header();
292  *opt++ = DHCP_OPT_MSG_TYPE;
293  *opt++ = 1;
294  *opt++ = DHCP_DISCOVER;
295 
296  opt = put_hardware_opt (opt);
297 
298  *opt++ = DHCP_OPT_MAX_MSG_SIZE; /* Maximum DHCP message size */
299  *opt++ = 2;
300  *(WORD*)opt = intel16 (sizeof(struct dhcp));
301  opt += 2;
302 
303  if (suggest_lease)
304  {
305  *opt++ = DHCP_OPT_IP_ADDR_LEASE_TIME;
306  *opt++ = sizeof (suggest_lease);
307  *(DWORD*)opt = intel (suggest_lease);
308  opt += sizeof (suggest_lease);
309  }
310  opt = put_request_list (opt, opt-start);
311  *opt++ = DHCP_OPT_END;
312  return DHCP_SEND (opt);
313 }
314 
318 static int DHCP_request (BOOL renew)
319 {
320  BYTE *opt = make_boot_header();
321 
322  *opt++ = DHCP_OPT_MSG_TYPE;
323  *opt++ = 1;
324  *opt++ = DHCP_REQUEST;
325 
326  if (!renew)
327  {
328  *opt++ = DHCP_OPT_SRV_IDENTIFIER;
329  *opt++ = sizeof (dhcp_server);
330  *(DWORD*)opt = intel (dhcp_server);
331  opt += sizeof (dhcp_server);
332  *opt++ = DHCP_OPT_REQUESTED_IP_ADDR;
333  *opt++ = sizeof (my_ip_addr);
334  *(DWORD*)opt = intel (my_ip_addr);
335  opt += sizeof (my_ip_addr);
336  }
337 
338  /* Some DHCP-daemons require this tag in a REQUEST.
339  */
340  opt = put_hardware_opt (opt);
341 
342 /* ... don't know why we would ever renew a lease
343  * for the time remaining on our current lease...
344  */
345 #if 0
346  if (dhcp_iplease && dhcp_iplease < (DWORD)-1)
347  {
348  *opt++ = DHCP_OPT_IP_ADDR_LEASE_TIME;
349  *opt++ = sizeof (dhcp_iplease);
350  *(DWORD*)opt = intel (dhcp_iplease);
351  opt += sizeof (dhcp_iplease);
352  }
353 #endif
354 
355  if (user_class.data)
356  {
357  *opt++ = DHCP_OPT_USER_CLASS;
358  *opt++ = user_class.size;
359  memcpy (opt, user_class.data, user_class.size);
360  opt += user_class.size;
361  }
362 
363  if (vend_class.data)
364  {
365  *opt++ = DHCP_OPT_CLASS_ID;
366  *opt++ = vend_class.size;
367  memcpy (opt, vend_class.data, vend_class.size);
368  opt += vend_class.size;
369  }
370 
371  *opt++ = DHCP_OPT_END;
372  TRACE (("DHCP req: renew(%d) dst(%s)\n",
373  renew, INET_NTOA(sock->udp.hisaddr)));
374  return DHCP_SEND (opt);
375 }
376 
382 static int DHCP_release_decline (int msg_type, const char *msg)
383 {
384  BYTE *opt;
385 
386  exchange_id = set_timeout (0); /* new exchange ID */
387  opt = make_boot_header();
388 
389  *opt++ = DHCP_OPT_MSG_TYPE;
390  *opt++ = 1;
391  *opt++ = msg_type;
392  *opt++ = DHCP_OPT_SRV_IDENTIFIER;
393  *opt++ = sizeof (dhcp_server);
394  *(DWORD*)opt = intel (dhcp_server);
395  opt += sizeof (dhcp_server);
396 
397  if (msg)
398  {
399  const BYTE *end = &dhcp_out.dh_opt [sizeof(dhcp_out.dh_opt)-1];
400  size_t len = strlen (msg);
401 
402  len = min (len, 255);
403  len = min (len, (size_t)(end-opt-3));
404  *opt++ = DHCP_OPT_MSG;
405  *opt++ = (BYTE)len;
406  memcpy (opt, msg, len);
407  opt += len;
408  }
409  *opt++ = DHCP_OPT_END;
410  return DHCP_SEND (opt);
411 }
412 
420 static BOOL DHCP_arp_check (DWORD my_ip)
421 {
422  eth_address eth;
423 
424  if (_pktserial)
425  return (TRUE);
426 
427  /* ARP broadcast to announce our new IP.
428  */
429  _arp_reply (NULL, my_ip, my_ip | ~sin_mask);
430  DHCP_did_gratuitous_arp = TRUE;
431 
432  if (!arp_check_ip)
433  return (TRUE);
434 
435  /* Check if IP is used by anybody else
436  */
437  TRACE (("Checking ARP.."));
438 
439  if (!_arp_check_own_ip(&eth))
440  {
441  TRACE (("station %s claims to have my IP %s! Declining..\n",
442  MAC_address(&eth), INET_NTOA(my_ip)));
443  if (!trace_on)
444  outsnl ("Someone claims to have my IP! Declining..\7");
445  return (FALSE);
446  }
447  TRACE (("\n"));
448  return (TRUE);
449 }
450 
455 static int DHCP_offer (const struct dhcp *in)
456 {
457  int len;
458  DWORD ip;
459  const BYTE *opt = (const BYTE*) &in->dh_opt[4];
460 
461  while (opt < in->dh_opt + sizeof(in->dh_opt))
462  {
463  switch (*opt)
464  {
465  case DHCP_OPT_PAD:
466  opt++;
467  continue;
468 
469  case DHCP_OPT_SUBNET_MASK:
470  sin_mask = intel (*(DWORD*)(opt+2));
471  TRACE (("Net-mask: %s\n", INET_NTOA(sin_mask)));
472  break;
473 
474  case DHCP_OPT_TIME_OFFSET:
475 #if defined(USE_DEBUG)
476  {
477  long ofs = *(long*)(opt+2);
478  TRACE (("Time-ofs: %.2fh\n", (double)ofs/3600.0));
479  }
480 #endif
481  break;
482 
483  case DHCP_OPT_ROUTERS_ON_SNET:
484  {
485  static BOOL gw_added = FALSE;
486 
487  if (!gw_added)
488  _arp_kill_gateways(); /* delete gateways from cfg-file */
489  gw_added = TRUE;
490  router = intel (*(DWORD*)(opt+2));
491  _arp_add_gateway (NULL, router);
492  TRACE (("Gateway: %s\n", INET_NTOA(router)));
493  }
494  break;
495 
496  case DHCP_OPT_DNS_SRV:
497  {
498  static BOOL dns_added = FALSE;
499 
500  for (len = 0; len < *(opt+1); len += sizeof(DWORD))
501  {
502  ip = intel (*(DWORD*)(opt+2+len));
503  if (!dns_added)
504  last_nameserver = 0; /* delete nameserver from cfg-file */
505 
506  /*
507  * !!fix-me: Add only first name-server cause resolve() doesn't
508  * handle multiple servers (in different nets) very well
509  */
510  if (len == 0)
511  {
512  nameserver = ip;
513  _add_server (&last_nameserver, def_nameservers,
514  DIM(def_nameservers), ip);
515  }
516  dns_added = TRUE;
517  TRACE (("DNS: %s\n", INET_NTOA(ip)));
518  }
519  }
520  break;
521 
522 #if defined(USE_BSD_API)
523  case DHCP_OPT_LOG_SRV:
524  if (!syslog_host_name[0] && /* not already set */
525  opt[1] % 4 == 0) /* length = n * 4 */
526  {
527  ip = intel (*(DWORD*)(opt+2)); /* select 1st host */
528  _strlcpy (syslog_host_name, _inet_ntoa(NULL,ip),
529  sizeof(syslog_host_name));
530  TRACE (("Syslog: %s\n", syslog_host_name));
531  }
532  break;
533 
534  case DHCP_OPT_NBIOS_NAME_SRV:
535  ip = intel (*(DWORD*)(opt+2));
536  TRACE (("WINS: %s\n", INET_NTOA(ip)));
538  break;
539 
540  case DHCP_OPT_NBIOS_NODE_TYPE:
541  TRACE (("NBT node: %02X\n", opt[2]));
542  break;
543 
544  case DHCP_OPT_NBIOS_SCOPE:
545  TRACE (("NBT scope: %.*s\n", opt[1], opt+2));
546  break;
547 #endif
548  case DHCP_OPT_HOST_NAME:
549  /* Don't use sethostname() because '*(opt+2)' is not a FQDN.
550  */
551  len = min (opt[1], sizeof(hostname));
552  memcpy (hostname, opt+2, len);
553  hostname[len] = '\0';
554  TRACE (("Host name: `%s'\n", hostname));
555  break;
556 
557  case DHCP_OPT_DOMAIN_NAME:
558  len = min (opt[1], sizeof(defaultdomain)-1);
559  setdomainname ((const char*)(opt+2), len+1);
560  TRACE (("Domain: `%s'\n", def_domain));
561  break;
562 
563  case DHCP_OPT_IP_DEFAULT_TTL:
564  case DHCP_OPT_TCP_DEFAULT_TTL:
565  _default_ttl = opt[2];
566  break;
567 
568  case DHCP_OPT_MSG_TYPE:
569  if (opt[2] == DHCP_OFFER)
570  got_offer = TRUE;
571  break;
572 
573  case DHCP_OPT_MSG:
574  outsn ((const char*)(opt+2), *(opt+1));
575  break;
576 
577  case DHCP_OPT_SRV_IDENTIFIER:
578  dhcp_server = intel (*(DWORD*)(opt+2));
579  TRACE (("Server: %s\n", INET_NTOA(dhcp_server)));
580  break;
581 
582  case DHCP_OPT_IP_ADDR_LEASE_TIME:
583  dhcp_iplease = intel (*(DWORD*)(opt+2));
584  TRACE (("IP lease: %s\n", period(dhcp_iplease)));
585  break;
586 
587  case DHCP_OPT_T1_VALUE:
588  dhcp_renewal = intel (*(DWORD*)(opt+2));
589  TRACE (("Renewal: %s\n", period(dhcp_renewal)));
590  break;
591 
592  case DHCP_OPT_T2_VALUE:
593  dhcp_rebind = intel (*(DWORD*)(opt+2));
594  TRACE (("Rebind: %s\n", period(dhcp_rebind)));
595  break;
596 
597  case DHCP_OPT_TCP_KEEPALIVE_INTERVAL:
598 #if !defined(USE_UDP_ONLY)
599  tcp_keep_intvl = intel (*(DWORD*)(opt+2));
600 #endif
601  break;
602 
603  case DHCP_OPT_OVERLOAD:
604  switch (opt[2])
605  {
606  case 1:
607  TRACE (("Overload: `dh_file' options\n"));
608  dhcp_options_add (in->dh_file, sizeof(in->dh_file));
609  break;
610  case 2:
611  TRACE (("Overload: `dh_sname' options\n"));
612  dhcp_options_add (in->dh_sname, sizeof(in->dh_sname));
613  break;
614  case 3:
615  TRACE (("Overload: `dh_file/dh_sname' options\n"));
616  dhcp_options_add (in->dh_file, sizeof(in->dh_file));
617  dhcp_options_add (in->dh_sname, sizeof(in->dh_sname));
618  break;
619  }
620  break;
621 
622 #if defined(USE_TFTP)
623  case DHCP_OPT_TFTP_SERVER:
624  {
625  const char *serv;
626  len = opt[1];
627  serv = tftp_set_server ((const char*)(opt+2), len);
628  TRACE (("TFTP-serv: `%s'\n", serv));
629  ARGSUSED (serv);
630  }
631  break;
632 
633  case DHCP_OPT_BOOT_FILENAME:
634  {
635  const char *file;
636  len = opt[1];
637  file = tftp_set_boot_fname ((const char*)(opt+2), len);
638  TRACE (("BOOT-file: `%s'\n", file));
639  ARGSUSED (file);
640  }
641  break;
642 #endif
643 
644  case DHCP_OPT_USER_CLASS: /* these options are ignored in replies */
645  case DHCP_OPT_CLASS_ID:
646  break;
647 
648  case DHCP_OPT_GRUB_MENU: /* GRUB loader config-file */
649  TRACE (("GRUB: %.*s\n", opt[1], opt+2));
650  break;
651 
652  case DHCP_OPT_ETHERNET_ENCAPSULATION:
653  TRACE (("Encap: %s\n", opt[2] ? "IEEE 802.3" : "Ethernet II"));
654  if (opt[2] != 0)
655  outsnl (_LANG("Only Ethernet II encapsulation supported"));
656  break;
657 
658  case DHCP_OPT_END:
659  TRACE (("got end-option\n"));
660  return (got_offer);
661 
662  default:
663  TRACE (("Ignoring option %d\n", *opt));
664  break;
665  }
666  opt += *(opt+1) + 2;
667  }
668 
669  if (extra_options.data)
670  {
671  struct dhcp ext;
672 
673  len = min (extra_options.size, sizeof(ext.dh_opt));
674  extra_options.data [len] = DHCP_OPT_END;
675  memcpy (ext.dh_opt, extra_options.data, len);
676  DHCP_offer (&ext);
677  }
678  return (got_offer);
679 }
680 
684 static int DHCP_is_ack (void)
685 {
686  const BYTE *opt = (const BYTE*) &dhcp_in.dh_opt[4];
687 
688  return (opt[0] == DHCP_OPT_MSG_TYPE && opt[1] == 1 && opt[2] == DHCP_ACK);
689 }
690 
694 static int DHCP_is_nack (void)
695 {
696  const BYTE *opt = (const BYTE*) &dhcp_in.dh_opt[4];
697 
698  return (opt[0] == DHCP_OPT_MSG_TYPE && opt[1] == 1 && opt[2] == DHCP_NAK);
699 }
700 
707 void W32_CALL DHCP_release (BOOL force)
708 {
709  if (force)
710  {
711  TRACE (("Sending DHCP release 01\n"));
712  DHCP_release_decline (DHCP_RELEASE, NULL);
713  }
714  else if (configured)
715  {
716  /* Don't release if:
717  * DHCP-config is saved on disk and remaining lease is
718  * above minimum.
719  */
720  if (!(cfg_saved &&
721  (lease_timeout && (lease_timeout - time(NULL) > DHCP_MIN_LEASE))))
722  {
723  TRACE (("Sending DHCP release 02\n"));
724  DHCP_release_decline (DHCP_RELEASE, NULL);
725  }
726  }
727  DAEMON_DEL(dhcp_fsm);
728  if (sock)
729  {
730  sock_close (sock);
731  free (sock);
732  sock = NULL;
733  }
734 }
735 
739 DWORD W32_CALL DHCP_get_server (void)
740 {
741  return (dhcp_server);
742 }
743 
747 static sock_type *dhcp_open (const char *msg, BOOL use_broadcast)
748 {
749  udp_Socket *sock = malloc (sizeof(*sock));
750  DWORD host;
751 
752  if (!sock)
753  {
754  outs (_LANG("DHCP: malloc failed\n"));
755  return (NULL);
756  }
757 
758  if (msg && (trace_on || debug_on))
759  outs (msg);
760 
761  if (use_broadcast)
762  host = IP_BCAST_ADDR; /* 255.255.255.255 */
763  else host = dhcp_server; /* default is 0.0.0.0 which maps to 255.255.255.255 */
764 
765  if (!udp_open (sock, IPPORT_BOOTPC, host, IPPORT_BOOTPS, NULL))
766  {
767  if (trace_on || debug_on)
768  {
769  outs ("DHCP: ");
770  outsnl (sock->err_msg);
771  }
772  free (sock);
773  sock = NULL;
774  }
775  TRACE (("DHCP open: bc(%d) srvr(%s)\n",
776  use_broadcast, INET_NTOA(dhcp_server)));
777  return (sock_type*)sock; /* sock is free'd in DHCP_exit() */
778 }
779 
783 static void store_DHCP_server_MAC (void)
784 {
785  if ((_pktdevclass == PDCLASS_ETHER || _pktdevclass == PDCLASS_TOKEN) &&
786  memcmp(sock->udp.his_ethaddr, _eth_brdcast, sizeof(_eth_brdcast)))
787  {
788  const eth_address *eth = (const eth_address*) sock->udp.his_ethaddr;
789  _arp_cache_add (dhcp_server, eth, TRUE);
790  }
791 }
792 
797 static void DHCP_state_BOUND (int event)
798 {
799  if (event == EVENT_T1_TIMEOUT) /* renewal timeout */
800  {
801  old_ip_addr = my_ip_addr; /* remember current address */
802  got_offer = FALSE;
803 
804  TRACE (("Sending DHCP request 01\n"));
805  DHCP_request (1);
806  DHCP_state = DHCP_state_RENEWING;
807  }
808 }
809 
816 static void DHCP_state_REQUESTING (int event)
817 {
818  if (event == EVENT_SEND_TIMEOUT)
819  {
820  TRACE (("Sending DHCP request 00\n"));
821  DHCP_request (0);
822 
823  /*UPDATE: 05MAR2006 paul.suggs@vgt.net
824  * There is a timing condition within the FSM where the state has changed
825  * to REQUESTING but in dhcp_fsm(), chk_timeout() will be evaluated true
826  * which sets send_timeout to 0 before the first execution of this state
827  * handler. Once set to 0, we have to wait for rollover to resend the
828  * above request if it is lost for some reason
829  */
830  send_timeout = set_timeout (Random(4000,6000));
831  }
832  else if (event == EVENT_ACK)
833  {
834  TRACE (("Got DHCP ack while requesting\n"));
835 
837  {
838  my_ip_addr = 0; /* decline from 0.0.0.0 */
839  DHCP_release_decline (DHCP_DECLINE, "IP is not free");
840  send_timeout = set_timeout (Random(4000,6000));
841  DHCP_state = DHCP_state_INIT;
842  }
843  else
844  {
845  DHCP_offer (&dhcp_in); /* parse options in the ack too */
846  configured = 1; /* we are (re)configured */
847  if (dhcp_server)
849  dhcp_set_timers();
850  send_timeout = 0UL;
851  DHCP_state = DHCP_state_BOUND;
852  }
853  }
854  else if (event == EVENT_NAK)
855  {
856  send_timeout = set_timeout (Random(4000,6000));
857  my_ip_addr = 0UL;
858  DHCP_state = DHCP_state_INIT;
859  }
860 }
861 
867 static void DHCP_state_REBINDING (int event)
868 {
869  if (event == EVENT_ACK)
870  {
871  TRACE (("Got DHCP ack while rebinding\n"));
872  dhcp_set_timers();
873  DHCP_offer (&dhcp_in);
874  change_ip_addr();
875  DHCP_state = DHCP_state_BOUND;
876  }
877  else if (event == EVENT_NAK)
878  {
879  send_timeout = set_timeout (Random(4000,6000));
880  my_ip_addr = 0UL;
881  DHCP_state = DHCP_state_INIT;
882  }
883 }
884 
892 static void DHCP_state_RENEWING (int event)
893 {
894  if (event == EVENT_SEND_TIMEOUT)
895  {
896  TRACE (("Sending DHCP request for renew\n"));
897  DHCP_request (1);
898  }
899  else if (event == EVENT_T2_TIMEOUT)
900  {
901  TRACE (("Sending DHCP request for rebind\n"));
902  bcast_flag = TRUE;
903  DHCP_request (1);
904  DHCP_state = DHCP_state_REBINDING;
905  }
906  else if (event == EVENT_ACK)
907  {
908  TRACE (("Got DHCP ack while renewing\n"));
909  dhcp_set_timers();
910  DHCP_offer (&dhcp_in);
911  change_ip_addr();
912  DHCP_state = DHCP_state_BOUND;
913  }
914  else if (event == EVENT_NAK)
915  {
916  TRACE (("Got DHCP nack while renewing\n"));
917  send_timeout = set_timeout (Random(4000,6000));
918  my_ip_addr = 0;
919  DHCP_state = DHCP_state_INIT;
920  }
921 }
922 
928 static void DHCP_state_SELECTING (int event)
929 {
930  if (event == EVENT_OFFER && !got_offer && DHCP_offer(&dhcp_in))
931  {
932  TRACE (("Got DHCP offer\n"));
933  send_timeout = set_timeout (Random(100,500));
934 
935  if (dhcp_renewal == 0L)
936  dhcp_renewal = dhcp_iplease / 2; /* default T1 time */
937  if (dhcp_rebind == 0)
938  dhcp_rebind = dhcp_iplease * 7 / 8; /* default T2 time */
939 
940  /* Remember my_ip_addr from OFFER because WinNT server
941  * doesn't include it in ACK message.
942  */
943  my_ip_addr = ((_udp_Socket*)sock)->myaddr = intel (dhcp_in.dh_yiaddr);
944  TRACE (("my_ip_addr = %s\n", INET_NTOA(my_ip_addr)));
945  send_timeout = set_timeout (100);
946  DHCP_state = DHCP_state_REQUESTING;
947  }
948  else if (event == EVENT_SEND_TIMEOUT) /* retransmit timeout */
949  {
950  DHCP_state = DHCP_state_INIT;
951  (*DHCP_state) (event);
952  }
953 }
954 
959 static void DHCP_state_INIT (int event)
960 {
961  if (event == EVENT_SEND_TIMEOUT)
962  {
963  discover_loops++;
964  exchange_id = set_timeout (0); /* random exchange ID */
965 
966  TRACE (("Sending DHCP discover (%d)\n", discover_loops));
967 
968  send_timeout = set_timeout (1000 * dhcp_timeout);
969  DHCP_discover();
970  DHCP_state = DHCP_state_SELECTING;
971  }
972 }
973 
974 #ifdef NOT_USED
975 
978 static void DHCP_state_REBOOTING (int event)
979 {
980  if (event == EVENT_ACK)
981  {
982  DHCP_state = DHCP_state_BOUND;
983  }
984  else if (event == EVENT_NAK)
985  {
986  send_timeout = set_timeout (Random(4000,6000));
987  DHCP_state = DHCP_state_INIT;
988  }
989 }
990 #endif
991 
995 static void dhcp_fsm (void)
996 {
997  if (sock_dataready(sock))
998  {
999  int len = sock_fastread (sock, (BYTE*)&dhcp_in, sizeof(dhcp_in));
1000 
1001  if (len >= DHCP_MIN_SIZE && /* packet large enough */
1002  dhcp_in.dh_op == BOOTP_REPLY && /* got a BOOTP reply */
1003  dhcp_in.dh_xid == dhcp_out.dh_xid && /* got our exchange ID */
1004  !memcmp(dhcp_in.dh_chaddr, _eth_addr, /* correct hardware addr */
1005  sizeof(eth_address)))
1006  {
1007  if (DHCP_is_ack())
1008  {
1009  TRACE (("%s/ACK: ", state_name()));
1010  (*DHCP_state) (EVENT_ACK);
1011  }
1012  else if (DHCP_is_nack())
1013  {
1014  TRACE (("%s/NAK: ", state_name()));
1015  (*DHCP_state) (EVENT_NAK);
1016  }
1017  else
1018  {
1019  TRACE (("%s/OFFER: ", state_name()));
1020  (*DHCP_state) (EVENT_OFFER);
1021  }
1022  }
1023  }
1024 
1025  if (chk_timeout(send_timeout))
1026  {
1027  send_timeout = 0UL;
1028  TRACE (("%s/SEND_TIMEOUT: ", state_name()));
1029  (*DHCP_state) (EVENT_SEND_TIMEOUT);
1030  }
1031 
1032  if (renewal_timeout && time(NULL) >= renewal_timeout)
1033  {
1034  renewal_timeout = 0UL;
1035  TRACE (("%s/T1_TIMEOUT: ", state_name()));
1036  (*DHCP_state) (EVENT_T1_TIMEOUT);
1037  }
1038 
1039  if (rebind_timeout && time(NULL) >= rebind_timeout)
1040  {
1041  rebind_timeout = 0UL;
1042  TRACE (("%s/T2_TIMEOUT: ", state_name()));
1043  (*DHCP_state) (EVENT_T2_TIMEOUT);
1044  }
1045 }
1046 
1056 int DHCP_do_boot (void)
1057 {
1058  int save_mtu = _mtu;
1059 
1060  if (cfg_read) /* DHCP_read_config() okay */
1061  return (1);
1062 
1063  outs (_LANG("Configuring through DHCP.."));
1064  if (!sock)
1065  sock = dhcp_open (NULL, bcast_flag);
1066  if (!sock)
1067  return (0);
1068 
1069  if (DHCP_state != DHCP_state_RENEWING &&
1070  DHCP_state != DHCP_state_REBINDING)
1071  {
1072  my_ip_addr = 0;
1073  sin_mask = 0;
1074  }
1075 
1076  _mtu = ETH_MAX_DATA;
1077  discover_loops = 0;
1078 
1079  erase_config(); /* delete old configuration */
1080  DAEMON_ADD (dhcp_fsm); /* add "background" daemon */
1081 
1082  /* kick start DISCOVER message
1083  */
1084  send_timeout = set_timeout (100);
1085 
1086  if (DHCP_state != DHCP_state_RENEWING &&
1087  DHCP_state != DHCP_state_REBINDING)
1088  DHCP_state = DHCP_state_INIT;
1089 
1090  while (DHCP_state != DHCP_state_BOUND)
1091  {
1092  tcp_tick (NULL);
1093  if (discover_loops >= max_retries) /* retries exhaused */
1094  break;
1095  }
1096 
1097  got_offer = FALSE; /* ready for next cycle */
1098  _mtu = save_mtu;
1099 
1100  if (my_ip_addr)
1101  {
1102  cfg_saved = write_config() > 0;
1103  return (1);
1104  }
1105  return (0);
1106 }
1107 
1111 static void dhcp_options_add (const BYTE *opt, unsigned max)
1112 {
1113  int len = 0;
1114  char *add;
1115  const BYTE *end = opt + max;
1116 
1117  extra_options.data = (BYTE*) realloc (extra_options.data, max);
1118  if (!extra_options.data)
1119  return;
1120 
1121  add = (char*)&extra_options.data + extra_options.size;
1122 
1123  /* Loop over `opt' and append to `add'. Strip away DHCP_OPT_END
1124  * and DHCP_OPT_PAD options.
1125  */
1126  while (opt < end)
1127  {
1128  if (*opt == DHCP_OPT_PAD)
1129  continue;
1130  if (*opt == DHCP_OPT_END)
1131  return;
1132 
1133  len = opt[1];
1134  memcpy (add, opt, len+2);
1135  add += len + 2;
1136  opt += len + 2;
1137  extra_options.size += len + 2;
1138  }
1139 }
1140 
1144 static void dhcp_set_timers (void)
1145 {
1146  time_t now = time (NULL);
1147 
1148  if (dhcp_iplease == 0UL)
1149  lease_timeout = (time_t)-1;
1150  else lease_timeout = now + dhcp_iplease;
1151 
1152  if (dhcp_renewal == 0UL)
1153  renewal_timeout = (time_t)-1;
1154  else renewal_timeout = now + dhcp_renewal;
1155 
1156  if (dhcp_rebind == 0UL)
1157  rebind_timeout = (time_t)-1;
1158  else
1159  {
1160  rebind_timeout = now + dhcp_rebind;
1161  if (rebind_timeout == renewal_timeout)
1162  rebind_timeout += 10; /* add 10 seconds */
1163  }
1164  if (dhcp_iplease == 0UL)
1165  TRACE (("Infinite lease!!\n"));
1166 }
1167 
1171 static void change_ip_addr (void)
1172 {
1173  _udp_Socket *udp;
1174  _tcp_Socket *tcp;
1175 
1176  if (my_ip_addr == old_ip_addr)
1177  return;
1178 
1179 #if !defined(USE_UDP_ONLY)
1180  for (tcp = _tcp_allsocs; tcp; tcp = tcp->next)
1181  if (tcp->myaddr)
1182  tcp->myaddr = my_ip_addr;
1183 #endif
1184  for (udp = _udp_allsocs; udp; udp = udp->next)
1185  if (udp->myaddr)
1186  udp->myaddr = my_ip_addr;
1187 }
1188 
1193 static int set_request_list (char *options)
1194 {
1195  static BOOL init = FALSE;
1196  int num = 0;
1197  int maxreq = 312 - 27; /* sizeof(dh_opt) - min size of rest */
1198  BYTE *list, *start, *tok, *end;
1199  char *tok_buf = NULL;
1200 
1201  if (init || (list = calloc(maxreq,1)) == NULL)
1202  return (0);
1203 
1204  init = TRUE;
1205  start = list;
1206  end = start + maxreq - 1;
1207  tok = (BYTE*) strtok_r (options, ", \t", &tok_buf);
1208 
1209  while (tok && list < end)
1210  {
1211  *list = ATOI ((const char*)tok);
1212  tok = (BYTE*) strtok_r (NULL, ", \t", &tok_buf);
1213 
1214  /* If request list start with Pad option ("DHCP.REQ_LIST=0"),
1215  * disable options all-together.
1216  */
1217  if (num == 0 && *list == '0')
1218  break;
1219  num++;
1220  list++;
1221  }
1222 
1223  request_list.data = start;
1224  request_list.size = num;
1225 
1226 #if 0 /* test */
1227  {
1228  int i;
1229  for (i = 0; i < request_list.size; i++)
1230  printf ("%2d, ", request_list.data[i]);
1231  puts ("");
1232  }
1233 #endif
1234  return (1);
1235 }
1236 
1240 static BYTE *put_request_list (BYTE *opt, int filled)
1241 {
1242  size_t size = min (request_list.size, sizeof(dhcp_out.dh_opt)-filled-1);
1243  /* room for DHCP_OPT_END ^ */
1244  if (size > 0 && request_list.data)
1245  {
1246  size = min (size, 255);
1247  *opt++ = DHCP_OPT_PARAM_REQUEST;
1248  *opt++ = (BYTE)size;
1249  memcpy (opt, request_list.data, size);
1250  opt += size;
1251  }
1252  return (opt);
1253 }
1254 
1260 static int set_user_class (const char *value)
1261 {
1262  static BOOL init = FALSE;
1263  int total = 0;
1264  int maxreq = sizeof(dhcp_out.dh_opt) - 27; /* adjust for rest */
1265  char *list, *list_start;
1266  char *list_end;
1267  const char *value_end;
1268 
1269  if (init || (list = (char*)calloc(maxreq,1)) == NULL)
1270  return (0);
1271 
1272  init = TRUE;
1273  list_start = list;
1274  list_end = list + maxreq - 1;
1275  value_end = value + strlen (value);
1276 
1277  while (list < list_end && value < value_end)
1278  {
1279  const char *comma = strchr (value, ',');
1280  int size;
1281 
1282  if (comma)
1283  size = comma - value; /* "xxx,xxx" */
1284  else size = strlen (value); /* "xxxx" */
1285  *list++ = size;
1286  memcpy (list, value, size);
1287  list += size;
1288  value += size + 1;
1289  total += size + 1;
1290  }
1291 
1292  user_class.data = (BYTE*) list_start;
1293  user_class.size = total;
1294 
1295 #if 0 /* test */
1296  {
1297  size_t i;
1298  for (i = 0; i < user_class.size; i++)
1299  {
1300  int ch = user_class.data[i];
1301 
1302  if (isprint(ch))
1303  printf ("%c", ch);
1304  else printf ("\\x%02X", ch);
1305  }
1306  puts ("");
1307  }
1308 #endif
1309  return (1);
1310 }
1311 
1315 static int set_vend_class (const char *value)
1316 {
1318  ARGSUSED (value);
1319  return (0);
1320 }
1321 
1322 /*-------------------------------------------------------------------*/
1323 
1324 static void (W32_CALL *prev_hook) (const char*, const char*) = NULL;
1325 
1326 /* Absolute times for expiry of lease, renewal and rebind
1327  * read from user (config_func) or transient config file.
1328  */
1329 static time_t cfg_dhcp_iplease;
1330 static time_t cfg_dhcp_renewal;
1331 static time_t cfg_dhcp_rebind;
1332 
1338 static void W32_CALL DHCP_cfg_hook (const char *name, const char *value)
1339 {
1340  static const struct config_table dhcp_cfg[] = {
1341  { "REQ_LIST", ARG_FUNC, (void*)set_request_list },
1342  { "TRACE", ARG_ATOI, (void*)&trace_on },
1343  { "BCAST", ARG_ATOI, (void*)&bcast_flag },
1344  { "TIMEOUT", ARG_ATOI, (void*)&dhcp_timeout },
1345  { "RETRIES", ARG_ATOI, (void*)&max_retries },
1346  { "ARPCHECK", ARG_ATOI, (void*)&arp_check_ip },
1347  { "HOST", ARG_RESOLVE,(void*)&dhcp_server },
1348  { "USERCLASS",ARG_FUNC, (void*)set_user_class },
1349  { "VENDCLASS",ARG_FUNC, (void*)set_vend_class },
1350  { "CONFIG", ARG_STRCPY, (void*)&config_file },
1351  { NULL, 0, NULL }
1352  };
1353 
1354  if (!parse_config_table(&dhcp_cfg[0], "DHCP.", name, value) && prev_hook)
1355  (*prev_hook) (name, value);
1356 }
1357 
1361 static void DHCP_exit (void)
1362 {
1363  if (_watt_fatal_error)
1364  return;
1365 
1366  DO_FREE (user_class.data);
1367  DO_FREE (extra_options.data);
1368  DO_FREE (sock);
1369  if (request_list.data != (BYTE*)&default_request_list)
1370  DO_FREE (request_list.data);
1371 
1372 #if defined(USE_BSD_API)
1373  syslog_host_name[0] = '\0';
1374 #endif
1375 }
1376 
1380 void DHCP_init (void)
1381 {
1382  prev_hook = usr_init;
1383  usr_init = DHCP_cfg_hook;
1384  RUNDOWN_ADD (DHCP_exit, 259);
1385 }
1386 
1390 static void set_my_ip (const char *value)
1391 {
1392  TRACE (("DHCP: using previous address %s\n", value));
1393  my_ip_addr = aton (value);
1394 }
1395 
1396 static void set_netmask (const char *value)
1397 {
1398  TRACE (("DHCP: using previous netmask %s\n", value));
1399  sin_mask = aton (value);
1400 }
1401 
1402 static void set_gateway (const char *value)
1403 {
1404  router = aton (value);
1405  if (router)
1406  {
1407  _arp_kill_gateways(); /* delete gateways from cfg-file */
1408  TRACE (("DHCP: using previous gateway %s\n", value));
1409  _arp_add_gateway (NULL, router);
1410  }
1411  else
1412  TRACE (("DHCP: previous gateway is 0.0.0.0!\n"));
1413 }
1414 
1415 static void set_nameserv (const char *value)
1416 {
1417  TRACE (("DHCP: using previous nameserv %s\n", value));
1418  nameserver = aton (value);
1419  _add_server (&last_nameserver, def_nameservers, DIM(def_nameservers), nameserver);
1420 }
1421 
1422 static void set_server (const char *value)
1423 {
1424  TRACE (("DHCP: using previous server %s\n", value));
1425  dhcp_server = aton (value);
1426 }
1427 
1428 static void set_domain (const char *value)
1429 {
1430  TRACE (("DHCP: using previous domain %s\n", value));
1431  setdomainname (value, strlen(value)+1);
1432 }
1433 
1434 static void set_lease (const char *value)
1435 {
1436  cfg_dhcp_iplease = ATOL (value);
1437 }
1438 
1439 static void set_renew (const char *value)
1440 {
1441  cfg_dhcp_renewal = ATOL (value);
1442 }
1443 
1444 static void set_rebind (const char *value)
1445 {
1446  cfg_dhcp_rebind = ATOL (value);
1447 }
1448 
1460 static BOOL eval_timers (void)
1461 {
1462  time_t now = time (NULL);
1463 #if defined(USE_DEBUG)
1464  char ct_buf[30];
1465 #endif
1466 
1467  TRACE (("DHCP: IP-lease expires %s", ctime_r(&cfg_dhcp_iplease,ct_buf)));
1468  TRACE (("DHCP: rebinding expires %s", ctime_r(&cfg_dhcp_rebind,ct_buf)));
1469  TRACE (("DHCP: renewal expires %s", ctime_r(&cfg_dhcp_renewal,ct_buf)));
1470 
1471  if (cfg_dhcp_iplease < now)
1472  dhcp_iplease = DHCP_MIN_LEASE;
1473  else dhcp_iplease = (DWORD)(cfg_dhcp_iplease - now);
1474 
1475  if (cfg_dhcp_renewal < now)
1476  dhcp_renewal = dhcp_iplease / 2;
1477  else dhcp_renewal = (DWORD)(cfg_dhcp_renewal - now);
1478 
1479  if (cfg_dhcp_rebind < now)
1480  dhcp_rebind = dhcp_iplease * 7 / 8;
1481  else dhcp_rebind = (DWORD)(cfg_dhcp_rebind - now);
1482 
1483  discover_loops = 0;
1484 
1485  if (now < cfg_dhcp_renewal)
1486  {
1487  TRACE (("DHCP: BOUND\n"));
1488  DHCP_state = DHCP_state_BOUND; /* no action, goto BOUND */
1489  return (TRUE);
1490  }
1491 
1492  if (now >= cfg_dhcp_renewal && now < cfg_dhcp_rebind)
1493  {
1494  TRACE (("DHCP: RENEWING\n"));
1495  DHCP_state = DHCP_state_BOUND;
1496  (*DHCP_state) (EVENT_T1_TIMEOUT); /* goto RENEWING */
1497  return (FALSE);
1498  }
1499 
1500  if (now >= cfg_dhcp_rebind && now < cfg_dhcp_iplease)
1501  {
1502  TRACE (("DHCP: REBINDING\n"));
1503  DHCP_state = DHCP_state_RENEWING;
1504  (*DHCP_state) (EVENT_T2_TIMEOUT); /* goto REBINDING */
1505  return (FALSE);
1506  }
1507  return (FALSE);
1508 }
1509 
1516 static const struct config_table transient_cfg[] = {
1517  { "DHCP.LEASE", ARG_FUNC, (void*)set_lease },
1518  { "DHCP.RENEW", ARG_FUNC, (void*)set_renew },
1519  { "DHCP.REBIND", ARG_FUNC, (void*)set_rebind },
1520  { "DHCP.MY_IP", ARG_FUNC, (void*)set_my_ip },
1521  { "DHCP.NETMASK", ARG_FUNC, (void*)set_netmask },
1522  { "DHCP.GATEWAY", ARG_FUNC, (void*)set_gateway },
1523  { "DHCP.NAMESERV", ARG_FUNC, (void*)set_nameserv },
1524  { "DHCP.SERVER", ARG_FUNC, (void*)set_server },
1525  { "DHCP.DOMAIN", ARG_FUNC, (void*)set_domain },
1526  { "DHCP.HOSTNAME", ARG_STRCPY, (void*)hostname },
1527 #if defined(USE_BSD_API)
1528  { "DHCP.LOGHOST", ARG_STRCPY, (void*)&syslog_host_name },
1529 #endif
1530  { "DHCP.DEF_TTL", ARG_ATOI, (void*)&_default_ttl },
1531 #if !defined(USE_UDP_ONLY)
1532  { "DHCP.TCP_KEEP", ARG_ATOI, (void*)&tcp_keep_intvl },
1533 #endif
1534  { NULL, 0, NULL }
1535  };
1536 
1537 /*
1538  * Return name of transient config-file.
1539  */
1540 static const char *get_config_file (void)
1541 {
1542  if (config_file[0])
1543  return (config_file);
1544  return expand_var_str ("$(TEMP)\\W32DHCP.TMP");
1545 }
1546 
1547 /*
1548  * The standard DHCP config eraser.
1549  */
1550 static void std_erase_config (void)
1551 {
1552  unlink (get_config_file());
1553 }
1554 
1555 /*
1556  * The standard DHCP config reader.
1557  */
1558 static int std_read_config (void)
1559 {
1560  WFILE file;
1561  const char *fname = get_config_file();
1562 
1563  if (!FOPEN_TXT(file, fname))
1564  {
1565  TRACE (("`%s' not found\n", fname));
1566  return (0);
1567  }
1568 
1569  prev_hook = usr_init;
1570  usr_init = NULL; /* don't chain to other parsers */
1571  tcp_parse_file (file, &transient_cfg[0]);
1572  usr_init = prev_hook;
1573 
1574  FCLOSE (file);
1575  return (1);
1576 }
1577 
1583 static int std_write_config (void)
1584 {
1585  char buf[20];
1586  FILE *file;
1587  time_t tim, now = time (NULL);
1588  int rc = 0;
1589  const char *fname = get_config_file();
1590  char ct_buf [30];
1591 
1592  if (!FILE_EXIST(fname)) /* file not found, create */
1593  {
1594  file = fopen (fname, "w+t");
1595  if (!file)
1596  goto fail;
1597 
1598  rc = fprintf (file,
1599  "#\n"
1600  "# GENERATED FILE. DO NOT EDIT!\n"
1601  "#\n"
1602  "# DHCP transient configuration; values that must\n"
1603  "# be known between consecutive runs of applications.\n"
1604  "# Version: %s\n"
1605  "#\n", wattcpVersion());
1606  }
1607  else
1608  {
1609  file = fopen (fname, "at");
1610  if (!file)
1611  goto fail;
1612  }
1613 
1614  rc += fprintf (file, "# This file saved at %.20s\n#\n", ctime_r(&now,ct_buf)+4);
1615 
1616  tim = dhcp_iplease + now;
1617  rc += fprintf (file, "DHCP.LEASE = %-20lu # lease expires %.20s\n",
1618  (DWORD)tim, ctime_r(&tim,ct_buf)+4);
1619 
1620  tim = dhcp_renewal + now;
1621  rc += fprintf (file, "DHCP.RENEW = %-20lu # renew expires %.20s\n",
1622  (DWORD)tim, ctime_r(&tim,ct_buf)+4);
1623 
1624  tim = dhcp_rebind + now;
1625  rc += fprintf (file,
1626  "DHCP.REBIND = %-20lu # rebind expires %.20s\n"
1627  "DHCP.MY_IP = %-20s # assigned ip-address\n",
1628  (DWORD)tim, ctime_r(&tim,ct_buf)+4, _inet_ntoa(buf,my_ip_addr));
1629 
1630  rc += fprintf (file, "DHCP.NETMASK = %-20s # assigned netmask\n",
1631  _inet_ntoa(buf,sin_mask));
1632 
1633  rc += fprintf (file, "DHCP.GATEWAY = %-20s # assigned gateway\n",
1634  _inet_ntoa(buf,router));
1635 
1636  rc += fprintf (file, "DHCP.NAMESERV = %-20s # assigned nameserver\n",
1637  _inet_ntoa(buf,nameserver));
1638 
1639  rc += fprintf (file, "DHCP.SERVER = %-20s # DHCP server\n",
1640  _inet_ntoa(buf,dhcp_server));
1641 
1642  rc += fprintf (file, "DHCP.HOSTNAME = %-20s # assigned hostname\n",
1643  hostname);
1644 
1645  rc += fprintf (file, "DHCP.DOMAIN = %-20s # assigned domain\n",
1646  def_domain);
1647 
1648 #if defined(USE_BSD_API)
1649  if (syslog_host_name[0])
1650  rc += fprintf (file, "DHCP.LOGHOST = %-20s # assigned syslog host\n",
1651  syslog_host_name);
1652 #endif
1653 
1654  rc += fprintf (file, "DHCP.DEF_TTL = %-20d # default TTL\n",
1655  _default_ttl);
1656 
1657 #if !defined(USE_UDP_ONLY)
1658  rc += fprintf (file, "DHCP.TCP_KEEP = %-20d # TCP keepalive interval\n",
1659  tcp_keep_intvl);
1660 #endif
1661 
1662  FCLOSE (file);
1663  return (rc);
1664 
1665 fail:
1666  TCP_CONSOLE_MSG (0, ("Writing %s failed; %s\n", fname, strerror(errno)));
1667  return (0);
1668 }
1669 
1670 /*
1671  * The user-defined DHCP config eraser.
1672  */
1673 static void usr_erase_config (void)
1674 {
1675  (*config_func) (DHCP_OP_ERASE, NULL);
1676 }
1677 
1678 static int usr_read_config (void)
1679 {
1680  struct DHCP_config cfg;
1681 
1682  memset (&cfg, 0, sizeof(cfg));
1683 
1684  if (!(*config_func)(DHCP_OP_READ, &cfg))
1685  return (0);
1686 
1687  TRACE (("DHCP: using previous address %s\n", INET_NTOA(cfg.my_ip)));
1688  my_ip_addr = cfg.my_ip;
1689 
1690  TRACE (("DHCP: using previous netmask %s\n", INET_NTOA(cfg.netmask)));
1691  sin_mask = cfg.netmask;
1692 
1693  if (!_arp_have_default_gw())
1694  {
1695  TRACE (("DHCP: using previous gateway %s\n", INET_NTOA(cfg.gateway)));
1696  router = cfg.gateway;
1697  _arp_add_gateway (NULL, router);
1698  }
1699  else
1700  TRACE (("DHCP: already have default gateway\n"));
1701 
1702  TRACE (("DHCP: using previous nameserv %s\n", INET_NTOA(cfg.nameserver)));
1703  nameserver = cfg.nameserver;
1704  _add_server (&last_nameserver, def_nameservers, DIM(def_nameservers), nameserver);
1705 
1706  TRACE (("DHCP: using previous server %s\n", INET_NTOA(cfg.server)));
1707  dhcp_server = cfg.server;
1708 
1709  cfg_dhcp_iplease = cfg.iplease;
1710  cfg_dhcp_renewal = cfg.renewal;
1711  cfg_dhcp_rebind = cfg.rebind;
1712 
1713  _default_ttl = cfg.default_ttl;
1714 #if !defined(USE_UDP_ONLY)
1715  tcp_keep_intvl = cfg.tcp_keep_intvl;
1716 #endif
1717 
1718  _strlcpy (hostname, cfg._hostname, sizeof(hostname));
1719 
1720  TRACE (("DHCP: using previous domain %s\n", cfg.domain));
1721  setdomainname (cfg.domain, strlen(cfg.domain)+1);
1722 
1723 #if defined(USE_BSD_API)
1724  if (cfg.loghost[0])
1725  _strlcpy (syslog_host_name, cfg.loghost, sizeof(syslog_host_name));
1726 #endif
1727 
1728  return (1);
1729 }
1730 
1731 static int usr_write_config (void)
1732 {
1733  struct DHCP_config cfg;
1734  time_t now = time (NULL);
1735 
1736  memset (&cfg, 0, sizeof(cfg));
1737  cfg.my_ip = my_ip_addr;
1738  cfg.netmask = sin_mask;
1739  cfg.gateway = router;
1740  cfg.nameserver = nameserver;
1741  cfg.server = dhcp_server;
1742  cfg.iplease = (DWORD)now + dhcp_iplease;
1743  cfg.renewal = (DWORD)now + dhcp_renewal;
1744  cfg.rebind = (DWORD)now + dhcp_rebind;
1745  cfg.default_ttl = _default_ttl;
1746 #if !defined(USE_UDP_ONLY)
1747  cfg.tcp_keep_intvl = tcp_keep_intvl;
1748 #endif
1749 
1750  _strlcpy (cfg._hostname, hostname, sizeof(cfg._hostname));
1751  _strlcpy (cfg.domain, def_domain, sizeof(cfg.domain));
1752 
1753 #if defined(USE_BSD_API)
1754  if (syslog_host_name[0])
1755  _strlcpy (cfg.loghost, syslog_host_name, sizeof(cfg.loghost));
1756 #endif
1757 
1758  return (*config_func) (DHCP_OP_WRITE, &cfg);
1759 }
1760 
1764 static void erase_config (void)
1765 {
1766  config_func ? usr_erase_config() : std_erase_config();
1767 }
1768 
1774 int W32_CALL DHCP_read_config (void)
1775 {
1776  cfg_read = FALSE;
1777 
1778  if (config_func ? usr_read_config() : std_read_config() > 0)
1779  {
1780  sock = dhcp_open (NULL, bcast_flag);
1781  if (!sock)
1782  return (0);
1783 
1784  if (eval_timers())
1785  {
1786  cfg_read = TRUE;
1787  dhcp_set_timers();
1788  return (1);
1789  }
1790  TRACE (("DHCP: config too old.\n"));
1791  sock->udp.myaddr = 0;
1792  sock->udp.hisaddr = IP_BCAST_ADDR;
1793  }
1794 
1795  /* Reading config failed or timers expired; must do the whole
1796  * DHCP configuration when sock_init.c calls DHCP_do_boot().
1797  */
1798  return (0);
1799 }
1800 
1805 static int write_config (void)
1806 {
1807  return (config_func ? usr_write_config() : std_write_config());
1808 }
1809 
1813 WattDHCPConfigFunc W32_CALL DHCP_set_config_func (WattDHCPConfigFunc fn)
1814 {
1815  WattDHCPConfigFunc old_fn = config_func;
1816  config_func = fn;
1817  return (old_fn);
1818 }
1819 #endif /* USE_DHCP */
int W32_CALL parse_config_table(const struct config_table *tab, const char *section, const char *name, const char *value)
Parse the config-table and if a match is found for ('section'+'.
Definition: pcconfig.c:379
static void erase_config(void)
Erase the transient configuration.
Definition: pcdhcp.c:1764
unsigned W32_CALL Random(unsigned a, unsigned b)
Returns a random integer in range [a..b].
Definition: misc.c:742
static void change_ip_addr(void)
Change my_ip_addr of all tcp/udp sockets.
Definition: pcdhcp.c:1171
mac_address _eth_brdcast
Link-layer broadcast address.
Definition: pcsed.c:51
static int DHCP_request(BOOL renew)
Send a DHCP request message.
Definition: pcdhcp.c:318
void DHCP_init(void)
Initialises the DHCP config-parser.
Definition: pcdhcp.c:1380
static BOOL eval_timers(void)
Check timers read from config-file and set state according to following rules:
Definition: pcdhcp.c:1460
DWORD W32_CALL aton(const char *str)
Converts [a.b.c.d] or a.b.c.d to 32 bit IPv4 address.
Definition: netaddr.c:86
static void dhcp_fsm(void)
DHCP state machine: the event driver.
Definition: pcdhcp.c:995
static void DHCP_state_RENEWING(int event)
DHCP state machine: state RENEWING.
Definition: pcdhcp.c:892
static BYTE * put_hardware_opt(BYTE *opt)
Fill in the hardware type/len in Client ID option tag.
Definition: pcdhcp.c:270
convert to int
Definition: tcp.h:424
call convertion function
Definition: tcp.h:434
void W32_CALL DHCP_release(BOOL force)
Possibly send a DHCP release.
Definition: pcdhcp.c:707
DWORD W32_CALL DHCP_get_server(void)
Return current DHCP server address.
Definition: pcdhcp.c:739
int W32_CALL udp_open(_udp_Socket *s, WORD lport, DWORD ip, WORD port, ProtoHandler handler)
UDP active open.
Definition: pctcp.c:192
int _default_ttl
Definition: ip4_out.c:54
BOOL W32_CALL _arp_cache_add(DWORD ip, const void *eth, BOOL expires)
Add given IP/Ether address to ARP-cache.
Definition: pcarp.c:1352
BYTE W32_CALL _eth_get_hwtype(BYTE *hwtype, BYTE *hwlen)
Fill in hardware address type/length for BOOTP/DHCP packets.
Definition: pcsed.c:751
static BYTE * put_request_list(BYTE *opt, int filled)
Append options from 'request_list' to 'opt'.
Definition: pcdhcp.c:1240
int W32_CALL sock_close(sock_type *s)
Close a UDP/TCP socket.
Definition: pctcp.c:3139
static void DHCP_state_INIT(int event)
DHCP state machine: state INIT.
Definition: pcdhcp.c:959
_udp_Socket * _udp_allsocs
list of udp-sockets
Definition: pctcp.c:73
BOOL W32_CALL _arp_add_gateway(const char *config_string, DWORD ip)
Add a gateway to the routing table.
Definition: pcarp.c:247
static int write_config(void)
Write the transient DHCP configuration, either to file or via user-defined function.
Definition: pcdhcp.c:1805
BOOL W32_CALL _arp_check_own_ip(eth_address *other_guy)
Used by DHCP initialisation.
Definition: pcarp.c:1729
copy string value
Definition: tcp.h:432
resolve host to IPv4-address
Definition: tcp.h:433
static void set_my_ip(const char *value)
Functions called by config-file parser while reading c:/W32DHCP.TMP.
Definition: pcdhcp.c:1390
void W32_CALL _arp_kill_gateways(void)
Delete all gateways.
Definition: pcarp.c:373
Core definitions.
char * _strlcpy(char *dst, const char *src, size_t len)
Similar to strncpy(), but always returns 'dst' with 0-termination.
Definition: strings.c:226
const char * MAC_address(const void *addr)
Return hexa-decimal string for an 6/7 byte MAC-address.
Definition: misc.c:963
mac_address _eth_addr
Local link-layer source address.
Definition: pcsed.c:50
static void DHCP_state_BOUND(int event)
DHCP state machine: state BOUND.
Definition: pcdhcp.c:797
static int DHCP_release_decline(int msg_type, const char *msg)
Send a DHCP release or decline message.
Definition: pcdhcp.c:382
static void DHCP_exit(void)
Free allocated memory.
Definition: pcdhcp.c:1361
const char * expand_var_str(const char *str)
Return a string with a environment variable expanded (only one).
Definition: pcconfig.c:231
static void store_DHCP_server_MAC(void)
Add MAC address of DHCP sever to our ARP cache.
Definition: pcdhcp.c:783
static int DHCP_discover(void)
Send a DHCP discover message.
Definition: pcdhcp.c:286
DWORD W32_CALL set_timeout(DWORD msec)
Return time for when given timeout (msec) expires.
Definition: timer.c:503
static int set_vend_class(const char *value)
Set vendor class option.
Definition: pcdhcp.c:1315
DWORD sin_mask
our net-mask, 255.255.255.0
Definition: pctcp.c:71
static void DHCP_state_REQUESTING(int event)
DHCP state machine: state REQUESTING.
Definition: pcdhcp.c:816
BYTE _eth_mac_len
Size of a MAC address.
Definition: pcsed.c:54
Definition: ip.h:67
int W32_CALL sock_fastread(sock_type *s, BYTE *buf, int len)
Read a socket with maximum 'len' bytes.
Definition: pctcp.c:2931
static int DHCP_offer(const struct dhcp *in)
Parse DHCP offer reply.
Definition: pcdhcp.c:455
int W32_CALL setdomainname(const char *name, size_t len)
BSD-style: Set the host's domain name.
Definition: bsdname.c:213
static int DHCP_is_nack(void)
Return TRUE if DHCP message is a NACK.
Definition: pcdhcp.c:694
WORD _pktdevclass
Ethernet, Token, FDDI etc.
Definition: pcpkt.c:51
static const char * state_name(void)
Return name of current DHCP state function.
Definition: pcdhcp.c:212
static void dhcp_set_timers(void)
Set all timers required by the DHCP state machine.
Definition: pcdhcp.c:1144
int W32_CALL DHCP_read_config(void)
Called from watt_sock_init() after "\c WATTCP.CFG" has been parsed.
Definition: pcdhcp.c:1774
BOOL W32_CALL _arp_have_default_gw(void)
Check if we have at least one default gateway.
Definition: pcarp.c:382
WattDHCPConfigFunc W32_CALL DHCP_set_config_func(WattDHCPConfigFunc fn)
Sets up an application hook for doing DHCP operations (DHCP_config_op)
Definition: pcdhcp.c:1813
BOOL _pktserial
using serial driver, SLIP/PPP
Definition: pcpkt.c:54
static void DHCP_state_REBOOTING(int event)
DHCP state machine: Combined BOOT and BOOTING state.
Definition: pcdhcp.c:978
static BYTE * make_boot_header(void)
Format a BOOTP header.
Definition: pcdhcp.c:242
char hostname[MAX_HOSTLEN+1]
Our configured hostname.
Definition: pctcp.c:59
static void DHCP_state_REBINDING(int event)
DHCP state machine: state REBINDING.
Definition: pcdhcp.c:867
static int std_write_config(void)
Write the transient DHCP configuration to file.
Definition: pcdhcp.c:1583
static sock_type * dhcp_open(const char *msg, BOOL use_broadcast)
Allocate and open a UDP socket for DHCP message exchange.
Definition: pcdhcp.c:747
static int DHCP_is_ack(void)
Return TRUE if DHCP message is an ACK.
Definition: pcdhcp.c:684
static int set_user_class(const char *value)
Parse user-class as specified in config-file.
Definition: pcdhcp.c:1260
BOOL W32_CALL _arp_reply(const void *e_dst, DWORD src_ip, DWORD dst_ip)
Send unicast/broadcast ARP reply.
Definition: pcarp.c:489
Definition: pcdhcp.h:127
DWORD my_ip_addr
our IP address
Definition: pctcp.c:70
BOOL _watt_fatal_error
Definition: misc.c:60
char defaultdomain[MAX_HOSTLEN+1]
The 2 next variables are loaded from WATTCP.CFG file.
Definition: pcdns.c:71
static const char * period(DWORD sec)
Return nicely formatted string for a time-period.
Definition: pcdhcp.c:225
WORD W32_CALL tcp_tick(sock_type *s)
Must be called periodically by user application (or BSD socket API).
Definition: pctcp.c:1389
char * ctime_r(const time_t *t, char *res)
A reentrant ctime().
Definition: misc.c:878
int DHCP_do_boot(void)
Our first time DHCP handler.
Definition: pcdhcp.c:1056
static void W32_CALL DHCP_cfg_hook(const char *name, const char *value)
Parser for DHCP configuration.
Definition: pcdhcp.c:1338
_tcp_Socket * _tcp_allsocs
list of tcp-sockets
Definition: pctcp.c:137
unsigned tcp_keep_intvl
time between keepalive probes
Definition: pctcp.c:133
char *W32_CALL _inet_ntoa(char *s, DWORD ip)
Convert an IP-address 'ip' into a string.
Definition: netaddr.c:43
const char *W32_CALL wattcpVersion(void)
Return string for Watt-32 version.
Definition: version.c:76
static void DHCP_state_SELECTING(int event)
DHCP state machine: state SELECTING.
Definition: pcdhcp.c:928
WORD W32_CALL sock_dataready(sock_type *s)
sock_dataready - returns number of bytes waiting to be read.
Definition: sock_io.c:246
BOOL W32_CALL chk_timeout(DWORD value)
Check if milli-sec value has expired:
Definition: timer.c:547
static const struct config_table transient_cfg[]
Open and parse Wattcp transient configuration.
Definition: pcdhcp.c:1516
static void dhcp_options_add(const BYTE *opt, unsigned max)
Add options from a DHCP_OPT_OVERLOAD tag to 'extra_options' list.
Definition: pcdhcp.c:1111
static int set_request_list(char *options)
Parse a list of DHCP-request options from config-file.
Definition: pcdhcp.c:1193
struct tcp_Socket * next
link to next tcp-socket
Definition: wattcp.h:613
static BOOL DHCP_arp_check(DWORD my_ip)
Send an ARP reply to announce our new IP-address.
Definition: pcdhcp.c:420