13229Spst/* 23229Spst * dovend.c : Inserts all but the first few vendor options. 318471Swosch * 450476Speter * $FreeBSD$ 53229Spst */ 63229Spst 73229Spst#include <sys/types.h> 83229Spst 93229Spst#include <netinet/in.h> 103229Spst#include <arpa/inet.h> /* inet_ntoa */ 113229Spst 123229Spst#include <stdlib.h> 133229Spst#include <stdio.h> 143229Spst#include <string.h> 153229Spst#include <errno.h> 163229Spst#include <syslog.h> 173229Spst 183229Spst#ifndef USE_BFUNCS 193229Spst# include <memory.h> 203229Spst/* Yes, memcpy is OK here (no overlapped copies). */ 213229Spst# define bcopy(a,b,c) memcpy(b,a,c) 223229Spst# define bzero(p,l) memset(p,0,l) 233229Spst# define bcmp(a,b,c) memcmp(a,b,c) 243229Spst# define index strchr 253229Spst#endif 263229Spst 273229Spst#include "bootp.h" 283229Spst#include "bootpd.h" 293229Spst#include "report.h" 303229Spst#include "dovend.h" 313229Spst 3297417SalfredPRIVATE int insert_generic(struct shared_bindata *, byte **, int *); 333229Spst 343229Spst/* 353229Spst * Insert the 2nd part of the options into an option buffer. 363229Spst * Return amount of space used. 373229Spst * 383229Spst * This inserts everything EXCEPT: 393229Spst * magic cookie, subnet mask, gateway, bootsize, extension file 403229Spst * Those are handled separately (in bootpd.c) to allow this function 413229Spst * to be shared between bootpd and bootpef. 423229Spst * 433229Spst * When an "extension file" is in use, the options inserted by 443229Spst * this function go into the exten_file, not the bootp response. 453229Spst */ 463229Spst 473229Spstint 483229Spstdovend_rfc1497(hp, buf, len) 493229Spst struct host *hp; 503229Spst byte *buf; 513229Spst int len; 523229Spst{ 533229Spst int bytesleft = len; 543229Spst byte *vp = buf; 553229Spst 5690159Skris static const char noroom[] = "%s: No room for \"%s\" option"; 573229Spst#define NEED(LEN, MSG) do \ 583229Spst if (bytesleft < (LEN)) { \ 593229Spst report(LOG_NOTICE, noroom, \ 603229Spst hp->hostname->string, MSG); \ 613229Spst return (vp - buf); \ 623229Spst } while (0) 633229Spst 643229Spst /* 653229Spst * Note that the following have already been inserted: 663229Spst * magic_cookie, subnet_mask, gateway, bootsize 673229Spst * 683229Spst * The remaining options are inserted in order of importance. 693229Spst * (Of course the importance of each is a matter of opinion.) 703229Spst * The option insertion order should probably be configurable. 713229Spst * 723229Spst * This is the order used in the NetBSD version. Can anyone 733229Spst * explain why the time_offset and swap_server are first? 743229Spst * Also, why is the hostname so far down the list? -gwr 753229Spst */ 763229Spst 773229Spst if (hp->flags.time_offset) { 783229Spst NEED(6, "to"); 793229Spst *vp++ = TAG_TIME_OFFSET;/* -1 byte */ 803229Spst *vp++ = 4; /* -1 byte */ 813229Spst insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */ 823229Spst bytesleft -= 6; 833229Spst } 843229Spst /* 853229Spst * swap server, root path, dump path 863229Spst */ 873229Spst if (hp->flags.swap_server) { 883229Spst NEED(6, "sw"); 893229Spst /* There is just one SWAP_SERVER, so it is not an iplist. */ 903229Spst *vp++ = TAG_SWAP_SERVER;/* -1 byte */ 913229Spst *vp++ = 4; /* -1 byte */ 923229Spst insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */ 933229Spst bytesleft -= 6; /* Fix real count */ 943229Spst } 953229Spst if (hp->flags.root_path) { 963229Spst /* 973229Spst * Check for room for root_path. Add 2 to account for 983229Spst * TAG_ROOT_PATH and length. 993229Spst */ 1003229Spst len = strlen(hp->root_path->string); 1013229Spst NEED((len + 2), "rp"); 1023229Spst *vp++ = TAG_ROOT_PATH; 1033229Spst *vp++ = (byte) (len & 0xFF); 1043229Spst bcopy(hp->root_path->string, vp, len); 1053229Spst vp += len; 1063229Spst bytesleft -= len + 2; 1073229Spst } 1083229Spst if (hp->flags.dump_file) { 1093229Spst /* 1103229Spst * Check for room for dump_file. Add 2 to account for 1113229Spst * TAG_DUMP_FILE and length. 1123229Spst */ 1133229Spst len = strlen(hp->dump_file->string); 1143229Spst NEED((len + 2), "df"); 1153229Spst *vp++ = TAG_DUMP_FILE; 1163229Spst *vp++ = (byte) (len & 0xFF); 1173229Spst bcopy(hp->dump_file->string, vp, len); 1183229Spst vp += len; 1193229Spst bytesleft -= len + 2; 1203229Spst } 1213229Spst /* 1223229Spst * DNS server and domain 1233229Spst */ 1243229Spst if (hp->flags.domain_server) { 1253229Spst if (insert_ip(TAG_DOMAIN_SERVER, 1263229Spst hp->domain_server, 1273229Spst &vp, &bytesleft)) 1283229Spst NEED(8, "ds"); 1293229Spst } 1303229Spst if (hp->flags.domain_name) { 1313229Spst /* 1323229Spst * Check for room for domain_name. Add 2 to account for 1333229Spst * TAG_DOMAIN_NAME and length. 1343229Spst */ 1353229Spst len = strlen(hp->domain_name->string); 1363229Spst NEED((len + 2), "dn"); 1373229Spst *vp++ = TAG_DOMAIN_NAME; 1383229Spst *vp++ = (byte) (len & 0xFF); 1393229Spst bcopy(hp->domain_name->string, vp, len); 1403229Spst vp += len; 1413229Spst bytesleft -= len + 2; 1423229Spst } 1433229Spst /* 1443229Spst * NIS (YP) server and domain 1453229Spst */ 1463229Spst if (hp->flags.nis_server) { 1473229Spst if (insert_ip(TAG_NIS_SERVER, 1483229Spst hp->nis_server, 1493229Spst &vp, &bytesleft)) 1503229Spst NEED(8, "ds"); 1513229Spst } 1523229Spst if (hp->flags.nis_domain) { 1533229Spst /* 1543229Spst * Check for room for nis_domain. Add 2 to account for 1553229Spst * TAG_NIS_DOMAIN and length. 1563229Spst */ 1573229Spst len = strlen(hp->nis_domain->string); 1583229Spst NEED((len + 2), "dn"); 1593229Spst *vp++ = TAG_NIS_DOMAIN; 1603229Spst *vp++ = (byte) (len & 0xFF); 1613229Spst bcopy(hp->nis_domain->string, vp, len); 1623229Spst vp += len; 1633229Spst bytesleft -= len + 2; 1643229Spst } 1653229Spst /* IEN 116 name server */ 1663229Spst if (hp->flags.name_server) { 1673229Spst if (insert_ip(TAG_NAME_SERVER, 1683229Spst hp->name_server, 1693229Spst &vp, &bytesleft)) 1703229Spst NEED(8, "ns"); 1713229Spst } 1723229Spst if (hp->flags.rlp_server) { 1733229Spst if (insert_ip(TAG_RLP_SERVER, 1743229Spst hp->rlp_server, 1753229Spst &vp, &bytesleft)) 1763229Spst NEED(8, "rl"); 1773229Spst } 1783229Spst /* Time server (RFC 868) */ 1793229Spst if (hp->flags.time_server) { 1803229Spst if (insert_ip(TAG_TIME_SERVER, 1813229Spst hp->time_server, 1823229Spst &vp, &bytesleft)) 1833229Spst NEED(8, "ts"); 1843229Spst } 1853229Spst /* NTP (time) Server (RFC 1129) */ 1863229Spst if (hp->flags.ntp_server) { 1873229Spst if (insert_ip(TAG_NTP_SERVER, 1883229Spst hp->ntp_server, 1893229Spst &vp, &bytesleft)) 1903229Spst NEED(8, "ts"); 1913229Spst } 1923229Spst /* 1933229Spst * I wonder: If the hostname were "promoted" into the BOOTP 1943229Spst * response part, might these "extension" files possibly be 1953229Spst * shared between several clients? 1963229Spst * 1973229Spst * Also, why not just use longer BOOTP packets with all the 1983229Spst * additional length used as option data. This bootpd version 1993229Spst * already supports that feature by replying with the same 2003229Spst * packet length as the client request packet. -gwr 2013229Spst */ 2023229Spst if (hp->flags.name_switch && hp->flags.send_name) { 2033229Spst /* 2043229Spst * Check for room for hostname. Add 2 to account for 2053229Spst * TAG_HOST_NAME and length. 2063229Spst */ 2073229Spst len = strlen(hp->hostname->string); 2083229Spst#if 0 2093229Spst /* 2103229Spst * XXX - Too much magic. The user can always set the hostname 2113229Spst * to the short version in the bootptab file. -gwr 2123229Spst */ 2133229Spst if ((len + 2) > bytesleft) { 2143229Spst /* 2153229Spst * Not enough room for full (domain-qualified) hostname, try 2163229Spst * stripping it down to just the first field (host). 2173229Spst */ 21813572Spst char *tmpstr = hp->hostname->string; 2193229Spst len = 0; 2203229Spst while (*tmpstr && (*tmpstr != '.')) { 2213229Spst tmpstr++; 2223229Spst len++; 2233229Spst } 2243229Spst } 2253229Spst#endif 2263229Spst NEED((len + 2), "hn"); 2273229Spst *vp++ = TAG_HOST_NAME; 2283229Spst *vp++ = (byte) (len & 0xFF); 2293229Spst bcopy(hp->hostname->string, vp, len); 2303229Spst vp += len; 2313229Spst bytesleft -= len + 2; 2323229Spst } 2333229Spst /* 2343229Spst * The rest of these are less important, so they go last. 2353229Spst */ 2363229Spst if (hp->flags.lpr_server) { 2373229Spst if (insert_ip(TAG_LPR_SERVER, 2383229Spst hp->lpr_server, 2393229Spst &vp, &bytesleft)) 2403229Spst NEED(8, "lp"); 2413229Spst } 2423229Spst if (hp->flags.cookie_server) { 2433229Spst if (insert_ip(TAG_COOKIE_SERVER, 2443229Spst hp->cookie_server, 2453229Spst &vp, &bytesleft)) 2463229Spst NEED(8, "cs"); 2473229Spst } 2483229Spst if (hp->flags.log_server) { 2493229Spst if (insert_ip(TAG_LOG_SERVER, 2503229Spst hp->log_server, 2513229Spst &vp, &bytesleft)) 2523229Spst NEED(8, "lg"); 2533229Spst } 2543229Spst /* 2553229Spst * XXX - Add new tags here (to insert options) 2563229Spst */ 2573229Spst if (hp->flags.generic) { 2583229Spst if (insert_generic(hp->generic, &vp, &bytesleft)) 2593229Spst NEED(64, "(generic)"); 2603229Spst } 2613229Spst /* 2623229Spst * The end marker is inserted by the caller. 2633229Spst */ 2643229Spst return (vp - buf); 2653229Spst#undef NEED 2663229Spst} /* dovend_rfc1497 */ 2673229Spst 2683229Spst 2693229Spst 2703229Spst/* 2713229Spst * Insert a tag value, a length value, and a list of IP addresses into the 2723229Spst * memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag 2733229Spst * number to use, "iplist" is a pointer to a list of IP addresses 2743229Spst * (struct in_addr_list), and "bytesleft" points to an integer which 2753229Spst * indicates the size of the "dest" buffer. 2763229Spst * 2773229Spst * Return zero if everything fits. 2783229Spst * 2793229Spst * This is used to fill the vendor-specific area of a bootp packet in 2803229Spst * conformance to RFC1048. 2813229Spst */ 2823229Spst 2833229Spstint 2843229Spstinsert_ip(tag, iplist, dest, bytesleft) 2853229Spst byte tag; 2863229Spst struct in_addr_list *iplist; 2873229Spst byte **dest; 2883229Spst int *bytesleft; 2893229Spst{ 2903229Spst struct in_addr *addrptr; 2913229Spst unsigned addrcount = 1; 2923229Spst byte *d; 2933229Spst 2943229Spst if (iplist == NULL) 2953229Spst return (0); 2963229Spst 2973229Spst if (*bytesleft >= 6) { 2983229Spst d = *dest; /* Save pointer for later */ 2993229Spst **dest = tag; 3003229Spst (*dest) += 2; 3013229Spst (*bytesleft) -= 2; /* Account for tag and length */ 3023229Spst addrptr = iplist->addr; 3033229Spst addrcount = iplist->addrcount; 3043229Spst while ((*bytesleft >= 4) && (addrcount > 0)) { 3053229Spst insert_u_long(addrptr->s_addr, dest); 3063229Spst addrptr++; 3073229Spst addrcount--; 3083229Spst (*bytesleft) -= 4; /* Four bytes per address */ 3093229Spst } 3103229Spst d[1] = (byte) ((*dest - d - 2) & 0xFF); 3113229Spst } 3123229Spst return (addrcount); 3133229Spst} 3143229Spst 3153229Spst 3163229Spst 3173229Spst/* 3183229Spst * Insert generic data into a bootp packet. The data is assumed to already 3193229Spst * be in RFC1048 format. It is inserted using a first-fit algorithm which 3203229Spst * attempts to insert as many tags as possible. Tags and data which are 3213229Spst * too large to fit are skipped; any remaining tags are tried until they 3223229Spst * have all been exhausted. 3233229Spst * Return zero if everything fits. 3243229Spst */ 3253229Spst 3263229Spststatic int 3273229Spstinsert_generic(gendata, buff, bytesleft) 3283229Spst struct shared_bindata *gendata; 3293229Spst byte **buff; 3303229Spst int *bytesleft; 3313229Spst{ 3323229Spst byte *srcptr; 3333229Spst int length, numbytes; 3343229Spst int skipped = 0; 3353229Spst 3363229Spst if (gendata == NULL) 3373229Spst return (0); 3383229Spst 3393229Spst srcptr = gendata->data; 3403229Spst length = gendata->length; 3413229Spst while ((length > 0) && (*bytesleft > 0)) { 3423229Spst switch (*srcptr) { 3433229Spst case TAG_END: 3443229Spst length = 0; /* Force an exit on next iteration */ 3453229Spst break; 3463229Spst case TAG_PAD: 3473229Spst *(*buff)++ = *srcptr++; 3483229Spst (*bytesleft)--; 3493229Spst length--; 3503229Spst break; 3513229Spst default: 3523229Spst numbytes = srcptr[1] + 2; 3533229Spst if (*bytesleft < numbytes) 3543229Spst skipped += numbytes; 3553229Spst else { 3563229Spst bcopy(srcptr, *buff, numbytes); 3573229Spst (*buff) += numbytes; 3583229Spst (*bytesleft) -= numbytes; 3593229Spst } 3603229Spst srcptr += numbytes; 3613229Spst length -= numbytes; 3623229Spst break; 3633229Spst } 3643229Spst } /* while */ 3653229Spst return (skipped); 3663229Spst} 3673229Spst 3683229Spst/* 3693229Spst * Insert the unsigned long "value" into memory starting at the byte 3703229Spst * pointed to by the byte pointer (*dest). (*dest) is updated to 3713229Spst * point to the next available byte. 3723229Spst * 3733229Spst * Since it is desirable to internally store network addresses in network 3743229Spst * byte order (in struct in_addr's), this routine expects longs to be 3753229Spst * passed in network byte order. 3763229Spst * 3773229Spst * However, due to the nature of the main algorithm, the long must be in 3783229Spst * host byte order, thus necessitating the use of ntohl() first. 3793229Spst */ 3803229Spst 3813229Spstvoid 3823229Spstinsert_u_long(value, dest) 3833229Spst u_int32 value; 3843229Spst byte **dest; 3853229Spst{ 3863229Spst byte *temp; 3873229Spst int n; 3883229Spst 3893229Spst value = ntohl(value); /* Must use host byte order here */ 3903229Spst temp = (*dest += 4); 3913229Spst for (n = 4; n > 0; n--) { 3923229Spst *--temp = (byte) (value & 0xFF); 3933229Spst value >>= 8; 3943229Spst } 3953229Spst /* Final result is network byte order */ 3963229Spst} 3973229Spst 3983229Spst/* 3993229Spst * Local Variables: 4003229Spst * tab-width: 4 4013229Spst * c-indent-level: 4 4023229Spst * c-argdecl-indent: 4 4033229Spst * c-continued-statement-offset: 4 4043229Spst * c-continued-brace-offset: -4 4053229Spst * c-label-offset: -4 4063229Spst * c-brace-offset: 0 4073229Spst * End: 4083229Spst */ 409