144099b7bSPaul Traina /*
244099b7bSPaul Traina * dovend.c : Inserts all but the first few vendor options.
344099b7bSPaul Traina */
444099b7bSPaul Traina
544099b7bSPaul Traina #include <sys/types.h>
644099b7bSPaul Traina
744099b7bSPaul Traina #include <netinet/in.h>
844099b7bSPaul Traina #include <arpa/inet.h> /* inet_ntoa */
944099b7bSPaul Traina
1044099b7bSPaul Traina #include <stdlib.h>
1144099b7bSPaul Traina #include <stdio.h>
1244099b7bSPaul Traina #include <string.h>
1344099b7bSPaul Traina #include <errno.h>
1444099b7bSPaul Traina #include <syslog.h>
1544099b7bSPaul Traina
1644099b7bSPaul Traina #include "bootp.h"
1744099b7bSPaul Traina #include "bootpd.h"
1844099b7bSPaul Traina #include "report.h"
1944099b7bSPaul Traina #include "dovend.h"
2044099b7bSPaul Traina
21f19d047aSAlfred Perlstein PRIVATE int insert_generic(struct shared_bindata *, byte **, int *);
2244099b7bSPaul Traina
2344099b7bSPaul Traina /*
2444099b7bSPaul Traina * Insert the 2nd part of the options into an option buffer.
2544099b7bSPaul Traina * Return amount of space used.
2644099b7bSPaul Traina *
2744099b7bSPaul Traina * This inserts everything EXCEPT:
2844099b7bSPaul Traina * magic cookie, subnet mask, gateway, bootsize, extension file
2944099b7bSPaul Traina * Those are handled separately (in bootpd.c) to allow this function
3044099b7bSPaul Traina * to be shared between bootpd and bootpef.
3144099b7bSPaul Traina *
3244099b7bSPaul Traina * When an "extension file" is in use, the options inserted by
3344099b7bSPaul Traina * this function go into the exten_file, not the bootp response.
3444099b7bSPaul Traina */
3544099b7bSPaul Traina
3644099b7bSPaul Traina int
dovend_rfc1497(struct host * hp,byte * buf,int len)37*8b356c88SJohn Baldwin dovend_rfc1497(struct host *hp, byte *buf, int len)
3844099b7bSPaul Traina {
3944099b7bSPaul Traina int bytesleft = len;
4044099b7bSPaul Traina byte *vp = buf;
4144099b7bSPaul Traina
42be9efd56SKris Kennaway static const char noroom[] = "%s: No room for \"%s\" option";
4344099b7bSPaul Traina #define NEED(LEN, MSG) do \
4444099b7bSPaul Traina if (bytesleft < (LEN)) { \
4544099b7bSPaul Traina report(LOG_NOTICE, noroom, \
4644099b7bSPaul Traina hp->hostname->string, MSG); \
4744099b7bSPaul Traina return (vp - buf); \
4844099b7bSPaul Traina } while (0)
4944099b7bSPaul Traina
5044099b7bSPaul Traina /*
5144099b7bSPaul Traina * Note that the following have already been inserted:
5244099b7bSPaul Traina * magic_cookie, subnet_mask, gateway, bootsize
5344099b7bSPaul Traina *
5444099b7bSPaul Traina * The remaining options are inserted in order of importance.
5544099b7bSPaul Traina * (Of course the importance of each is a matter of opinion.)
5644099b7bSPaul Traina * The option insertion order should probably be configurable.
5744099b7bSPaul Traina *
5844099b7bSPaul Traina * This is the order used in the NetBSD version. Can anyone
5944099b7bSPaul Traina * explain why the time_offset and swap_server are first?
6044099b7bSPaul Traina * Also, why is the hostname so far down the list? -gwr
6144099b7bSPaul Traina */
6244099b7bSPaul Traina
6344099b7bSPaul Traina if (hp->flags.time_offset) {
6444099b7bSPaul Traina NEED(6, "to");
6544099b7bSPaul Traina *vp++ = TAG_TIME_OFFSET;/* -1 byte */
6644099b7bSPaul Traina *vp++ = 4; /* -1 byte */
6744099b7bSPaul Traina insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */
6844099b7bSPaul Traina bytesleft -= 6;
6944099b7bSPaul Traina }
7044099b7bSPaul Traina /*
7144099b7bSPaul Traina * swap server, root path, dump path
7244099b7bSPaul Traina */
7344099b7bSPaul Traina if (hp->flags.swap_server) {
7444099b7bSPaul Traina NEED(6, "sw");
7544099b7bSPaul Traina /* There is just one SWAP_SERVER, so it is not an iplist. */
7644099b7bSPaul Traina *vp++ = TAG_SWAP_SERVER;/* -1 byte */
7744099b7bSPaul Traina *vp++ = 4; /* -1 byte */
7844099b7bSPaul Traina insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */
7944099b7bSPaul Traina bytesleft -= 6; /* Fix real count */
8044099b7bSPaul Traina }
8144099b7bSPaul Traina if (hp->flags.root_path) {
8244099b7bSPaul Traina /*
8344099b7bSPaul Traina * Check for room for root_path. Add 2 to account for
8444099b7bSPaul Traina * TAG_ROOT_PATH and length.
8544099b7bSPaul Traina */
8644099b7bSPaul Traina len = strlen(hp->root_path->string);
8744099b7bSPaul Traina NEED((len + 2), "rp");
8844099b7bSPaul Traina *vp++ = TAG_ROOT_PATH;
8944099b7bSPaul Traina *vp++ = (byte) (len & 0xFF);
9044099b7bSPaul Traina bcopy(hp->root_path->string, vp, len);
9144099b7bSPaul Traina vp += len;
9244099b7bSPaul Traina bytesleft -= len + 2;
9344099b7bSPaul Traina }
9444099b7bSPaul Traina if (hp->flags.dump_file) {
9544099b7bSPaul Traina /*
9644099b7bSPaul Traina * Check for room for dump_file. Add 2 to account for
9744099b7bSPaul Traina * TAG_DUMP_FILE and length.
9844099b7bSPaul Traina */
9944099b7bSPaul Traina len = strlen(hp->dump_file->string);
10044099b7bSPaul Traina NEED((len + 2), "df");
10144099b7bSPaul Traina *vp++ = TAG_DUMP_FILE;
10244099b7bSPaul Traina *vp++ = (byte) (len & 0xFF);
10344099b7bSPaul Traina bcopy(hp->dump_file->string, vp, len);
10444099b7bSPaul Traina vp += len;
10544099b7bSPaul Traina bytesleft -= len + 2;
10644099b7bSPaul Traina }
10744099b7bSPaul Traina /*
10844099b7bSPaul Traina * DNS server and domain
10944099b7bSPaul Traina */
11044099b7bSPaul Traina if (hp->flags.domain_server) {
11144099b7bSPaul Traina if (insert_ip(TAG_DOMAIN_SERVER,
11244099b7bSPaul Traina hp->domain_server,
11344099b7bSPaul Traina &vp, &bytesleft))
11444099b7bSPaul Traina NEED(8, "ds");
11544099b7bSPaul Traina }
11644099b7bSPaul Traina if (hp->flags.domain_name) {
11744099b7bSPaul Traina /*
11844099b7bSPaul Traina * Check for room for domain_name. Add 2 to account for
11944099b7bSPaul Traina * TAG_DOMAIN_NAME and length.
12044099b7bSPaul Traina */
12144099b7bSPaul Traina len = strlen(hp->domain_name->string);
12244099b7bSPaul Traina NEED((len + 2), "dn");
12344099b7bSPaul Traina *vp++ = TAG_DOMAIN_NAME;
12444099b7bSPaul Traina *vp++ = (byte) (len & 0xFF);
12544099b7bSPaul Traina bcopy(hp->domain_name->string, vp, len);
12644099b7bSPaul Traina vp += len;
12744099b7bSPaul Traina bytesleft -= len + 2;
12844099b7bSPaul Traina }
12944099b7bSPaul Traina /*
13044099b7bSPaul Traina * NIS (YP) server and domain
13144099b7bSPaul Traina */
13244099b7bSPaul Traina if (hp->flags.nis_server) {
13344099b7bSPaul Traina if (insert_ip(TAG_NIS_SERVER,
13444099b7bSPaul Traina hp->nis_server,
13544099b7bSPaul Traina &vp, &bytesleft))
13634366bc9SEd Maste NEED(8, "ys");
13744099b7bSPaul Traina }
13844099b7bSPaul Traina if (hp->flags.nis_domain) {
13944099b7bSPaul Traina /*
14044099b7bSPaul Traina * Check for room for nis_domain. Add 2 to account for
14144099b7bSPaul Traina * TAG_NIS_DOMAIN and length.
14244099b7bSPaul Traina */
14344099b7bSPaul Traina len = strlen(hp->nis_domain->string);
14434366bc9SEd Maste NEED((len + 2), "yn");
14544099b7bSPaul Traina *vp++ = TAG_NIS_DOMAIN;
14644099b7bSPaul Traina *vp++ = (byte) (len & 0xFF);
14744099b7bSPaul Traina bcopy(hp->nis_domain->string, vp, len);
14844099b7bSPaul Traina vp += len;
14944099b7bSPaul Traina bytesleft -= len + 2;
15044099b7bSPaul Traina }
15144099b7bSPaul Traina /* IEN 116 name server */
15244099b7bSPaul Traina if (hp->flags.name_server) {
15344099b7bSPaul Traina if (insert_ip(TAG_NAME_SERVER,
15444099b7bSPaul Traina hp->name_server,
15544099b7bSPaul Traina &vp, &bytesleft))
15644099b7bSPaul Traina NEED(8, "ns");
15744099b7bSPaul Traina }
15844099b7bSPaul Traina if (hp->flags.rlp_server) {
15944099b7bSPaul Traina if (insert_ip(TAG_RLP_SERVER,
16044099b7bSPaul Traina hp->rlp_server,
16144099b7bSPaul Traina &vp, &bytesleft))
16244099b7bSPaul Traina NEED(8, "rl");
16344099b7bSPaul Traina }
16444099b7bSPaul Traina /* Time server (RFC 868) */
16544099b7bSPaul Traina if (hp->flags.time_server) {
16644099b7bSPaul Traina if (insert_ip(TAG_TIME_SERVER,
16744099b7bSPaul Traina hp->time_server,
16844099b7bSPaul Traina &vp, &bytesleft))
16944099b7bSPaul Traina NEED(8, "ts");
17044099b7bSPaul Traina }
17144099b7bSPaul Traina /* NTP (time) Server (RFC 1129) */
17244099b7bSPaul Traina if (hp->flags.ntp_server) {
17344099b7bSPaul Traina if (insert_ip(TAG_NTP_SERVER,
17444099b7bSPaul Traina hp->ntp_server,
17544099b7bSPaul Traina &vp, &bytesleft))
17634366bc9SEd Maste NEED(8, "nt");
17744099b7bSPaul Traina }
17844099b7bSPaul Traina /*
17944099b7bSPaul Traina * I wonder: If the hostname were "promoted" into the BOOTP
18044099b7bSPaul Traina * response part, might these "extension" files possibly be
18144099b7bSPaul Traina * shared between several clients?
18244099b7bSPaul Traina *
18344099b7bSPaul Traina * Also, why not just use longer BOOTP packets with all the
18444099b7bSPaul Traina * additional length used as option data. This bootpd version
18544099b7bSPaul Traina * already supports that feature by replying with the same
18644099b7bSPaul Traina * packet length as the client request packet. -gwr
18744099b7bSPaul Traina */
18844099b7bSPaul Traina if (hp->flags.name_switch && hp->flags.send_name) {
18944099b7bSPaul Traina /*
19044099b7bSPaul Traina * Check for room for hostname. Add 2 to account for
19144099b7bSPaul Traina * TAG_HOST_NAME and length.
19244099b7bSPaul Traina */
19344099b7bSPaul Traina len = strlen(hp->hostname->string);
19444099b7bSPaul Traina #if 0
19544099b7bSPaul Traina /*
19644099b7bSPaul Traina * XXX - Too much magic. The user can always set the hostname
19744099b7bSPaul Traina * to the short version in the bootptab file. -gwr
19844099b7bSPaul Traina */
19944099b7bSPaul Traina if ((len + 2) > bytesleft) {
20044099b7bSPaul Traina /*
20144099b7bSPaul Traina * Not enough room for full (domain-qualified) hostname, try
20244099b7bSPaul Traina * stripping it down to just the first field (host).
20344099b7bSPaul Traina */
204e08ac58bSPaul Traina char *tmpstr = hp->hostname->string;
20544099b7bSPaul Traina len = 0;
20644099b7bSPaul Traina while (*tmpstr && (*tmpstr != '.')) {
20744099b7bSPaul Traina tmpstr++;
20844099b7bSPaul Traina len++;
20944099b7bSPaul Traina }
21044099b7bSPaul Traina }
21144099b7bSPaul Traina #endif
21244099b7bSPaul Traina NEED((len + 2), "hn");
21344099b7bSPaul Traina *vp++ = TAG_HOST_NAME;
21444099b7bSPaul Traina *vp++ = (byte) (len & 0xFF);
21544099b7bSPaul Traina bcopy(hp->hostname->string, vp, len);
21644099b7bSPaul Traina vp += len;
21744099b7bSPaul Traina bytesleft -= len + 2;
21844099b7bSPaul Traina }
21944099b7bSPaul Traina /*
22044099b7bSPaul Traina * The rest of these are less important, so they go last.
22144099b7bSPaul Traina */
22244099b7bSPaul Traina if (hp->flags.lpr_server) {
22344099b7bSPaul Traina if (insert_ip(TAG_LPR_SERVER,
22444099b7bSPaul Traina hp->lpr_server,
22544099b7bSPaul Traina &vp, &bytesleft))
22644099b7bSPaul Traina NEED(8, "lp");
22744099b7bSPaul Traina }
22844099b7bSPaul Traina if (hp->flags.cookie_server) {
22944099b7bSPaul Traina if (insert_ip(TAG_COOKIE_SERVER,
23044099b7bSPaul Traina hp->cookie_server,
23144099b7bSPaul Traina &vp, &bytesleft))
23244099b7bSPaul Traina NEED(8, "cs");
23344099b7bSPaul Traina }
23444099b7bSPaul Traina if (hp->flags.log_server) {
23544099b7bSPaul Traina if (insert_ip(TAG_LOG_SERVER,
23644099b7bSPaul Traina hp->log_server,
23744099b7bSPaul Traina &vp, &bytesleft))
23844099b7bSPaul Traina NEED(8, "lg");
23944099b7bSPaul Traina }
24044099b7bSPaul Traina /*
24144099b7bSPaul Traina * XXX - Add new tags here (to insert options)
24244099b7bSPaul Traina */
24344099b7bSPaul Traina if (hp->flags.generic) {
24444099b7bSPaul Traina if (insert_generic(hp->generic, &vp, &bytesleft))
24544099b7bSPaul Traina NEED(64, "(generic)");
24644099b7bSPaul Traina }
24744099b7bSPaul Traina /*
24844099b7bSPaul Traina * The end marker is inserted by the caller.
24944099b7bSPaul Traina */
25044099b7bSPaul Traina return (vp - buf);
25144099b7bSPaul Traina #undef NEED
25244099b7bSPaul Traina } /* dovend_rfc1497 */
25344099b7bSPaul Traina
25444099b7bSPaul Traina
25544099b7bSPaul Traina
25644099b7bSPaul Traina /*
25744099b7bSPaul Traina * Insert a tag value, a length value, and a list of IP addresses into the
25844099b7bSPaul Traina * memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag
25944099b7bSPaul Traina * number to use, "iplist" is a pointer to a list of IP addresses
26044099b7bSPaul Traina * (struct in_addr_list), and "bytesleft" points to an integer which
26144099b7bSPaul Traina * indicates the size of the "dest" buffer.
26244099b7bSPaul Traina *
26344099b7bSPaul Traina * Return zero if everything fits.
26444099b7bSPaul Traina *
26544099b7bSPaul Traina * This is used to fill the vendor-specific area of a bootp packet in
26644099b7bSPaul Traina * conformance to RFC1048.
26744099b7bSPaul Traina */
26844099b7bSPaul Traina
26944099b7bSPaul Traina int
insert_ip(byte tag,struct in_addr_list * iplist,byte ** dest,int * bytesleft)270*8b356c88SJohn Baldwin insert_ip(byte tag, struct in_addr_list *iplist, byte **dest, int *bytesleft)
27144099b7bSPaul Traina {
27244099b7bSPaul Traina struct in_addr *addrptr;
27344099b7bSPaul Traina unsigned addrcount = 1;
27444099b7bSPaul Traina byte *d;
27544099b7bSPaul Traina
27644099b7bSPaul Traina if (iplist == NULL)
27744099b7bSPaul Traina return (0);
27844099b7bSPaul Traina
27944099b7bSPaul Traina if (*bytesleft >= 6) {
28044099b7bSPaul Traina d = *dest; /* Save pointer for later */
28144099b7bSPaul Traina **dest = tag;
28244099b7bSPaul Traina (*dest) += 2;
28344099b7bSPaul Traina (*bytesleft) -= 2; /* Account for tag and length */
28444099b7bSPaul Traina addrptr = iplist->addr;
28544099b7bSPaul Traina addrcount = iplist->addrcount;
28644099b7bSPaul Traina while ((*bytesleft >= 4) && (addrcount > 0)) {
28744099b7bSPaul Traina insert_u_long(addrptr->s_addr, dest);
28844099b7bSPaul Traina addrptr++;
28944099b7bSPaul Traina addrcount--;
29044099b7bSPaul Traina (*bytesleft) -= 4; /* Four bytes per address */
29144099b7bSPaul Traina }
29244099b7bSPaul Traina d[1] = (byte) ((*dest - d - 2) & 0xFF);
29344099b7bSPaul Traina }
29444099b7bSPaul Traina return (addrcount);
29544099b7bSPaul Traina }
29644099b7bSPaul Traina
29744099b7bSPaul Traina
29844099b7bSPaul Traina
29944099b7bSPaul Traina /*
30044099b7bSPaul Traina * Insert generic data into a bootp packet. The data is assumed to already
30144099b7bSPaul Traina * be in RFC1048 format. It is inserted using a first-fit algorithm which
30244099b7bSPaul Traina * attempts to insert as many tags as possible. Tags and data which are
30344099b7bSPaul Traina * too large to fit are skipped; any remaining tags are tried until they
30444099b7bSPaul Traina * have all been exhausted.
30544099b7bSPaul Traina * Return zero if everything fits.
30644099b7bSPaul Traina */
30744099b7bSPaul Traina
30844099b7bSPaul Traina static int
insert_generic(struct shared_bindata * gendata,byte ** buff,int * bytesleft)309*8b356c88SJohn Baldwin insert_generic(struct shared_bindata *gendata, byte **buff, int *bytesleft)
31044099b7bSPaul Traina {
31144099b7bSPaul Traina byte *srcptr;
31244099b7bSPaul Traina int length, numbytes;
31344099b7bSPaul Traina int skipped = 0;
31444099b7bSPaul Traina
31544099b7bSPaul Traina if (gendata == NULL)
31644099b7bSPaul Traina return (0);
31744099b7bSPaul Traina
31844099b7bSPaul Traina srcptr = gendata->data;
31944099b7bSPaul Traina length = gendata->length;
32044099b7bSPaul Traina while ((length > 0) && (*bytesleft > 0)) {
32144099b7bSPaul Traina switch (*srcptr) {
32244099b7bSPaul Traina case TAG_END:
32344099b7bSPaul Traina length = 0; /* Force an exit on next iteration */
32444099b7bSPaul Traina break;
32544099b7bSPaul Traina case TAG_PAD:
32644099b7bSPaul Traina *(*buff)++ = *srcptr++;
32744099b7bSPaul Traina (*bytesleft)--;
32844099b7bSPaul Traina length--;
32944099b7bSPaul Traina break;
33044099b7bSPaul Traina default:
33144099b7bSPaul Traina numbytes = srcptr[1] + 2;
33244099b7bSPaul Traina if (*bytesleft < numbytes)
33344099b7bSPaul Traina skipped += numbytes;
33444099b7bSPaul Traina else {
33544099b7bSPaul Traina bcopy(srcptr, *buff, numbytes);
33644099b7bSPaul Traina (*buff) += numbytes;
33744099b7bSPaul Traina (*bytesleft) -= numbytes;
33844099b7bSPaul Traina }
33944099b7bSPaul Traina srcptr += numbytes;
34044099b7bSPaul Traina length -= numbytes;
34144099b7bSPaul Traina break;
34244099b7bSPaul Traina }
34344099b7bSPaul Traina } /* while */
34444099b7bSPaul Traina return (skipped);
34544099b7bSPaul Traina }
34644099b7bSPaul Traina
34744099b7bSPaul Traina /*
34844099b7bSPaul Traina * Insert the unsigned long "value" into memory starting at the byte
34944099b7bSPaul Traina * pointed to by the byte pointer (*dest). (*dest) is updated to
35044099b7bSPaul Traina * point to the next available byte.
35144099b7bSPaul Traina *
35244099b7bSPaul Traina * Since it is desirable to internally store network addresses in network
35344099b7bSPaul Traina * byte order (in struct in_addr's), this routine expects longs to be
35444099b7bSPaul Traina * passed in network byte order.
35544099b7bSPaul Traina *
35644099b7bSPaul Traina * However, due to the nature of the main algorithm, the long must be in
35744099b7bSPaul Traina * host byte order, thus necessitating the use of ntohl() first.
35844099b7bSPaul Traina */
35944099b7bSPaul Traina
36044099b7bSPaul Traina void
insert_u_long(u_int32 value,byte ** dest)361*8b356c88SJohn Baldwin insert_u_long(u_int32 value, byte **dest)
36244099b7bSPaul Traina {
36344099b7bSPaul Traina byte *temp;
36444099b7bSPaul Traina int n;
36544099b7bSPaul Traina
36644099b7bSPaul Traina value = ntohl(value); /* Must use host byte order here */
36744099b7bSPaul Traina temp = (*dest += 4);
36844099b7bSPaul Traina for (n = 4; n > 0; n--) {
36944099b7bSPaul Traina *--temp = (byte) (value & 0xFF);
37044099b7bSPaul Traina value >>= 8;
37144099b7bSPaul Traina }
37244099b7bSPaul Traina /* Final result is network byte order */
37344099b7bSPaul Traina }
37444099b7bSPaul Traina
37544099b7bSPaul Traina /*
37644099b7bSPaul Traina * Local Variables:
37744099b7bSPaul Traina * tab-width: 4
37844099b7bSPaul Traina * c-indent-level: 4
37944099b7bSPaul Traina * c-argdecl-indent: 4
38044099b7bSPaul Traina * c-continued-statement-offset: 4
38144099b7bSPaul Traina * c-continued-brace-offset: -4
38244099b7bSPaul Traina * c-label-offset: -4
38344099b7bSPaul Traina * c-brace-offset: 0
38444099b7bSPaul Traina * End:
38544099b7bSPaul Traina */
386