• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* dnsmasq is Copyright (c) 2000-2009 Simon Kelley
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 dated June, 1991, or
6    (at your option) version 3 dated 29 June, 2007.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15 */
16 
17 #include "dnsmasq.h"
18 
19 #ifdef HAVE_DHCP
20 
21 #define BOOTREQUEST              1
22 #define BOOTREPLY                2
23 #define DHCP_COOKIE              0x63825363
24 
25 /* The Linux in-kernel DHCP client silently ignores any packet
26    smaller than this. Sigh...........   */
27 #define MIN_PACKETSZ             300
28 
29 #define OPTION_PAD               0
30 #define OPTION_NETMASK           1
31 #define OPTION_ROUTER            3
32 #define OPTION_DNSSERVER         6
33 #define OPTION_HOSTNAME          12
34 #define OPTION_DOMAINNAME        15
35 #define OPTION_BROADCAST         28
36 #define OPTION_VENDOR_CLASS_OPT  43
37 #define OPTION_REQUESTED_IP      50
38 #define OPTION_LEASE_TIME        51
39 #define OPTION_OVERLOAD          52
40 #define OPTION_MESSAGE_TYPE      53
41 #define OPTION_SERVER_IDENTIFIER 54
42 #define OPTION_REQUESTED_OPTIONS 55
43 #define OPTION_MESSAGE           56
44 #define OPTION_MAXMESSAGE        57
45 #define OPTION_T1                58
46 #define OPTION_T2                59
47 #define OPTION_VENDOR_ID         60
48 #define OPTION_CLIENT_ID         61
49 #define OPTION_SNAME             66
50 #define OPTION_FILENAME          67
51 #define OPTION_USER_CLASS        77
52 #define OPTION_CLIENT_FQDN       81
53 #define OPTION_AGENT_ID          82
54 #define OPTION_ARCH              93
55 #define OPTION_PXE_UUID          97
56 #define OPTION_SUBNET_SELECT     118
57 #define OPTION_END               255
58 
59 #define SUBOPT_CIRCUIT_ID        1
60 #define SUBOPT_REMOTE_ID         2
61 #define SUBOPT_SUBNET_SELECT     5     /* RFC 3527 */
62 #define SUBOPT_SUBSCR_ID         6     /* RFC 3393 */
63 #define SUBOPT_SERVER_OR         11    /* RFC 5107 */
64 
65 #define SUBOPT_PXE_BOOT_ITEM     71    /* PXE standard */
66 #define SUBOPT_PXE_DISCOVERY     6
67 #define SUBOPT_PXE_SERVERS       8
68 #define SUBOPT_PXE_MENU          9
69 #define SUBOPT_PXE_MENU_PROMPT   10
70 
71 #define DHCPDISCOVER             1
72 #define DHCPOFFER                2
73 #define DHCPREQUEST              3
74 #define DHCPDECLINE              4
75 #define DHCPACK                  5
76 #define DHCPNAK                  6
77 #define DHCPRELEASE              7
78 #define DHCPINFORM               8
79 
80 #define have_config(config, mask) ((config) && ((config)->flags & (mask)))
81 #define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
82 #define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
83 
84 static int sanitise(unsigned char *opt, char *buf);
85 static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
86 static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
87 static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
88 static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
89 			      int opt, char *string, int null_term);
90 static struct in_addr option_addr(unsigned char *opt);
91 static struct in_addr option_addr_arr(unsigned char *opt, int offset);
92 static unsigned int option_uint(unsigned char *opt, int i, int size);
93 static void log_packet(char *type, void *addr, unsigned char *ext_mac,
94 		       int mac_len, char *interface, char *string, u32 xid);
95 static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
96 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
97 static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
98 			       unsigned char *agent_id, unsigned char *real_end);
99 static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
100 static void do_options(struct dhcp_context *context,
101 		       struct dhcp_packet *mess,
102 		       unsigned char *real_end,
103 		       unsigned char *req_options,
104 		       char *hostname,
105 		       char *domain, char *config_domain,
106 		       struct dhcp_netid *netid,
107 		       struct in_addr subnet_addr,
108 		       unsigned char fqdn_flags,
109 		       int null_term, int pxearch,
110 		       unsigned char *uuid);
111 
112 
113 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
114 static void do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
115 static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
116 static int prune_vendor_opts(struct dhcp_netid *netid);
117 static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid);
118 struct dhcp_boot *find_boot(struct dhcp_netid *netid);
119 
120 
dhcp_reply(struct dhcp_context * context,char * iface_name,int int_index,size_t sz,time_t now,int unicast_dest,int * is_inform)121 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
122 		  size_t sz, time_t now, int unicast_dest, int *is_inform)
123 {
124   unsigned char *opt, *clid = NULL;
125   struct dhcp_lease *ltmp, *lease = NULL;
126   struct dhcp_vendor *vendor;
127   struct dhcp_mac *mac;
128   struct dhcp_netid_list *id_list;
129   int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
130   struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
131   unsigned char *end = (unsigned char *)(mess + 1);
132   unsigned char *real_end = (unsigned char *)(mess + 1);
133   char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
134   int hostname_auth = 0, borken_opt = 0;
135   unsigned char *req_options = NULL;
136   char *message = NULL;
137   unsigned int time;
138   struct dhcp_config *config;
139   struct dhcp_netid *netid;
140   struct in_addr subnet_addr, fallback, override;
141   unsigned short fuzz = 0;
142   unsigned int mess_type = 0;
143   unsigned char fqdn_flags = 0;
144   unsigned char *agent_id = NULL, *uuid = NULL;
145   unsigned char *emac = NULL;
146   int emac_len = 0;
147   struct dhcp_netid known_id, iface_id;
148   struct dhcp_opt *o;
149   unsigned char pxe_uuid[17];
150 
151   subnet_addr.s_addr = override.s_addr = 0;
152 
153   /* set tag with name == interface */
154   iface_id.net = iface_name;
155   iface_id.next = NULL;
156   netid = &iface_id;
157 
158   if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
159     return 0;
160 
161   if (mess->htype == 0 && mess->hlen != 0)
162     return 0;
163 
164   /* check for DHCP rather than BOOTP */
165   if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
166     {
167       mess_type = option_uint(opt, 0, 1);
168 
169       /* only insist on a cookie for DHCP. */
170       if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
171 	return 0;
172 
173       /* two things to note here: expand_buf may move the packet,
174 	 so reassign mess from daemon->packet. Also, the size
175 	 sent includes the IP and UDP headers, hence the magic "-28" */
176       if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
177 	{
178 	  size_t size = (size_t)option_uint(opt, 0, 2) - 28;
179 
180 	  if (size > DHCP_PACKET_MAX)
181 	    size = DHCP_PACKET_MAX;
182 	  else if (size < sizeof(struct dhcp_packet))
183 	    size = sizeof(struct dhcp_packet);
184 
185 	  if (expand_buf(&daemon->dhcp_packet, size))
186 	    {
187 	      mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
188 	      real_end = end = ((unsigned char *)mess) + size;
189 	    }
190 	}
191 
192       /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
193 	 it can affect the context-determination code. */
194       if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
195 	mess->ciaddr.s_addr = 0;
196 
197       if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
198 	{
199 	  /* Any agent-id needs to be copied back out, verbatim, as the last option
200 	     in the packet. Here, we shift it to the very end of the buffer, if it doesn't
201 	     get overwritten, then it will be shuffled back at the end of processing.
202 	     Note that the incoming options must not be overwritten here, so there has to
203 	     be enough free space at the end of the packet to copy the option. */
204 	  unsigned char *sopt;
205 	  unsigned int total = option_len(opt) + 2;
206 	  unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
207 	  if (last_opt && last_opt < end - total)
208 	    {
209 	      end -= total;
210 	      agent_id = end;
211 	      memcpy(agent_id, opt, total);
212 	    }
213 
214 	  /* look for RFC3527 Link selection sub-option */
215 	  if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
216 	    subnet_addr = option_addr(sopt);
217 
218 	  /* look for RFC5107 server-identifier-override */
219 	  if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
220 	    override = option_addr(sopt);
221 
222 	  /* if a circuit-id or remote-is option is provided, exact-match to options. */
223 	  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
224 	    {
225 	      int search;
226 
227 	      if (vendor->match_type == MATCH_CIRCUIT)
228 		search = SUBOPT_CIRCUIT_ID;
229 	      else if (vendor->match_type == MATCH_REMOTE)
230 		search = SUBOPT_REMOTE_ID;
231 	      else if (vendor->match_type == MATCH_SUBSCRIBER)
232 		search = SUBOPT_SUBSCR_ID;
233 	      else
234 		continue;
235 
236 	      if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
237 		  vendor->len == option_len(sopt) &&
238 		  memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
239 		{
240 		  vendor->netid.next = netid;
241 		  netid = &vendor->netid;
242 		  break;
243 		}
244 	    }
245 	}
246 
247       /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
248       if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
249 	subnet_addr = option_addr(opt);
250 
251       /* If there is no client identifier option, use the hardware address */
252       if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
253 	{
254 	  clid_len = option_len(opt);
255 	  clid = option_ptr(opt, 0);
256 	}
257 
258       /* do we have a lease in store? */
259       lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
260 
261       /* If this request is missing a clid, but we've seen one before,
262 	 use it again for option matching etc. */
263       if (lease && !clid && lease->clid)
264 	{
265 	  clid_len = lease->clid_len;
266 	  clid = lease->clid;
267 	}
268 
269       /* find mac to use for logging and hashing */
270       emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
271     }
272 
273   for (mac = daemon->dhcp_macs; mac; mac = mac->next)
274     if (mac->hwaddr_len == mess->hlen &&
275 	(mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
276 	memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
277       {
278 	mac->netid.next = netid;
279 	netid = &mac->netid;
280       }
281 
282   /* Determine network for this packet. Our caller will have already linked all the
283      contexts which match the addresses of the receiving interface but if the
284      machine has an address already, or came via a relay, or we have a subnet selector,
285      we search again. If we don't have have a giaddr or explicit subnet selector,
286      use the ciaddr. This is necessary because a  machine which got a lease via a
287      relay won't use the relay to renew. If matching a ciaddr fails but we have a context
288      from the physical network, continue using that to allow correct DHCPNAK generation later. */
289   if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
290     {
291       struct dhcp_context *context_tmp, *context_new = NULL;
292       struct in_addr addr;
293       int force = 0;
294 
295       if (subnet_addr.s_addr)
296 	{
297 	  addr = subnet_addr;
298 	  force = 1;
299 	}
300       else if (mess->giaddr.s_addr)
301 	{
302 	  addr = mess->giaddr;
303 	  force = 1;
304 	}
305       else
306 	{
307 	  /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
308 	  addr = mess->ciaddr;
309 	  for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
310 	    if (context_tmp->netmask.s_addr &&
311 		is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
312 		is_same_net(addr, context_tmp->end, context_tmp->netmask))
313 	      {
314 		context_new = context;
315 		break;
316 	      }
317 	}
318 
319       if (!context_new)
320 	for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
321 	  if (context_tmp->netmask.s_addr  &&
322 	      is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
323 	      is_same_net(addr, context_tmp->end, context_tmp->netmask))
324 	    {
325 	      context_tmp->current = context_new;
326 	      context_new = context_tmp;
327 	    }
328 
329       if (context_new || force)
330 	context = context_new;
331 
332     }
333 
334   if (!context)
335     {
336 //      my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
337 //		subnet_addr.s_addr ? _("with subnet selector") : _("via"),
338 //		subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
339       return 0;
340     }
341 
342   /* keep _a_ local address available. */
343   fallback = context->local;
344 
345   if (daemon->options & OPT_LOG_OPTS)
346     {
347       struct dhcp_context *context_tmp;
348       for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
349 	{
350 	  strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
351 	  if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
352 	    my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP subnet: %s/%s"),
353 		      ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
354 	  else
355 	    my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP range: %s -- %s"),
356 		      ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
357 	}
358     }
359 
360   mess->op = BOOTREPLY;
361 
362   config = find_config(daemon->dhcp_conf, context, clid, clid_len,
363 		       mess->chaddr, mess->hlen, mess->htype, NULL);
364 
365   /* set "known" tag for known hosts */
366   if (config)
367     {
368       known_id.net = "known";
369       known_id.next = netid;
370       netid = &known_id;
371     }
372 
373   if (mess_type == 0)
374     {
375       /* BOOTP request */
376       struct dhcp_netid id, bootp_id;
377       struct in_addr *logaddr = NULL;
378 
379       /* must have a MAC addr for bootp */
380       if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
381 	return 0;
382 
383       if (have_config(config, CONFIG_DISABLE))
384 	message = _("disabled");
385 
386       end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
387 
388       if (have_config(config, CONFIG_NAME))
389 	{
390 	  hostname = config->hostname;
391 	  domain = config->domain;
392 	}
393 
394       if (have_config(config, CONFIG_NETID))
395 	{
396 	  config->netid.next = netid;
397 	  netid = &config->netid;
398 	}
399 
400       /* Match incoming filename field as a netid. */
401       if (mess->file[0])
402 	{
403 	  memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
404 	  daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
405 	  id.net = (char *)daemon->dhcp_buff2;
406 	  id.next = netid;
407 	  netid = &id;
408 	}
409 
410       /* Add "bootp" as a tag to allow different options, address ranges etc
411 	 for BOOTP clients */
412       bootp_id.net = "bootp";
413       bootp_id.next = netid;
414       netid = &bootp_id;
415 
416       for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
417 	if (match_netid(id_list->list, netid, 0))
418 	  message = _("ignored");
419 
420       if (!message)
421 	{
422 	  int nailed = 0;
423 
424 	  if (have_config(config, CONFIG_ADDR))
425 	    {
426 	      nailed = 1;
427 	      logaddr = &config->addr;
428 	      mess->yiaddr = config->addr;
429 	      if ((lease = lease_find_by_addr(config->addr)) &&
430 		  (lease->hwaddr_len != mess->hlen ||
431 		   lease->hwaddr_type != mess->htype ||
432 		   memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
433 		message = _("address in use");
434 	    }
435 	  else
436 	    {
437 	      if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
438 		  !address_available(context, lease->addr, netid))
439 		{
440 		   if (lease)
441 		     {
442 		       /* lease exists, wrong network. */
443 		       lease_prune(lease, now);
444 		       lease = NULL;
445 		     }
446 		   if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
447 		     message = _("no address available");
448 		}
449 	      else
450 		mess->yiaddr = lease->addr;
451 	    }
452 
453 	  if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
454 	    message = _("wrong network");
455 	  else if (context->netid.net)
456 	    {
457 	      context->netid.next = netid;
458 	      netid = &context->netid;
459 	    }
460 
461 	  if (!message && !nailed)
462 	    {
463 	      for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
464 		if ((!id_list->list) || match_netid(id_list->list, netid, 0))
465 		  break;
466 	      if (!id_list)
467 		message = _("no address configured");
468 	    }
469 
470 	  if (!message &&
471 	      !lease &&
472 	      (!(lease = lease_allocate(mess->yiaddr))))
473 	    message = _("no leases left");
474 
475 	  if (!message)
476 	    {
477 	      logaddr = &mess->yiaddr;
478 
479 	      lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
480 	      if (hostname)
481 		lease_set_hostname(lease, hostname, 1);
482 	      /* infinite lease unless nailed in dhcp-host line. */
483 	      lease_set_expires(lease,
484 				have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
485 				now);
486 	      lease_set_interface(lease, int_index);
487 
488 	      clear_packet(mess, end);
489 	      do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
490 			 domain, netid, subnet_addr, 0, 0, 0, NULL);
491 	    }
492 	}
493 
494       log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
495 
496       return message ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
497     }
498 
499   if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
500     {
501       /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
502       int len = option_len(opt);
503       char *pq = daemon->dhcp_buff;
504       unsigned char *pp, *op = option_ptr(opt, 0);
505 
506       fqdn_flags = *op;
507       len -= 3;
508       op += 3;
509       pp = op;
510 
511       /* Always force update, since the client has no way to do it itself. */
512       if (!(fqdn_flags & 0x01))
513 	fqdn_flags |= 0x02;
514 
515       fqdn_flags &= ~0x08;
516       fqdn_flags |= 0x01;
517 
518       if (fqdn_flags & 0x04)
519 	while (*op != 0 && ((op + (*op) + 1) - pp) < len)
520 	  {
521 	    memcpy(pq, op+1, *op);
522 	    pq += *op;
523 	    op += (*op)+1;
524 	    *(pq++) = '.';
525 	  }
526       else
527 	{
528 	  memcpy(pq, op, len);
529 	  if (len > 0 && op[len-1] == 0)
530 	    borken_opt = 1;
531 	  pq += len + 1;
532 	}
533 
534       if (pq != daemon->dhcp_buff)
535 	pq--;
536 
537       *pq = 0;
538 
539       if (legal_hostname(daemon->dhcp_buff))
540 	offer_hostname = client_hostname = daemon->dhcp_buff;
541     }
542   else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
543     {
544       int len = option_len(opt);
545       memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
546       /* Microsoft clients are broken, and need zero-terminated strings
547 	 in options. We detect this state here, and do the same in
548 	 any options we send */
549       if (len > 0 && daemon->dhcp_buff[len-1] == 0)
550 	borken_opt = 1;
551       else
552 	daemon->dhcp_buff[len] = 0;
553       if (legal_hostname(daemon->dhcp_buff))
554 	client_hostname = daemon->dhcp_buff;
555     }
556 
557   if (client_hostname && daemon->options & OPT_LOG_OPTS)
558     my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
559 
560   if (have_config(config, CONFIG_NAME))
561     {
562       hostname = config->hostname;
563       domain = config->domain;
564       hostname_auth = 1;
565       /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
566       if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
567         offer_hostname = hostname;
568     }
569   else if (client_hostname)
570     {
571       domain = strip_hostname(client_hostname);
572 
573       if (strlen(client_hostname) != 0)
574 	{
575 	  hostname = client_hostname;
576 	  if (!config)
577 	    {
578 	      /* Search again now we have a hostname.
579 		 Only accept configs without CLID and HWADDR here, (they won't match)
580 		 to avoid impersonation by name. */
581 	      struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
582 						    mess->chaddr, mess->hlen,
583 						    mess->htype, hostname);
584 	      if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
585 		{
586 		  config = new;
587 		  /* set "known" tag for known hosts */
588 		  known_id.net = "known";
589 		  known_id.next = netid;
590 		  netid = &known_id;
591 		}
592 	    }
593 	}
594     }
595 
596   if (have_config(config, CONFIG_NETID))
597     {
598       config->netid.next = netid;
599       netid = &config->netid;
600     }
601 
602   /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
603      Otherwise assume the option is an array, and look for a matching element.
604      If no data given, existance of the option is enough. */
605   for (o = daemon->dhcp_match; o; o = o->next)
606     {
607       int i, matched = 0;
608 
609       if (!(opt = option_find(mess, sz, o->opt, 1)) ||
610 	  o->len > option_len(opt))
611 	continue;
612 
613       if (o->len == 0)
614 	matched = 1;
615       else if (o->flags & DHOPT_HEX)
616 	{
617 	  if (memcmp_masked(o->val, option_ptr(opt, 0), o->len, o->u.wildcard_mask))
618 	    matched = 1;
619 	}
620       else
621 	for (i = 0; i <= (option_len(opt) - o->len); )
622 	  {
623 	    if (memcmp(o->val, option_ptr(opt, i), o->len) == 0)
624 	      {
625 		matched = 1;
626 		break;
627 	      }
628 
629 	    if (o->flags & DHOPT_STRING)
630 	      i++;
631 	    else
632 	      i += o->len;
633 	  }
634 
635       if (matched)
636 	{
637 	  o->netid->next = netid;
638 	  netid = o->netid;
639 	}
640     }
641 
642   /* user-class options are, according to RFC3004, supposed to contain
643      a set of counted strings. Here we check that this is so (by seeing
644      if the counts are consistent with the overall option length) and if
645      so zero the counts so that we don't get spurious matches between
646      the vendor string and the counts. If the lengths don't add up, we
647      assume that the option is a single string and non RFC3004 compliant
648      and just do the substring match. dhclient provides these broken options.
649      The code, later, which sends user-class data to the lease-change script
650      relies on the transformation done here.
651   */
652 
653   if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
654     {
655       unsigned char *ucp = option_ptr(opt, 0);
656       int tmp, j;
657       for (j = 0; j < option_len(opt); j += ucp[j] + 1);
658       if (j == option_len(opt))
659 	for (j = 0; j < option_len(opt); j = tmp)
660 	  {
661 	    tmp = j + ucp[j] + 1;
662 	    ucp[j] = 0;
663 	  }
664     }
665 
666   for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
667     {
668       int mopt;
669 
670       if (vendor->match_type == MATCH_VENDOR)
671 	mopt = OPTION_VENDOR_ID;
672       else if (vendor->match_type == MATCH_USER)
673 	mopt = OPTION_USER_CLASS;
674       else
675 	continue;
676 
677       if ((opt = option_find(mess, sz, mopt, 1)))
678 	{
679 	  int i;
680 	  for (i = 0; i <= (option_len(opt) - vendor->len); i++)
681 	    if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
682 	      {
683 		vendor->netid.next = netid;
684 		netid = &vendor->netid;
685 		break;
686 	      }
687 	}
688     }
689 
690   /* mark vendor-encapsulated options which match the client-supplied vendor class */
691   match_vendor_opts(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->dhcp_opts);
692 
693   if (daemon->options & OPT_LOG_OPTS)
694     {
695       if (sanitise(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->namebuff))
696 	my_syslog(MS_DHCP | LOG_INFO, _("%u Vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
697       if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
698 	my_syslog(MS_DHCP | LOG_INFO, _("%u User class: %s"), ntohl(mess->xid), daemon->namebuff);
699     }
700 
701   /* if all the netids in the ignore list are present, ignore this client */
702   for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
703     if (match_netid(id_list->list, netid, 0))
704       ignore = 1;
705 
706   /* Can have setting to ignore the client ID for a particular MAC address or hostname */
707   if (have_config(config, CONFIG_NOCLID))
708     clid = NULL;
709 
710   /* Check if client is PXE client. */
711   if (daemon->enable_pxe &&
712       (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
713       strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
714     {
715       if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
716 	{
717 	  memcpy(pxe_uuid, option_ptr(opt, 0), 17);
718 	  uuid = pxe_uuid;
719 	}
720 
721       /* Check if this is really a PXE bootserver request, and handle specially if so. */
722       if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
723 	  (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
724 	  (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
725 	{
726 	  struct pxe_service *service;
727 	  int type = option_uint(opt, 0, 2);
728 	  int layer = option_uint(opt, 2, 2);
729 	  unsigned char save71[4];
730 	  struct dhcp_opt opt71;
731 
732 	  if (ignore)
733 	    return 0;
734 
735 	  if (layer & 0x8000)
736 	    {
737 	      my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
738 	      return 0;
739 	    }
740 
741 	  memcpy(save71, option_ptr(opt, 0), 4);
742 
743 	  for (service = daemon->pxe_services; service; service = service->next)
744 	    if (service->type == type)
745 	      break;
746 
747 	  if (!service || !service->basename)
748 	    return 0;
749 
750 	  clear_packet(mess, end);
751 
752 	  mess->yiaddr = mess->ciaddr;
753 	  mess->ciaddr.s_addr = 0;
754 	  if (service->server.s_addr != 0)
755 	    mess->siaddr = service->server;
756 	  else
757 	    mess->siaddr = context->local;
758 
759 	  snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
760 	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
761 	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
762 	  pxe_misc(mess, end, uuid);
763 
764 	  prune_vendor_opts(netid);
765 	  opt71.val = save71;
766 	  opt71.opt = SUBOPT_PXE_BOOT_ITEM;
767 	  opt71.len = 4;
768 	  opt71.flags = DHOPT_VENDOR_MATCH;
769 	  opt71.netid = NULL;
770 	  opt71.next = daemon->dhcp_opts;
771 	  do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
772 
773 	  log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
774 	  return dhcp_packet_size(mess, netid, agent_id, real_end);
775 	}
776 
777       if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
778 	{
779 	  pxearch = option_uint(opt, 0, 2);
780 
781 	  /* proxy DHCP here. The DHCPREQUEST stuff is for gPXE */
782 	  if ((mess_type == DHCPDISCOVER || mess_type == DHCPREQUEST) &&
783 	      (context->flags & CONTEXT_PROXY))
784 	    {
785 	      struct dhcp_boot *boot = find_boot(netid);
786 
787 	      mess->yiaddr.s_addr = 0;
788 	      if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
789 		{
790 		  mess->ciaddr.s_addr = 0;
791 		  mess->flags |= htons(0x8000); /* broadcast */
792 		}
793 
794 	      clear_packet(mess, end);
795 
796 	      /* Provide the bootfile here, for gPXE, and in case we have no menu items
797 		 and set discovery_control = 8 */
798 	      if (boot)
799 		{
800 		  if (boot->next_server.s_addr)
801 		    mess->siaddr = boot->next_server;
802 
803 		  if (boot->file)
804 		    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
805 		}
806 
807 	      option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
808 			 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
809 	      option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
810 	      pxe_misc(mess, end, uuid);
811 	      prune_vendor_opts(netid);
812 	      do_encap_opts(pxe_opts(pxearch, netid), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
813 
814 	      log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy" : "proxy-ignored", mess->xid);
815 	      return ignore ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
816 	    }
817 	}
818     }
819 
820   /* if we're just a proxy server, go no further */
821   if (context->flags & CONTEXT_PROXY)
822     return 0;
823 
824   if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
825     {
826       req_options = (unsigned char *)daemon->dhcp_buff2;
827       memcpy(req_options, option_ptr(opt, 0), option_len(opt));
828       req_options[option_len(opt)] = OPTION_END;
829     }
830 
831   switch (mess_type)
832     {
833     case DHCPDECLINE:
834       if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
835 	  option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
836 	return 0;
837 
838       /* sanitise any message. Paranoid? Moi? */
839       sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
840 
841       if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
842 	return 0;
843 
844       log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
845 
846       if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
847 	lease_prune(lease, now);
848 
849       if (have_config(config, CONFIG_ADDR) &&
850 	  config->addr.s_addr == option_addr(opt).s_addr)
851 	{
852 	  prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
853 	  my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
854 		    inet_ntoa(config->addr), daemon->dhcp_buff);
855 	  config->flags |= CONFIG_DECLINED;
856 	  config->decline_time = now;
857 	}
858       else
859 	/* make sure this host gets a different address next time. */
860 	for (; context; context = context->current)
861 	  context->addr_epoch++;
862 
863       return 0;
864 
865     case DHCPRELEASE:
866       if (!(context = narrow_context(context, mess->ciaddr, netid)) ||
867 	  !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
868 	  option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
869 	return 0;
870 
871       if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
872 	lease_prune(lease, now);
873       else
874 	message = _("unknown lease");
875 
876       log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
877 
878       return 0;
879 
880     case DHCPDISCOVER:
881       if (ignore || have_config(config, CONFIG_DISABLE))
882 	{
883 	  message = _("ignored");
884 	  opt = NULL;
885 	}
886       else
887 	{
888 	  struct in_addr addr, conf;
889 
890 	  addr.s_addr = conf.s_addr = 0;
891 
892 	  if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
893 	    addr = option_addr(opt);
894 
895 	  if (have_config(config, CONFIG_ADDR))
896 	    {
897 	      char *addrs = inet_ntoa(config->addr);
898 
899 	      if ((ltmp = lease_find_by_addr(config->addr)) &&
900 		  ltmp != lease &&
901 		  !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
902 		{
903 		  int len;
904 		  unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
905 						       ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
906 		  my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
907 			    addrs, print_mac(daemon->namebuff, mac, len));
908 		}
909 	      else
910 		{
911 		  struct dhcp_context *tmp;
912 		  for (tmp = context; tmp; tmp = tmp->current)
913 		    if (context->router.s_addr == config->addr.s_addr)
914 		      break;
915 		  if (tmp)
916 		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
917 		  else if (have_config(config, CONFIG_DECLINED) &&
918 			   difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
919 		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
920 		  else
921 		    conf = config->addr;
922 		}
923 	    }
924 
925 	  if (conf.s_addr)
926 	    mess->yiaddr = conf;
927 	  else if (lease &&
928 		   address_available(context, lease->addr, netid) &&
929 		   !config_find_by_address(daemon->dhcp_conf, lease->addr))
930 	    mess->yiaddr = lease->addr;
931 	  else if (opt && address_available(context, addr, netid) && !lease_find_by_addr(addr) &&
932 		   !config_find_by_address(daemon->dhcp_conf, addr))
933 	    mess->yiaddr = addr;
934 	  else if (emac_len == 0)
935 	    message = _("no unique-id");
936 	  else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, netid, now))
937 	    message = _("no address available");
938 	}
939 
940       log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
941 
942       if (message || !(context = narrow_context(context, mess->yiaddr, netid)))
943 	return 0;
944 
945       log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
946 
947       if (context->netid.net)
948 	{
949 	  context->netid.next = netid;
950 	  netid = &context->netid;
951 	}
952 
953       time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
954       clear_packet(mess, end);
955       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
956       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
957       option_put(mess, end, OPTION_LEASE_TIME, 4, time);
958       /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
959       if (time != 0xffffffff)
960 	{
961 	  option_put(mess, end, OPTION_T1, 4, (time/2));
962 	  option_put(mess, end, OPTION_T2, 4, (time*7)/8);
963 	}
964       do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
965 		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
966 
967       return dhcp_packet_size(mess, netid, agent_id, real_end);
968 
969     case DHCPREQUEST:
970       if (ignore || have_config(config, CONFIG_DISABLE))
971 	return 0;
972       if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
973 	{
974 	  /* SELECTING  or INIT_REBOOT */
975 	  mess->yiaddr = option_addr(opt);
976 
977 	  /* send vendor and user class info for new or recreated lease */
978 	  do_classes = 1;
979 
980 	  if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
981 	    {
982 	      /* SELECTING */
983 	      selecting = 1;
984 
985 	      if (override.s_addr != 0)
986 		{
987 		  if (option_addr(opt).s_addr != override.s_addr)
988 		    return 0;
989 		}
990 	      else
991 		{
992 		  for (; context; context = context->current)
993 		    if (context->local.s_addr == option_addr(opt).s_addr)
994 		      break;
995 
996 		  if (!context)
997 		    {
998 		      /* In auth mode, a REQUEST sent to the wrong server
999 			 should be faulted, so that the client establishes
1000 			 communication with us, otherwise, silently ignore. */
1001 		      if (!(daemon->options & OPT_AUTHORITATIVE))
1002 			return 0;
1003 		      message = _("wrong server-ID");
1004 		    }
1005 		}
1006 
1007 	      /* If a lease exists for this host and another address, squash it. */
1008 	      if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1009 		{
1010 		  lease_prune(lease, now);
1011 		  lease = NULL;
1012 		}
1013 	    }
1014 	  else
1015 	    {
1016 	      /* INIT-REBOOT */
1017 	      if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
1018 		return 0;
1019 
1020 	      if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1021 		{
1022 		  message = _("wrong address");
1023 		  /* avoid loops when client brain-dead */
1024 		  lease_prune(lease, now);
1025 		  lease = NULL;
1026 		}
1027 	    }
1028 	}
1029       else
1030 	{
1031 	  /* RENEWING or REBINDING */
1032 	  /* Check existing lease for this address.
1033 	     We allow it to be missing if dhcp-authoritative mode
1034 	     as long as we can allocate the lease now - checked below.
1035 	     This makes for a smooth recovery from a lost lease DB */
1036 	  if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
1037 	      (!lease && !(daemon->options & OPT_AUTHORITATIVE)))
1038 	    {
1039 	      message = _("lease not found");
1040 	      /* ensure we broadcast NAK */
1041 	      unicast_dest = 0;
1042 	    }
1043 	  /* desynchronise renewals */
1044 	  fuzz = rand16();
1045 	  mess->yiaddr = mess->ciaddr;
1046 	}
1047 
1048       log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
1049 
1050       if (!message)
1051 	{
1052 	  struct dhcp_config *addr_config;
1053 	  struct dhcp_context *tmp = NULL;
1054 
1055 	  if (have_config(config, CONFIG_ADDR))
1056 	    for (tmp = context; tmp; tmp = tmp->current)
1057 	      if (context->router.s_addr == config->addr.s_addr)
1058 		break;
1059 
1060 	  if (!(context = narrow_context(context, mess->yiaddr, netid)))
1061 	    {
1062 	      /* If a machine moves networks whilst it has a lease, we catch that here. */
1063 	      message = _("wrong network");
1064 	      /* ensure we broadcast NAK */
1065 	      unicast_dest = 0;
1066 	    }
1067 
1068 	  /* Check for renewal of a lease which is outside the allowed range. */
1069 	  else if (!address_available(context, mess->yiaddr, netid) &&
1070 		   (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
1071 	    message = _("address not available");
1072 
1073 	  /* Check if a new static address has been configured. Be very sure that
1074 	     when the client does DISCOVER, it will get the static address, otherwise
1075 	     an endless protocol loop will ensue. */
1076 	  else if (!tmp && !selecting &&
1077 		   have_config(config, CONFIG_ADDR) &&
1078 		   (!have_config(config, CONFIG_DECLINED) ||
1079 		    difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
1080 		   config->addr.s_addr != mess->yiaddr.s_addr &&
1081 		   (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
1082 	    message = _("static lease available");
1083 
1084 	  /* Check to see if the address is reserved as a static address for another host */
1085 	  else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
1086 	    message = _("address reserved");
1087 
1088 	  else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1089 	    {
1090 	      /* If a host is configured with more than one MAC address, it's OK to 'nix
1091 		 a lease from one of it's MACs to give the address to another. */
1092 	      if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1093 		{
1094 		  my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
1095 			    print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1096 			    inet_ntoa(ltmp->addr));
1097 		  lease = ltmp;
1098 		}
1099 	      else
1100 		message = _("address in use");
1101 	    }
1102 
1103 	  if (!message)
1104 	    {
1105 	      if (emac_len == 0)
1106 		message = _("no unique-id");
1107 
1108 	      else if (!lease)
1109 		{
1110 		  if ((lease = lease_allocate(mess->yiaddr)))
1111 		    do_classes = 1;
1112 		  else
1113 		    message = _("no leases left");
1114 		}
1115 	    }
1116 	}
1117 
1118       if (message)
1119 	{
1120 	  log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
1121 
1122 	  mess->yiaddr.s_addr = 0;
1123 	  clear_packet(mess, end);
1124 	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
1125 	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1126 	  option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
1127 	  /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1128 	     a distant subnet which unicast a REQ to us won't work. */
1129 	  if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1130 	      mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1131 	    {
1132 	      mess->flags |= htons(0x8000); /* broadcast */
1133 	      mess->ciaddr.s_addr = 0;
1134 	    }
1135 	}
1136       else
1137 	{
1138 	   if (do_classes)
1139 	     {
1140 	       if (mess->giaddr.s_addr)
1141 		 lease->giaddr = mess->giaddr;
1142 
1143 	       lease->changed = 1;
1144 	       /* copy user-class and vendor class into new lease, for the script */
1145 	       if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1146 		 {
1147 		   int len = option_len(opt);
1148 		   unsigned char *ucp = option_ptr(opt, 0);
1149 		   /* If the user-class option started as counted strings, the first byte will be zero. */
1150 		   if (len != 0 && ucp[0] == 0)
1151 		     ucp++, len--;
1152 		   free(lease->userclass);
1153 		   if ((lease->userclass = whine_malloc(len+1)))
1154 		     {
1155 		       memcpy(lease->userclass, ucp, len);
1156 		       lease->userclass[len] = 0;
1157 		       lease->userclass_len = len+1;
1158 		     }
1159 		 }
1160 	       if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
1161 		 {
1162 		   int len = option_len(opt);
1163 		   unsigned char *ucp = option_ptr(opt, 0);
1164 		   free(lease->vendorclass);
1165 		   if ((lease->vendorclass = whine_malloc(len+1)))
1166 		     {
1167 		       memcpy(lease->vendorclass, ucp, len);
1168 		       lease->vendorclass[len] = 0;
1169 		       lease->vendorclass_len = len+1;
1170 		     }
1171 		 }
1172 	       if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
1173 		 {
1174 		   int len = option_len(opt);
1175 		   unsigned char *ucp = option_ptr(opt, 0);
1176 		   free(lease->supplied_hostname);
1177 		   if ((lease->supplied_hostname = whine_malloc(len+1)))
1178 		     {
1179 		       memcpy(lease->supplied_hostname, ucp, len);
1180 		       lease->supplied_hostname[len] = 0;
1181 		       lease->supplied_hostname_len = len+1;
1182 		     }
1183 		 }
1184 	     }
1185 
1186 	   if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1187 	     {
1188 	      hostname = client_hostname;
1189 	      hostname_auth = 1;
1190 	    }
1191 
1192 	  if (context->netid.net)
1193 	    {
1194 	      context->netid.next = netid;
1195 	      netid = &context->netid;
1196 	    }
1197 
1198 	  time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
1199 	  lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
1200 
1201 	  /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1202 	  if (!hostname_auth)
1203 	    {
1204 	      for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
1205 		if ((!id_list->list) || match_netid(id_list->list, netid, 0))
1206 		  break;
1207 	      if (id_list)
1208 		hostname = NULL;
1209 	    }
1210 	  if (hostname)
1211 	    lease_set_hostname(lease, hostname, hostname_auth);
1212 
1213 	  lease_set_expires(lease, time, now);
1214 	  lease_set_interface(lease, int_index);
1215 
1216 	  if (override.s_addr != 0)
1217 	    lease->override = override;
1218 	  else
1219 	    override = lease->override;
1220 
1221 	  log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
1222 
1223 	  clear_packet(mess, end);
1224 	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
1225 	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1226 	  option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1227 	  if (time != 0xffffffff)
1228 	    {
1229 	      while (fuzz > (time/16))
1230 		fuzz = fuzz/2;
1231 	      option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1232 	      option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
1233 	    }
1234 	  do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
1235 		     domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
1236 	}
1237 
1238       return dhcp_packet_size(mess, netid, agent_id, real_end);
1239 
1240     case DHCPINFORM:
1241       if (ignore || have_config(config, CONFIG_DISABLE))
1242 	message = _("ignored");
1243 
1244       log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
1245 
1246       if (message || mess->ciaddr.s_addr == 0)
1247 	return 0;
1248 
1249       /* For DHCPINFORM only, cope without a valid context */
1250       context = narrow_context(context, mess->ciaddr, netid);
1251 
1252       /* Find a least based on IP address if we didn't
1253 	 get one from MAC address/client-d */
1254       if (!lease &&
1255 	  (lease = lease_find_by_addr(mess->ciaddr)) &&
1256 	  lease->hostname)
1257 	hostname = lease->hostname;
1258 
1259       if (!hostname)
1260 	hostname = host_from_dns(mess->ciaddr);
1261 
1262       log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
1263 
1264       if (context && context->netid.net)
1265 	{
1266 	  context->netid.next = netid;
1267 	  netid = &context->netid;
1268 	}
1269 
1270       if (lease)
1271 	{
1272 	  if (override.s_addr != 0)
1273 	    lease->override = override;
1274 	  else
1275 	    override = lease->override;
1276 	}
1277 
1278       clear_packet(mess, end);
1279       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
1280       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1281 
1282       if (lease)
1283 	{
1284 	  if (lease->expires == 0)
1285 	    time = 0xffffffff;
1286 	  else
1287 	    time = (unsigned int)difftime(lease->expires, now);
1288 	  option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1289 	  lease_set_interface(lease, int_index);
1290 	}
1291 
1292       do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
1293 		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
1294 
1295       *is_inform = 1; /* handle reply differently */
1296       return dhcp_packet_size(mess, netid, agent_id, real_end);
1297     }
1298 
1299   return 0;
1300 }
1301 
1302 /* find a good value to use as MAC address for logging and address-allocation hashing.
1303    This is normally just the chaddr field from the DHCP packet,
1304    but eg Firewire will have hlen == 0 and use the client-id instead.
1305    This could be anything, but will normally be EUI64 for Firewire.
1306    We assume that if the first byte of the client-id equals the htype byte
1307    then the client-id is using the usual encoding and use the rest of the
1308    client-id: if not we can use the whole client-id. This should give
1309    sane MAC address logs. */
extended_hwaddr(int hwtype,int hwlen,unsigned char * hwaddr,int clid_len,unsigned char * clid,int * len_out)1310 unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
1311 				      int clid_len, unsigned char *clid, int *len_out)
1312 {
1313   if (hwlen == 0 && clid && clid_len > 3)
1314     {
1315       if (clid[0]  == hwtype)
1316 	{
1317 	  *len_out = clid_len - 1 ;
1318 	  return clid + 1;
1319 	}
1320 
1321 #if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1322       if (clid[0] ==  ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1323 	{
1324 	  *len_out = clid_len - 1 ;
1325 	  return clid + 1;
1326 	}
1327 #endif
1328 
1329       *len_out = clid_len;
1330       return clid;
1331     }
1332 
1333   *len_out = hwlen;
1334   return hwaddr;
1335 }
1336 
calc_time(struct dhcp_context * context,struct dhcp_config * config,unsigned char * opt)1337 static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
1338 {
1339   unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
1340 
1341   if (opt)
1342     {
1343       unsigned int req_time = option_uint(opt, 0, 4);
1344       if (req_time < 120 )
1345 	req_time = 120; /* sanity */
1346       if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1347 	time = req_time;
1348     }
1349 
1350   return time;
1351 }
1352 
server_id(struct dhcp_context * context,struct in_addr override,struct in_addr fallback)1353 static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
1354 {
1355   if (override.s_addr != 0)
1356     return override;
1357   else if (context)
1358     return context->local;
1359   else
1360     return fallback;
1361 }
1362 
sanitise(unsigned char * opt,char * buf)1363 static int sanitise(unsigned char *opt, char *buf)
1364 {
1365   char *p;
1366   int i;
1367 
1368   *buf = 0;
1369 
1370   if (!opt)
1371     return 0;
1372 
1373   p = option_ptr(opt, 0);
1374 
1375   for (i = option_len(opt); i > 0; i--)
1376     {
1377       char c = *p++;
1378       if (isprint((int)c))
1379 	*buf++ = c;
1380     }
1381   *buf = 0; /* add terminator */
1382 
1383   return 1;
1384 }
1385 
log_packet(char * type,void * addr,unsigned char * ext_mac,int mac_len,char * interface,char * string,u32 xid)1386 static void log_packet(char *type, void *addr, unsigned char *ext_mac,
1387 		       int mac_len, char *interface, char *string, u32 xid)
1388 {
1389   struct in_addr a;
1390 
1391   /* addr may be misaligned */
1392   if (addr)
1393     memcpy(&a, addr, sizeof(a));
1394 
1395   print_mac(daemon->namebuff, ext_mac, mac_len);
1396 
1397   if(daemon->options & OPT_LOG_OPTS)
1398      my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
1399 	       ntohl(xid),
1400 	       type,
1401 	       interface,
1402 	       addr ? inet_ntoa(a) : "",
1403 	       addr ? " " : "",
1404 	       daemon->namebuff,
1405 	       string ? string : "");
1406   else
1407     my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
1408 	      type,
1409 	      interface,
1410 	      addr ? inet_ntoa(a) : "",
1411 	      addr ? " " : "",
1412 	      daemon->namebuff,
1413 	      string ? string : "");
1414 }
1415 
log_options(unsigned char * start,u32 xid)1416 static void log_options(unsigned char *start, u32 xid)
1417 {
1418   while (*start != OPTION_END)
1419     {
1420       int is_ip, is_name, i;
1421       char *text = option_string(start[0], &is_ip, &is_name);
1422       unsigned char trunc = option_len(start);
1423 
1424       if (is_ip)
1425 	for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ)
1426 	  {
1427 	    if (i != 0)
1428 	      strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
1429 	    strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
1430 	  }
1431       else if (!is_name || !sanitise(start, daemon->namebuff))
1432 	{
1433 	  if (trunc > 13)
1434 	    trunc = 13;
1435 	  print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
1436 	}
1437 
1438       my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s",
1439 		ntohl(xid), option_len(start), start[0],
1440 		text ? ":" : "", text ? text : "",
1441 		trunc == 0 ? "" : "  ",
1442 		trunc == 0 ? "" : daemon->namebuff,
1443 		trunc == option_len(start) ? "" : "...");
1444       start += start[1] + 2;
1445     }
1446 }
1447 
option_find1(unsigned char * p,unsigned char * end,int opt,int minsize)1448 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
1449 {
1450   while (1)
1451     {
1452       if (p > end)
1453 	return NULL;
1454       else if (*p == OPTION_END)
1455 	return opt == OPTION_END ? p : NULL;
1456       else if (*p == OPTION_PAD)
1457 	p++;
1458       else
1459 	{
1460 	  int opt_len;
1461 	  if (p > end - 2)
1462 	    return NULL; /* malformed packet */
1463 	  opt_len = option_len(p);
1464 	  if (p > end - (2 + opt_len))
1465 	    return NULL; /* malformed packet */
1466 	  if (*p == opt && opt_len >= minsize)
1467 	    return p;
1468 	  p += opt_len + 2;
1469 	}
1470     }
1471 }
1472 
option_find(struct dhcp_packet * mess,size_t size,int opt_type,int minsize)1473 static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
1474 {
1475   unsigned char *ret, *overload;
1476 
1477   /* skip over DHCP cookie; */
1478   if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1479     return ret;
1480 
1481   /* look for overload option. */
1482   if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
1483     return NULL;
1484 
1485   /* Can we look in filename area ? */
1486   if ((overload[2] & 1) &&
1487       (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1488     return ret;
1489 
1490   /* finally try sname area */
1491   if ((overload[2] & 2) &&
1492       (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1493     return ret;
1494 
1495   return NULL;
1496 }
1497 
option_addr_arr(unsigned char * opt,int offset)1498 static struct in_addr option_addr_arr(unsigned char *opt, int offset)
1499 {
1500   /* this worries about unaligned data in the option. */
1501   /* struct in_addr is network byte order */
1502   struct in_addr ret;
1503 
1504   memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
1505 
1506   return ret;
1507 }
1508 
option_addr(unsigned char * opt)1509 static struct in_addr option_addr(unsigned char *opt)
1510 {
1511   return option_addr_arr(opt, 0);
1512 }
1513 
option_uint(unsigned char * opt,int offset,int size)1514 static unsigned int option_uint(unsigned char *opt, int offset, int size)
1515 {
1516   /* this worries about unaligned data and byte order */
1517   unsigned int ret = 0;
1518   int i;
1519   unsigned char *p = option_ptr(opt, offset);
1520 
1521   for (i = 0; i < size; i++)
1522     ret = (ret << 8) | *p++;
1523 
1524   return ret;
1525 }
1526 
dhcp_skip_opts(unsigned char * start)1527 static unsigned char *dhcp_skip_opts(unsigned char *start)
1528 {
1529   while (*start != 0)
1530     start += start[1] + 2;
1531   return start;
1532 }
1533 
1534 /* only for use when building packet: doesn't check for bad data. */
find_overload(struct dhcp_packet * mess)1535 static unsigned char *find_overload(struct dhcp_packet *mess)
1536 {
1537   unsigned char *p = &mess->options[0] + sizeof(u32);
1538 
1539   while (*p != 0)
1540     {
1541       if (*p == OPTION_OVERLOAD)
1542 	return p;
1543       p += p[1] + 2;
1544     }
1545   return NULL;
1546 }
1547 
dhcp_packet_size(struct dhcp_packet * mess,struct dhcp_netid * netid,unsigned char * agent_id,unsigned char * real_end)1548 static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
1549 			       unsigned char *agent_id, unsigned char *real_end)
1550 {
1551   unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1552   unsigned char *overload;
1553   size_t ret;
1554   struct dhcp_netid_list *id_list;
1555   struct dhcp_netid *n;
1556 
1557   /* move agent_id back down to the end of the packet */
1558   if (agent_id)
1559     {
1560       memmove(p, agent_id, real_end - agent_id);
1561       p += real_end - agent_id;
1562       memset(p, 0, real_end - p); /* in case of overlap */
1563     }
1564 
1565   /* We do logging too */
1566   if (netid && (daemon->options & OPT_LOG_OPTS))
1567     {
1568       char *s = daemon->namebuff;
1569       for (*s = 0; netid; netid = netid->next)
1570 	{
1571 	  /* kill dupes. */
1572 	  for (n = netid->next; n; n = n->next)
1573 	    if (strcmp(netid->net, n->net) == 0)
1574 	      break;
1575 
1576 	  if (!n)
1577 	    {
1578 	      strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
1579 	      if (netid->next)
1580 		strncat (s, ", ", (MAXDNAME-1) - strlen(s));
1581 	    }
1582 	}
1583       my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
1584     }
1585 
1586   /* add END options to the regions. */
1587   overload = find_overload(mess);
1588 
1589   if (overload && (option_uint(overload, 0, 1) & 1))
1590     {
1591       *dhcp_skip_opts(mess->file) = OPTION_END;
1592       if (daemon->options & OPT_LOG_OPTS)
1593 	log_options(mess->file, mess->xid);
1594     }
1595   else if ((daemon->options & OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
1596     my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1597 
1598   if (overload && (option_uint(overload, 0, 1) & 2))
1599     {
1600       *dhcp_skip_opts(mess->sname) = OPTION_END;
1601       if (daemon->options & OPT_LOG_OPTS)
1602 	log_options(mess->sname, mess->xid);
1603     }
1604   else if ((daemon->options & OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
1605     my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1606 
1607 
1608   *p++ = OPTION_END;
1609 
1610   if (daemon->options & OPT_LOG_OPTS)
1611     {
1612       if (mess->siaddr.s_addr != 0)
1613 	my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1614 
1615       log_options(&mess->options[0] + sizeof(u32), mess->xid);
1616     }
1617 
1618   for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
1619     if (match_netid(id_list->list, netid, 0))
1620       mess->flags |= htons(0x8000); /* force broadcast */
1621 
1622   ret = (size_t)(p - (unsigned char *)mess);
1623 
1624   if (ret < MIN_PACKETSZ)
1625     ret = MIN_PACKETSZ;
1626 
1627   return ret;
1628 }
1629 
free_space(struct dhcp_packet * mess,unsigned char * end,int opt,int len)1630 static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1631 {
1632   unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1633 
1634   if (p + len + 3 >= end)
1635     /* not enough space in options area, try and use overload, if poss */
1636     {
1637       unsigned char *overload;
1638 
1639       if (!(overload = find_overload(mess)) &&
1640 	  (mess->file[0] == 0 || mess->sname[0] == 0))
1641 	{
1642 	  /* attempt to overload fname and sname areas, we've reserved space for the
1643 	     overflow option previuously. */
1644 	  overload = p;
1645 	  *(p++) = OPTION_OVERLOAD;
1646 	  *(p++) = 1;
1647 	}
1648 
1649       p = NULL;
1650 
1651       /* using filename field ? */
1652       if (overload)
1653 	{
1654 	  if (mess->file[0] == 0)
1655 	    overload[2] |= 1;
1656 
1657 	  if (overload[2] & 1)
1658 	    {
1659 	      p = dhcp_skip_opts(mess->file);
1660 	      if (p + len + 3 >= mess->file + sizeof(mess->file))
1661 		p = NULL;
1662 	    }
1663 
1664 	  if (!p)
1665 	    {
1666 	      /* try to bring sname into play (it may be already) */
1667 	      if (mess->sname[0] == 0)
1668 		overload[2] |= 2;
1669 
1670 	      if (overload[2] & 2)
1671 		{
1672 		  p = dhcp_skip_opts(mess->sname);
1673 		  if (p + len + 3 >= mess->sname + sizeof(mess->file))
1674 		    p = NULL;
1675 		}
1676 	    }
1677 	}
1678 
1679       if (!p)
1680 	my_syslog(MS_DHCP | LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
1681     }
1682 
1683   if (p)
1684     {
1685       *(p++) = opt;
1686       *(p++) = len;
1687     }
1688 
1689   return p;
1690 }
1691 
option_put(struct dhcp_packet * mess,unsigned char * end,int opt,int len,unsigned int val)1692 static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1693 {
1694   int i;
1695   unsigned char *p = free_space(mess, end, opt, len);
1696 
1697   if (p)
1698     for (i = 0; i < len; i++)
1699       *(p++) = val >> (8 * (len - (i + 1)));
1700 }
1701 
option_put_string(struct dhcp_packet * mess,unsigned char * end,int opt,char * string,int null_term)1702 static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1703 			      char *string, int null_term)
1704 {
1705   unsigned char *p;
1706   size_t len = strlen(string);
1707 
1708   if (null_term && len != 255)
1709     len++;
1710 
1711   if ((p = free_space(mess, end, opt, len)))
1712     memcpy(p, string, len);
1713 }
1714 
1715 /* return length, note this only does the data part */
do_opt(struct dhcp_opt * opt,unsigned char * p,struct dhcp_context * context,int null_term)1716 static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
1717 {
1718   int len = opt->len;
1719 
1720   if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1721     len++;
1722 
1723   if (p && len != 0)
1724     {
1725       if (context && (opt->flags & DHOPT_ADDR))
1726 	{
1727 	  int j;
1728 	  struct in_addr *a = (struct in_addr *)opt->val;
1729 	  for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1730 	    {
1731 	      /* zero means "self" (but not in vendorclass options.) */
1732 	      if (a->s_addr == 0)
1733 		memcpy(p, &context->local, INADDRSZ);
1734 	      else
1735 		memcpy(p, a, INADDRSZ);
1736 	      p += INADDRSZ;
1737 	    }
1738 	}
1739       else
1740 	memcpy(p, opt->val, len);
1741     }
1742   return len;
1743 }
1744 
in_list(unsigned char * list,int opt)1745 static int in_list(unsigned char *list, int opt)
1746 {
1747   int i;
1748 
1749    /* If no requested options, send everything, not nothing. */
1750   if (!list)
1751     return 1;
1752 
1753   for (i = 0; list[i] != OPTION_END; i++)
1754     if (opt == list[i])
1755       return 1;
1756 
1757   return 0;
1758 }
1759 
option_find2(struct dhcp_netid * netid,struct dhcp_opt * opts,int opt)1760 static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
1761 {
1762   struct dhcp_opt *tmp;
1763   for (tmp = opts; tmp; tmp = tmp->next)
1764     if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)))
1765       if (match_netid(tmp->netid, netid, netid ? 0 : 1))
1766 	return tmp;
1767 
1768   return netid ? option_find2(NULL, opts, opt) : NULL;
1769 }
1770 
1771 /* mark vendor-encapsulated options which match the client-supplied  or
1772    config-supplied vendor class */
match_vendor_opts(unsigned char * opt,struct dhcp_opt * dopt)1773 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1774 {
1775   for (; dopt; dopt = dopt->next)
1776     {
1777       dopt->flags &= ~DHOPT_VENDOR_MATCH;
1778       if (opt && (dopt->flags & DHOPT_VENDOR))
1779 	{
1780 	  int i, len = 0;
1781 	  if (dopt->u.vendor_class)
1782 	    len = strlen((char *)dopt->u.vendor_class);
1783 	  for (i = 0; i <= (option_len(opt) - len); i++)
1784 	    if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
1785 	      {
1786 		dopt->flags |= DHOPT_VENDOR_MATCH;
1787 		break;
1788 	      }
1789 	}
1790     }
1791 }
1792 
do_encap_opts(struct dhcp_opt * opt,int encap,int flag,struct dhcp_packet * mess,unsigned char * end,int null_term)1793 static void do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1794 			  struct dhcp_packet *mess, unsigned char *end, int null_term)
1795 {
1796   int len, enc_len;
1797   struct dhcp_opt *start;
1798   unsigned char *p;
1799 
1800   /* find size in advance */
1801   for (enc_len = 0, start = opt; opt; opt = opt->next)
1802     if (opt->flags & flag)
1803       {
1804 	int new = do_opt(opt, NULL, NULL, null_term) + 2;
1805 	if (enc_len + new <= 255)
1806 	  enc_len += new;
1807 	else
1808 	  {
1809 	    p = free_space(mess, end, encap, enc_len);
1810 	    for (; start && start != opt; start = start->next)
1811 	      if (p && (start->flags & flag))
1812 		{
1813 		  len = do_opt(start, p + 2, NULL, null_term);
1814 		  *(p++) = start->opt;
1815 		  *(p++) = len;
1816 		  p += len;
1817 		}
1818 	    enc_len = new;
1819 	    start = opt;
1820 	  }
1821       }
1822 
1823   if (enc_len != 0 &&
1824       (p = free_space(mess, end, encap, enc_len + 1)))
1825     {
1826       for (; start; start = start->next)
1827 	if (start->flags & flag)
1828 	  {
1829 	    len = do_opt(start, p + 2, NULL, null_term);
1830 	    *(p++) = start->opt;
1831 	    *(p++) = len;
1832 	    p += len;
1833 	  }
1834       *p = OPTION_END;
1835     }
1836 }
1837 
pxe_misc(struct dhcp_packet * mess,unsigned char * end,unsigned char * uuid)1838 static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
1839 {
1840   unsigned char *p;
1841 
1842   option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
1843   if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
1844     memcpy(p, uuid, 17);
1845 }
1846 
prune_vendor_opts(struct dhcp_netid * netid)1847 static int prune_vendor_opts(struct dhcp_netid *netid)
1848 {
1849   int force = 0;
1850   struct dhcp_opt *opt;
1851 
1852   /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
1853   for (opt = daemon->dhcp_opts; opt; opt = opt->next)
1854     if (opt->flags & DHOPT_VENDOR_MATCH)
1855       {
1856 	if (!match_netid(opt->netid, netid, 1))
1857 	  opt->flags &= ~DHOPT_VENDOR_MATCH;
1858 	else if (opt->flags & DHOPT_FORCE)
1859 	  force = 1;
1860       }
1861   return force;
1862 }
1863 
pxe_opts(int pxe_arch,struct dhcp_netid * netid)1864 static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid)
1865 {
1866 #define NUM_OPTS 4
1867 
1868   unsigned  char *p, *q;
1869   struct pxe_service *service;
1870   static struct dhcp_opt *o, *ret;
1871   int i, j = NUM_OPTS - 1;
1872 
1873   /* We pass back references to these, hence they are declared static */
1874   static unsigned char discovery_control;
1875   static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
1876   static struct dhcp_opt *fake_opts = NULL;
1877 
1878   /* We are found by broadcast, so disable multicast. It gets switched on again
1879      if we point to other servers and don't give a unicast address. Note that
1880      we don't provide our own address for services we are the boot server for because unicast
1881      discovery is to port 4011 and we don't listen there. If you are using proxy DHCP
1882      and DHCP relays, the relay will need to forward to the proxy too. */
1883   discovery_control = 2;
1884 
1885   ret = daemon->dhcp_opts;
1886 
1887   if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
1888     return ret;
1889 
1890   for (i = 0; i < NUM_OPTS; i++)
1891     {
1892       fake_opts[i].flags = DHOPT_VENDOR_MATCH;
1893       fake_opts[i].netid = NULL;
1894       fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
1895     }
1896 
1897   /* create the data for the PXE_MENU and PXE_SERVERS options. */
1898   p = (unsigned char *)daemon->dhcp_buff;
1899   q = (unsigned char *)daemon->dhcp_buff2;
1900 
1901   for (i = 0, service = daemon->pxe_services; service; service = service->next)
1902     if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
1903       {
1904 	size_t len = strlen(service->menu);
1905 	/* opt 43 max size is 255. encapsulated option has type and length
1906 	   bytes, so its max size is 253. */
1907 	if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
1908 	  {
1909 	    *(p++) = service->type >> 8;
1910 	    *(p++) = service->type;
1911 	    *(p++) = len;
1912 	    memcpy(p, service->menu, len);
1913 	    p += len;
1914 	    i++;
1915 	  }
1916 	else
1917 	  {
1918 	  toobig:
1919 	    my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
1920 	    return daemon->dhcp_opts;
1921 	  }
1922 
1923 	if (!service->basename)
1924 	  {
1925 	    if (service->server.s_addr != 0)
1926 	      {
1927 		if (q - (unsigned char *)daemon->dhcp_buff2 + 3 + INADDRSZ >= 253)
1928 		  goto toobig;
1929 
1930 		/* Boot service with known address - give it */
1931 		*(q++) = service->type >> 8;
1932 		*(q++) = service->type;
1933 		*(q++) = 1;
1934 		/* dest misaligned */
1935 		memcpy(q, &service->server.s_addr, INADDRSZ);
1936 		q += INADDRSZ;
1937 	      }
1938 	    else if (service->type != 0)
1939 	      /* We're not supplying a server, so let the client multicast.
1940 		 type zero is "local boot" so no need for M/C on that. */
1941 	      discovery_control = 0;
1942 	  }
1943       }
1944 
1945   /* if no prompt, wait forever if there's a choice */
1946   fake_prompt[0] = (i > 1) ? 255 : 0;
1947 
1948   if (i == 0)
1949     discovery_control = 8; /* no menu - just use use mess->filename */
1950   else
1951     {
1952       ret = &fake_opts[j--];
1953       ret->len = p - (unsigned char *)daemon->dhcp_buff;
1954       ret->val = (unsigned char *)daemon->dhcp_buff;
1955       ret->opt = SUBOPT_PXE_MENU;
1956 
1957       if (q - (unsigned char *)daemon->dhcp_buff2 != 0)
1958 	{
1959 	  ret = &fake_opts[j--];
1960 	  ret->len = q - (unsigned char *)daemon->dhcp_buff2;
1961 	  ret->val = (unsigned char *)daemon->dhcp_buff2;
1962 	  ret->opt = SUBOPT_PXE_SERVERS;
1963 	}
1964     }
1965 
1966   for (o = daemon->dhcp_opts; o; o = o->next)
1967     if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
1968       break;
1969 
1970   if (!o)
1971     {
1972       ret = &fake_opts[j--];
1973       ret->len = sizeof(fake_prompt);
1974       ret->val = fake_prompt;
1975       ret->opt = SUBOPT_PXE_MENU_PROMPT;
1976     }
1977 
1978   if (discovery_control != 0)
1979     {
1980       ret = &fake_opts[j--];
1981       ret->len = 1;
1982       ret->opt = SUBOPT_PXE_DISCOVERY;
1983       ret->val= &discovery_control;
1984     }
1985 
1986   return ret;
1987 }
1988 
clear_packet(struct dhcp_packet * mess,unsigned char * end)1989 static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
1990 {
1991   memset(mess->sname, 0, sizeof(mess->sname));
1992   memset(mess->file, 0, sizeof(mess->file));
1993   memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
1994   mess->siaddr.s_addr = 0;
1995 }
1996 
find_boot(struct dhcp_netid * netid)1997 struct dhcp_boot *find_boot(struct dhcp_netid *netid)
1998 {
1999   struct dhcp_boot *boot;
2000 
2001   /* decide which dhcp-boot option we're using */
2002   for (boot = daemon->boot_config; boot; boot = boot->next)
2003     if (match_netid(boot->netid, netid, 0))
2004       break;
2005   if (!boot)
2006     /* No match, look for one without a netid */
2007     for (boot = daemon->boot_config; boot; boot = boot->next)
2008       if (match_netid(boot->netid, netid, 1))
2009 	break;
2010 
2011   return boot;
2012 }
2013 
do_options(struct dhcp_context * context,struct dhcp_packet * mess,unsigned char * end,unsigned char * req_options,char * hostname,char * domain,char * config_domain,struct dhcp_netid * netid,struct in_addr subnet_addr,unsigned char fqdn_flags,int null_term,int pxe_arch,unsigned char * uuid)2014 static void do_options(struct dhcp_context *context,
2015 		       struct dhcp_packet *mess,
2016 		       unsigned char *end,
2017 		       unsigned char *req_options,
2018 		       char *hostname,
2019 		       char *domain, char *config_domain,
2020 		       struct dhcp_netid *netid,
2021 		       struct in_addr subnet_addr,
2022 		       unsigned char fqdn_flags,
2023 		       int null_term, int pxe_arch,
2024 		       unsigned char *uuid)
2025 {
2026   struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2027   struct dhcp_boot *boot;
2028   unsigned char *p;
2029   int i, len, force_encap = 0;
2030   unsigned char f0 = 0, s0 = 0;
2031   int done_file = 0, done_server = 0;
2032 
2033   if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
2034     my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
2035 
2036   /* logging */
2037   if ((daemon->options & OPT_LOG_OPTS) && req_options)
2038     {
2039       char *q = daemon->namebuff;
2040       for (i = 0; req_options[i] != OPTION_END; i++)
2041 	{
2042 	  char *s = option_string(req_options[i], NULL, NULL);
2043 	  q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2044 			"%d%s%s%s",
2045 			req_options[i],
2046 			s ? ":" : "",
2047 			s ? s : "",
2048 			req_options[i+1] == OPTION_END ? "" : ", ");
2049 	  if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2050 	    {
2051 	      q = daemon->namebuff;
2052 	      my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2053 	    }
2054 	}
2055     }
2056 
2057   if (context)
2058     mess->siaddr = context->local;
2059 
2060   /* See if we can send the boot stuff as options.
2061      To do this we need a requested option list, BOOTP
2062      and very old DHCP clients won't have this, we also
2063      provide an manual option to disable it.
2064      Some PXE ROMs have bugs (surprise!) and need zero-terminated
2065      names, so we always send those.  */
2066   if ((boot = find_boot(netid)))
2067     {
2068       if (boot->sname)
2069 	{
2070 	  if (!(daemon->options & OPT_NO_OVERRIDE) &&
2071 	      req_options &&
2072 	      in_list(req_options, OPTION_SNAME))
2073 	    option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2074 	  else
2075 	    strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
2076 	}
2077 
2078       if (boot->file)
2079 	{
2080 	  if (!(daemon->options & OPT_NO_OVERRIDE) &&
2081 	      req_options &&
2082 	      in_list(req_options, OPTION_FILENAME))
2083 	    option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2084 	  else
2085 	    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
2086 	}
2087 
2088       if (boot->next_server.s_addr)
2089 	mess->siaddr = boot->next_server;
2090     }
2091   else
2092     /* Use the values of the relevant options if no dhcp-boot given and
2093        they're not explicitly asked for as options. OPTION_END is used
2094        as an internal way to specify siaddr without using dhcp-boot, for use in
2095        dhcp-optsfile. */
2096     {
2097       if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && mess->file[0] == 0 &&
2098 	  (opt = option_find2(netid, config_opts, OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
2099 	{
2100 	  strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2101 	  done_file = 1;
2102 	}
2103 
2104       if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
2105 	  (opt = option_find2(netid, config_opts, OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
2106 	{
2107 	  strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2108 	  done_server = 1;
2109 	}
2110 
2111       if ((opt = option_find2(netid, config_opts, OPTION_END)))
2112 	mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
2113     }
2114 
2115   /* We don't want to do option-overload for BOOTP, so make the file and sname
2116      fields look like they are in use, even when they aren't. This gets restored
2117      at the end of this function. */
2118 
2119   if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
2120     {
2121       f0 = mess->file[0];
2122       mess->file[0] = 1;
2123       s0 = mess->sname[0];
2124       mess->sname[0] = 1;
2125     }
2126 
2127   /* At this point, if mess->sname or mess->file are zeroed, they are available
2128      for option overload, reserve space for the overload option. */
2129   if (mess->file[0] == 0 || mess->sname[0] == 0)
2130     end -= 3;
2131 
2132   /* rfc3011 says this doesn't need to be in the requested options list. */
2133   if (subnet_addr.s_addr)
2134     option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
2135 
2136   /* replies to DHCPINFORM may not have a valid context */
2137   if (context)
2138     {
2139       if (!option_find2(netid, config_opts, OPTION_NETMASK))
2140 	option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2141 
2142       /* May not have a "guessed" broadcast address if we got no packets via a relay
2143 	 from this net yet (ie just unicast renewals after a restart */
2144       if (context->broadcast.s_addr &&
2145 	  !option_find2(netid, config_opts, OPTION_BROADCAST))
2146 	option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2147 
2148       /* Same comments as broadcast apply, and also may not be able to get a sensible
2149 	 default when using subnet select.  User must configure by steam in that case. */
2150       if (context->router.s_addr &&
2151 	  in_list(req_options, OPTION_ROUTER) &&
2152 	  !option_find2(netid, config_opts, OPTION_ROUTER))
2153 	option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2154 
2155       if (in_list(req_options, OPTION_DNSSERVER) &&
2156 	  !option_find2(netid, config_opts, OPTION_DNSSERVER))
2157 	option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2158     }
2159 
2160   if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
2161       !option_find2(netid, config_opts, OPTION_DOMAINNAME))
2162     option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
2163 
2164   /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
2165   if (hostname)
2166     {
2167       if (in_list(req_options, OPTION_HOSTNAME) &&
2168 	  !option_find2(netid, config_opts, OPTION_HOSTNAME))
2169 	option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
2170 
2171       if (fqdn_flags != 0)
2172 	{
2173 	  len = strlen(hostname) + 3;
2174 
2175 	  if (fqdn_flags & 0x04)
2176 	    len += 2;
2177 	  else if (null_term)
2178 	    len++;
2179 
2180 	  if (domain)
2181 	    len += strlen(domain) + 1;
2182 
2183 	  if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
2184 	    {
2185 	      *(p++) = fqdn_flags;
2186 	      *(p++) = 255;
2187 	      *(p++) = 255;
2188 
2189 	      if (fqdn_flags & 0x04)
2190 		{
2191 		  p = do_rfc1035_name(p, hostname);
2192 		  if (domain)
2193 		    p = do_rfc1035_name(p, domain);
2194 		  *p++ = 0;
2195 		}
2196 	      else
2197 		{
2198 		  memcpy(p, hostname, strlen(hostname));
2199 		  p += strlen(hostname);
2200 		  if (domain)
2201 		    {
2202 		      *(p++) = '.';
2203 		      memcpy(p, domain, strlen(domain));
2204 		      p += strlen(domain);
2205 		    }
2206 		  if (null_term)
2207 		    *(p++) = 0;
2208 		}
2209 	    }
2210 	}
2211     }
2212 
2213   for (opt = config_opts; opt; opt = opt->next)
2214     {
2215       int optno = opt->opt;
2216 
2217       /* was it asked for, or are we sending it anyway? */
2218       if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
2219 	continue;
2220 
2221       /* prohibit some used-internally options */
2222       if (optno == OPTION_CLIENT_FQDN ||
2223 	  optno == OPTION_MAXMESSAGE ||
2224 	  optno == OPTION_OVERLOAD ||
2225 	  optno == OPTION_PAD ||
2226 	  optno == OPTION_END)
2227 	continue;
2228 
2229       if (optno == OPTION_SNAME && done_server)
2230 	continue;
2231 
2232       if (optno == OPTION_FILENAME && done_file)
2233 	continue;
2234 
2235       /* netids match and not encapsulated? */
2236       if (opt != option_find2(netid, config_opts, optno))
2237 	continue;
2238 
2239       /* For the options we have default values on
2240 	 dhc-option=<optionno> means "don't include this option"
2241 	 not "include a zero-length option" */
2242       if (opt->len == 0 &&
2243 	  (optno == OPTION_NETMASK ||
2244 	   optno == OPTION_BROADCAST ||
2245 	   optno == OPTION_ROUTER ||
2246 	   optno == OPTION_DNSSERVER ||
2247 	   optno == OPTION_DOMAINNAME ||
2248 	   optno == OPTION_HOSTNAME))
2249 	continue;
2250 
2251       /* vendor-class comes from elsewhere for PXE */
2252       if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2253 	continue;
2254 
2255       /* always force null-term for filename and servername - buggy PXE again. */
2256       len = do_opt(opt, NULL, context,
2257 		   (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2258 
2259       if ((p = free_space(mess, end, optno, len)))
2260 	{
2261 	  do_opt(opt, p, context,
2262 		 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2263 
2264 	  /* If we send a vendor-id, revisit which vendor-ops we consider
2265 	     it appropriate to send. */
2266 	  if (optno == OPTION_VENDOR_ID)
2267 	    match_vendor_opts(p - 2, config_opts);
2268 	}
2269     }
2270 
2271   /* Now send options to be encapsulated in arbitrary options,
2272      eg dhcp-option=encap:172,17,.......
2273      The may be more that one "outer" to do, so group
2274      all the options which match each outer in turn. */
2275   for (opt = config_opts; opt; opt = opt->next)
2276     opt->flags &= ~DHOPT_ENCAP_DONE;
2277 
2278   for (opt = config_opts; opt; opt = opt->next)
2279     if ((opt->flags & (DHOPT_ENCAPSULATE | DHOPT_ENCAP_DONE)) ==  DHOPT_ENCAPSULATE)
2280       {
2281 	struct dhcp_opt *o;
2282 	int found = 0;
2283 
2284 	for (o = config_opts; o; o = o->next)
2285 	  {
2286 	    o->flags &= ~DHOPT_ENCAP_MATCH;
2287 	    if ((o->flags & DHOPT_ENCAPSULATE) && opt->u.encap == o->u.encap)
2288 	      {
2289 		o->flags |= DHOPT_ENCAP_DONE;
2290 		if (match_netid(o->netid, netid, 1) &&
2291 		    (o->flags & DHOPT_FORCE || in_list(req_options, o->u.encap)))
2292 		  {
2293 		    o->flags |= DHOPT_ENCAP_MATCH;
2294 		    found = 1;
2295 		  }
2296 	      }
2297 	  }
2298 
2299 	if (found)
2300 	  do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2301       }
2302 
2303   /* Must precede pxe_opts, since it overwrites req_options */
2304   force_encap = prune_vendor_opts(netid);
2305   if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
2306     force_encap = 1;
2307 
2308   if (pxe_arch != -1)
2309     {
2310       pxe_misc(mess, end, uuid);
2311       config_opts = pxe_opts(pxe_arch, netid);
2312     }
2313 
2314   if (force_encap)
2315     do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term);
2316 
2317    /* restore BOOTP anti-overload hack */
2318   if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
2319     {
2320       mess->file[0] = f0;
2321       mess->sname[0] = s0;
2322     }
2323 }
2324 
2325 #endif
2326