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