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