diff --git a/contrib/isc-dhcp/client/dhclient.c b/contrib/isc-dhcp/client/dhclient.c index a84acb31a3a3..df36a9af52b3 100644 --- a/contrib/isc-dhcp/client/dhclient.c +++ b/contrib/isc-dhcp/client/dhclient.c @@ -3,8 +3,8 @@ DHCP Client. */ /* - * Copyright (c) 1995, 1996, 1997, 1998, 1999 - * The Internet Software Consortium. All rights reserved. + * Copyright (c) 1995-2001 Internet Software Consortium. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -15,7 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of The Internet Software Consortium nor the names + * 3. Neither the name of Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -33,30 +33,15 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * This software has been written for the Internet Software Consortium - * by Ted Lemon in cooperation with Vixie - * Enterprises. To learn more about the Internet Software Consortium, - * see ``http://www.vix.com/isc''. To learn more about Vixie - * Enterprises, see ``http://www.vix.com''. - * - * This client was substantially modified and enhanced by Elliot Poger - * for use on Linux while he was working on the MosquitoNet project at - * Stanford. - * - * The current version owes much to Elliot's Linux enhancements, but - * was substantially reorganized and partially rewritten by Ted Lemon - * so as to use the same networking framework that the Internet Software - * Consortium DHCP server uses. Much system-specific configuration code - * was moved into a shell script so that as support for more operating - * systems is added, it will not be necessary to port and maintain - * system-specific configuration code to these operating systems - instead, - * the shell script can invoke the native tools to accomplish the same - * purpose. + * This code is based on the original client state machine that was + * written by Elliot Poger. The code has been extensively hacked on + * by Ted Lemon since then, so any mistakes you find are probably his + * fault and not Elliot's. */ #ifndef lint static char ocopyright[] = -"$Id: dhclient.c,v 1.44.2.47 2000/09/06 20:59:09 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n" +"$Id: dhclient.c,v 1.129.2.7 2001/08/08 14:46:14 mellon Exp $ Copyright (c) 1995-2001 Internet Software Consortium. All rights reserved.\n" "$FreeBSD$\n"; #endif /* not lint */ @@ -66,40 +51,45 @@ static char ocopyright[] = TIME cur_time; TIME default_lease_time = 43200; /* 12 hours... */ TIME max_lease_time = 86400; /* 24 hours... */ -struct tree_cache *global_options [256]; -char *path_dhclient_conf = _PATH_DHCLIENT_CONF; -char *path_dhclient_db = _PATH_DHCLIENT_DB; -char *path_dhclient_pid = _PATH_DHCLIENT_PID; +const char *path_dhclient_conf = _PATH_DHCLIENT_CONF; +const char *path_dhclient_db = _PATH_DHCLIENT_DB; +const char *path_dhclient_pid = _PATH_DHCLIENT_PID; +static char path_dhclient_script_array [] = _PATH_DHCLIENT_SCRIPT; +char *path_dhclient_script = path_dhclient_script_array; + +int dhcp_max_agent_option_packet_length = 0; int interfaces_requested = 0; -int log_perror = 1; - struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } }; struct in_addr inaddr_any; struct sockaddr_in sockaddr_broadcast; +struct in_addr giaddr; /* ASSERT_STATE() does nothing now; it used to be assert (state_is == state_shouldbe). */ #define ASSERT_STATE(state_is, state_shouldbe) {} -u_int16_t local_port; -u_int16_t remote_port; -int log_priority; -int no_daemon; -int save_scripts; -int onetry; - -static char copyright[] = -"Copyright 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium."; +static char copyright[] = "Copyright 1995-2001 Internet Software Consortium."; static char arr [] = "All rights reserved."; static char message [] = "Internet Software Consortium DHCP Client"; -static char contrib [] = "Please contribute if you find this software useful."; -static char url [] = "For info, please visit http://www.isc.org/dhcp-contrib.html"; +static char url [] = "For info, please visit http://www.isc.org/products/DHCP"; -static void usage PROTO ((char *)); +u_int16_t local_port; +u_int16_t remote_port; +int no_daemon; +int save_scripts; +struct string_list *client_env; +int client_env_count; +int onetry; +int quiet = 1; +int nowait; + +static void usage PROTO ((void)); + +void do_release(struct client_state *); int main (argc, argv, envp) int argc; @@ -108,107 +98,242 @@ int main (argc, argv, envp) int i; struct servent *ent; struct interface_info *ip; - int seed; - int quiet = 1; + struct client_state *client; + unsigned seed; + char *server = (char *)0; + char *relay = (char *)0; + isc_result_t status; + int release_mode = 0; + omapi_object_t *listener; + isc_result_t result; + int persist = 0; + int omapi_port; + int no_dhclient_conf = 0; + int no_dhclient_db = 0; + int no_dhclient_pid = 0; + int no_dhclient_script = 0; char *s; - s = strrchr (argv [0], '/'); - if (!s) - s = argv [0]; - else - s++; + /* Make sure we have stdin, stdout and stderr. */ + i = open ("/dev/null", O_RDWR); + if (i == 0) + i = open ("/dev/null", O_RDWR); + if (i == 1) { + i = open ("/dev/null", O_RDWR); + log_perror = 0; /* No sense logging to /dev/null. */ + } else if (i != -1) + close (i); - /* Initially, log errors to stderr as well as to syslogd. */ #ifdef SYSLOG_4_2 - openlog (s, LOG_NDELAY); - log_priority = DHCPD_LOG_FACILITY; + openlog ("dhclient", LOG_NDELAY); + log_priority = LOG_DAEMON; #else - openlog (s, LOG_NDELAY, DHCPD_LOG_FACILITY); + openlog ("dhclient", LOG_NDELAY, LOG_DAEMON); #endif #if !(defined (DEBUG) || defined (SYSLOG_4_2) || defined (__CYGWIN32__)) setlogmask (LOG_UPTO (LOG_INFO)); #endif + /* Set up the OMAPI. */ + status = omapi_init (); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't initialize OMAPI: %s", + isc_result_totext (status)); + + /* Set up the OMAPI wrappers for various server database internal + objects. */ + dhcp_common_objects_setup (); + + dhcp_interface_discovery_hook = dhclient_interface_discovery_hook; + dhcp_interface_shutdown_hook = dhclient_interface_shutdown_hook; + dhcp_interface_startup_hook = dhclient_interface_startup_hook; + for (i = 1; i < argc; i++) { - if (!strcmp (argv [i], "-p")) { + if (!strcmp (argv [i], "-r")) { + release_mode = 1; + no_daemon = 1; + } else if (!strcmp (argv [i], "-p")) { if (++i == argc) - usage (s); + usage (); local_port = htons (atoi (argv [i])); - debug ("binding to user-specified port %d", + log_debug ("binding to user-specified port %d", ntohs (local_port)); } else if (!strcmp (argv [i], "-d")) { no_daemon = 1; - } else if (!strcmp (argv [i], "-D")) { - save_scripts = 1; - } else if (!strcmp (argv [i], "-cf")) { + } else if (!strcmp (argv [i], "-pf")) { + if (++i == argc) + usage (); + path_dhclient_pid = argv [i]; + no_dhclient_pid = 1; + } else if (!strcmp (argv [i], "-cf")) { + if (++i == argc) + usage (); + path_dhclient_conf = argv [i]; + no_dhclient_conf = 1; + } else if (!strcmp (argv [i], "-lf")) { + if (++i == argc) + usage (); + path_dhclient_db = argv [i]; + no_dhclient_db = 1; + } else if (!strcmp (argv [i], "-sf")) { if (++i == argc) - usage (s); - path_dhclient_conf = argv [i]; - } else if (!strcmp (argv [i], "-pf")) { - if (++i == argc) - usage (s); - path_dhclient_pid = argv [i]; - } else if (!strcmp (argv [i], "-lf")) { - if (++i == argc) - usage (s); - path_dhclient_db = argv [i]; + usage (); + path_dhclient_script = argv [i]; + no_dhclient_script = 1; + } else if (!strcmp (argv [i], "-1")) { + onetry = 1; } else if (!strcmp (argv [i], "-q")) { quiet = 1; quiet_interface_discovery = 1; - } else if (!strcmp (argv [i], "-1")) { - onetry = 1; + } else if (!strcmp (argv [i], "-s")) { + if (++i == argc) + usage (); + server = argv [i]; + } else if (!strcmp (argv [i], "-g")) { + if (++i == argc) + usage (); + relay = argv [i]; + } else if (!strcmp (argv [i], "-n")) { + /* do not start up any interfaces */ + interfaces_requested = 1; + } else if (!strcmp (argv [i], "-w")) { + /* do not exit if there are no broadcast interfaces. */ + persist = 1; } else if (argv [i][0] == '-') { - usage (s); + usage (); + } else if (!strcmp (argv [i], "-e")) { + struct string_list *tmp; + if (++i == argc) + usage (); + tmp = dmalloc (strlen (argv [i]) + sizeof *tmp, MDL); + if (!tmp) + log_fatal ("No memory for %s", argv [i]); + strcpy (tmp -> string, argv [i]); + tmp -> next = client_env; + client_env = tmp; + client_env_count++; + } else if (!strcmp (argv [i], "--version")) { + log_info ("isc-dhclient-%s", DHCP_VERSION); + exit (0); + } else if (!strcmp (argv [i], "-nw")) { + nowait = 1; } else { - struct interface_info *tmp = - ((struct interface_info *) - dmalloc (sizeof *tmp, "specified_interface")); - if (!tmp) - error ("Insufficient memory to %s %s", - "record interface", argv [i]); - memset (tmp, 0, sizeof *tmp); + struct interface_info *tmp = (struct interface_info *)0; + status = interface_allocate (&tmp, MDL); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't record interface %s:%s", + argv [i], isc_result_totext (status)); + if (strlen (argv [i]) > sizeof tmp -> name) + log_fatal ("%s: interface name too long (max %ld)", + argv [i], (long)strlen (argv [i])); strlcpy (tmp -> name, argv [i], IFNAMSIZ); - tmp -> next = interfaces; + if (interfaces) { + interface_reference (&tmp -> next, + interfaces, MDL); + interface_dereference (&interfaces, MDL); + } + interface_reference (&interfaces, tmp, MDL); tmp -> flags = INTERFACE_REQUESTED; interfaces_requested = 1; - interfaces = tmp; } } + if (!no_dhclient_conf && (s = getenv ("PATH_DHCLIENT_CONF"))) { + path_dhclient_conf = s; + } + if (!no_dhclient_db && (s = getenv ("PATH_DHCLIENT_DB"))) { + path_dhclient_db = s; + } + if (!no_dhclient_pid && (s = getenv ("PATH_DHCLIENT_PID"))) { + path_dhclient_pid = s; + } + if (!no_dhclient_script && (s = getenv ("PATH_DHCLIENT_SCRIPT"))) { + path_dhclient_script = s; + } + + /* first kill of any currently running client */ + if (release_mode) { + /* XXX inelegant hack to prove concept */ + char command[1024]; + +#if !defined (NO_SNPRINTF) + snprintf (command, 1024, "kill `cat %s`", path_dhclient_pid); +#else + sprintf (command, "kill `cat %s`", path_dhclient_pid); +#endif + system (command); + } + if (!quiet) { - note ("%s %s", message, DHCP_VERSION); - note (copyright); - note (arr); - note (""); - note (contrib); - note (url); - note (""); + log_info ("%s %s", message, DHCP_VERSION); + log_info (copyright); + log_info (arr); + log_info (url); + log_info ("%s", ""); } else log_perror = 0; + /* If we're given a relay agent address to insert, for testing + purposes, figure out what it is. */ + if (relay) { + if (!inet_aton (relay, &giaddr)) { + struct hostent *he; + he = gethostbyname (relay); + if (he) { + memcpy (&giaddr, he -> h_addr_list [0], + sizeof giaddr); + } else { + log_fatal ("%s: no such host", relay); + } + } + } + /* Default to the DHCP/BOOTP port. */ if (!local_port) { - ent = getservbyname ("dhcpc", "udp"); - if (!ent) - local_port = htons (68); - else - local_port = ent -> s_port; + if (relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) { + local_port = htons (67); + } else { + ent = getservbyname ("dhcpc", "udp"); + if (!ent) + local_port = htons (68); + else + local_port = ent -> s_port; #ifndef __CYGWIN32__ - endservent (); + endservent (); #endif + } } - remote_port = htons (ntohs (local_port) - 1); /* XXX */ + + /* If we're faking a relay agent, and we're not using loopback, + use the server port, not the client port. */ + if (relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) { + local_port = htons (ntohs (local_port) - 1); + remote_port = local_port; + } else + remote_port = htons (ntohs (local_port) - 1); /* XXX */ /* Get the current time... */ GET_TIME (&cur_time); sockaddr_broadcast.sin_family = AF_INET; sockaddr_broadcast.sin_port = remote_port; - sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; -#ifdef HAVE_SA_LEN - sockaddr_broadcast.sin_len = sizeof sockaddr_broadcast; -#endif + if (server) { + if (!inet_aton (server, &sockaddr_broadcast.sin_addr)) { + struct hostent *he; + he = gethostbyname (server); + if (he) { + memcpy (&sockaddr_broadcast.sin_addr, + he -> h_addr_list [0], + sizeof sockaddr_broadcast.sin_addr); + } else + sockaddr_broadcast.sin_addr.s_addr = + INADDR_BROADCAST; + } + } else { + sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; + } + inaddr_any.s_addr = INADDR_ANY; /* Discover all the network interfaces. */ @@ -223,17 +348,26 @@ int main (argc, argv, envp) /* Rewrite the lease database... */ rewrite_client_leases (); + /* XXX */ +/* config_counter(&snd_counter, &rcv_counter); */ + /* If no broadcast interfaces were discovered, call the script and tell it so. */ if (!interfaces) { - script_init ((struct interface_info *)0, "NBI", + /* Call dhclient-script with the NBI flag, in case somebody + cares. */ + script_init ((struct client_state *)0, "NBI", (struct string_list *)0); - script_go ((struct interface_info *)0); + script_go ((struct client_state *)0); - note ("No broadcast interfaces found - exiting."); - /* Nothing more to do. */ - exit (0); - } else { + /* If we haven't been asked to persist, waiting for new + interfaces, then just exit. */ + if (!persist) { + /* Nothing more to do. */ + log_info ("No broadcast interfaces found - exiting."); + exit (0); + } + } else if (!release_mode) { /* Call the script with the list of interfaces. */ for (ip = interfaces; ip; ip = ip -> next) { /* If interfaces were specified, don't configure @@ -243,11 +377,12 @@ int main (argc, argv, envp) INTERFACE_AUTOMATIC)) != INTERFACE_REQUESTED)) continue; - script_init (ip, "PREINIT", (struct string_list *)0); + script_init (ip -> client, + "PREINIT", (struct string_list *)0); if (ip -> client -> alias) - script_write_params (ip, "alias_", + script_write_params (ip -> client, "alias_", ip -> client -> alias); - script_go (ip); + script_go (ip -> client); } } @@ -264,25 +399,71 @@ int main (argc, argv, envp) interface's hardware address interpreted as an integer. Not much entropy, but we're booting, so we're not likely to find anything better. */ - seed = 0; /* Unfortunately, what's on the stack isn't random. :') */ + seed = 0; for (ip = interfaces; ip; ip = ip -> next) { int junk; memcpy (&junk, - &ip -> hw_address.haddr [ip -> hw_address.hlen - - sizeof seed], sizeof seed); + &ip -> hw_address.hbuf [ip -> hw_address.hlen - + sizeof seed], sizeof seed); seed += junk; } srandom (seed + cur_time); /* Start a configuration state machine for each interface. */ for (ip = interfaces; ip; ip = ip -> next) { - ip -> client -> state = S_INIT; - state_reboot (ip); + ip -> flags |= INTERFACE_RUNNING; + for (client = ip -> client; client; client = client -> next) { + if (release_mode) + do_release (client); + else { + client -> state = S_INIT; + /* Set up a timeout to start the initialization + process. */ + add_timeout (cur_time + random () % 5, + state_reboot, client, 0, 0); + } + } + } + + if (release_mode) + return 0; + + /* Start up a listener for the object management API protocol. */ + if (top_level_config.omapi_port != -1) { + listener = (omapi_object_t *)0; + result = omapi_generic_new (&listener, MDL); + if (result != ISC_R_SUCCESS) + log_fatal ("Can't allocate new generic object: %s\n", + isc_result_totext (result)); + result = omapi_protocol_listen (listener, + (unsigned) + top_level_config.omapi_port, + 1); + if (result != ISC_R_SUCCESS) + log_fatal ("Can't start OMAPI protocol: %s", + isc_result_totext (result)); } /* Set up the bootp packet handler... */ bootp_packet_handler = do_packet; +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + dmalloc_cutoff_generation = dmalloc_generation; + dmalloc_longterm = dmalloc_outstanding; + dmalloc_outstanding = 0; +#endif + + /* If we're not supposed to wait before getting the address, + don't. */ + if (nowait) + go_daemon (); + + /* If we're not going to daemonize, write the pid file + now. */ + if (no_daemon || nowait) + write_client_pid_file (); + /* Start dispatching packets and timeouts... */ dispatch (); @@ -290,25 +471,51 @@ int main (argc, argv, envp) return 0; } -static void usage (appname) - char *appname; +static void usage () { - note (message); - note (copyright); - note (arr); - note (""); - note (contrib); - note (url); - note (""); + log_info ("%s %s", message, DHCP_VERSION); + log_info (copyright); + log_info (arr); + log_info (url); - warn ("Usage: %s [-D] [-d] [-p ] [-cf conf-file]", appname); - error (" [-lf lease-file] [-pf pidfile] [-q] [-1] [interface]"); + log_error ("Usage: dhclient [-1Ddqr] [-nw] [-p ] %s", + "[-s server]"); + log_error (" [-cf config-file] [-lf lease-file]%s", + "[-pf pid-file] [-e VAR=val]"); + log_fatal (" [-sf script-file] [interface]"); } -void cleanup () +isc_result_t find_class (struct class **c, + const char *s, const char *file, int line) { - /* Make sure the pidfile is gone. */ - unlink (path_dhclient_pid); + return 0; +} + +int check_collection (packet, lease, collection) + struct packet *packet; + struct lease *lease; + struct collection *collection; +{ + return 0; +} + +void classify (packet, class) + struct packet *packet; + struct class *class; +{ +} + +int unbill_class (lease, class) + struct lease *lease; + struct class *class; +{ + return 0; +} + +int find_subnet (struct subnet **sp, + struct iaddr addr, const char *file, int line) +{ + return 0; } /* Individual States: @@ -340,152 +547,141 @@ void cleanup () * can no longer legitimately use the lease. */ -void state_reboot (ipp) - void *ipp; +void state_reboot (cpp) + void *cpp; { - struct interface_info *ip = ipp; + struct client_state *client = cpp; /* If we don't remember an active lease, go straight to INIT. */ - if (!ip -> client -> active || - ip -> client -> active -> is_bootp) { - state_init (ip); + if (!client -> active || + client -> active -> is_bootp || + client -> active -> expiry <= cur_time) { + state_init (client); return; } /* We are in the rebooting state. */ - ip -> client -> state = S_REBOOTING; + client -> state = S_REBOOTING; /* make_request doesn't initialize xid because it normally comes from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, so pick an xid now. */ - ip -> client -> xid = random (); + client -> xid = random (); /* Make a DHCPREQUEST packet, and set appropriate per-interface flags. */ - make_request (ip, ip -> client -> active); - ip -> client -> destination = iaddr_broadcast; - ip -> client -> first_sending = cur_time; - ip -> client -> interval = ip -> client -> config -> initial_interval; + make_request (client, client -> active); + client -> destination = iaddr_broadcast; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; /* Zap the medium list... */ - ip -> client -> medium = (struct string_list *)0; + client -> medium = (struct string_list *)0; /* Send out the first DHCPREQUEST packet. */ - send_request (ip); + send_request (client); } /* Called when a lease has completely expired and we've been unable to renew it. */ -void state_init (ipp) - void *ipp; +void state_init (cpp) + void *cpp; { - struct interface_info *ip = ipp; + struct client_state *client = cpp; ASSERT_STATE(state, S_INIT); /* Make a DHCPDISCOVER packet, and set appropriate per-interface flags. */ - make_discover (ip, ip -> client -> active); - ip -> client -> xid = ip -> client -> packet.xid; - ip -> client -> destination = iaddr_broadcast; - ip -> client -> state = S_SELECTING; - ip -> client -> first_sending = cur_time; - ip -> client -> interval = ip -> client -> config -> initial_interval; + make_discover (client, client -> active); + client -> xid = client -> packet.xid; + client -> destination = iaddr_broadcast; + client -> state = S_SELECTING; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; /* Add an immediate timeout to cause the first DHCPDISCOVER packet to go out. */ - send_discover (ip); + send_discover (client); } /* state_selecting is called when one or more DHCPOFFER packets have been received and a configurable period of time has passed. */ -void state_selecting (ipp) - void *ipp; +void state_selecting (cpp) + void *cpp; { - struct interface_info *ip = ipp; - + struct client_state *client = cpp; struct client_lease *lp, *next, *picked; + ASSERT_STATE(state, S_SELECTING); /* Cancel state_selecting and send_discover timeouts, since either one could have got us here. */ - cancel_timeout (state_selecting, ip); - cancel_timeout (send_discover, ip); + cancel_timeout (state_selecting, client); + cancel_timeout (send_discover, client); /* We have received one or more DHCPOFFER packets. Currently, the only criterion by which we judge leases is whether or not we get a response when we arp for them. */ picked = (struct client_lease *)0; - for (lp = ip -> client -> offered_leases; lp; lp = next) { + for (lp = client -> offered_leases; lp; lp = next) { next = lp -> next; /* Check to see if we got an ARPREPLY for the address in this particular lease. */ if (!picked) { - script_init (ip, "ARPCHECK", lp -> medium); - script_write_params (ip, "check_", lp); - - /* If the ARPCHECK code detects another - machine using the offered address, it exits - nonzero. We need to send a DHCPDECLINE and - toss the lease. */ - if (script_go (ip)) { - make_decline (ip, lp); - send_decline (ip); - goto freeit; - } picked = lp; picked -> next = (struct client_lease *)0; } else { freeit: - free_client_lease (lp); + destroy_client_lease (lp); } } - ip -> client -> offered_leases = (struct client_lease *)0; + client -> offered_leases = (struct client_lease *)0; /* If we just tossed all the leases we were offered, go back to square one. */ if (!picked) { - ip -> client -> state = S_INIT; - state_init (ip); + client -> state = S_INIT; + state_init (client); return; } /* If it was a BOOTREPLY, we can just take the address right now. */ - if (!picked -> options [DHO_DHCP_MESSAGE_TYPE].len) { - ip -> client -> new = picked; + if (picked -> is_bootp) { + client -> new = picked; /* Make up some lease expiry times XXX these should be configurable. */ - ip -> client -> new -> expiry = cur_time + 12000; - ip -> client -> new -> renewal += cur_time + 8000; - ip -> client -> new -> rebind += cur_time + 10000; + client -> new -> expiry = cur_time + 12000; + client -> new -> renewal += cur_time + 8000; + client -> new -> rebind += cur_time + 10000; - ip -> client -> state = S_REQUESTING; + client -> state = S_REQUESTING; /* Bind to the address we received. */ - bind_lease (ip); + bind_lease (client); return; } /* Go to the REQUESTING state. */ - ip -> client -> destination = iaddr_broadcast; - ip -> client -> state = S_REQUESTING; - ip -> client -> first_sending = cur_time; - ip -> client -> interval = ip -> client -> config -> initial_interval; + client -> destination = iaddr_broadcast; + client -> state = S_REQUESTING; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; /* Make a DHCPREQUEST packet from the lease we picked. */ - make_request (ip, picked); - ip -> client -> xid = ip -> client -> packet.xid; + make_request (client, picked); + client -> xid = client -> packet.xid; /* Toss the lease we picked - we'll get it back in a DHCPACK. */ - free_client_lease (picked); + destroy_client_lease (picked); /* Add an immediate timeout to send the first DHCPREQUEST packet. */ - send_request (ip); + send_request (client); } /* state_requesting is called when we receive a DHCPACK message after @@ -495,128 +691,200 @@ void dhcpack (packet) struct packet *packet; { struct interface_info *ip = packet -> interface; + struct client_state *client; struct client_lease *lease; + struct option_cache *oc; + struct data_string ds; + int i; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ - if (packet -> interface -> client -> xid != packet -> raw -> xid || - (packet -> interface -> hw_address.hlen != + for (client = ip -> client; client; client = client -> next) { + if (client -> xid == packet -> raw -> xid) + break; + } + if (!client || + (packet -> interface -> hw_address.hlen - 1 != packet -> raw -> hlen) || - (memcmp (packet -> interface -> hw_address.haddr, + (memcmp (&packet -> interface -> hw_address.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen))) { #if defined (DEBUG) - debug ("DHCPACK in wrong transaction."); + log_debug ("DHCPACK in wrong transaction."); #endif return; } - if (ip -> client -> state != S_REBOOTING && - ip -> client -> state != S_REQUESTING && - ip -> client -> state != S_RENEWING && - ip -> client -> state != S_REBINDING) { + if (client -> state != S_REBOOTING && + client -> state != S_REQUESTING && + client -> state != S_RENEWING && + client -> state != S_REBINDING) { #if defined (DEBUG) - debug ("DHCPACK in wrong state."); + log_debug ("DHCPACK in wrong state."); #endif return; } - note ("DHCPACK from %s", piaddr (packet -> client_addr)); + log_info ("DHCPACK from %s", piaddr (packet -> client_addr)); - lease = packet_to_lease (packet); + lease = packet_to_lease (packet, client); if (!lease) { - note ("packet_to_lease failed."); + log_info ("packet_to_lease failed."); return; } - ip -> client -> new = lease; + client -> new = lease; /* Stop resending DHCPREQUEST. */ - cancel_timeout (send_request, ip); + cancel_timeout (send_request, client); /* Figure out the lease time. */ - ip -> client -> new -> expiry = - getULong (ip -> client -> - new -> options [DHO_DHCP_LEASE_TIME].data); + oc = lookup_option (&dhcp_universe, client -> new -> options, + DHO_DHCP_LEASE_TIME); + memset (&ds, 0, sizeof ds); + if (oc && + evaluate_option_cache (&ds, packet, (struct lease *)0, client, + packet -> options, client -> new -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) + client -> new -> expiry = getULong (ds.data); + else + client -> new -> expiry = 0; + data_string_forget (&ds, MDL); + } else + client -> new -> expiry = 0; + + if (!client -> new -> expiry) { + log_error ("no expiry time on offered lease."); + /* XXX this is going to be bad - if this _does_ + XXX happen, we should probably dynamically + XXX disqualify the DHCP server that gave us the + XXX bad packet from future selections and + XXX then go back into the init state. */ + state_init (client); + return; + } + /* A number that looks negative here is really just very large, because the lease expiry offset is unsigned. */ - if (ip -> client -> new -> expiry < 0) - ip -> client -> new -> expiry = TIME_MAX; + if (client -> new -> expiry < 0) + client -> new -> expiry = TIME_MAX; + /* Take the server-provided renewal time if there is one. */ + oc = lookup_option (&dhcp_universe, client -> new -> options, + DHO_DHCP_RENEWAL_TIME); + if (oc && + evaluate_option_cache (&ds, packet, (struct lease *)0, client, + packet -> options, client -> new -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) + client -> new -> renewal = getULong (ds.data); + else + client -> new -> renewal = 0; + data_string_forget (&ds, MDL); + } else + client -> new -> renewal = 0; - /* Take the server-provided renewal time if there is one; - otherwise figure it out according to the spec. */ - if (ip -> client -> new -> options [DHO_DHCP_RENEWAL_TIME].len) - ip -> client -> new -> renewal = - getULong (ip -> client -> - new -> options [DHO_DHCP_RENEWAL_TIME].data); - else - ip -> client -> new -> renewal = - ip -> client -> new -> expiry / 2; + /* If it wasn't specified by the server, calculate it. */ + if (!client -> new -> renewal) + client -> new -> renewal = + client -> new -> expiry / 2; + + /* Now introduce some randomness to the renewal time: */ + client -> new -> renewal = (((client -> new -> renewal + 3) * 3 / 4) + + (random () % /* XXX NUMS */ + ((client -> new -> renewal + 3) / 4))); /* Same deal with the rebind time. */ - if (ip -> client -> new -> options [DHO_DHCP_REBINDING_TIME].len) - ip -> client -> new -> rebind = - getULong (ip -> client -> new -> - options [DHO_DHCP_REBINDING_TIME].data); - else - ip -> client -> new -> rebind = - ip -> client -> new -> renewal + - ip -> client -> new -> renewal / 2 + - ip -> client -> new -> renewal / 4; + oc = lookup_option (&dhcp_universe, client -> new -> options, + DHO_DHCP_REBINDING_TIME); + if (oc && + evaluate_option_cache (&ds, packet, (struct lease *)0, client, + packet -> options, client -> new -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) + client -> new -> rebind = getULong (ds.data); + else + client -> new -> rebind = 0; + data_string_forget (&ds, MDL); + } else + client -> new -> rebind = 0; - ip -> client -> new -> expiry += cur_time; + if (!client -> new -> rebind) + client -> new -> rebind = + (client -> new -> expiry * 7) / 8; /* XXX NUMS */ + + /* Make sure our randomness didn't run the renewal time past the + rebind time. */ + if (client -> new -> renewal > client -> new -> rebind) + client -> new -> renewal = (client -> new -> rebind * 3) / 4; + + client -> new -> expiry += cur_time; /* Lease lengths can never be negative. */ - if (ip -> client -> new -> expiry < cur_time) - ip -> client -> new -> expiry = TIME_MAX; - ip -> client -> new -> renewal += cur_time; - if (ip -> client -> new -> renewal < cur_time) - ip -> client -> new -> renewal = TIME_MAX; - ip -> client -> new -> rebind += cur_time; - if (ip -> client -> new -> rebind < cur_time) - ip -> client -> new -> rebind = TIME_MAX; + if (client -> new -> expiry < cur_time) + client -> new -> expiry = TIME_MAX; + client -> new -> renewal += cur_time; + if (client -> new -> renewal < cur_time) + client -> new -> renewal = TIME_MAX; + client -> new -> rebind += cur_time; + if (client -> new -> rebind < cur_time) + client -> new -> rebind = TIME_MAX; - bind_lease (ip); + bind_lease (client); } -void bind_lease (ip) - struct interface_info *ip; +void bind_lease (client) + struct client_state *client; { - /* Remember the medium. */ - ip -> client -> new -> medium = ip -> client -> medium; + struct interface_info *ip = client -> interface; - /* Write out the new lease. */ - write_client_lease (ip, ip -> client -> new, 0); + /* Remember the medium. */ + client -> new -> medium = client -> medium; /* Run the client script with the new parameters. */ - script_init (ip, (ip -> client -> state == S_REQUESTING + script_init (client, (client -> state == S_REQUESTING ? "BOUND" - : (ip -> client -> state == S_RENEWING + : (client -> state == S_RENEWING ? "RENEW" - : (ip -> client -> state == S_REBOOTING + : (client -> state == S_REBOOTING ? "REBOOT" : "REBIND"))), - ip -> client -> new -> medium); - if (ip -> client -> active && ip -> client -> state != S_REBOOTING) - script_write_params (ip, "old_", ip -> client -> active); - script_write_params (ip, "new_", ip -> client -> new); - if (ip -> client -> alias) - script_write_params (ip, "alias_", ip -> client -> alias); - script_go (ip); + client -> new -> medium); + if (client -> active && client -> state != S_REBOOTING) + script_write_params (client, "old_", client -> active); + script_write_params (client, "new_", client -> new); + if (client -> alias) + script_write_params (client, "alias_", client -> alias); + + /* If the BOUND/RENEW code detects another machine using the + offered address, it exits nonzero. We need to send a + DHCPDECLINE and toss the lease. */ + if (script_go (client)) { + make_decline (client, client -> new); + send_decline (client); + destroy_client_lease (client -> new); + client -> new = (struct client_lease *)0; + state_init (client); + return; + } + + /* Write out the new lease. */ + write_client_lease (client, client -> new, 0, 0); /* Replace the old active lease with the new one. */ - if (ip -> client -> active) - free_client_lease (ip -> client -> active); - ip -> client -> active = ip -> client -> new; - ip -> client -> new = (struct client_lease *)0; + if (client -> active) + destroy_client_lease (client -> active); + client -> active = client -> new; + client -> new = (struct client_lease *)0; /* Set up a timeout to start the renewal process. */ - add_timeout (ip -> client -> active -> renewal, - state_bound, ip); + add_timeout (client -> active -> renewal, + state_bound, client, 0, 0); - note ("bound to %s -- renewal in %d seconds.", - piaddr (ip -> client -> active -> address), - ip -> client -> active -> renewal - cur_time); - ip -> client -> state = S_BOUND; + log_info ("bound to %s -- renewal in %ld seconds.", + piaddr (client -> active -> address), + (long)(client -> active -> renewal - cur_time)); + client -> state = S_BOUND; reinitialize_interfaces (); go_daemon (); + client_dns_update (client, 1); } /* state_bound is called when we've successfully bound to a particular @@ -624,32 +892,68 @@ void bind_lease (ip) expected to unicast a DHCPREQUEST to the server that gave us our original lease. */ -void state_bound (ipp) - void *ipp; +void state_bound (cpp) + void *cpp; { - struct interface_info *ip = ipp; + struct client_state *client = cpp; + int i; + struct option_cache *oc; + struct data_string ds; ASSERT_STATE(state, S_BOUND); /* T1 has expired. */ - make_request (ip, ip -> client -> active); - ip -> client -> xid = ip -> client -> packet.xid; + make_request (client, client -> active); + client -> xid = client -> packet.xid; - if (ip -> client -> active -> - options [DHO_DHCP_SERVER_IDENTIFIER].len == 4) { - memcpy (ip -> client -> destination.iabuf, - ip -> client -> active -> - options [DHO_DHCP_SERVER_IDENTIFIER].data, 4); - ip -> client -> destination.len = 4; + memset (&ds, 0, sizeof ds); + oc = lookup_option (&dhcp_universe, client -> active -> options, + DHO_DHCP_SERVER_IDENTIFIER); + if (oc && + evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, + client, (struct option_state *)0, + client -> active -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) { + memcpy (client -> destination.iabuf, ds.data, 4); + client -> destination.len = 4; + } else + client -> destination = iaddr_broadcast; } else - ip -> client -> destination = iaddr_broadcast; + client -> destination = iaddr_broadcast; - ip -> client -> first_sending = cur_time; - ip -> client -> interval = ip -> client -> config -> initial_interval; - ip -> client -> state = S_RENEWING; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; + client -> state = S_RENEWING; /* Send the first packet immediately. */ - send_request (ip); + send_request (client); +} + +/* state_stop is called when we've been told to shut down. We unconfigure + the interfaces, and then stop operating until told otherwise. */ + +void state_stop (cpp) + void *cpp; +{ + struct client_state *client = cpp; + int i; + + /* Cancel all timeouts. */ + cancel_timeout (state_selecting, client); + cancel_timeout (send_discover, client); + cancel_timeout (send_request, client); + cancel_timeout (state_bound, client); + + /* If we have an address, unconfigure it. */ + if (client -> active) { + script_init (client, "STOP", client -> active -> medium); + script_write_params (client, "old_", client -> active); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_go (client); + } } int commit_leases () @@ -663,7 +967,14 @@ int write_lease (lease) return 0; } -void db_startup () +int write_host (host) + struct host_decl *host; +{ + return 0; +} + +void db_startup (testp) + int testp; { } @@ -680,7 +991,7 @@ void bootp (packet) for (ap = packet -> interface -> client -> config -> reject_list; ap; ap = ap -> next) { if (addr_eq (packet -> client_addr, ap -> addr)) { - note ("BOOTREPLY from %s rejected.", + log_info ("BOOTREPLY from %s rejected.", piaddr (ap -> addr)); return; } @@ -695,7 +1006,7 @@ void dhcp (packet) { struct iaddrlist *ap; void (*handler) PROTO ((struct packet *)); - char *type; + const char *type; switch (packet -> packet_type) { case DHCPOFFER: @@ -722,7 +1033,7 @@ void dhcp (packet) for (ap = packet -> interface -> client -> config -> reject_list; ap; ap = ap -> next) { if (addr_eq (packet -> client_addr, ap -> addr)) { - note ("%s from %s rejected.", + log_info ("%s from %s rejected.", type, piaddr (ap -> addr)); return; } @@ -734,186 +1045,196 @@ void dhcpoffer (packet) struct packet *packet; { struct interface_info *ip = packet -> interface; + struct client_state *client; struct client_lease *lease, *lp; int i; - int arp_timeout_needed, stop_selecting; - char *name = (packet -> options [DHO_DHCP_MESSAGE_TYPE].len - ? "DHCPOFFER" : "BOOTREPLY"); + int stop_selecting; + const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY"; + struct iaddrlist *ap; + struct option_cache *oc; + char obuf [1024]; #ifdef DEBUG_PACKET dump_packet (packet); #endif + /* Find a client state that matches the xid... */ + for (client = ip -> client; client; client = client -> next) + if (client -> xid == packet -> raw -> xid) + break; + /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ - if (ip -> client -> state != S_SELECTING || - packet -> interface -> client -> xid != packet -> raw -> xid || - (packet -> interface -> hw_address.hlen != + if (!client || + client -> state != S_SELECTING || + (packet -> interface -> hw_address.hlen - 1 != packet -> raw -> hlen) || - (memcmp (packet -> interface -> hw_address.haddr, + (memcmp (&packet -> interface -> hw_address.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen))) { #if defined (DEBUG) - debug ("%s in wrong transaction.", name); + log_debug ("%s in wrong transaction.", name); #endif return; } - note ("%s from %s", name, piaddr (packet -> client_addr)); + sprintf (obuf, "%s from %s", name, piaddr (packet -> client_addr)); /* If this lease doesn't supply the minimum required parameters, blow it off. */ - for (i = 0; ip -> client -> config -> required_options [i]; i++) { - if (!packet -> options [ip -> client -> config -> - required_options [i]].len) { - note ("%s isn't satisfactory.", name); - return; + if (client -> config -> required_options) { + for (i = 0; client -> config -> required_options [i]; i++) { + if (!lookup_option + (&dhcp_universe, packet -> options, + client -> config -> required_options [i])) { + log_info ("%s: no %s option.", + obuf, (dhcp_universe.options + [client -> config -> required_options [i]] + -> name)); + return; + } } } /* If we've already seen this lease, don't record it again. */ - for (lease = ip -> client -> offered_leases; - lease; lease = lease -> next) { + for (lease = client -> offered_leases; lease; lease = lease -> next) { if (lease -> address.len == sizeof packet -> raw -> yiaddr && !memcmp (lease -> address.iabuf, &packet -> raw -> yiaddr, lease -> address.len)) { - debug ("%s already seen.", name); + log_debug ("%s: already seen.", obuf); return; } } - lease = packet_to_lease (packet); + lease = packet_to_lease (packet, client); if (!lease) { - note ("packet_to_lease failed."); + log_info ("%s: packet_to_lease failed.", obuf); return; } /* If this lease was acquired through a BOOTREPLY, record that fact. */ - if (!packet -> options [DHO_DHCP_MESSAGE_TYPE].len) + if (!packet -> options_valid || !packet -> packet_type) lease -> is_bootp = 1; /* Record the medium under which this lease was offered. */ - lease -> medium = ip -> client -> medium; - - /* Send out an ARP Request for the offered IP address. */ - script_init (ip, "ARPSEND", lease -> medium); - script_write_params (ip, "check_", lease); - /* If the script can't send an ARP request without waiting, - we'll be waiting when we do the ARPCHECK, so don't wait now. */ - if (script_go (ip)) - arp_timeout_needed = 0; - else - arp_timeout_needed = 2; + lease -> medium = client -> medium; /* Figure out when we're supposed to stop selecting. */ - stop_selecting = (ip -> client -> first_sending + - ip -> client -> config -> select_interval); + stop_selecting = (client -> first_sending + + client -> config -> select_interval); /* If this is the lease we asked for, put it at the head of the list, and don't mess with the arp request timeout. */ - if (lease -> address.len == ip -> client -> requested_address.len && + if (lease -> address.len == client -> requested_address.len && !memcmp (lease -> address.iabuf, - ip -> client -> requested_address.iabuf, - ip -> client -> requested_address.len)) { - lease -> next = ip -> client -> offered_leases; - ip -> client -> offered_leases = lease; + client -> requested_address.iabuf, + client -> requested_address.len)) { + lease -> next = client -> offered_leases; + client -> offered_leases = lease; } else { - /* If we already have an offer, and arping for this - offer would take us past the selection timeout, - then don't extend the timeout - just hope for the - best. */ - if (ip -> client -> offered_leases && - (cur_time + arp_timeout_needed) > stop_selecting) - arp_timeout_needed = 0; - /* Put the lease at the end of the list. */ lease -> next = (struct client_lease *)0; - if (!ip -> client -> offered_leases) - ip -> client -> offered_leases = lease; + if (!client -> offered_leases) + client -> offered_leases = lease; else { - for (lp = ip -> client -> offered_leases; lp -> next; + for (lp = client -> offered_leases; lp -> next; lp = lp -> next) ; lp -> next = lease; } } - /* If we're supposed to stop selecting before we've had time - to wait for the ARPREPLY, add some delay to wait for - the ARPREPLY. */ - if (stop_selecting - cur_time < arp_timeout_needed) - stop_selecting = cur_time + arp_timeout_needed; - /* If the selecting interval has expired, go immediately to state_selecting(). Otherwise, time out into state_selecting at the select interval. */ if (stop_selecting <= 0) - state_selecting (ip); + state_selecting (client); else { - add_timeout (stop_selecting, state_selecting, ip); - cancel_timeout (send_discover, ip); + add_timeout (stop_selecting, state_selecting, client, 0, 0); + cancel_timeout (send_discover, client); } + log_info ("%s", obuf); } /* Allocate a client_lease structure and initialize it from the parameters in the specified packet. */ -struct client_lease *packet_to_lease (packet) +struct client_lease *packet_to_lease (packet, client) struct packet *packet; + struct client_state *client; { struct client_lease *lease; - int i; + unsigned i; + struct option_cache *oc; + struct data_string data; - lease = (struct client_lease *)malloc (sizeof (struct client_lease)); + lease = (struct client_lease *)new_client_lease (MDL); if (!lease) { - warn ("dhcpoffer: no memory to record lease.\n"); + log_error ("packet_to_lease: no memory to record lease.\n"); return (struct client_lease *)0; } memset (lease, 0, sizeof *lease); /* Copy the lease options. */ - for (i = 0; i < 256; i++) { - if (packet -> options [i].len) { - lease -> options [i].data = - (unsigned char *) - malloc (packet -> options [i].len + 1); - if (!lease -> options [i].data) { - warn ("dhcpoffer: no memory for option %d\n", - i); - free_client_lease (lease); - return (struct client_lease *)0; - } else { - memcpy (lease -> options [i].data, - packet -> options [i].data, - packet -> options [i].len); - lease -> options [i].len = - packet -> options [i].len; - lease -> options [i].data - [lease -> options [i].len] = 0; - } - } - } + option_state_reference (&lease -> options, packet -> options, MDL); lease -> address.len = sizeof (packet -> raw -> yiaddr); memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr, lease -> address.len); + if (client -> config -> vendor_space_name) { + i = DHO_VENDOR_ENCAPSULATED_OPTIONS; + + /* See if there was a vendor encapsulation option. */ + oc = lookup_option (&dhcp_universe, lease -> options, i); + memset (&data, 0, sizeof data); + if (oc && + client -> config -> vendor_space_name && + evaluate_option_cache (&data, packet, + (struct lease *)0, client, + packet -> options, lease -> options, + &global_scope, oc, MDL)) { + if (data.len) { + parse_encapsulated_suboptions + (packet -> options, &dhcp_options [i], + data.data, data.len, &dhcp_universe, + client -> config -> vendor_space_name + ); + } + data_string_forget (&data, MDL); + } + } else + i = 0; + + /* Figure out the overload flag. */ + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_OPTION_OVERLOAD); + if (oc && + evaluate_option_cache (&data, packet, (struct lease *)0, client, + packet -> options, lease -> options, + &global_scope, oc, MDL)) { + if (data.len > 0) + i = data.data [0]; + else + i = 0; + data_string_forget (&data, MDL); + } else + i = 0; + /* If the server name was filled out, copy it. */ - if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len || - !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)) && - packet -> raw -> sname [0]) { - int len; + if (!(i & 2) && packet -> raw -> sname [0]) { + unsigned len; /* Don't count on the NUL terminator. */ for (len = 0; len < 64; len++) if (!packet -> raw -> sname [len]) break; - lease -> server_name = malloc (len + 1); + lease -> server_name = dmalloc (len + 1, MDL); if (!lease -> server_name) { - warn ("dhcpoffer: no memory for filename.\n"); - free_client_lease (lease); + log_error ("dhcpoffer: no memory for filename.\n"); + destroy_client_lease (lease); return (struct client_lease *)0; } else { memcpy (lease -> server_name, @@ -923,18 +1244,16 @@ struct client_lease *packet_to_lease (packet) } /* Ditto for the filename. */ - if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len || - !(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)) && - packet -> raw -> file [0]) { - int len; + if (!(i & 1) && packet -> raw -> file [0]) { + unsigned len; /* Don't count on the NUL terminator. */ for (len = 0; len < 64; len++) if (!packet -> raw -> file [len]) break; - lease -> filename = malloc (len + 1); + lease -> filename = dmalloc (len + 1, MDL); if (!lease -> filename) { - warn ("dhcpoffer: no memory for filename.\n"); - free_client_lease (lease); + log_error ("dhcpoffer: no memory for filename.\n"); + destroy_client_lease (lease); return (struct client_lease *)0; } else { memcpy (lease -> filename, @@ -942,6 +1261,15 @@ struct client_lease *packet_to_lease (packet) lease -> filename [len] = 0; } } + + execute_statements_in_scope ((struct binding_value **)0, + (struct packet *)packet, + (struct lease *)0, client, + lease -> options, lease -> options, + &global_scope, + client -> config -> on_receipt, + (struct group *)0); + return lease; } @@ -949,95 +1277,103 @@ void dhcpnak (packet) struct packet *packet; { struct interface_info *ip = packet -> interface; + struct client_state *client; + + /* Find a client state that matches the xid... */ + for (client = ip -> client; client; client = client -> next) + if (client -> xid == packet -> raw -> xid) + break; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ - if (packet -> interface -> client -> xid != packet -> raw -> xid || - (packet -> interface -> hw_address.hlen != + if (!client || + (packet -> interface -> hw_address.hlen - 1 != packet -> raw -> hlen) || - (memcmp (packet -> interface -> hw_address.haddr, + (memcmp (&packet -> interface -> hw_address.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen))) { #if defined (DEBUG) - debug ("DHCPNAK in wrong transaction."); + log_debug ("DHCPNAK in wrong transaction."); #endif return; } - if (ip -> client -> state != S_REBOOTING && - ip -> client -> state != S_REQUESTING && - ip -> client -> state != S_RENEWING && - ip -> client -> state != S_REBINDING) { + if (client -> state != S_REBOOTING && + client -> state != S_REQUESTING && + client -> state != S_RENEWING && + client -> state != S_REBINDING) { #if defined (DEBUG) - debug ("DHCPNAK in wrong state."); + log_debug ("DHCPNAK in wrong state."); #endif return; } - note ("DHCPNAK from %s", piaddr (packet -> client_addr)); + log_info ("DHCPNAK from %s", piaddr (packet -> client_addr)); - if (!ip -> client -> active) { - note ("DHCPNAK with no active lease.\n"); + if (!client -> active) { +#if defined (DEBUG) + log_info ("DHCPNAK with no active lease.\n"); +#endif return; } - free_client_lease (ip -> client -> active); - ip -> client -> active = (struct client_lease *)0; + destroy_client_lease (client -> active); + client -> active = (struct client_lease *)0; /* Stop sending DHCPREQUEST packets... */ - cancel_timeout (send_request, ip); + cancel_timeout (send_request, client); - ip -> client -> state = S_INIT; - state_init (ip); + client -> state = S_INIT; + state_init (client); } /* Send out a DHCPDISCOVER packet, and set a timeout to send out another one after the right interval has expired. If we don't get an offer by the time we reach the panic interval, call the panic function. */ -void send_discover (ipp) - void *ipp; +void send_discover (cpp) + void *cpp; { - struct interface_info *ip = ipp; + struct client_state *client = cpp; int result; int interval; int increase = 1; /* Figure out how long it's been since we started transmitting. */ - interval = cur_time - ip -> client -> first_sending; + interval = cur_time - client -> first_sending; /* If we're past the panic timeout, call the script and tell it we haven't found anything for this interface yet. */ - if (interval > ip -> client -> config -> timeout) { - state_panic (ip); + if (interval > client -> config -> timeout) { + state_panic (client); return; } /* If we're selecting media, try the whole list before doing the exponential backoff, but if we've already received an offer, stop looping, because we obviously have it right. */ - if (!ip -> client -> offered_leases && - ip -> client -> config -> media) { + if (!client -> offered_leases && + client -> config -> media) { int fail = 0; again: - if (ip -> client -> medium) { - ip -> client -> medium = - ip -> client -> medium -> next; + if (client -> medium) { + client -> medium = client -> medium -> next; increase = 0; } - if (!ip -> client -> medium) { + if (!client -> medium) { if (fail) - error ("No valid media types for %s!", - ip -> name); - ip -> client -> medium = - ip -> client -> config -> media; + log_fatal ("No valid media types for %s!", + client -> interface -> name); + client -> medium = + client -> config -> media; increase = 1; } - note ("Trying medium \"%s\" %d", - ip -> client -> medium -> string, increase); - script_init (ip, "MEDIUM", ip -> client -> medium); - if (script_go (ip)) { + log_info ("Trying medium \"%s\" %d", + client -> medium -> string, increase); + script_init (client, "MEDIUM", client -> medium); + if (script_go (client)) { + fail = 1; goto again; } } @@ -1048,54 +1384,52 @@ void send_discover (ipp) zero and two times itself. On average, this means that it will double with every transmission. */ if (increase) { - if (!ip -> client -> interval) - ip -> client -> interval = - ip -> client -> config -> initial_interval; - else { - ip -> client -> interval += - ((random () >> 2) % - (2 * ip -> client -> interval)); - } + if (!client -> interval) + client -> interval = + client -> config -> initial_interval; + else + client -> interval += ((random () >> 2) % + (2 * client -> interval)); /* Don't backoff past cutoff. */ - if (ip -> client -> interval > - ip -> client -> config -> backoff_cutoff) - ip -> client -> interval = - ((ip -> client -> config -> backoff_cutoff / 2) + if (client -> interval > + client -> config -> backoff_cutoff) + client -> interval = + ((client -> config -> backoff_cutoff / 2) + ((random () >> 2) % - ip -> client -> config -> backoff_cutoff)); - } else if (!ip -> client -> interval) - ip -> client -> interval = - ip -> client -> config -> initial_interval; + client -> config -> backoff_cutoff)); + } else if (!client -> interval) + client -> interval = client -> config -> initial_interval; /* If the backoff would take us to the panic timeout, just use that as the interval. */ - if (cur_time + ip -> client -> interval > - ip -> client -> first_sending + ip -> client -> config -> timeout) - ip -> client -> interval = - (ip -> client -> first_sending + - ip -> client -> config -> timeout) - cur_time + 1; + if (cur_time + client -> interval > + client -> first_sending + client -> config -> timeout) + client -> interval = + (client -> first_sending + + client -> config -> timeout) - cur_time + 1; /* Record the number of seconds since we started sending. */ if (interval < 65536) - ip -> client -> packet.secs = htons (interval); + client -> packet.secs = htons (interval); else - ip -> client -> packet.secs = htons (65535); - ip -> client -> secs = ip -> client -> packet.secs; + client -> packet.secs = htons (65535); + client -> secs = client -> packet.secs; - note ("DHCPDISCOVER on %s to %s port %d interval %ld", - ip -> name, + log_info ("DHCPDISCOVER on %s to %s port %d interval %ld", + client -> name ? client -> name : client -> interface -> name, inet_ntoa (sockaddr_broadcast.sin_addr), - ntohs (sockaddr_broadcast.sin_port), ip -> client -> interval); + ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval)); /* Send out a packet. */ - result = send_packet (ip, (struct packet *)0, - &ip -> client -> packet, - ip -> client -> packet_length, + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, inaddr_any, &sockaddr_broadcast, (struct hardware *)0); - add_timeout (cur_time + ip -> client -> interval, send_discover, ip); + add_timeout (cur_time + client -> interval, + send_discover, client, 0, 0); } /* state_panic gets called if we haven't received any offers in a preset @@ -1103,110 +1437,113 @@ void send_discover (ipp) haven't yet expired, and failing that, we call the client script and hope it can do something. */ -void state_panic (ipp) - void *ipp; +void state_panic (cpp) + void *cpp; { - struct interface_info *ip = ipp; - - struct client_lease *loop = ip -> client -> active; + struct client_state *client = cpp; + struct client_lease *loop; struct client_lease *lp; - note ("No DHCPOFFERS received."); + loop = lp = client -> active; + + log_info ("No DHCPOFFERS received."); /* We may not have an active lease, but we may have some predefined leases that we can try. */ - if (!ip -> client -> active && ip -> client -> leases) + if (!client -> active && client -> leases) goto activate_next; /* Run through the list of leases and see if one can be used. */ - while (ip -> client -> active) { - if (ip -> client -> active -> expiry > cur_time) { - note ("Trying recorded lease %s", - piaddr (ip -> client -> active -> address)); + while (client -> active) { + if (client -> active -> expiry > cur_time) { + log_info ("Trying recorded lease %s", + piaddr (client -> active -> address)); /* Run the client script with the existing parameters. */ - script_init (ip, "TIMEOUT", - ip -> client -> active -> medium); - script_write_params (ip, "new_", - ip -> client -> active); - if (ip -> client -> alias) - script_write_params (ip, "alias_", - ip -> client -> alias); + script_init (client, "TIMEOUT", + client -> active -> medium); + script_write_params (client, "new_", client -> active); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); /* If the old lease is still good and doesn't yet need renewal, go into BOUND state and timeout at the renewal time. */ - if (!script_go (ip)) { - if (cur_time < - ip -> client -> active -> renewal) { - ip -> client -> state = S_BOUND; - note ("bound: renewal in %d seconds.", - ip -> client -> active -> renewal - - cur_time); - add_timeout ((ip -> client -> - active -> renewal), - state_bound, ip); - } else { - ip -> client -> state = S_BOUND; - note ("bound: immediate renewal."); - state_bound (ip); - } - reinitialize_interfaces (); - go_daemon (); - return; + if (!script_go (client)) { + if (cur_time < client -> active -> renewal) { + client -> state = S_BOUND; + log_info ("bound: renewal in %ld %s.", + (long)(client -> active -> renewal - + cur_time), "seconds"); + add_timeout (client -> active -> renewal, + state_bound, client, 0, 0); + } else { + client -> state = S_BOUND; + log_info ("bound: immediate renewal."); + state_bound (client); + } + reinitialize_interfaces (); + go_daemon (); + return; } } /* If there are no other leases, give up. */ - if (!ip -> client -> leases) { - ip -> client -> leases = ip -> client -> active; - ip -> client -> active = (struct client_lease *)0; + if (!client -> leases) { + client -> leases = client -> active; + client -> active = (struct client_lease *)0; break; } activate_next: /* Otherwise, put the active lease at the end of the lease list, and try another lease.. */ - for (lp = ip -> client -> leases; lp -> next; lp = lp -> next) + for (lp = client -> leases; lp -> next; lp = lp -> next) ; - lp -> next = ip -> client -> active; + lp -> next = client -> active; if (lp -> next) { lp -> next -> next = (struct client_lease *)0; } - ip -> client -> active = ip -> client -> leases; - ip -> client -> leases = ip -> client -> leases -> next; + client -> active = client -> leases; + client -> leases = client -> leases -> next; /* If we already tried this lease, we've exhausted the set of leases, so we might as well give up for now. */ - if (ip -> client -> active == loop) + if (client -> active == loop) break; else if (!loop) - loop = ip -> client -> active; + loop = client -> active; } /* No leases were available, or what was available didn't work, so tell the shell script that we failed to allocate an address, and try again later. */ if (onetry) { - exit(2); - note ("Unable to obtain a lease on first try - exiting.\n"); + if (!quiet) + log_info ("Unable to obtain a lease on first try.%s", + " Exiting."); + exit (2); } - note ("No working leases in persistent database - sleeping.\n"); - script_init (ip, "FAIL", (struct string_list *)0); - if (ip -> client -> alias) - script_write_params (ip, "alias_", ip -> client -> alias); - script_go (ip); - ip -> client -> state = S_INIT; - add_timeout (cur_time + ip -> client -> config -> retry_interval, - state_init, ip); + + log_info ("No working leases in persistent database - sleeping."); + script_init (client, "FAIL", (struct string_list *)0); + if (client -> alias) + script_write_params (client, "alias_", client -> alias); + script_go (client); + client -> state = S_INIT; + add_timeout (cur_time + + ((client -> config -> retry_interval + 1) / 2 + + (random () % client -> config -> retry_interval)), + state_init, client, 0, 0); go_daemon (); } -void send_request (ipp) - void *ipp; +void send_request (cpp) + void *cpp; { - struct interface_info *ip = ipp; + struct client_state *client = cpp; int result; int interval; @@ -1214,7 +1551,7 @@ void send_request (ipp) struct in_addr from; /* Figure out how long it's been since we started transmitting. */ - interval = cur_time - ip -> client -> first_sending; + interval = cur_time - client -> first_sending; /* If we're in the INIT-REBOOT or REQUESTING state and we're past the reboot timeout, go to INIT and see if we can @@ -1226,91 +1563,87 @@ void send_request (ipp) us a new address, but we could also have successfully reused our old address. In the former case, we're hosed anyway. This is not a win-prone situation. */ - if ((ip -> client -> state == S_REBOOTING || - ip -> client -> state == S_REQUESTING) && - interval > ip -> client -> config -> reboot_timeout) { + if ((client -> state == S_REBOOTING || + client -> state == S_REQUESTING) && + interval > client -> config -> reboot_timeout) { cancel: - ip -> client -> state = S_INIT; - cancel_timeout (send_request, ip); - state_init (ip); + client -> state = S_INIT; + cancel_timeout (send_request, client); + state_init (client); return; } /* If we're in the reboot state, make sure the media is set up correctly. */ - if (ip -> client -> state == S_REBOOTING && - !ip -> client -> medium && - ip -> client -> active -> medium ) { - script_init (ip, "MEDIUM", ip -> client -> active -> medium); + if (client -> state == S_REBOOTING && + !client -> medium && + client -> active -> medium ) { + script_init (client, "MEDIUM", client -> active -> medium); /* If the medium we chose won't fly, go to INIT state. */ - if (script_go (ip)) + if (script_go (client)) goto cancel; /* Record the medium. */ - ip -> client -> medium = ip -> client -> active -> medium; + client -> medium = client -> active -> medium; } /* If the lease has expired, relinquish the address and go back to the INIT state. */ - if (ip -> client -> state != S_REQUESTING && - cur_time > ip -> client -> active -> expiry) { + if (client -> state != S_REQUESTING && + cur_time > client -> active -> expiry) { /* Run the client script with the new parameters. */ - script_init (ip, "EXPIRE", (struct string_list *)0); - script_write_params (ip, "old_", ip -> client -> active); - if (ip -> client -> alias) - script_write_params (ip, "alias_", - ip -> client -> alias); - script_go (ip); + script_init (client, "EXPIRE", (struct string_list *)0); + script_write_params (client, "old_", client -> active); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_go (client); /* Now do a preinit on the interface so that we can discover a new address. */ - script_init (ip, "PREINIT", (struct string_list *)0); - if (ip -> client -> alias) - script_write_params (ip, "alias_", - ip -> client -> alias); - script_go (ip); + script_init (client, "PREINIT", (struct string_list *)0); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_go (client); - ip -> client -> state = S_INIT; - state_init (ip); + client -> state = S_INIT; + state_init (client); return; } /* Do the exponential backoff... */ - if (!ip -> client -> interval) - ip -> client -> interval = - ip -> client -> config -> initial_interval; + if (!client -> interval) + client -> interval = client -> config -> initial_interval; else { - ip -> client -> interval += - ((random () >> 2) % - (2 * ip -> client -> interval)); + client -> interval += ((random () >> 2) % + (2 * client -> interval)); } /* Don't backoff past cutoff. */ - if (ip -> client -> interval > - ip -> client -> config -> backoff_cutoff) - ip -> client -> interval = - ((ip -> client -> config -> backoff_cutoff / 2) - + ((random () >> 2) - % ip -> client -> interval)); + if (client -> interval > + client -> config -> backoff_cutoff) + client -> interval = + ((client -> config -> backoff_cutoff / 2) + + ((random () >> 2) % client -> interval)); /* If the backoff would take us to the expiry time, just set the timeout to the expiry time. */ - if (ip -> client -> state != S_REQUESTING && - cur_time + ip -> client -> interval > - ip -> client -> active -> expiry) - ip -> client -> interval = - ip -> client -> active -> expiry - cur_time + 1; + if (client -> state != S_REQUESTING && + cur_time + client -> interval > client -> active -> expiry) + client -> interval = + client -> active -> expiry - cur_time + 1; /* If the lease T2 time has elapsed, or if we're not yet bound, broadcast the DHCPREQUEST rather than unicasting. */ - if (ip -> client -> state == S_REQUESTING || - ip -> client -> state == S_REBOOTING || - cur_time > ip -> client -> active -> rebind) - destination.sin_addr.s_addr = INADDR_BROADCAST; + if (client -> state == S_REQUESTING || + client -> state == S_REBOOTING || + cur_time > client -> active -> rebind) + destination.sin_addr = sockaddr_broadcast.sin_addr; else memcpy (&destination.sin_addr.s_addr, - ip -> client -> destination.iabuf, + client -> destination.iabuf, sizeof destination.sin_addr.s_addr); destination.sin_port = remote_port; destination.sin_family = AF_INET; @@ -1318,23 +1651,25 @@ void send_request (ipp) destination.sin_len = sizeof destination; #endif - if (ip -> client -> state != S_REQUESTING) - memcpy (&from, ip -> client -> active -> address.iabuf, + if (client -> state == S_RENEWING || + client -> state == S_REBINDING) + memcpy (&from, client -> active -> address.iabuf, sizeof from); else from.s_addr = INADDR_ANY; /* Record the number of seconds since we started sending. */ - if (ip -> client -> state == S_REQUESTING) - ip -> client -> packet.secs = ip -> client -> secs; + if (client -> state == S_REQUESTING) + client -> packet.secs = client -> secs; else { if (interval < 65536) - ip -> client -> packet.secs = htons (interval); + client -> packet.secs = htons (interval); else - ip -> client -> packet.secs = htons (65535); + client -> packet.secs = htons (65535); } - note ("DHCPREQUEST on %s to %s port %d", ip -> name, + log_info ("DHCPREQUEST on %s to %s port %d", + client -> name ? client -> name : client -> interface -> name, inet_ntoa (destination.sin_addr), ntohs (destination.sin_port)); @@ -1342,451 +1677,429 @@ void send_request (ipp) fallback_interface) result = send_packet (fallback_interface, (struct packet *)0, - &ip -> client -> packet, - ip -> client -> packet_length, + &client -> packet, + client -> packet_length, from, &destination, (struct hardware *)0); else /* Send out a packet. */ - result = send_packet (ip, (struct packet *)0, - &ip -> client -> packet, - ip -> client -> packet_length, + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, from, &destination, (struct hardware *)0); - add_timeout (cur_time + ip -> client -> interval, - send_request, ip); + add_timeout (cur_time + client -> interval, + send_request, client, 0, 0); } -void send_decline (ipp) - void *ipp; +void send_decline (cpp) + void *cpp; { - struct interface_info *ip = ipp; + struct client_state *client = cpp; int result; - note ("DHCPDECLINE on %s to %s port %d", ip -> name, + log_info ("DHCPDECLINE on %s to %s port %d", + client -> name ? client -> name : client -> interface -> name, inet_ntoa (sockaddr_broadcast.sin_addr), ntohs (sockaddr_broadcast.sin_port)); /* Send out a packet. */ - result = send_packet (ip, (struct packet *)0, - &ip -> client -> packet, - ip -> client -> packet_length, + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, inaddr_any, &sockaddr_broadcast, (struct hardware *)0); } -void send_release (ipp) - void *ipp; +void send_release (cpp) + void *cpp; { - struct interface_info *ip = ipp; + struct client_state *client = cpp; int result; + struct sockaddr_in destination; + struct in_addr from; - note ("DHCPRELEASE on %s to %s port %d", ip -> name, - inet_ntoa (sockaddr_broadcast.sin_addr), - ntohs (sockaddr_broadcast.sin_port)); + memcpy (&from, client -> active -> address.iabuf, + sizeof from); + memcpy (&destination.sin_addr.s_addr, + client -> destination.iabuf, + sizeof destination.sin_addr.s_addr); + destination.sin_port = remote_port; + destination.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + destination.sin_len = sizeof destination; +#endif - /* Send out a packet. */ - result = send_packet (ip, (struct packet *)0, - &ip -> client -> packet, - ip -> client -> packet_length, - inaddr_any, &sockaddr_broadcast, - (struct hardware *)0); + /* Set the lease to end now, so that we don't accidentally + reuse it if we restart before the old expiry time. */ + client -> active -> expiry = + client -> active -> renewal = + client -> active -> rebind = cur_time; + if (!write_client_lease (client, client -> active, 1, 1)) { + log_error ("Can't release lease: lease write failed."); + return; + } + + log_info ("DHCPRELEASE on %s to %s port %d", + client -> name ? client -> name : client -> interface -> name, + inet_ntoa (destination.sin_addr), + ntohs (destination.sin_port)); + + if (fallback_interface) + result = send_packet (fallback_interface, + (struct packet *)0, + &client -> packet, + client -> packet_length, + from, &destination, + (struct hardware *)0); + else + /* Send out a packet. */ + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, + from, &destination, + (struct hardware *)0); } -void make_discover (ip, lease) - struct interface_info *ip; +void make_client_options (client, lease, type, sid, rip, prl, op) + struct client_state *client; + struct client_lease *lease; + u_int8_t *type; + struct option_cache *sid; + struct iaddr *rip; + u_int32_t *prl; + struct option_state **op; +{ + unsigned i; + struct option_cache *oc; + struct buffer *bp = (struct buffer *)0; + + /* If there are any leftover options, get rid of them. */ + if (*op) + option_state_dereference (op, MDL); + + /* Allocate space for options. */ + option_state_allocate (op, MDL); + + /* Send the server identifier if provided. */ + if (sid) + save_option (&dhcp_universe, *op, sid); + + oc = (struct option_cache *)0; + + /* Send the requested address if provided. */ + if (rip) { + client -> requested_address = *rip; + if (!(make_const_option_cache + (&oc, (struct buffer **)0, rip -> iabuf, rip -> len, + &dhcp_options [DHO_DHCP_REQUESTED_ADDRESS], MDL))) + log_error ("can't make requested address cache."); + else { + save_option (&dhcp_universe, *op, oc); + option_cache_dereference (&oc, MDL); + } + } else { + client -> requested_address.len = 0; + } + + if (!(make_const_option_cache + (&oc, (struct buffer **)0, + type, 1, &dhcp_options [DHO_DHCP_MESSAGE_TYPE], MDL))) + log_error ("can't make message type."); + else { + save_option (&dhcp_universe, *op, oc); + option_cache_dereference (&oc, MDL); + } + + if (prl) { + /* Figure out how many parameters were requested. */ + for (i = 0; prl [i]; i++) + ; + if (!buffer_allocate (&bp, i, MDL)) + log_error ("can't make parameter list buffer."); + else { + for (i = 0; prl [i]; i++) + bp -> data [i] = prl [i]; + if (!(make_const_option_cache + (&oc, &bp, (u_int8_t *)0, i, + &dhcp_options [DHO_DHCP_PARAMETER_REQUEST_LIST], + MDL))) + log_error ("can't make option cache"); + else { + save_option (&dhcp_universe, *op, oc); + option_cache_dereference (&oc, MDL); + } + } + } + + /* Run statements that need to be run on transmission. */ + if (client -> config -> on_transmission) + execute_statements_in_scope + ((struct binding_value **)0, + (struct packet *)0, (struct lease *)0, client, + (lease ? lease -> options : (struct option_state *)0), + *op, &global_scope, + client -> config -> on_transmission, + (struct group *)0); +} + +void make_discover (client, lease) + struct client_state *client; struct client_lease *lease; { unsigned char discover = DHCPDISCOVER; int i; + struct option_state *options = (struct option_state *)0; - struct tree_cache *options [256]; - struct tree_cache option_elements [256]; + memset (&client -> packet, 0, sizeof (client -> packet)); - memset (option_elements, 0, sizeof option_elements); - memset (options, 0, sizeof options); - memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); - - /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ - i = DHO_DHCP_MESSAGE_TYPE; - options [i] = &option_elements [i]; - options [i] -> value = &discover; - options [i] -> len = sizeof discover; - options [i] -> buf_size = sizeof discover; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* Request the options we want */ - i = DHO_DHCP_PARAMETER_REQUEST_LIST; - options [i] = &option_elements [i]; - options [i] -> value = ip -> client -> config -> requested_options; - options [i] -> len = ip -> client -> config -> requested_option_count; - options [i] -> buf_size = - ip -> client -> config -> requested_option_count; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* If we had an address, try to get it again. */ - if (lease) { - ip -> client -> requested_address = lease -> address; - i = DHO_DHCP_REQUESTED_ADDRESS; - options [i] = &option_elements [i]; - options [i] -> value = lease -> address.iabuf; - options [i] -> len = lease -> address.len; - options [i] -> buf_size = lease -> address.len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - } else { - ip -> client -> requested_address.len = 0; - } - - /* Send any options requested in the config file. */ - for (i = 0; i < 256; i++) { - if (!options [i] && - ip -> client -> config -> send_options [i].data) { - options [i] = &option_elements [i]; - options [i] -> value = ip -> client -> config -> - send_options [i].data; - options [i] -> len = ip -> client -> config -> - send_options [i].len; - options [i] -> buf_size = ip -> client -> config -> - send_options [i].len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - } - } + make_client_options (client, + lease, &discover, (struct option_cache *)0, + lease ? &lease -> address : (struct iaddr *)0, + client -> config -> requested_options, + &options); /* Set up the option buffer... */ - ip -> client -> packet_length = - cons_options ((struct packet *)0, &ip -> client -> packet, 0, - options, 0, 0, 0, (u_int8_t *)0, 0); - if (ip -> client -> packet_length < BOOTP_MIN_LEN) - ip -> client -> packet_length = BOOTP_MIN_LEN; + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, 0, + (struct option_state *)0, options, + &global_scope, 0, 0, 0, (struct data_string *)0, + client -> config -> vendor_space_name); + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; - ip -> client -> packet.op = BOOTREQUEST; - ip -> client -> packet.htype = ip -> hw_address.htype; - ip -> client -> packet.hlen = ip -> hw_address.hlen; - ip -> client -> packet.hops = 0; - ip -> client -> packet.xid = random (); - ip -> client -> packet.secs = 0; /* filled in by send_discover. */ + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = random (); + client -> packet.secs = 0; /* filled in by send_discover. */ - if (can_receive_unicast_unconfigured (ip)) - ip -> client -> packet.flags = 0; + if (can_receive_unicast_unconfigured (client -> interface)) + client -> packet.flags = 0; else - ip -> client -> packet.flags = htons (BOOTP_BROADCAST); + client -> packet.flags = htons (BOOTP_BROADCAST); - memset (&(ip -> client -> packet.ciaddr), - 0, sizeof ip -> client -> packet.ciaddr); - memset (&(ip -> client -> packet.yiaddr), - 0, sizeof ip -> client -> packet.yiaddr); - memset (&(ip -> client -> packet.siaddr), - 0, sizeof ip -> client -> packet.siaddr); - memset (&(ip -> client -> packet.giaddr), - 0, sizeof ip -> client -> packet.giaddr); - memcpy (ip -> client -> packet.chaddr, - ip -> hw_address.haddr, ip -> hw_address.hlen); + memset (&(client -> packet.ciaddr), + 0, sizeof client -> packet.ciaddr); + memset (&(client -> packet.yiaddr), + 0, sizeof client -> packet.yiaddr); + memset (&(client -> packet.siaddr), + 0, sizeof client -> packet.siaddr); + client -> packet.giaddr = giaddr; + if (client -> interface -> hw_address.hlen > 0) + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + (unsigned)(client -> interface -> hw_address.hlen - 1)); #ifdef DEBUG_PACKET - dump_packet (sendpkt); - dump_raw ((unsigned char *)ip -> client -> packet, - sendpkt->packet_length); + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } -void make_request (ip, lease) - struct interface_info *ip; +void make_request (client, lease) + struct client_state *client; struct client_lease *lease; { unsigned char request = DHCPREQUEST; - int i; + int i, j; + unsigned char *tmp, *digest; + unsigned char *old_digest_loc; + struct option_cache *oc; - struct tree_cache *options [256]; - struct tree_cache option_elements [256]; + memset (&client -> packet, 0, sizeof (client -> packet)); - memset (options, 0, sizeof options); - memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); + if (client -> state == S_REQUESTING) + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_SERVER_IDENTIFIER); + else + oc = (struct option_cache *)0; - /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ - i = DHO_DHCP_MESSAGE_TYPE; - options [i] = &option_elements [i]; - options [i] -> value = &request; - options [i] -> len = sizeof request; - options [i] -> buf_size = sizeof request; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* Request the options we want */ - i = DHO_DHCP_PARAMETER_REQUEST_LIST; - options [i] = &option_elements [i]; - options [i] -> value = ip -> client -> config -> requested_options; - options [i] -> len = ip -> client -> config -> requested_option_count; - options [i] -> buf_size = - ip -> client -> config -> requested_option_count; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* If we are requesting an address that hasn't yet been assigned - to us, use the DHCP Requested Address option. */ - if (ip -> client -> state == S_REQUESTING) { - /* Send back the server identifier... */ - i = DHO_DHCP_SERVER_IDENTIFIER; - options [i] = &option_elements [i]; - options [i] -> value = lease -> options [i].data; - options [i] -> len = lease -> options [i].len; - options [i] -> buf_size = lease -> options [i].len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - } - if (ip -> client -> state == S_REQUESTING || - ip -> client -> state == S_REBOOTING) { - ip -> client -> requested_address = lease -> address; - i = DHO_DHCP_REQUESTED_ADDRESS; - options [i] = &option_elements [i]; - options [i] -> value = lease -> address.iabuf; - options [i] -> len = lease -> address.len; - options [i] -> buf_size = lease -> address.len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - } else { - ip -> client -> requested_address.len = 0; - } - - /* Send any options requested in the config file. */ - for (i = 0; i < 256; i++) { - if (!options [i] && - ip -> client -> config -> send_options [i].data) { - options [i] = &option_elements [i]; - options [i] -> value = ip -> client -> config -> - send_options [i].data; - options [i] -> len = ip -> client -> config -> - send_options [i].len; - options [i] -> buf_size = ip -> client -> config -> - send_options [i].len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - } - } + make_client_options (client, lease, &request, oc, + ((client -> state == S_REQUESTING || + client -> state == S_REBOOTING) + ? &lease -> address + : (struct iaddr *)0), + client -> config -> requested_options, + &client -> sent_options); /* Set up the option buffer... */ - ip -> client -> packet_length = - cons_options ((struct packet *)0, &ip -> client -> packet, 0, - options, 0, 0, 0, (u_int8_t *)0, 0); - if (ip -> client -> packet_length < BOOTP_MIN_LEN) - ip -> client -> packet_length = BOOTP_MIN_LEN; + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, 0, + (struct option_state *)0, client -> sent_options, + &global_scope, 0, 0, 0, (struct data_string *)0, + client -> config -> vendor_space_name); + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; - ip -> client -> packet.op = BOOTREQUEST; - ip -> client -> packet.htype = ip -> hw_address.htype; - ip -> client -> packet.hlen = ip -> hw_address.hlen; - ip -> client -> packet.hops = 0; - ip -> client -> packet.xid = ip -> client -> xid; - ip -> client -> packet.secs = 0; /* Filled in by send_request. */ + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = client -> xid; + client -> packet.secs = 0; /* Filled in by send_request. */ /* If we own the address we're requesting, put it in ciaddr; otherwise set ciaddr to zero. */ - if (ip -> client -> state == S_BOUND || - ip -> client -> state == S_RENEWING || - ip -> client -> state == S_REBINDING) { - memcpy (&ip -> client -> packet.ciaddr, + if (client -> state == S_BOUND || + client -> state == S_RENEWING || + client -> state == S_REBINDING) { + memcpy (&client -> packet.ciaddr, lease -> address.iabuf, lease -> address.len); - ip -> client -> packet.flags = 0; + client -> packet.flags = 0; } else { - memset (&ip -> client -> packet.ciaddr, 0, - sizeof ip -> client -> packet.ciaddr); - if (can_receive_unicast_unconfigured (ip)) - ip -> client -> packet.flags = 0; + memset (&client -> packet.ciaddr, 0, + sizeof client -> packet.ciaddr); + if (can_receive_unicast_unconfigured (client -> interface)) + client -> packet.flags = 0; else - ip -> client -> packet.flags = htons (BOOTP_BROADCAST); + client -> packet.flags = htons (BOOTP_BROADCAST); } - memset (&ip -> client -> packet.yiaddr, 0, - sizeof ip -> client -> packet.yiaddr); - memset (&ip -> client -> packet.siaddr, 0, - sizeof ip -> client -> packet.siaddr); - memset (&ip -> client -> packet.giaddr, 0, - sizeof ip -> client -> packet.giaddr); - memcpy (ip -> client -> packet.chaddr, - ip -> hw_address.haddr, ip -> hw_address.hlen); + memset (&client -> packet.yiaddr, 0, + sizeof client -> packet.yiaddr); + memset (&client -> packet.siaddr, 0, + sizeof client -> packet.siaddr); + if (client -> state != S_BOUND && + client -> state != S_RENEWING) + client -> packet.giaddr = giaddr; + else + memset (&client -> packet.giaddr, 0, + sizeof client -> packet.giaddr); + if (client -> interface -> hw_address.hlen > 0) + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + (unsigned)(client -> interface -> hw_address.hlen - 1)); #ifdef DEBUG_PACKET - dump_packet (sendpkt); - dump_raw ((unsigned char *)ip -> client -> packet, sendpkt->packet_length); + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } -void make_decline (ip, lease) - struct interface_info *ip; +void make_decline (client, lease) + struct client_state *client; struct client_lease *lease; { unsigned char decline = DHCPDECLINE; int i; + struct option_cache *oc; - struct tree_cache *options [256]; - struct tree_cache message_type_tree; - struct tree_cache requested_address_tree; - struct tree_cache server_id_tree; - struct tree_cache client_id_tree; - - memset (options, 0, sizeof options); - memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); - - /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ - i = DHO_DHCP_MESSAGE_TYPE; - options [i] = &message_type_tree; - options [i] -> value = &decline; - options [i] -> len = sizeof decline; - options [i] -> buf_size = sizeof decline; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* Send back the server identifier... */ - i = DHO_DHCP_SERVER_IDENTIFIER; - options [i] = &server_id_tree; - options [i] -> value = lease -> options [i].data; - options [i] -> len = lease -> options [i].len; - options [i] -> buf_size = lease -> options [i].len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* Send back the address we're declining. */ - i = DHO_DHCP_REQUESTED_ADDRESS; - options [i] = &requested_address_tree; - options [i] -> value = lease -> address.iabuf; - options [i] -> len = lease -> address.len; - options [i] -> buf_size = lease -> address.len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* Send the uid if the user supplied one. */ - i = DHO_DHCP_CLIENT_IDENTIFIER; - if (ip -> client -> config -> send_options [i].len) { - options [i] = &client_id_tree; - options [i] -> value = ip -> client -> config -> - send_options [i].data; - options [i] -> len = ip -> client -> config -> - send_options [i].len; - options [i] -> buf_size = ip -> client -> config -> - send_options [i].len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - } + struct option_state *options = (struct option_state *)0; + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_SERVER_IDENTIFIER); + make_client_options (client, lease, &decline, oc, + &lease -> address, (u_int32_t *)0, &options); /* Set up the option buffer... */ - ip -> client -> packet_length = - cons_options ((struct packet *)0, &ip -> client -> packet, 0, - options, 0, 0, 0, (u_int8_t *)0, 0); - if (ip -> client -> packet_length < BOOTP_MIN_LEN) - ip -> client -> packet_length = BOOTP_MIN_LEN; + memset (&client -> packet, 0, sizeof (client -> packet)); + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, 0, + (struct option_state *)0, options, + &global_scope, 0, 0, 0, (struct data_string *)0, + client -> config -> vendor_space_name); + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; + option_state_dereference (&options, MDL); - ip -> client -> packet.op = BOOTREQUEST; - ip -> client -> packet.htype = ip -> hw_address.htype; - ip -> client -> packet.hlen = ip -> hw_address.hlen; - ip -> client -> packet.hops = 0; - ip -> client -> packet.xid = ip -> client -> xid; - ip -> client -> packet.secs = 0; /* Filled in by send_request. */ - ip -> client -> packet.flags = 0; + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = client -> xid; + client -> packet.secs = 0; /* Filled in by send_request. */ + if (can_receive_unicast_unconfigured (client -> interface)) + client -> packet.flags = 0; + else + client -> packet.flags = htons (BOOTP_BROADCAST); /* ciaddr must always be zero. */ - memset (&ip -> client -> packet.ciaddr, 0, - sizeof ip -> client -> packet.ciaddr); - memset (&ip -> client -> packet.yiaddr, 0, - sizeof ip -> client -> packet.yiaddr); - memset (&ip -> client -> packet.siaddr, 0, - sizeof ip -> client -> packet.siaddr); - memset (&ip -> client -> packet.giaddr, 0, - sizeof ip -> client -> packet.giaddr); - memcpy (ip -> client -> packet.chaddr, - ip -> hw_address.haddr, ip -> hw_address.hlen); + memset (&client -> packet.ciaddr, 0, + sizeof client -> packet.ciaddr); + memset (&client -> packet.yiaddr, 0, + sizeof client -> packet.yiaddr); + memset (&client -> packet.siaddr, 0, + sizeof client -> packet.siaddr); + client -> packet.giaddr = giaddr; + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + client -> interface -> hw_address.hlen); #ifdef DEBUG_PACKET - dump_packet (sendpkt); - dump_raw ((unsigned char *)ip -> client -> packet, sendpkt->packet_length); + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } -void make_release (ip, lease) - struct interface_info *ip; +void make_release (client, lease) + struct client_state *client; struct client_lease *lease; { unsigned char request = DHCPRELEASE; int i; + struct option_cache *oc; - struct tree_cache *options [256]; - struct tree_cache message_type_tree; - struct tree_cache server_id_tree; + struct option_state *options = (struct option_state *)0; - memset (options, 0, sizeof options); - memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet)); + memset (&client -> packet, 0, sizeof (client -> packet)); - /* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */ - i = DHO_DHCP_MESSAGE_TYPE; - options [i] = &message_type_tree; - options [i] -> value = &request; - options [i] -> len = sizeof request; - options [i] -> buf_size = sizeof request; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; - - /* Send back the server identifier... */ - i = DHO_DHCP_SERVER_IDENTIFIER; - options [i] = &server_id_tree; - options [i] -> value = lease -> options [i].data; - options [i] -> len = lease -> options [i].len; - options [i] -> buf_size = lease -> options [i].len; - options [i] -> timeout = 0xFFFFFFFF; - options [i] -> tree = (struct tree *)0; + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_SERVER_IDENTIFIER); + make_client_options (client, lease, &request, oc, + (struct iaddr *)0, (u_int32_t *)0, + &options); /* Set up the option buffer... */ - ip -> client -> packet_length = - cons_options ((struct packet *)0, &ip -> client -> packet, 0, - options, 0, 0, 0, (u_int8_t *)0, 0); - if (ip -> client -> packet_length < BOOTP_MIN_LEN) - ip -> client -> packet_length = BOOTP_MIN_LEN; + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, 0, + (struct option_state *)0, options, + &global_scope, 0, 0, 0, (struct data_string *)0, + client -> config -> vendor_space_name); + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; + option_state_dereference (&options, MDL); - ip -> client -> packet.op = BOOTREQUEST; - ip -> client -> packet.htype = ip -> hw_address.htype; - ip -> client -> packet.hlen = ip -> hw_address.hlen; - ip -> client -> packet.hops = 0; - ip -> client -> packet.xid = random (); - ip -> client -> packet.secs = 0; - ip -> client -> packet.flags = 0; - - memset (&ip -> client -> packet.ciaddr, 0, - sizeof ip -> client -> packet.ciaddr); - memset (&ip -> client -> packet.yiaddr, 0, - sizeof ip -> client -> packet.yiaddr); - memset (&ip -> client -> packet.siaddr, 0, - sizeof ip -> client -> packet.siaddr); - memset (&ip -> client -> packet.giaddr, 0, - sizeof ip -> client -> packet.giaddr); - memcpy (ip -> client -> packet.chaddr, - ip -> hw_address.haddr, ip -> hw_address.hlen); + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = random (); + client -> packet.secs = 0; + client -> packet.flags = 0; + memcpy (&client -> packet.ciaddr, + lease -> address.iabuf, lease -> address.len); + memset (&client -> packet.yiaddr, 0, + sizeof client -> packet.yiaddr); + memset (&client -> packet.siaddr, 0, + sizeof client -> packet.siaddr); + client -> packet.giaddr = giaddr; + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + client -> interface -> hw_address.hlen); #ifdef DEBUG_PACKET - dump_packet (sendpkt); - dump_raw ((unsigned char *)ip -> client -> packet, - ip -> client -> packet_length); + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); #endif } -void free_client_lease (lease) +void destroy_client_lease (lease) struct client_lease *lease; { int i; if (lease -> server_name) - free (lease -> server_name); + dfree (lease -> server_name, MDL); if (lease -> filename) - free (lease -> filename); - for (i = 0; i < 256; i++) { - if (lease -> options [i].len) - free (lease -> options [i].data); - } - free (lease); + dfree (lease -> filename, MDL); + option_state_dereference (&lease -> options, MDL); + free_client_lease (lease, MDL); } FILE *leaseFile; @@ -1794,44 +2107,92 @@ FILE *leaseFile; void rewrite_client_leases () { struct interface_info *ip; + struct client_state *client; struct client_lease *lp; if (leaseFile) fclose (leaseFile); leaseFile = fopen (path_dhclient_db, "w"); - if (!leaseFile) - error ("can't create %s: %m", path_dhclient_db); + if (!leaseFile) { + log_error ("can't create %s: %m", path_dhclient_db); + return; + } /* Write out all the leases attached to configured interfaces that we know about. */ for (ip = interfaces; ip; ip = ip -> next) { - for (lp = ip -> client -> leases; lp; lp = lp -> next) { - write_client_lease (ip, lp, 1); + for (client = ip -> client; client; client = client -> next) { + for (lp = client -> leases; lp; lp = lp -> next) { + write_client_lease (client, lp, 1, 0); + } + if (client -> active) + write_client_lease (client, + client -> active, 1, 0); } - if (ip -> client -> active) - write_client_lease (ip, ip -> client -> active, 1); } /* Write out any leases that are attached to interfaces that aren't currently configured. */ for (ip = dummy_interfaces; ip; ip = ip -> next) { - for (lp = ip -> client -> leases; lp; lp = lp -> next) { - write_client_lease (ip, lp, 1); + for (client = ip -> client; client; client = client -> next) { + for (lp = client -> leases; lp; lp = lp -> next) { + write_client_lease (client, lp, 1, 0); + } + if (client -> active) + write_client_lease (client, + client -> active, 1, 0); } - if (ip -> client -> active) - write_client_lease (ip, ip -> client -> active, 1); } fflush (leaseFile); } -void write_client_lease (ip, lease, rewrite) - struct interface_info *ip; +void write_lease_option (struct option_cache *oc, + struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff) +{ + const char *name, *dot; + struct data_string ds; + int status; + struct client_state *client; + + memset (&ds, 0, sizeof ds); + + if (u != &dhcp_universe) { + name = u -> name; + dot = "."; + } else { + name = ""; + dot = ""; + } + if (evaluate_option_cache (&ds, packet, lease, client_state, + in_options, cfg_options, scope, oc, MDL)) { + fprintf (leaseFile, + " option %s%s%s %s;\n", + name, dot, oc -> option -> name, + pretty_print_option (oc -> option, + ds.data, ds.len, 1, 1)); + data_string_forget (&ds, MDL); + } +} + +int write_client_lease (client, lease, rewrite, makesure) + struct client_state *client; struct client_lease *lease; int rewrite; + int makesure; { int i; struct tm *t; static int leases_written; + struct option_cache *oc; + struct data_string ds; + pair *hash; + int errors = 0; + char *s; if (!rewrite) { if (leases_written++ > 20) { @@ -1843,38 +2204,94 @@ void write_client_lease (ip, lease, rewrite) /* If the lease came from the config file, we don't need to stash a copy in the lease database. */ if (lease -> is_static) - return; + return 1; if (!leaseFile) { /* XXX */ leaseFile = fopen (path_dhclient_db, "w"); - if (!leaseFile) - error ("can't create %s: %m", path_dhclient_db); + if (!leaseFile) { + log_error ("can't create %s: %m", path_dhclient_db); + return 0; + } } + errno = 0; fprintf (leaseFile, "lease {\n"); - if (lease -> is_bootp) + if (lease -> is_bootp) { fprintf (leaseFile, " bootp;\n"); - fprintf (leaseFile, " interface \"%s\";\n", ip -> name); + if (errno) { + ++errors; + errno = 0; + } + } + fprintf (leaseFile, " interface \"%s\";\n", + client -> interface -> name); + if (errno) { + ++errors; + errno = 0; + } + if (client -> name) { + fprintf (leaseFile, " name \"%s\";\n", client -> name); + if (errno) { + ++errors; + errno = 0; + } + } fprintf (leaseFile, " fixed-address %s;\n", piaddr (lease -> address)); - if (lease -> filename) - fprintf (leaseFile, " filename \"%s\";\n", - lease -> filename); - if (lease -> server_name) - fprintf (leaseFile, " server-name \"%s\";\n", - lease -> server_name); - if (lease -> medium) - fprintf (leaseFile, " medium \"%s\";\n", - lease -> medium -> string); - for (i = 0; i < 256; i++) { - if (lease -> options [i].len) { - fprintf (leaseFile, - " option %s %s;\n", - dhcp_options [i].name, - pretty_print_option - (i, lease -> options [i].data, - lease -> options [i].len, 1, 1)); - } + if (errno) { + ++errors; + errno = 0; + } + if (lease -> filename) { + s = quotify_string (lease -> filename, MDL); + if (s) { + fprintf (leaseFile, " filename \"%s\";\n", s); + if (errno) { + ++errors; + errno = 0; + } + dfree (s, MDL); + } else + errors++; + + } + if (lease -> server_name) { + s = quotify_string (lease -> filename, MDL); + if (s) { + fprintf (leaseFile, " server-name \"%s\";\n", s); + if (errno) { + ++errors; + errno = 0; + } + dfree (s, MDL); + } else + ++errors; + } + if (lease -> medium) { + s = quotify_string (lease -> medium -> string, MDL); + if (s) { + fprintf (leaseFile, " medium \"%s\";\n", s); + if (errno) { + ++errors; + errno = 0; + } + dfree (s, MDL); + } else + errors++; + } + if (errno != 0) { + errors++; + errno = 0; + } + + memset (&ds, 0, sizeof ds); + + for (i = 0; i < lease -> options -> universe_count; i++) { + option_space_foreach ((struct packet *)0, (struct lease *)0, + client, (struct option_state *)0, + lease -> options, &global_scope, + universes [i], + client, write_lease_option); } /* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until @@ -1886,20 +2303,47 @@ void write_client_lease (ip, lease, rewrite) t -> tm_wday, t -> tm_year + 1900, t -> tm_mon + 1, t -> tm_mday, t -> tm_hour, t -> tm_min, t -> tm_sec); + if (errno != 0) { + errors++; + errno = 0; + } t = gmtime (&lease -> rebind); fprintf (leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n", t -> tm_wday, t -> tm_year + 1900, t -> tm_mon + 1, t -> tm_mday, t -> tm_hour, t -> tm_min, t -> tm_sec); + if (errno != 0) { + errors++; + errno = 0; + } t = gmtime (&lease -> expiry); fprintf (leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n", t -> tm_wday, t -> tm_year + 1900, t -> tm_mon + 1, t -> tm_mday, t -> tm_hour, t -> tm_min, t -> tm_sec); + if (errno != 0) { + errors++; + errno = 0; + } fprintf (leaseFile, "}\n"); + if (errno != 0) { + errors++; + errno = 0; + } fflush (leaseFile); + if (errno != 0) { + errors++; + errno = 0; + } + if (!errors && makesure) { + if (fsync (fileno (leaseFile)) < 0) { + log_info ("write_client_lease: %m"); + return 0; + } + } + return errors ? 0 : 1; } /* Variables holding name of script and file pointer for writing to @@ -1908,41 +2352,88 @@ void write_client_lease (ip, lease, rewrite) char scriptName [256]; FILE *scriptFile; -void script_init (ip, reason, medium) - struct interface_info *ip; - char *reason; +void script_init (client, reason, medium) + struct client_state *client; + const char *reason; struct string_list *medium; { struct string_list *sl, *next; - if (ip) { - for (sl = ip -> client -> env; sl; sl = next) { + if (client) { + for (sl = client -> env; sl; sl = next) { next = sl -> next; - dfree (sl, "script_init"); + dfree (sl, MDL); } - ip -> client -> env = (struct string_list *)0; - ip -> client -> envc = 0; + client -> env = (struct string_list *)0; + client -> envc = 0; - client_envadd (ip -> client, "", "interface", "%s", - ip -> name); + if (client -> interface) { + client_envadd (client, "", "interface", "%s", + client -> interface -> name); + } + if (client -> name) + client_envadd (client, + "", "client", "%s", client -> name); if (medium) - client_envadd (ip -> client, + client_envadd (client, "", "medium", "%s", medium -> string); - client_envadd (ip -> client, "", "reason", "%s", reason); + client_envadd (client, "", "reason", "%s", reason); + client_envadd (client, "", "pid", "%ld", (long int)getpid ()); } } -void script_write_params (ip, prefix, lease) - struct interface_info *ip; - char *prefix; +struct envadd_state { + struct client_state *client; + const char *prefix; +}; + +void client_option_envadd (struct option_cache *oc, + struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff) +{ + struct envadd_state *es = stuff; + struct data_string data; + memset (&data, 0, sizeof data); + + if (evaluate_option_cache (&data, packet, lease, client_state, + in_options, cfg_options, scope, oc, MDL)) { + if (data.len) { + char name [256]; + if (dhcp_option_ev_name (name, sizeof name, + oc -> option)) { + client_envadd (es -> client, es -> prefix, + name, "%s", + (pretty_print_option + (oc -> option, + data.data, data.len, + 0, 0))); + data_string_forget (&data, MDL); + } + } + } +} + +void script_write_params (client, prefix, lease) + struct client_state *client; + const char *prefix; struct client_lease *lease; { int i; - u_int8_t dbuf [1500]; - int len; + struct data_string data; + struct option_cache *oc; + pair *hash; + char *s, *t; + struct envadd_state es; - client_envadd (ip -> client, + es.client = client; + es.prefix = prefix; + + client_envadd (client, prefix, "ip_address", "%s", piaddr (lease -> address)); /* For the benefit of Linux (and operating systems which may @@ -1952,125 +2443,66 @@ void script_write_params (ip, prefix, lease) broadcast address, not the host address all zeroes broadcast address). */ - if (lease -> options [DHO_SUBNET_MASK].len && - (lease -> options [DHO_SUBNET_MASK].len < - sizeof lease -> address.iabuf)) { - struct iaddr netmask, subnet, broadcast; + memset (&data, 0, sizeof data); + oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK); + if (oc && evaluate_option_cache (&data, (struct packet *)0, + (struct lease *)0, client, + (struct option_state *)0, + lease -> options, + &global_scope, oc, MDL)) { + if (data.len > 3) { + struct iaddr netmask, subnet, broadcast; - memcpy (netmask.iabuf, - lease -> options [DHO_SUBNET_MASK].data, - lease -> options [DHO_SUBNET_MASK].len); - netmask.len = lease -> options [DHO_SUBNET_MASK].len; + memcpy (netmask.iabuf, data.data, data.len); + netmask.len = data.len; + data_string_forget (&data, MDL); - subnet = subnet_number (lease -> address, netmask); - if (subnet.len) { - client_envadd (ip -> client, prefix, "network_number", - "%s", piaddr (subnet)); + subnet = subnet_number (lease -> address, netmask); + if (subnet.len) { + client_envadd (client, prefix, "network_number", + "%s", piaddr (subnet)); - if (!lease -> options [DHO_BROADCAST_ADDRESS].len) { + oc = lookup_option (&dhcp_universe, + lease -> options, + DHO_BROADCAST_ADDRESS); + if (!oc || + !(evaluate_option_cache + (&data, (struct packet *)0, + (struct lease *)0, client, + (struct option_state *)0, + lease -> options, + &global_scope, oc, MDL))) { broadcast = broadcast_addr (subnet, netmask); if (broadcast.len) { - client_envadd (ip -> client, + client_envadd (client, prefix, "broadcast_address", "%s", piaddr (broadcast)); } + } } } + data_string_forget (&data, MDL); } if (lease -> filename) - client_envadd (ip -> client, + client_envadd (client, prefix, "filename", "%s", lease -> filename); if (lease -> server_name) - client_envadd (ip -> client, prefix, "server_name", + client_envadd (client, prefix, "server_name", "%s", lease -> server_name); - for (i = 0; i < 256; i++) { - u_int8_t *dp; - if (ip -> client -> config -> defaults [i].len) { - if (lease -> options [i].len) { - switch (ip -> client -> - config -> default_actions [i]) { - case ACTION_DEFAULT: - dp = lease -> options [i].data; - len = lease -> options [i].len; - break; - case ACTION_SUPERSEDE: - supersede: - dp = ip -> client -> - config -> defaults [i].data; - len = ip -> client -> - config -> defaults [i].len; - break; - case ACTION_PREPEND: - len = (ip -> client -> - config -> defaults [i].len + - lease -> options [i].len); - if (len > sizeof dbuf) { - warn ("no space to %s %s", - "prepend option", - dhcp_options [i].name); - goto supersede; - } - dp = dbuf; - memcpy (dp, - ip -> client -> - config -> defaults [i].data, - ip -> client -> - config -> defaults [i].len); - memcpy (dp + ip -> client -> - config -> defaults [i].len, - lease -> options [i].data, - lease -> options [i].len); - break; - case ACTION_APPEND: - len = (ip -> client -> - config -> defaults [i].len + - lease -> options [i].len); - if (len > sizeof dbuf) { - warn ("no space to %s %s", - "append option", - dhcp_options [i].name); - goto supersede; - } - dp = dbuf; - memcpy (dp, - lease -> options [i].data, - lease -> options [i].len); - memcpy (dp + lease -> options [i].len, - ip -> client -> - config -> defaults [i].data, - ip -> client -> - config -> defaults [i].len); - } - } else { - dp = ip -> client -> - config -> defaults [i].data; - len = ip -> client -> - config -> defaults [i].len; - } - } else if (lease -> options [i].len) { - len = lease -> options [i].len; - dp = lease -> options [i].data; - } else { - len = 0; - } - if (len) { - char name [256]; - if (dhcp_option_ev_name (name, sizeof name, - &dhcp_options [i])) { - client_envadd (ip -> client, prefix, name, "%s", - (pretty_print_option (i, dp, - len, 0, 0))); - } - } + for (i = 0; i < lease -> options -> universe_count; i++) { + option_space_foreach ((struct packet *)0, (struct lease *)0, + client, (struct option_state *)0, + lease -> options, &global_scope, + universes [i], + &es, client_option_envadd); } - client_envadd (ip -> client, - prefix, "expiry", "%d", (int)(lease -> expiry)); + client_envadd (client, prefix, "expiry", "%d", (int)(lease -> expiry)); } -int script_go (ip) - struct interface_info *ip; +int script_go (client) + struct client_state *client; { int rval; char *scriptName; @@ -2083,41 +2515,48 @@ int script_go (ip) struct string_list *sp, *next; int pid, wpid, wstatus; - if (ip) { - scriptName = ip -> client -> config -> script_name; - envp = dmalloc ((ip -> client -> envc + 2) * sizeof (char *), - "script_go"); - if (!envp) { - error ("No memory for client script environment."); - return 0; - } - i = 0; - for (sp = ip -> client -> env; sp; sp = sp -> next) { + if (client) + scriptName = client -> config -> script_name; + else + scriptName = top_level_config.script_name; + + envp = dmalloc (((client ? client -> envc : 2) + + client_env_count + 2) * sizeof (char *), MDL); + if (!envp) { + log_error ("No memory for client script environment."); + return 0; + } + i = 0; + /* Copy out the environment specified on the command line, + if any. */ + for (sp = client_env; sp; sp = sp -> next) { + envp [i++] = sp -> string; + } + /* Copy out the environment specified by dhclient. */ + if (client) { + for (sp = client -> env; sp; sp = sp -> next) { envp [i++] = sp -> string; } - envp [i++] = client_path; - envp [i] = (char *)0; } else { - scriptName = top_level_config.script_name; - epp [0] = reason; - epp [1] = client_path; - epp [2] = (char *)0; - envp = epp; + envp [i++] = reason; } + /* Set $PATH. */ + envp [i++] = client_path; + envp [i] = (char *)0; argv [0] = scriptName; argv [1] = (char *)0; pid = fork (); if (pid < 0) { - error ("fork: %m"); + log_error ("fork: %m"); wstatus = 0; } else if (pid) { do { wpid = wait (&wstatus); } while (wpid != pid && wpid > 0); if (wpid < 0) { - error ("wait: %m"); + log_error ("wait: %m"); wstatus = 0; } } else { @@ -2129,20 +2568,22 @@ int script_go (ip) close(i); } execve (scriptName, argv, envp); - error ("execve (%s, ...): %m", scriptName); + log_error ("execve (%s, ...): %m", scriptName); exit (0); } - if (ip) { - for (sp = ip -> client -> env; sp; sp = next) { + if (client) { + for (sp = client -> env; sp; sp = next) { next = sp -> next; - dfree (sp, "script_go"); + dfree (sp, MDL); } - ip -> client -> env = (struct string_list *)0; - ip -> client -> envc = 0; - dfree (envp, "script_go"); + client -> env = (struct string_list *)0; + client -> envc = 0; } - return wstatus & 0xff; + dfree (envp, MDL); + GET_TIME (&cur_time); + return (WIFEXITED (wstatus) ? + WEXITSTATUS (wstatus) : -WTERMSIG (wstatus)); } void client_envadd (struct client_state *client, @@ -2159,7 +2600,7 @@ void client_envadd (struct client_state *client, va_end (list); val = dmalloc (strlen (prefix) + strlen (name) + 1 /* = */ + - len + sizeof *val, "client_envadd"); + len + sizeof *val, MDL); if (!val) return; s = val -> string; @@ -2183,25 +2624,46 @@ int dhcp_option_ev_name (buf, buflen, option) size_t buflen; struct option *option; { - int i; + int i, j; + const char *s; - for (i = 0; option -> name [i]; i++) { - if (i + 1 == buflen) - return 0; - if (option -> name [i] == '-') - buf [i] = '_'; - else - buf [i] = option -> name [i]; + j = 0; + if (option -> universe != &dhcp_universe) { + s = option -> universe -> name; + i = 0; + } else { + s = option -> name; + i = 1; } - buf [i] = 0; + do { + while (*s) { + if (j + 1 == buflen) + return 0; + if (*s == '-') + buf [j++] = '_'; + else + buf [j++] = *s; + ++s; + } + if (!i) { + s = option -> name; + if (j + 1 == buflen) + return 0; + buf [j++] = '_'; + } + ++i; + } while (i != 2); + + buf [j] = 0; return 1; } - + void go_daemon () { static int state = 0; int pid; + int i; /* Don't become a daemon if the user requested otherwise. */ if (no_daemon) { @@ -2219,7 +2681,7 @@ void go_daemon () /* Become a daemon... */ if ((pid = fork ()) < 0) - error ("Can't fork daemon: %m"); + log_fatal ("Can't fork daemon: %m"); else if (pid) exit (0); /* Become session leader and get pid... */ @@ -2230,6 +2692,16 @@ void go_daemon () close(1); close(2); + /* Reopen them on /dev/null. */ + i = open ("/dev/null", O_RDWR); + if (i == 0) + i = open ("/dev/null", O_RDWR); + if (i == 1) { + i = open ("/dev/null", O_RDWR); + log_perror = 0; /* No sense logging to /dev/null. */ + } else if (i != -1) + close (i); + write_client_pid_file (); } @@ -2241,15 +2713,384 @@ void write_client_pid_file () pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644); if (pfdesc < 0) { - warn ("Can't create %s: %m", path_dhclient_pid); + log_error ("Can't create %s: %m", path_dhclient_pid); return; } pf = fdopen (pfdesc, "w"); if (!pf) - warn ("Can't fdopen %s: %m", path_dhclient_pid); + log_error ("Can't fdopen %s: %m", path_dhclient_pid); else { fprintf (pf, "%ld\n", (long)getpid ()); fclose (pf); } } + +void client_location_changed () +{ + struct interface_info *ip; + struct client_state *client; + + for (ip = interfaces; ip; ip = ip -> next) { + for (client = ip -> client; client; client = client -> next) { + switch (client -> state) { + case S_SELECTING: + cancel_timeout (send_discover, client); + break; + + case S_BOUND: + cancel_timeout (state_bound, client); + break; + + case S_REBOOTING: + case S_REQUESTING: + case S_RENEWING: + cancel_timeout (send_request, client); + break; + + case S_INIT: + case S_REBINDING: + case S_STOPPED: + break; + } + client -> state = S_INIT; + state_reboot (client); + } + } +} + +void do_release(client) + struct client_state *client; +{ + struct data_string ds; + struct option_cache *oc; + + /* Pick a random xid. */ + client -> xid = random (); + + /* is there even a lease to release? */ + if (client -> active) { + /* Make a DHCPRELEASE packet, and set appropriate per-interface + flags. */ + make_release (client, client -> active); + + memset (&ds, 0, sizeof ds); + oc = lookup_option (&dhcp_universe, + client -> active -> options, + DHO_DHCP_SERVER_IDENTIFIER); + if (oc && + evaluate_option_cache (&ds, (struct packet *)0, + (struct lease *)0, client, + (struct option_state *)0, + client -> active -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) { + memcpy (client -> destination.iabuf, + ds.data, 4); + client -> destination.len = 4; + } else + client -> destination = iaddr_broadcast; + } else + client -> destination = iaddr_broadcast; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; + + /* Zap the medium list... */ + client -> medium = (struct string_list *)0; + + /* Send out the first and only DHCPRELEASE packet. */ + send_release (client); + + /* Do the client script RELEASE operation. */ + script_init (client, + "RELEASE", (struct string_list *)0); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_write_params (client, "old_", client -> active); + script_go (client); + } + + /* Cancel any timeouts. */ + cancel_timeout (state_bound, client); + cancel_timeout (send_discover, client); + cancel_timeout (state_init, client); + cancel_timeout (send_request, client); + cancel_timeout (state_reboot, client); + client -> state = S_STOPPED; +} + +int dhclient_interface_shutdown_hook (struct interface_info *interface) +{ + do_release (interface -> client); + + return 1; +} + +int dhclient_interface_discovery_hook (struct interface_info *tmp) +{ + struct interface_info *last, *ip; + /* See if we can find the client from dummy_interfaces */ + last = 0; + for (ip = dummy_interfaces; ip; ip = ip -> next) { + if (!strcmp (ip -> name, tmp -> name)) { + /* Remove from dummy_interfaces */ + if (last) { + ip = (struct interface_info *)0; + interface_reference (&ip, last -> next, MDL); + interface_dereference (&last -> next, MDL); + if (ip -> next) { + interface_reference (&last -> next, + ip -> next, MDL); + interface_dereference (&ip -> next, + MDL); + } + } else { + ip = (struct interface_info *)0; + interface_reference (&ip, + dummy_interfaces, MDL); + interface_dereference (&dummy_interfaces, MDL); + if (ip -> next) { + interface_reference (&dummy_interfaces, + ip -> next, MDL); + interface_dereference (&ip -> next, + MDL); + } + } + /* Copy "client" to tmp */ + if (ip -> client) { + tmp -> client = ip -> client; + tmp -> client -> interface = tmp; + } + interface_dereference (&ip, MDL); + break; + } + last = ip; + } + return 1; +} + +isc_result_t dhclient_interface_startup_hook (struct interface_info *interface) +{ + struct interface_info *ip; + struct client_state *client; + + /* This code needs some rethinking. It doesn't test against + a signal name, and it just kind of bulls into doing something + that may or may not be appropriate. */ + + if (interfaces) { + interface_reference (&interface -> next, interfaces, MDL); + interface_dereference (&interfaces, MDL); + } + interface_reference (&interfaces, interface, MDL); + + discover_interfaces (DISCOVER_UNCONFIGURED); + + for (ip = interfaces; ip; ip = ip -> next) { + /* If interfaces were specified, don't configure + interfaces that weren't specified! */ + if (ip -> flags & INTERFACE_RUNNING || + (ip -> flags & (INTERFACE_REQUESTED | + INTERFACE_AUTOMATIC)) != + INTERFACE_REQUESTED) + continue; + script_init (ip -> client, + "PREINIT", (struct string_list *)0); + if (ip -> client -> alias) + script_write_params (ip -> client, "alias_", + ip -> client -> alias); + script_go (ip -> client); + } + + discover_interfaces (interfaces_requested + ? DISCOVER_REQUESTED + : DISCOVER_RUNNING); + + for (ip = interfaces; ip; ip = ip -> next) { + if (ip -> flags & INTERFACE_RUNNING) + continue; + ip -> flags |= INTERFACE_RUNNING; + for (client = ip -> client; client; client = client -> next) { + client -> state = S_INIT; + /* Set up a timeout to start the initialization + process. */ + add_timeout (cur_time + random () % 5, + state_reboot, client, 0, 0); + } + } + return ISC_R_SUCCESS; +} + +/* The client should never receive a relay agent information option, + so if it does, log it and discard it. */ + +int parse_agent_information_option (packet, len, data) + struct packet *packet; + int len; + u_int8_t *data; +{ + return 1; +} + +/* The client never sends relay agent information options. */ + +unsigned cons_agent_information_options (cfg_options, outpacket, + agentix, length) + struct option_state *cfg_options; + struct dhcp_packet *outpacket; + unsigned agentix; + unsigned length; +{ + return length; +} + +static void shutdown_exit (void *foo) +{ + exit (0); +} + +isc_result_t dhcp_set_control_state (control_object_state_t oldstate, + control_object_state_t newstate) +{ + struct interface_info *ip; + struct client_state *client; + + /* Do the right thing for each interface. */ + for (ip = interfaces; ip; ip = ip -> next) { + for (client = ip -> client; client; client = client -> next) { + switch (newstate) { + case server_startup: + return ISC_R_SUCCESS; + + case server_running: + return ISC_R_SUCCESS; + + case server_shutdown: + if (client -> active && + client -> active -> expiry > cur_time) { + client_dns_update (client, 0); + do_release (client); + } + break; + + case server_hibernate: + state_stop (client); + break; + + case server_awaken: + state_reboot (client); + break; + } + } + } + if (newstate == server_shutdown) + add_timeout (cur_time + 1, shutdown_exit, 0, 0, 0); + return ISC_R_SUCCESS; +} + +/* See if we should do a DNS update, and if so, do it. */ + +void client_dns_update (struct client_state *client, int addp) +{ + struct data_string ddns_fqdn, ddns_fwd_name, + ddns_dhcid, client_identifier; + struct option_cache *oc; + int ignorep; + int result; + isc_result_t rcode; + + /* If we didn't send an FQDN option, we certainly aren't going to + be doing an update. */ + if (!client -> sent_options) + return; + + /* If we don't have a lease, we can't do an update. */ + if (!client -> active) + return; + + /* If we set the no client update flag, don't do the update. */ + if ((oc = lookup_option (&fqdn_universe, client -> sent_options, + FQDN_NO_CLIENT_UPDATE)) && + evaluate_boolean_option_cache (&ignorep, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) + return; + + /* If we set the "server, please update" flag, or didn't set it + to false, don't do the update. */ + if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, + FQDN_SERVER_UPDATE)) || + evaluate_boolean_option_cache (&ignorep, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) + return; + + /* If no FQDN option was supplied, don't do the update. */ + memset (&ddns_fwd_name, 0, sizeof ddns_fwd_name); + if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, + FQDN_FQDN)) || + !evaluate_option_cache (&ddns_fwd_name, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) + return; + + /* Make a dhcid string out of either the client identifier, + if we are sending one, or the interface's MAC address, + otherwise. */ + memset (&ddns_dhcid, 0, sizeof ddns_dhcid); + + memset (&client_identifier, 0, sizeof client_identifier); + if ((oc = lookup_option (&dhcp_universe, client -> sent_options, + DHO_DHCP_CLIENT_IDENTIFIER)) && + evaluate_option_cache (&client_identifier, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) { + result = get_dhcid (&ddns_dhcid, + DHO_DHCP_CLIENT_IDENTIFIER, + client_identifier.data, + client_identifier.len); + data_string_forget (&client_identifier, MDL); + } else + result = get_dhcid (&ddns_dhcid, 0, + client -> interface -> hw_address.hbuf, + client -> interface -> hw_address.hlen); + if (!result) { + data_string_forget (&ddns_fwd_name, MDL); + return; + } + + /* Start the resolver, if necessary. */ + if (!resolver_inited) { + minires_ninit (&resolver_state); + resolver_inited = 1; + resolver_state.retrans = 1; + resolver_state.retry = 1; + } + + /* + * Perform updates. + */ + if (ddns_fwd_name.len && ddns_dhcid.len) { + if (addp) + rcode = ddns_update_a (&ddns_fwd_name, + client -> active -> address, + &ddns_dhcid, DEFAULT_DDNS_TTL, + 1); + else + rcode = ddns_remove_a (&ddns_fwd_name, + client -> active -> address, + &ddns_dhcid); + } + + data_string_forget (&ddns_fwd_name, MDL); + data_string_forget (&ddns_dhcid, MDL); +}