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