/*1* dovend.c : Inserts all but the first few vendor options.2*/34#include <sys/types.h>56#include <netinet/in.h>7#include <arpa/inet.h> /* inet_ntoa */89#include <stdlib.h>10#include <stdio.h>11#include <string.h>12#include <errno.h>13#include <syslog.h>1415#include "bootp.h"16#include "bootpd.h"17#include "report.h"18#include "dovend.h"1920PRIVATE int insert_generic(struct shared_bindata *, byte **, int *);2122/*23* Insert the 2nd part of the options into an option buffer.24* Return amount of space used.25*26* This inserts everything EXCEPT:27* magic cookie, subnet mask, gateway, bootsize, extension file28* Those are handled separately (in bootpd.c) to allow this function29* to be shared between bootpd and bootpef.30*31* When an "extension file" is in use, the options inserted by32* this function go into the exten_file, not the bootp response.33*/3435int36dovend_rfc1497(struct host *hp, byte *buf, int len)37{38int bytesleft = len;39byte *vp = buf;4041static const char noroom[] = "%s: No room for \"%s\" option";42#define NEED(LEN, MSG) do \43if (bytesleft < (LEN)) { \44report(LOG_NOTICE, noroom, \45hp->hostname->string, MSG); \46return (vp - buf); \47} while (0)4849/*50* Note that the following have already been inserted:51* magic_cookie, subnet_mask, gateway, bootsize52*53* The remaining options are inserted in order of importance.54* (Of course the importance of each is a matter of opinion.)55* The option insertion order should probably be configurable.56*57* This is the order used in the NetBSD version. Can anyone58* explain why the time_offset and swap_server are first?59* Also, why is the hostname so far down the list? -gwr60*/6162if (hp->flags.time_offset) {63NEED(6, "to");64*vp++ = TAG_TIME_OFFSET;/* -1 byte */65*vp++ = 4; /* -1 byte */66insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */67bytesleft -= 6;68}69/*70* swap server, root path, dump path71*/72if (hp->flags.swap_server) {73NEED(6, "sw");74/* There is just one SWAP_SERVER, so it is not an iplist. */75*vp++ = TAG_SWAP_SERVER;/* -1 byte */76*vp++ = 4; /* -1 byte */77insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */78bytesleft -= 6; /* Fix real count */79}80if (hp->flags.root_path) {81/*82* Check for room for root_path. Add 2 to account for83* TAG_ROOT_PATH and length.84*/85len = strlen(hp->root_path->string);86NEED((len + 2), "rp");87*vp++ = TAG_ROOT_PATH;88*vp++ = (byte) (len & 0xFF);89bcopy(hp->root_path->string, vp, len);90vp += len;91bytesleft -= len + 2;92}93if (hp->flags.dump_file) {94/*95* Check for room for dump_file. Add 2 to account for96* TAG_DUMP_FILE and length.97*/98len = strlen(hp->dump_file->string);99NEED((len + 2), "df");100*vp++ = TAG_DUMP_FILE;101*vp++ = (byte) (len & 0xFF);102bcopy(hp->dump_file->string, vp, len);103vp += len;104bytesleft -= len + 2;105}106/*107* DNS server and domain108*/109if (hp->flags.domain_server) {110if (insert_ip(TAG_DOMAIN_SERVER,111hp->domain_server,112&vp, &bytesleft))113NEED(8, "ds");114}115if (hp->flags.domain_name) {116/*117* Check for room for domain_name. Add 2 to account for118* TAG_DOMAIN_NAME and length.119*/120len = strlen(hp->domain_name->string);121NEED((len + 2), "dn");122*vp++ = TAG_DOMAIN_NAME;123*vp++ = (byte) (len & 0xFF);124bcopy(hp->domain_name->string, vp, len);125vp += len;126bytesleft -= len + 2;127}128/*129* NIS (YP) server and domain130*/131if (hp->flags.nis_server) {132if (insert_ip(TAG_NIS_SERVER,133hp->nis_server,134&vp, &bytesleft))135NEED(8, "ys");136}137if (hp->flags.nis_domain) {138/*139* Check for room for nis_domain. Add 2 to account for140* TAG_NIS_DOMAIN and length.141*/142len = strlen(hp->nis_domain->string);143NEED((len + 2), "yn");144*vp++ = TAG_NIS_DOMAIN;145*vp++ = (byte) (len & 0xFF);146bcopy(hp->nis_domain->string, vp, len);147vp += len;148bytesleft -= len + 2;149}150/* IEN 116 name server */151if (hp->flags.name_server) {152if (insert_ip(TAG_NAME_SERVER,153hp->name_server,154&vp, &bytesleft))155NEED(8, "ns");156}157if (hp->flags.rlp_server) {158if (insert_ip(TAG_RLP_SERVER,159hp->rlp_server,160&vp, &bytesleft))161NEED(8, "rl");162}163/* Time server (RFC 868) */164if (hp->flags.time_server) {165if (insert_ip(TAG_TIME_SERVER,166hp->time_server,167&vp, &bytesleft))168NEED(8, "ts");169}170/* NTP (time) Server (RFC 1129) */171if (hp->flags.ntp_server) {172if (insert_ip(TAG_NTP_SERVER,173hp->ntp_server,174&vp, &bytesleft))175NEED(8, "nt");176}177/*178* I wonder: If the hostname were "promoted" into the BOOTP179* response part, might these "extension" files possibly be180* shared between several clients?181*182* Also, why not just use longer BOOTP packets with all the183* additional length used as option data. This bootpd version184* already supports that feature by replying with the same185* packet length as the client request packet. -gwr186*/187if (hp->flags.name_switch && hp->flags.send_name) {188/*189* Check for room for hostname. Add 2 to account for190* TAG_HOST_NAME and length.191*/192len = strlen(hp->hostname->string);193#if 0194/*195* XXX - Too much magic. The user can always set the hostname196* to the short version in the bootptab file. -gwr197*/198if ((len + 2) > bytesleft) {199/*200* Not enough room for full (domain-qualified) hostname, try201* stripping it down to just the first field (host).202*/203char *tmpstr = hp->hostname->string;204len = 0;205while (*tmpstr && (*tmpstr != '.')) {206tmpstr++;207len++;208}209}210#endif211NEED((len + 2), "hn");212*vp++ = TAG_HOST_NAME;213*vp++ = (byte) (len & 0xFF);214bcopy(hp->hostname->string, vp, len);215vp += len;216bytesleft -= len + 2;217}218/*219* The rest of these are less important, so they go last.220*/221if (hp->flags.lpr_server) {222if (insert_ip(TAG_LPR_SERVER,223hp->lpr_server,224&vp, &bytesleft))225NEED(8, "lp");226}227if (hp->flags.cookie_server) {228if (insert_ip(TAG_COOKIE_SERVER,229hp->cookie_server,230&vp, &bytesleft))231NEED(8, "cs");232}233if (hp->flags.log_server) {234if (insert_ip(TAG_LOG_SERVER,235hp->log_server,236&vp, &bytesleft))237NEED(8, "lg");238}239/*240* XXX - Add new tags here (to insert options)241*/242if (hp->flags.generic) {243if (insert_generic(hp->generic, &vp, &bytesleft))244NEED(64, "(generic)");245}246/*247* The end marker is inserted by the caller.248*/249return (vp - buf);250#undef NEED251} /* dovend_rfc1497 */252253254255/*256* Insert a tag value, a length value, and a list of IP addresses into the257* memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag258* number to use, "iplist" is a pointer to a list of IP addresses259* (struct in_addr_list), and "bytesleft" points to an integer which260* indicates the size of the "dest" buffer.261*262* Return zero if everything fits.263*264* This is used to fill the vendor-specific area of a bootp packet in265* conformance to RFC1048.266*/267268int269insert_ip(byte tag, struct in_addr_list *iplist, byte **dest, int *bytesleft)270{271struct in_addr *addrptr;272unsigned addrcount = 1;273byte *d;274275if (iplist == NULL)276return (0);277278if (*bytesleft >= 6) {279d = *dest; /* Save pointer for later */280**dest = tag;281(*dest) += 2;282(*bytesleft) -= 2; /* Account for tag and length */283addrptr = iplist->addr;284addrcount = iplist->addrcount;285while ((*bytesleft >= 4) && (addrcount > 0)) {286insert_u_long(addrptr->s_addr, dest);287addrptr++;288addrcount--;289(*bytesleft) -= 4; /* Four bytes per address */290}291d[1] = (byte) ((*dest - d - 2) & 0xFF);292}293return (addrcount);294}295296297298/*299* Insert generic data into a bootp packet. The data is assumed to already300* be in RFC1048 format. It is inserted using a first-fit algorithm which301* attempts to insert as many tags as possible. Tags and data which are302* too large to fit are skipped; any remaining tags are tried until they303* have all been exhausted.304* Return zero if everything fits.305*/306307static int308insert_generic(struct shared_bindata *gendata, byte **buff, int *bytesleft)309{310byte *srcptr;311int length, numbytes;312int skipped = 0;313314if (gendata == NULL)315return (0);316317srcptr = gendata->data;318length = gendata->length;319while ((length > 0) && (*bytesleft > 0)) {320switch (*srcptr) {321case TAG_END:322length = 0; /* Force an exit on next iteration */323break;324case TAG_PAD:325*(*buff)++ = *srcptr++;326(*bytesleft)--;327length--;328break;329default:330numbytes = srcptr[1] + 2;331if (*bytesleft < numbytes)332skipped += numbytes;333else {334bcopy(srcptr, *buff, numbytes);335(*buff) += numbytes;336(*bytesleft) -= numbytes;337}338srcptr += numbytes;339length -= numbytes;340break;341}342} /* while */343return (skipped);344}345346/*347* Insert the unsigned long "value" into memory starting at the byte348* pointed to by the byte pointer (*dest). (*dest) is updated to349* point to the next available byte.350*351* Since it is desirable to internally store network addresses in network352* byte order (in struct in_addr's), this routine expects longs to be353* passed in network byte order.354*355* However, due to the nature of the main algorithm, the long must be in356* host byte order, thus necessitating the use of ntohl() first.357*/358359void360insert_u_long(u_int32 value, byte **dest)361{362byte *temp;363int n;364365value = ntohl(value); /* Must use host byte order here */366temp = (*dest += 4);367for (n = 4; n > 0; n--) {368*--temp = (byte) (value & 0xFF);369value >>= 8;370}371/* Final result is network byte order */372}373374/*375* Local Variables:376* tab-width: 4377* c-indent-level: 4378* c-argdecl-indent: 4379* c-continued-statement-offset: 4380* c-continued-brace-offset: -4381* c-label-offset: -4382* c-brace-offset: 0383* End:384*/385386387