1 /* 2 * bootptest.c - Test out a bootp server. 3 * 4 * This simple program was put together from pieces taken from 5 * various places, including the CMU BOOTP client and server. 6 * The packet printing routine is from the Berkeley "tcpdump" 7 * program with some enhancements I added. The print-bootp.c 8 * file was shared with my copy of "tcpdump" and therefore uses 9 * some unusual utility routines that would normally be provided 10 * by various parts of the tcpdump program. Gordon W. Ross 11 * 12 * Boilerplate: 13 * 14 * This program includes software developed by the University of 15 * California, Lawrence Berkeley Laboratory and its contributors. 16 * (See the copyright notice in print-bootp.c) 17 * 18 * The remainder of this program is public domain. You may do 19 * whatever you like with it except claim that you wrote it. 20 * 21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 22 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 24 * 25 * HISTORY: 26 * 27 * 12/02/93 Released version 1.4 (with bootp-2.3.2) 28 * 11/05/93 Released version 1.3 29 * 10/14/93 Released version 1.2 30 * 10/11/93 Released version 1.1 31 * 09/28/93 Released version 1.0 32 * 09/93 Original developed by Gordon W. Ross <gwr@mc.com> 33 * 34 */ 35 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 char *usage = "bootptest [-h] server-name [vendor-data-template-file]"; 40 41 #include <sys/types.h> 42 #include <sys/socket.h> 43 #include <sys/ioctl.h> 44 #include <sys/file.h> 45 #include <sys/time.h> 46 #include <sys/stat.h> 47 #include <sys/utsname.h> 48 49 #include <net/if.h> 50 #include <netinet/in.h> 51 #include <arpa/inet.h> /* inet_ntoa */ 52 53 #ifndef NO_UNISTD 54 #include <unistd.h> 55 #endif 56 57 #include <err.h> 58 #include <stdlib.h> 59 #include <signal.h> 60 #include <stdio.h> 61 #include <string.h> 62 #include <errno.h> 63 #include <ctype.h> 64 #include <netdb.h> 65 #include <assert.h> 66 67 #include "bootp.h" 68 #include "bootptest.h" 69 #include "getif.h" 70 #include "getether.h" 71 72 #include "patchlevel.h" 73 74 static void send_request(int s); 75 76 #define LOG_ERR 1 77 #define BUFLEN 1024 78 #define WAITSECS 1 79 #define MAXWAIT 10 80 81 int vflag = 1; 82 int tflag = 0; 83 int thiszone; 84 char *progname; 85 unsigned char *packetp; 86 unsigned char *snapend; 87 int snaplen; 88 89 90 /* 91 * IP port numbers for client and server obtained from /etc/services 92 */ 93 94 u_short bootps_port, bootpc_port; 95 96 97 /* 98 * Internet socket and interface config structures 99 */ 100 101 struct sockaddr_in sin_server; /* where to send requests */ 102 struct sockaddr_in sin_client; /* for bind and listen */ 103 struct sockaddr_in sin_from; /* Packet source */ 104 u_char eaddr[16]; /* Ethernet address */ 105 106 /* 107 * General 108 */ 109 110 int debug = 1; /* Debugging flag (level) */ 111 char *sndbuf; /* Send packet buffer */ 112 char *rcvbuf; /* Receive packet buffer */ 113 114 struct utsname my_uname; 115 char *hostname; 116 117 /* 118 * Vendor magic cookies for CMU and RFC1048 119 */ 120 121 unsigned char vm_cmu[4] = VM_CMU; 122 unsigned char vm_rfc1048[4] = VM_RFC1048; 123 short secs; /* How long client has waited */ 124 125 /* 126 * Initialization such as command-line processing is done, then 127 * the receiver loop is started. Die when interrupted. 128 */ 129 130 int 131 main(int argc, char **argv) 132 { 133 struct bootp *bp; 134 struct servent *sep; 135 struct hostent *hep; 136 137 char *servername = NULL; 138 char *vendor_file = NULL; 139 char *bp_file = NULL; 140 int32 server_addr; /* inet addr, network order */ 141 int s; /* Socket file descriptor */ 142 int n, fromlen, recvcnt; 143 int use_hwa = 0; 144 int32 vend_magic; 145 int32 xid; 146 147 progname = strrchr(argv[0], '/'); 148 if (progname) 149 progname++; 150 else 151 progname = argv[0]; 152 argc--; 153 argv++; 154 155 if (debug) 156 printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); 157 158 /* 159 * Verify that "struct bootp" has the correct official size. 160 * (Catch evil compilers that do struct padding.) 161 */ 162 assert(sizeof(struct bootp) == BP_MINPKTSZ); 163 164 if (uname(&my_uname) < 0) 165 errx(1, "can't get hostname"); 166 hostname = my_uname.nodename; 167 168 sndbuf = malloc(BUFLEN); 169 rcvbuf = malloc(BUFLEN); 170 if (!sndbuf || !rcvbuf) { 171 printf("malloc failed\n"); 172 exit(1); 173 } 174 175 /* default magic number */ 176 bcopy(vm_rfc1048, (char*)&vend_magic, 4); 177 178 /* Handle option switches. */ 179 while (argc > 0) { 180 if (argv[0][0] != '-') 181 break; 182 switch (argv[0][1]) { 183 184 case 'f': /* File name to request. */ 185 if (argc < 2) 186 goto error; 187 argc--; argv++; 188 bp_file = *argv; 189 break; 190 191 case 'h': /* Use hardware address. */ 192 use_hwa = 1; 193 break; 194 195 case 'm': /* Magic number value. */ 196 if (argc < 2) 197 goto error; 198 argc--; argv++; 199 vend_magic = inet_addr(*argv); 200 break; 201 202 error: 203 default: 204 puts(usage); 205 exit(1); 206 207 } 208 argc--; 209 argv++; 210 } 211 212 /* Get server name (or address) for query. */ 213 if (argc > 0) { 214 servername = *argv; 215 argc--; 216 argv++; 217 } 218 /* Get optional vendor-data-template-file. */ 219 if (argc > 0) { 220 vendor_file = *argv; 221 argc--; 222 argv++; 223 } 224 if (!servername) { 225 printf("missing server name.\n"); 226 puts(usage); 227 exit(1); 228 } 229 /* 230 * Create a socket. 231 */ 232 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 233 perror("socket"); 234 exit(1); 235 } 236 /* 237 * Get server's listening port number 238 */ 239 sep = getservbyname("bootps", "udp"); 240 if (sep) { 241 bootps_port = ntohs((u_short) sep->s_port); 242 } else { 243 warnx("bootps/udp: unknown service -- using port %d", 244 IPPORT_BOOTPS); 245 bootps_port = (u_short) IPPORT_BOOTPS; 246 } 247 248 /* 249 * Set up server socket address (for send) 250 */ 251 if (servername) { 252 if (isdigit(servername[0])) 253 server_addr = inet_addr(servername); 254 else { 255 hep = gethostbyname(servername); 256 if (!hep) 257 errx(1, "%s: unknown host", servername); 258 bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); 259 } 260 } else { 261 /* Get broadcast address */ 262 /* XXX - not yet */ 263 server_addr = INADDR_ANY; 264 } 265 sin_server.sin_family = AF_INET; 266 sin_server.sin_port = htons(bootps_port); 267 sin_server.sin_addr.s_addr = server_addr; 268 269 /* 270 * Get client's listening port number 271 */ 272 sep = getservbyname("bootpc", "udp"); 273 if (sep) { 274 bootpc_port = ntohs(sep->s_port); 275 } else { 276 warnx("bootpc/udp: unknown service -- using port %d", 277 IPPORT_BOOTPC); 278 bootpc_port = (u_short) IPPORT_BOOTPC; 279 } 280 281 /* 282 * Set up client socket address (for listen) 283 */ 284 sin_client.sin_family = AF_INET; 285 sin_client.sin_port = htons(bootpc_port); 286 sin_client.sin_addr.s_addr = INADDR_ANY; 287 288 /* 289 * Bind client socket to BOOTPC port. 290 */ 291 if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { 292 if (errno == EACCES) { 293 warn("bind BOOTPC port"); 294 errx(1, "you need to run this as root"); 295 } 296 else 297 err(1, "bind BOOTPC port"); 298 } 299 /* 300 * Build a request. 301 */ 302 bp = (struct bootp *) sndbuf; 303 bzero(bp, sizeof(*bp)); 304 bp->bp_op = BOOTREQUEST; 305 xid = (int32) getpid(); 306 bp->bp_xid = (u_int32) htonl(xid); 307 if (bp_file) 308 strncpy(bp->bp_file, bp_file, BP_FILE_LEN); 309 310 /* 311 * Fill in the hardware address (or client IP address) 312 */ 313 if (use_hwa) { 314 struct ifreq *ifr; 315 316 ifr = getif(s, &sin_server.sin_addr); 317 if (!ifr) { 318 printf("No interface for %s\n", servername); 319 exit(1); 320 } 321 if (getether(ifr->ifr_name, (char*)eaddr)) { 322 printf("Can not get ether addr for %s\n", ifr->ifr_name); 323 exit(1); 324 } 325 /* Copy Ethernet address into request packet. */ 326 bp->bp_htype = 1; 327 bp->bp_hlen = 6; 328 bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); 329 } else { 330 /* Fill in the client IP address. */ 331 hep = gethostbyname(hostname); 332 if (!hep) { 333 printf("Can not get my IP address\n"); 334 exit(1); 335 } 336 bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); 337 } 338 339 /* 340 * Copy in the default vendor data. 341 */ 342 bcopy((char*)&vend_magic, bp->bp_vend, 4); 343 if (vend_magic) 344 bp->bp_vend[4] = TAG_END; 345 346 /* 347 * Read in the "options" part of the request. 348 * This also determines the size of the packet. 349 */ 350 snaplen = sizeof(*bp); 351 if (vendor_file) { 352 int fd = open(vendor_file, 0); 353 if (fd < 0) { 354 perror(vendor_file); 355 exit(1); 356 } 357 /* Compute actual space for options. */ 358 n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; 359 n = read(fd, bp->bp_vend, n); 360 close(fd); 361 if (n < 0) { 362 perror(vendor_file); 363 exit(1); 364 } 365 printf("read %d bytes of vendor template\n", n); 366 if (n > BP_VEND_LEN) { 367 printf("warning: extended options in use (len > %d)\n", 368 BP_VEND_LEN); 369 snaplen += (n - BP_VEND_LEN); 370 } 371 } 372 /* 373 * Set globals needed by print_bootp 374 * (called by send_request) 375 */ 376 packetp = (unsigned char *) eaddr; 377 snapend = (unsigned char *) sndbuf + snaplen; 378 379 /* Send a request once per second while waiting for replies. */ 380 recvcnt = 0; 381 bp->bp_secs = secs = 0; 382 send_request(s); 383 while (1) { 384 struct timeval tv; 385 int readfds; 386 387 tv.tv_sec = WAITSECS; 388 tv.tv_usec = 0L; 389 readfds = (1 << s); 390 n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv); 391 if (n < 0) { 392 perror("select"); 393 break; 394 } 395 if (n == 0) { 396 /* 397 * We have not received a response in the last second. 398 * If we have ever received any responses, exit now. 399 * Otherwise, bump the "wait time" field and re-send. 400 */ 401 if (recvcnt > 0) 402 exit(0); 403 secs += WAITSECS; 404 if (secs > MAXWAIT) 405 break; 406 bp->bp_secs = htons(secs); 407 send_request(s); 408 continue; 409 } 410 fromlen = sizeof(sin_from); 411 n = recvfrom(s, rcvbuf, BUFLEN, 0, 412 (struct sockaddr *) &sin_from, &fromlen); 413 if (n <= 0) { 414 continue; 415 } 416 if (n < sizeof(struct bootp)) { 417 printf("received short packet\n"); 418 continue; 419 } 420 recvcnt++; 421 422 /* Print the received packet. */ 423 printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); 424 /* set globals needed by bootp_print() */ 425 snaplen = n; 426 snapend = (unsigned char *) rcvbuf + snaplen; 427 bootp_print((struct bootp *)rcvbuf, n, sin_from.sin_port, 0); 428 putchar('\n'); 429 /* 430 * This no longer exits immediately after receiving 431 * one response because it is useful to know if the 432 * client might get multiple responses. This code 433 * will now listen for one second after a response. 434 */ 435 } 436 errx(1, "no response from %s", servername); 437 } 438 439 static void 440 send_request(int s) 441 { 442 /* Print the request packet. */ 443 printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); 444 bootp_print((struct bootp *)sndbuf, snaplen, sin_from.sin_port, 0); 445 putchar('\n'); 446 447 /* Send the request packet. */ 448 if (sendto(s, sndbuf, snaplen, 0, 449 (struct sockaddr *) &sin_server, 450 sizeof(sin_server)) < 0) 451 { 452 perror("sendto server"); 453 exit(1); 454 } 455 } 456 457 /* 458 * Print out a filename (or other ascii string). 459 * Return true if truncated. 460 */ 461 int 462 printfn(u_char *s, u_char *ep) 463 { 464 u_char c; 465 466 putchar('"'); 467 while ((c = *s++) != '\0') { 468 if (s > ep) { 469 putchar('"'); 470 return (1); 471 } 472 if (!isascii(c)) { 473 c = toascii(c); 474 putchar('M'); 475 putchar('-'); 476 } 477 if (!isprint(c)) { 478 c ^= 0x40; /* DEL to ?, others to alpha */ 479 putchar('^'); 480 } 481 putchar(c); 482 } 483 putchar('"'); 484 return (0); 485 } 486 487 /* 488 * Convert an IP addr to a string. 489 * (like inet_ntoa, but ina is a pointer) 490 */ 491 char * 492 ipaddr_string(struct in_addr *ina) 493 { 494 static char b[24]; 495 u_char *p; 496 497 p = (u_char *) ina; 498 snprintf(b, sizeof(b), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); 499 return (b); 500 } 501 502 /* 503 * Local Variables: 504 * tab-width: 4 505 * c-indent-level: 4 506 * c-argdecl-indent: 4 507 * c-continued-statement-offset: 4 508 * c-continued-brace-offset: -4 509 * c-label-offset: -4 510 * c-brace-offset: 0 511 * End: 512 */ 513