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