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