Home | History | Annotate | Line # | Download | only in common
      1 /*	$NetBSD: dovend.c,v 1.6 2007/05/27 16:31:42 tls Exp $	*/
      2 
      3 #include <sys/cdefs.h>
      4 #ifndef lint
      5 __RCSID("$NetBSD: dovend.c,v 1.6 2007/05/27 16:31:42 tls Exp $");
      6 #endif
      7 
      8 /*
      9  * dovend.c : Inserts all but the first few vendor options.
     10  */
     11 
     12 #include <sys/types.h>
     13 
     14 #include <netinet/in.h>
     15 #include <arpa/inet.h>			/* inet_ntoa */
     16 
     17 #include <stdlib.h>
     18 #include <stdio.h>
     19 #include <string.h>
     20 #include <strings.h>
     21 #include <errno.h>
     22 #include <syslog.h>
     23 
     24 #include "bootp.h"
     25 #include "bootpd.h"
     26 #include "report.h"
     27 #include "dovend.h"
     28 
     29 PRIVATE int insert_generic(struct shared_bindata *, byte **, int *);
     30 
     31 /*
     32  * Insert the 2nd part of the options into an option buffer.
     33  * Return amount of space used.
     34  *
     35  * This inserts everything EXCEPT:
     36  *   magic cookie, subnet mask, gateway, bootsize, extension file
     37  * Those are handled separately (in bootpd.c) to allow this function
     38  * to be shared between bootpd and bootpef.
     39  *
     40  * When an "extension file" is in use, the options inserted by
     41  * this function go into the exten_file, not the bootp response.
     42  */
     43 
     44 int
     45 dovend_rfc1497(struct host *hp, byte *buf, int len)
     46 {
     47 	int bytesleft = len;
     48 	byte *vp = buf;
     49 #if 0
     50 	char *tmpstr;
     51 #endif
     52 
     53 	static const char noroom[] = "%s: No room for \"%s\" option";
     54 #define	NEED(LEN, MSG) do                       \
     55 		if (bytesleft < (LEN)) {         	    \
     56 			report(LOG_NOTICE, noroom,          \
     57 				   hp->hostname->string, MSG);  \
     58 			return (vp - buf);                  \
     59 		} while (0)
     60 
     61 	/*
     62 	 * Note that the following have already been inserted:
     63 	 *   magic_cookie, subnet_mask, gateway, bootsize
     64 	 *
     65 	 * The remaining options are inserted in order of importance.
     66 	 * (Of course the importance of each is a matter of opinion.)
     67 	 * The option insertion order should probably be configurable.
     68 	 *
     69 	 * This is the order used in the NetBSD version.  Can anyone
     70 	 * explain why the time_offset and swap_server are first?
     71 	 * Also, why is the hostname so far down the list?  -gwr
     72 	 */
     73 
     74 	if (hp->flags.time_offset) {
     75 		NEED(6, "to");
     76 		*vp++ = TAG_TIME_OFFSET;/* -1 byte  */
     77 		*vp++ = 4;				/* -1 byte  */
     78 		insert_u_long(htonl(hp->time_offset), &vp);	/* -4 bytes */
     79 		bytesleft -= 6;
     80 	}
     81 	/*
     82 	 * swap server, root path, dump path
     83 	 */
     84 	if (hp->flags.swap_server) {
     85 		NEED(6, "sw");
     86 		/* There is just one SWAP_SERVER, so it is not an iplist. */
     87 		*vp++ = TAG_SWAP_SERVER;/* -1 byte  */
     88 		*vp++ = 4;				/* -1 byte  */
     89 		insert_u_long(hp->swap_server.s_addr, &vp);	/* -4 bytes */
     90 		bytesleft -= 6;			/* Fix real count */
     91 	}
     92 	if (hp->flags.root_path) {
     93 		/*
     94 		 * Check for room for root_path.  Add 2 to account for
     95 		 * TAG_ROOT_PATH and length.
     96 		 */
     97 		len = strlen(hp->root_path->string);
     98 		NEED((len + 2), "rp");
     99 		*vp++ = TAG_ROOT_PATH;
    100 		*vp++ = (byte) (len & 0xFF);
    101 		bcopy(hp->root_path->string, vp, len);
    102 		vp += len;
    103 		bytesleft -= len + 2;
    104 	}
    105 	if (hp->flags.dump_file) {
    106 		/*
    107 		 * Check for room for dump_file.  Add 2 to account for
    108 		 * TAG_DUMP_FILE and length.
    109 		 */
    110 		len = strlen(hp->dump_file->string);
    111 		NEED((len + 2), "df");
    112 		*vp++ = TAG_DUMP_FILE;
    113 		*vp++ = (byte) (len & 0xFF);
    114 		bcopy(hp->dump_file->string, vp, len);
    115 		vp += len;
    116 		bytesleft -= len + 2;
    117 	}
    118 	/*
    119 	 * DNS server and domain
    120 	 */
    121 	if (hp->flags.domain_server) {
    122 		if (insert_ip(TAG_DOMAIN_SERVER,
    123 					  hp->domain_server,
    124 					  &vp, &bytesleft))
    125 			NEED(8, "ds");
    126 	}
    127 	if (hp->flags.domain_name) {
    128 		/*
    129 		 * Check for room for domain_name.  Add 2 to account for
    130 		 * TAG_DOMAIN_NAME and length.
    131 		 */
    132 		len = strlen(hp->domain_name->string);
    133 		NEED((len + 2), "dn");
    134 		*vp++ = TAG_DOMAIN_NAME;
    135 		*vp++ = (byte) (len & 0xFF);
    136 		bcopy(hp->domain_name->string, vp, len);
    137 		vp += len;
    138 		bytesleft -= len + 2;
    139 	}
    140 	/*
    141 	 * NIS (YP) server and domain
    142 	 */
    143 	if (hp->flags.nis_server) {
    144 		if (insert_ip(TAG_NIS_SERVER,
    145 					  hp->nis_server,
    146 					  &vp, &bytesleft))
    147 			NEED(8, "ds");
    148 	}
    149 	if (hp->flags.nis_domain) {
    150 		/*
    151 		 * Check for room for nis_domain.  Add 2 to account for
    152 		 * TAG_NIS_DOMAIN and length.
    153 		 */
    154 		len = strlen(hp->nis_domain->string);
    155 		NEED((len + 2), "dn");
    156 		*vp++ = TAG_NIS_DOMAIN;
    157 		*vp++ = (byte) (len & 0xFF);
    158 		bcopy(hp->nis_domain->string, vp, len);
    159 		vp += len;
    160 		bytesleft -= len + 2;
    161 	}
    162 	/* IEN 116 name server */
    163 	if (hp->flags.name_server) {
    164 		if (insert_ip(TAG_NAME_SERVER,
    165 					  hp->name_server,
    166 					  &vp, &bytesleft))
    167 			NEED(8, "ns");
    168 	}
    169 	if (hp->flags.rlp_server) {
    170 		if (insert_ip(TAG_RLP_SERVER,
    171 					  hp->rlp_server,
    172 					  &vp, &bytesleft))
    173 			NEED(8, "rl");
    174 	}
    175 	/* Time server (RFC 868) */
    176 	if (hp->flags.time_server) {
    177 		if (insert_ip(TAG_TIME_SERVER,
    178 					  hp->time_server,
    179 					  &vp, &bytesleft))
    180 			NEED(8, "ts");
    181 	}
    182 	/* NTP (time) Server (RFC 1129) */
    183 	if (hp->flags.ntp_server) {
    184 		if (insert_ip(TAG_NTP_SERVER,
    185 					  hp->ntp_server,
    186 					  &vp, &bytesleft))
    187 			NEED(8, "ts");
    188 	}
    189 	/*
    190 	 * I wonder:  If the hostname were "promoted" into the BOOTP
    191 	 * response part, might these "extension" files possibly be
    192 	 * shared between several clients?
    193 	 *
    194 	 * Also, why not just use longer BOOTP packets with all the
    195 	 * additional length used as option data.  This bootpd version
    196 	 * already supports that feature by replying with the same
    197 	 * packet length as the client request packet. -gwr
    198 	 */
    199 	if (hp->flags.name_switch && hp->flags.send_name) {
    200 		/*
    201 		 * Check for room for hostname.  Add 2 to account for
    202 		 * TAG_HOST_NAME and length.
    203 		 */
    204 		len = strlen(hp->hostname->string);
    205 #if 0
    206 		/*
    207 		 * XXX - Too much magic.  The user can always set the hostname
    208 		 * to the short version in the bootptab file. -gwr
    209 		 */
    210 		if ((len + 2) > bytesleft) {
    211 			/*
    212 			 * Not enough room for full (domain-qualified) hostname, try
    213 			 * stripping it down to just the first field (host).
    214 			 */
    215 			tmpstr = hp->hostname->string;
    216 			len = 0;
    217 			while (*tmpstr && (*tmpstr != '.')) {
    218 				tmpstr++;
    219 				len++;
    220 			}
    221 		}
    222 #endif
    223 		NEED((len + 2), "hn");
    224 		*vp++ = TAG_HOST_NAME;
    225 		*vp++ = (byte) (len & 0xFF);
    226 		bcopy(hp->hostname->string, vp, len);
    227 		vp += len;
    228 		bytesleft -= len + 2;
    229 	}
    230 	/*
    231 	 * The rest of these are less important, so they go last.
    232 	 */
    233 	if (hp->flags.lpr_server) {
    234 		if (insert_ip(TAG_LPR_SERVER,
    235 					  hp->lpr_server,
    236 					  &vp, &bytesleft))
    237 			NEED(8, "lp");
    238 	}
    239 	if (hp->flags.cookie_server) {
    240 		if (insert_ip(TAG_COOKIE_SERVER,
    241 					  hp->cookie_server,
    242 					  &vp, &bytesleft))
    243 			NEED(8, "cs");
    244 	}
    245 	if (hp->flags.log_server) {
    246 		if (insert_ip(TAG_LOG_SERVER,
    247 					  hp->log_server,
    248 					  &vp, &bytesleft))
    249 			NEED(8, "lg");
    250 	}
    251 	/*
    252 	 * XXX - Add new tags here (to insert options)
    253 	 */
    254 	if (hp->flags.generic) {
    255 		if (insert_generic(hp->generic, &vp, &bytesleft))
    256 			NEED(64, "(generic)");
    257 	}
    258 	/*
    259 	 * The end marker is inserted by the caller.
    260 	 */
    261 	return (vp - buf);
    262 #undef	NEED
    263 }								/* dovend_rfc1497 */
    264 
    265 
    267 
    268 /*
    269  * Insert a tag value, a length value, and a list of IP addresses into the
    270  * memory buffer indirectly pointed to by "dest".  "tag" is the RFC1048 tag
    271  * number to use, "iplist" is a pointer to a list of IP addresses
    272  * (struct in_addr_list), and "bytesleft" points to an integer which
    273  * indicates the size of the "dest" buffer.
    274  *
    275  * Return zero if everything fits.
    276  *
    277  * This is used to fill the vendor-specific area of a bootp packet in
    278  * conformance to RFC1048.
    279  */
    280 
    281 int
    282 insert_ip(byte tag, struct in_addr_list *iplist, byte **dest, int *bytesleft)
    283 {
    284 	struct in_addr *addrptr;
    285 	unsigned addrcount = 1;
    286 	byte *d;
    287 
    288 	if (iplist == NULL)
    289 		return (0);
    290 
    291 	if (*bytesleft >= 6) {
    292 		d = *dest;				/* Save pointer for later */
    293 		**dest = tag;
    294 		(*dest) += 2;
    295 		(*bytesleft) -= 2;		/* Account for tag and length */
    296 		addrptr = iplist->addr;
    297 		addrcount = iplist->addrcount;
    298 		while ((*bytesleft >= 4) && (addrcount > 0)) {
    299 			insert_u_long(addrptr->s_addr, dest);
    300 			addrptr++;
    301 			addrcount--;
    302 			(*bytesleft) -= 4;	/* Four bytes per address */
    303 		}
    304 		d[1] = (byte) ((*dest - d - 2) & 0xFF);
    305 	}
    306 	return (addrcount);
    307 }
    308 
    309 
    311 
    312 /*
    313  * Insert generic data into a bootp packet.  The data is assumed to already
    314  * be in RFC1048 format.  It is inserted using a first-fit algorithm which
    315  * attempts to insert as many tags as possible.  Tags and data which are
    316  * too large to fit are skipped; any remaining tags are tried until they
    317  * have all been exhausted.
    318  * Return zero if everything fits.
    319  */
    320 
    321 static int
    322 insert_generic(struct shared_bindata *gendata, byte **buff, int *bytesleft)
    323 {
    324 	byte *srcptr;
    325 	int length, numbytes;
    326 	int skipped = 0;
    327 
    328 	if (gendata == NULL)
    329 		return (0);
    330 
    331 	srcptr = gendata->data;
    332 	length = gendata->length;
    333 	while ((length > 0) && (*bytesleft > 0)) {
    334 		switch (*srcptr) {
    335 		case TAG_END:
    336 			length = 0;			/* Force an exit on next iteration */
    337 			break;
    338 		case TAG_PAD:
    339 			*(*buff)++ = *srcptr++;
    340 			(*bytesleft)--;
    341 			length--;
    342 			break;
    343 		default:
    344 			numbytes = srcptr[1] + 2;
    345 			if (*bytesleft < numbytes)
    346 				skipped += numbytes;
    347 			else {
    348 				bcopy(srcptr, *buff, numbytes);
    349 				(*buff) += numbytes;
    350 				(*bytesleft) -= numbytes;
    351 			}
    352 			srcptr += numbytes;
    353 			length -= numbytes;
    354 			break;
    355 		}
    356 	} /* while */
    357 	return (skipped);
    358 }
    359 
    360 /*
    361  * Insert the unsigned long "value" into memory starting at the byte
    362  * pointed to by the byte pointer (*dest).  (*dest) is updated to
    363  * point to the next available byte.
    364  *
    365  * Since it is desirable to internally store network addresses in network
    366  * byte order (in struct in_addr's), this routine expects longs to be
    367  * passed in network byte order.
    368  *
    369  * However, due to the nature of the main algorithm, the long must be in
    370  * host byte order, thus necessitating the use of ntohl() first.
    371  */
    372 
    373 void
    374 insert_u_long(u_int32 value, byte **dest)
    375 {
    376 	byte *temp;
    377 	int n;
    378 
    379 	value = ntohl(value);		/* Must use host byte order here */
    380 	temp = (*dest += 4);
    381 	for (n = 4; n > 0; n--) {
    382 		*--temp = (byte) (value & 0xFF);
    383 		value >>= 8;
    384 	}
    385 	/* Final result is network byte order */
    386 }
    387 
    388 /*
    389  * Local Variables:
    390  * tab-width: 4
    391  * c-indent-level: 4
    392  * c-argdecl-indent: 4
    393  * c-continued-statement-offset: 4
    394  * c-continued-brace-offset: -4
    395  * c-label-offset: -4
    396  * c-brace-offset: 0
    397  * End:
    398  */
    399