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