13229Spst/* 23229Spst * bootptest.c - Test out a bootp server. 33229Spst * 43229Spst * This simple program was put together from pieces taken from 53229Spst * various places, including the CMU BOOTP client and server. 63229Spst * The packet printing routine is from the Berkeley "tcpdump" 73229Spst * program with some enhancements I added. The print-bootp.c 83229Spst * file was shared with my copy of "tcpdump" and therefore uses 93229Spst * some unusual utility routines that would normally be provided 103229Spst * by various parts of the tcpdump program. Gordon W. Ross 113229Spst * 123229Spst * Boilerplate: 133229Spst * 143229Spst * This program includes software developed by the University of 153229Spst * California, Lawrence Berkeley Laboratory and its contributors. 163229Spst * (See the copyright notice in print-bootp.c) 173229Spst * 183229Spst * The remainder of this program is public domain. You may do 193229Spst * whatever you like with it except claim that you wrote it. 203229Spst * 213229Spst * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 223229Spst * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 233229Spst * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 243229Spst * 253229Spst * HISTORY: 263229Spst * 273229Spst * 12/02/93 Released version 1.4 (with bootp-2.3.2) 283229Spst * 11/05/93 Released version 1.3 293229Spst * 10/14/93 Released version 1.2 303229Spst * 10/11/93 Released version 1.1 313229Spst * 09/28/93 Released version 1.0 323229Spst * 09/93 Original developed by Gordon W. Ross <gwr@mc.com> 3318471Swosch * 343229Spst */ 353229Spst 36110395Scharnier#include <sys/cdefs.h> 37110395Scharnier__FBSDID("$FreeBSD$"); 38110395Scharnier 393229Spstchar *usage = "bootptest [-h] server-name [vendor-data-template-file]"; 403229Spst 413229Spst#include <sys/types.h> 423229Spst#include <sys/socket.h> 433229Spst#include <sys/ioctl.h> 443229Spst#include <sys/file.h> 453229Spst#include <sys/time.h> 463229Spst#include <sys/stat.h> 4713572Spst#include <sys/utsname.h> 483229Spst 493229Spst#include <net/if.h> 503229Spst#include <netinet/in.h> 513229Spst#include <arpa/inet.h> /* inet_ntoa */ 523229Spst 5313572Spst#ifndef NO_UNISTD 5413572Spst#include <unistd.h> 5513572Spst#endif 5613572Spst 57110395Scharnier#include <err.h> 583229Spst#include <stdlib.h> 593229Spst#include <signal.h> 603229Spst#include <stdio.h> 613229Spst#include <string.h> 623229Spst#include <errno.h> 633229Spst#include <ctype.h> 643229Spst#include <netdb.h> 653229Spst#include <assert.h> 663229Spst 673229Spst#include "bootp.h" 683229Spst#include "bootptest.h" 693229Spst#include "getif.h" 7013572Spst#include "getether.h" 7113572Spst 723229Spst#include "patchlevel.h" 733229Spst 7413572Spststatic void send_request(); 7513572Spst 763229Spst#define LOG_ERR 1 773229Spst#define BUFLEN 1024 783229Spst#define WAITSECS 1 793229Spst#define MAXWAIT 10 803229Spst 813229Spstint vflag = 1; 823229Spstint tflag = 0; 833229Spstint thiszone; 843229Spstchar *progname; 853229Spstunsigned char *packetp; 863229Spstunsigned char *snapend; 873229Spstint snaplen; 883229Spst 893229Spst 903229Spst/* 913229Spst * IP port numbers for client and server obtained from /etc/services 923229Spst */ 933229Spst 943229Spstu_short bootps_port, bootpc_port; 953229Spst 963229Spst 973229Spst/* 983229Spst * Internet socket and interface config structures 993229Spst */ 1003229Spst 1013229Spststruct sockaddr_in sin_server; /* where to send requests */ 1023229Spststruct sockaddr_in sin_client; /* for bind and listen */ 1033229Spststruct sockaddr_in sin_from; /* Packet source */ 1043229Spstu_char eaddr[16]; /* Ethernet address */ 1053229Spst 1063229Spst/* 1073229Spst * General 1083229Spst */ 1093229Spst 1103229Spstint debug = 1; /* Debugging flag (level) */ 1113229Spstchar *sndbuf; /* Send packet buffer */ 1123229Spstchar *rcvbuf; /* Receive packet buffer */ 1133229Spst 11413572Spststruct utsname my_uname; 11513572Spstchar *hostname; 11613572Spst 1173229Spst/* 1183229Spst * Vendor magic cookies for CMU and RFC1048 1193229Spst */ 1203229Spst 1213229Spstunsigned char vm_cmu[4] = VM_CMU; 1223229Spstunsigned char vm_rfc1048[4] = VM_RFC1048; 1233229Spstshort secs; /* How long client has waited */ 1243229Spst 1253229Spstchar *get_errmsg(); 1263229Spstextern void bootp_print(); 1273229Spst 1283229Spst/* 1293229Spst * Initialization such as command-line processing is done, then 1303229Spst * the receiver loop is started. Die when interrupted. 1313229Spst */ 1323229Spst 13346078Simpint 1343229Spstmain(argc, argv) 1353229Spst int argc; 1363229Spst char **argv; 1373229Spst{ 1383229Spst struct bootp *bp; 1393229Spst struct servent *sep; 1403229Spst struct hostent *hep; 1413229Spst 1423229Spst char *servername = NULL; 1433229Spst char *vendor_file = NULL; 1443229Spst char *bp_file = NULL; 1453229Spst int32 server_addr; /* inet addr, network order */ 1463229Spst int s; /* Socket file descriptor */ 14713572Spst int n, fromlen, recvcnt; 1483229Spst int use_hwa = 0; 1493229Spst int32 vend_magic; 1503229Spst int32 xid; 1513229Spst 1523229Spst progname = strrchr(argv[0], '/'); 1533229Spst if (progname) 1543229Spst progname++; 1553229Spst else 1563229Spst progname = argv[0]; 1573229Spst argc--; 1583229Spst argv++; 1593229Spst 1603229Spst if (debug) 1613229Spst printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); 1623229Spst 1633229Spst /* 1643229Spst * Verify that "struct bootp" has the correct official size. 1653229Spst * (Catch evil compilers that do struct padding.) 1663229Spst */ 1673229Spst assert(sizeof(struct bootp) == BP_MINPKTSZ); 1683229Spst 169110395Scharnier if (uname(&my_uname) < 0) 170110395Scharnier errx(1, "can't get hostname"); 17113572Spst hostname = my_uname.nodename; 17213572Spst 1733229Spst sndbuf = malloc(BUFLEN); 1743229Spst rcvbuf = malloc(BUFLEN); 1753229Spst if (!sndbuf || !rcvbuf) { 1763229Spst printf("malloc failed\n"); 1773229Spst exit(1); 1783229Spst } 1793229Spst 1803229Spst /* default magic number */ 1813229Spst bcopy(vm_rfc1048, (char*)&vend_magic, 4); 1823229Spst 1833229Spst /* Handle option switches. */ 1843229Spst while (argc > 0) { 1853229Spst if (argv[0][0] != '-') 1863229Spst break; 1873229Spst switch (argv[0][1]) { 1883229Spst 1893229Spst case 'f': /* File name to reqest. */ 1903229Spst if (argc < 2) 1913229Spst goto error; 1923229Spst argc--; argv++; 1933229Spst bp_file = *argv; 1943229Spst break; 1953229Spst 1963229Spst case 'h': /* Use hardware address. */ 1973229Spst use_hwa = 1; 1983229Spst break; 1993229Spst 2003229Spst case 'm': /* Magic number value. */ 2013229Spst if (argc < 2) 2023229Spst goto error; 2033229Spst argc--; argv++; 2043229Spst vend_magic = inet_addr(*argv); 2053229Spst break; 2063229Spst 2073229Spst error: 2083229Spst default: 2093229Spst puts(usage); 2103229Spst exit(1); 2113229Spst 2123229Spst } 2133229Spst argc--; 2143229Spst argv++; 2153229Spst } 2163229Spst 2173229Spst /* Get server name (or address) for query. */ 2183229Spst if (argc > 0) { 2193229Spst servername = *argv; 2203229Spst argc--; 2213229Spst argv++; 2223229Spst } 2233229Spst /* Get optional vendor-data-template-file. */ 2243229Spst if (argc > 0) { 2253229Spst vendor_file = *argv; 2263229Spst argc--; 2273229Spst argv++; 2283229Spst } 2293229Spst if (!servername) { 2303229Spst printf("missing server name.\n"); 2313229Spst puts(usage); 2323229Spst exit(1); 2333229Spst } 2343229Spst /* 2353229Spst * Create a socket. 2363229Spst */ 2373229Spst if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 2383229Spst perror("socket"); 2393229Spst exit(1); 2403229Spst } 2413229Spst /* 2423229Spst * Get server's listening port number 2433229Spst */ 2443229Spst sep = getservbyname("bootps", "udp"); 2453229Spst if (sep) { 2463229Spst bootps_port = ntohs((u_short) sep->s_port); 2473229Spst } else { 248110395Scharnier warnx("bootps/udp: unknown service -- using port %d", 2493229Spst IPPORT_BOOTPS); 2503229Spst bootps_port = (u_short) IPPORT_BOOTPS; 2513229Spst } 2523229Spst 2533229Spst /* 2543229Spst * Set up server socket address (for send) 2553229Spst */ 2563229Spst if (servername) { 2573229Spst if (isdigit(servername[0])) 2583229Spst server_addr = inet_addr(servername); 2593229Spst else { 2603229Spst hep = gethostbyname(servername); 261110395Scharnier if (!hep) 262110395Scharnier errx(1, "%s: unknown host", servername); 2633229Spst bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); 2643229Spst } 2653229Spst } else { 2663229Spst /* Get broadcast address */ 2673229Spst /* XXX - not yet */ 2683229Spst server_addr = INADDR_ANY; 2693229Spst } 2703229Spst sin_server.sin_family = AF_INET; 2713229Spst sin_server.sin_port = htons(bootps_port); 2723229Spst sin_server.sin_addr.s_addr = server_addr; 2733229Spst 2743229Spst /* 2753229Spst * Get client's listening port number 2763229Spst */ 2773229Spst sep = getservbyname("bootpc", "udp"); 2783229Spst if (sep) { 2793229Spst bootpc_port = ntohs(sep->s_port); 2803229Spst } else { 281110395Scharnier warnx("bootpc/udp: unknown service -- using port %d", 2823229Spst IPPORT_BOOTPC); 2833229Spst bootpc_port = (u_short) IPPORT_BOOTPC; 2843229Spst } 2853229Spst 2863229Spst /* 2873229Spst * Set up client socket address (for listen) 2883229Spst */ 2893229Spst sin_client.sin_family = AF_INET; 2903229Spst sin_client.sin_port = htons(bootpc_port); 2913229Spst sin_client.sin_addr.s_addr = INADDR_ANY; 2923229Spst 2933229Spst /* 2943229Spst * Bind client socket to BOOTPC port. 2953229Spst */ 2963229Spst if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { 297110395Scharnier if (errno == EACCES) { 298110395Scharnier warn("bind BOOTPC port"); 299110395Scharnier errx(1, "you need to run this as root"); 300110395Scharnier } 301110395Scharnier else 302110395Scharnier err(1, "bind BOOTPC port"); 3033229Spst } 3043229Spst /* 3053229Spst * Build a request. 3063229Spst */ 3073229Spst bp = (struct bootp *) sndbuf; 3083229Spst bzero(bp, sizeof(*bp)); 3093229Spst bp->bp_op = BOOTREQUEST; 3103229Spst xid = (int32) getpid(); 3113229Spst bp->bp_xid = (u_int32) htonl(xid); 3123229Spst if (bp_file) 3133229Spst strncpy(bp->bp_file, bp_file, BP_FILE_LEN); 3143229Spst 3153229Spst /* 3163229Spst * Fill in the hardware address (or client IP address) 3173229Spst */ 3183229Spst if (use_hwa) { 3193229Spst struct ifreq *ifr; 3203229Spst 3213229Spst ifr = getif(s, &sin_server.sin_addr); 3223229Spst if (!ifr) { 3233229Spst printf("No interface for %s\n", servername); 3243229Spst exit(1); 3253229Spst } 32613572Spst if (getether(ifr->ifr_name, (char*)eaddr)) { 3273229Spst printf("Can not get ether addr for %s\n", ifr->ifr_name); 3283229Spst exit(1); 3293229Spst } 3303229Spst /* Copy Ethernet address into request packet. */ 3313229Spst bp->bp_htype = 1; 3323229Spst bp->bp_hlen = 6; 3333229Spst bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); 3343229Spst } else { 3353229Spst /* Fill in the client IP address. */ 3363229Spst hep = gethostbyname(hostname); 3373229Spst if (!hep) { 3383229Spst printf("Can not get my IP address\n"); 3393229Spst exit(1); 3403229Spst } 3413229Spst bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); 3423229Spst } 3433229Spst 3443229Spst /* 3453229Spst * Copy in the default vendor data. 3463229Spst */ 3473229Spst bcopy((char*)&vend_magic, bp->bp_vend, 4); 3483229Spst if (vend_magic) 3493229Spst bp->bp_vend[4] = TAG_END; 3503229Spst 3513229Spst /* 3523229Spst * Read in the "options" part of the request. 3533229Spst * This also determines the size of the packet. 3543229Spst */ 3553229Spst snaplen = sizeof(*bp); 3563229Spst if (vendor_file) { 3573229Spst int fd = open(vendor_file, 0); 3583229Spst if (fd < 0) { 3593229Spst perror(vendor_file); 3603229Spst exit(1); 3613229Spst } 3623229Spst /* Compute actual space for options. */ 3633229Spst n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; 3643229Spst n = read(fd, bp->bp_vend, n); 3653229Spst close(fd); 3663229Spst if (n < 0) { 3673229Spst perror(vendor_file); 3683229Spst exit(1); 3693229Spst } 3703229Spst printf("read %d bytes of vendor template\n", n); 3713229Spst if (n > BP_VEND_LEN) { 3723229Spst printf("warning: extended options in use (len > %d)\n", 3733229Spst BP_VEND_LEN); 3743229Spst snaplen += (n - BP_VEND_LEN); 3753229Spst } 3763229Spst } 3773229Spst /* 3783229Spst * Set globals needed by print_bootp 3793229Spst * (called by send_request) 3803229Spst */ 3813229Spst packetp = (unsigned char *) eaddr; 3823229Spst snapend = (unsigned char *) sndbuf + snaplen; 3833229Spst 3843229Spst /* Send a request once per second while waiting for replies. */ 3853229Spst recvcnt = 0; 3863229Spst bp->bp_secs = secs = 0; 3873229Spst send_request(s); 3883229Spst while (1) { 3893229Spst struct timeval tv; 3903229Spst int readfds; 3913229Spst 3923229Spst tv.tv_sec = WAITSECS; 3933229Spst tv.tv_usec = 0L; 3943229Spst readfds = (1 << s); 3953229Spst n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv); 3963229Spst if (n < 0) { 3973229Spst perror("select"); 3983229Spst break; 3993229Spst } 4003229Spst if (n == 0) { 4013229Spst /* 4023229Spst * We have not received a response in the last second. 4033229Spst * If we have ever received any responses, exit now. 4043229Spst * Otherwise, bump the "wait time" field and re-send. 4053229Spst */ 4063229Spst if (recvcnt > 0) 4073229Spst exit(0); 4083229Spst secs += WAITSECS; 4093229Spst if (secs > MAXWAIT) 4103229Spst break; 4113229Spst bp->bp_secs = htons(secs); 4123229Spst send_request(s); 4133229Spst continue; 4143229Spst } 4153229Spst fromlen = sizeof(sin_from); 4163229Spst n = recvfrom(s, rcvbuf, BUFLEN, 0, 4173229Spst (struct sockaddr *) &sin_from, &fromlen); 4183229Spst if (n <= 0) { 4193229Spst continue; 4203229Spst } 4213229Spst if (n < sizeof(struct bootp)) { 4223229Spst printf("received short packet\n"); 4233229Spst continue; 4243229Spst } 4253229Spst recvcnt++; 4263229Spst 4273229Spst /* Print the received packet. */ 4283229Spst printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); 4293229Spst /* set globals needed by bootp_print() */ 4303229Spst snaplen = n; 4313229Spst snapend = (unsigned char *) rcvbuf + snaplen; 4323229Spst bootp_print(rcvbuf, n, sin_from.sin_port, 0); 4333229Spst putchar('\n'); 4343229Spst /* 4353229Spst * This no longer exits immediately after receiving 4363229Spst * one response because it is useful to know if the 4373229Spst * client might get multiple responses. This code 4383229Spst * will now listen for one second after a response. 4393229Spst */ 4403229Spst } 441110395Scharnier errx(1, "no response from %s", servername); 4423229Spst} 4433229Spst 44413572Spststatic void 4453229Spstsend_request(s) 4463229Spst int s; 4473229Spst{ 4483229Spst /* Print the request packet. */ 4493229Spst printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); 4503229Spst bootp_print(sndbuf, snaplen, sin_from.sin_port, 0); 4513229Spst putchar('\n'); 4523229Spst 4533229Spst /* Send the request packet. */ 4543229Spst if (sendto(s, sndbuf, snaplen, 0, 4553229Spst (struct sockaddr *) &sin_server, 4563229Spst sizeof(sin_server)) < 0) 4573229Spst { 4583229Spst perror("sendto server"); 4593229Spst exit(1); 4603229Spst } 4613229Spst} 4623229Spst 4633229Spst/* 4643229Spst * Print out a filename (or other ascii string). 4653229Spst * Return true if truncated. 4663229Spst */ 4673229Spstint 4683229Spstprintfn(s, ep) 4693229Spst register u_char *s, *ep; 4703229Spst{ 4713229Spst register u_char c; 4723229Spst 4733229Spst putchar('"'); 47413572Spst while ((c = *s++) != '\0') { 4753229Spst if (s > ep) { 4763229Spst putchar('"'); 4773229Spst return (1); 4783229Spst } 4793229Spst if (!isascii(c)) { 4803229Spst c = toascii(c); 4813229Spst putchar('M'); 4823229Spst putchar('-'); 4833229Spst } 4843229Spst if (!isprint(c)) { 4853229Spst c ^= 0x40; /* DEL to ?, others to alpha */ 4863229Spst putchar('^'); 4873229Spst } 4883229Spst putchar(c); 4893229Spst } 4903229Spst putchar('"'); 4913229Spst return (0); 4923229Spst} 4933229Spst 4943229Spst/* 4953229Spst * Convert an IP addr to a string. 4963229Spst * (like inet_ntoa, but ina is a pointer) 4973229Spst */ 4983229Spstchar * 4993229Spstipaddr_string(ina) 5003229Spst struct in_addr *ina; 5013229Spst{ 5023229Spst static char b[24]; 5033229Spst u_char *p; 5043229Spst 5053229Spst p = (u_char *) ina; 50631971Simp snprintf(b, sizeof(b), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); 5073229Spst return (b); 5083229Spst} 5093229Spst 5103229Spst/* 5113229Spst * Local Variables: 5123229Spst * tab-width: 4 5133229Spst * c-indent-level: 4 5143229Spst * c-argdecl-indent: 4 5153229Spst * c-continued-statement-offset: 4 5163229Spst * c-continued-brace-offset: -4 5173229Spst * c-label-offset: -4 5183229Spst * c-brace-offset: 0 5193229Spst * End: 5203229Spst */ 521