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