bootptest.c revision 46078
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 * 3446078Simp * $Id: bootptest.c,v 1.5 1997/12/24 18:56:03 imp Exp $ 353229Spst */ 363229Spst 373229Spstchar *usage = "bootptest [-h] server-name [vendor-data-template-file]"; 383229Spst 393229Spst#include <sys/types.h> 403229Spst#include <sys/socket.h> 413229Spst#include <sys/ioctl.h> 423229Spst#include <sys/file.h> 433229Spst#include <sys/time.h> 443229Spst#include <sys/stat.h> 4513572Spst#include <sys/utsname.h> 463229Spst 473229Spst#include <net/if.h> 483229Spst#include <netinet/in.h> 493229Spst#include <arpa/inet.h> /* inet_ntoa */ 503229Spst 5113572Spst#ifndef NO_UNISTD 5213572Spst#include <unistd.h> 5313572Spst#endif 5413572Spst 553229Spst#include <stdlib.h> 563229Spst#include <signal.h> 573229Spst#include <stdio.h> 583229Spst#include <string.h> 593229Spst#include <errno.h> 603229Spst#include <ctype.h> 613229Spst#include <netdb.h> 623229Spst#include <assert.h> 633229Spst 643229Spst#include "bootp.h" 653229Spst#include "bootptest.h" 663229Spst#include "getif.h" 6713572Spst#include "getether.h" 6813572Spst 693229Spst#include "patchlevel.h" 703229Spst 7113572Spststatic void send_request(); 7213572Spst 733229Spst#define LOG_ERR 1 743229Spst#define BUFLEN 1024 753229Spst#define WAITSECS 1 763229Spst#define MAXWAIT 10 773229Spst 783229Spstint vflag = 1; 793229Spstint tflag = 0; 803229Spstint thiszone; 813229Spstchar *progname; 823229Spstunsigned char *packetp; 833229Spstunsigned char *snapend; 843229Spstint snaplen; 853229Spst 863229Spst 873229Spst/* 883229Spst * IP port numbers for client and server obtained from /etc/services 893229Spst */ 903229Spst 913229Spstu_short bootps_port, bootpc_port; 923229Spst 933229Spst 943229Spst/* 953229Spst * Internet socket and interface config structures 963229Spst */ 973229Spst 983229Spststruct sockaddr_in sin_server; /* where to send requests */ 993229Spststruct sockaddr_in sin_client; /* for bind and listen */ 1003229Spststruct sockaddr_in sin_from; /* Packet source */ 1013229Spstu_char eaddr[16]; /* Ethernet address */ 1023229Spst 1033229Spst/* 1043229Spst * General 1053229Spst */ 1063229Spst 1073229Spstint debug = 1; /* Debugging flag (level) */ 1083229Spstchar *sndbuf; /* Send packet buffer */ 1093229Spstchar *rcvbuf; /* Receive packet buffer */ 1103229Spst 11113572Spststruct utsname my_uname; 11213572Spstchar *hostname; 11313572Spst 1143229Spst/* 1153229Spst * Vendor magic cookies for CMU and RFC1048 1163229Spst */ 1173229Spst 1183229Spstunsigned char vm_cmu[4] = VM_CMU; 1193229Spstunsigned char vm_rfc1048[4] = VM_RFC1048; 1203229Spstshort secs; /* How long client has waited */ 1213229Spst 1223229Spstchar *get_errmsg(); 1233229Spstextern void bootp_print(); 1243229Spst 1253229Spst/* 1263229Spst * Initialization such as command-line processing is done, then 1273229Spst * the receiver loop is started. Die when interrupted. 1283229Spst */ 1293229Spst 13046078Simpint 1313229Spstmain(argc, argv) 1323229Spst int argc; 1333229Spst char **argv; 1343229Spst{ 1353229Spst struct bootp *bp; 1363229Spst struct servent *sep; 1373229Spst struct hostent *hep; 1383229Spst 1393229Spst char *servername = NULL; 1403229Spst char *vendor_file = NULL; 1413229Spst char *bp_file = NULL; 1423229Spst int32 server_addr; /* inet addr, network order */ 1433229Spst int s; /* Socket file descriptor */ 14413572Spst int n, fromlen, recvcnt; 1453229Spst int use_hwa = 0; 1463229Spst int32 vend_magic; 1473229Spst int32 xid; 1483229Spst 1493229Spst progname = strrchr(argv[0], '/'); 1503229Spst if (progname) 1513229Spst progname++; 1523229Spst else 1533229Spst progname = argv[0]; 1543229Spst argc--; 1553229Spst argv++; 1563229Spst 1573229Spst if (debug) 1583229Spst printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); 1593229Spst 1603229Spst /* 1613229Spst * Verify that "struct bootp" has the correct official size. 1623229Spst * (Catch evil compilers that do struct padding.) 1633229Spst */ 1643229Spst assert(sizeof(struct bootp) == BP_MINPKTSZ); 1653229Spst 16613572Spst if (uname(&my_uname) < 0) { 16713572Spst fprintf(stderr, "%s: can't get hostname\n", argv[0]); 16813572Spst exit(1); 16913572Spst } 17013572Spst hostname = my_uname.nodename; 17113572Spst 1723229Spst sndbuf = malloc(BUFLEN); 1733229Spst rcvbuf = malloc(BUFLEN); 1743229Spst if (!sndbuf || !rcvbuf) { 1753229Spst printf("malloc failed\n"); 1763229Spst exit(1); 1773229Spst } 1783229Spst 1793229Spst /* default magic number */ 1803229Spst bcopy(vm_rfc1048, (char*)&vend_magic, 4); 1813229Spst 1823229Spst /* Handle option switches. */ 1833229Spst while (argc > 0) { 1843229Spst if (argv[0][0] != '-') 1853229Spst break; 1863229Spst switch (argv[0][1]) { 1873229Spst 1883229Spst case 'f': /* File name to reqest. */ 1893229Spst if (argc < 2) 1903229Spst goto error; 1913229Spst argc--; argv++; 1923229Spst bp_file = *argv; 1933229Spst break; 1943229Spst 1953229Spst case 'h': /* Use hardware address. */ 1963229Spst use_hwa = 1; 1973229Spst break; 1983229Spst 1993229Spst case 'm': /* Magic number value. */ 2003229Spst if (argc < 2) 2013229Spst goto error; 2023229Spst argc--; argv++; 2033229Spst vend_magic = inet_addr(*argv); 2043229Spst break; 2053229Spst 2063229Spst error: 2073229Spst default: 2083229Spst puts(usage); 2093229Spst exit(1); 2103229Spst 2113229Spst } 2123229Spst argc--; 2133229Spst argv++; 2143229Spst } 2153229Spst 2163229Spst /* Get server name (or address) for query. */ 2173229Spst if (argc > 0) { 2183229Spst servername = *argv; 2193229Spst argc--; 2203229Spst argv++; 2213229Spst } 2223229Spst /* Get optional vendor-data-template-file. */ 2233229Spst if (argc > 0) { 2243229Spst vendor_file = *argv; 2253229Spst argc--; 2263229Spst argv++; 2273229Spst } 2283229Spst if (!servername) { 2293229Spst printf("missing server name.\n"); 2303229Spst puts(usage); 2313229Spst exit(1); 2323229Spst } 2333229Spst /* 2343229Spst * Create a socket. 2353229Spst */ 2363229Spst if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 2373229Spst perror("socket"); 2383229Spst exit(1); 2393229Spst } 2403229Spst /* 2413229Spst * Get server's listening port number 2423229Spst */ 2433229Spst sep = getservbyname("bootps", "udp"); 2443229Spst if (sep) { 2453229Spst bootps_port = ntohs((u_short) sep->s_port); 2463229Spst } else { 2473229Spst fprintf(stderr, "udp/bootps: unknown service -- using port %d\n", 2483229Spst IPPORT_BOOTPS); 2493229Spst bootps_port = (u_short) IPPORT_BOOTPS; 2503229Spst } 2513229Spst 2523229Spst /* 2533229Spst * Set up server socket address (for send) 2543229Spst */ 2553229Spst if (servername) { 2563229Spst if (isdigit(servername[0])) 2573229Spst server_addr = inet_addr(servername); 2583229Spst else { 2593229Spst hep = gethostbyname(servername); 2603229Spst if (!hep) { 2613229Spst fprintf(stderr, "%s: unknown host\n", servername); 2623229Spst exit(1); 2633229Spst } 2643229Spst bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); 2653229Spst } 2663229Spst } else { 2673229Spst /* Get broadcast address */ 2683229Spst /* XXX - not yet */ 2693229Spst server_addr = INADDR_ANY; 2703229Spst } 2713229Spst sin_server.sin_family = AF_INET; 2723229Spst sin_server.sin_port = htons(bootps_port); 2733229Spst sin_server.sin_addr.s_addr = server_addr; 2743229Spst 2753229Spst /* 2763229Spst * Get client's listening port number 2773229Spst */ 2783229Spst sep = getservbyname("bootpc", "udp"); 2793229Spst if (sep) { 2803229Spst bootpc_port = ntohs(sep->s_port); 2813229Spst } else { 2823229Spst fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n", 2833229Spst IPPORT_BOOTPC); 2843229Spst bootpc_port = (u_short) IPPORT_BOOTPC; 2853229Spst } 2863229Spst 2873229Spst /* 2883229Spst * Set up client socket address (for listen) 2893229Spst */ 2903229Spst sin_client.sin_family = AF_INET; 2913229Spst sin_client.sin_port = htons(bootpc_port); 2923229Spst sin_client.sin_addr.s_addr = INADDR_ANY; 2933229Spst 2943229Spst /* 2953229Spst * Bind client socket to BOOTPC port. 2963229Spst */ 2973229Spst if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { 2983229Spst perror("bind BOOTPC port"); 2993229Spst if (errno == EACCES) 3003229Spst fprintf(stderr, "You need to run this as root\n"); 3013229Spst exit(1); 3023229Spst } 3033229Spst /* 3043229Spst * Build a request. 3053229Spst */ 3063229Spst bp = (struct bootp *) sndbuf; 3073229Spst bzero(bp, sizeof(*bp)); 3083229Spst bp->bp_op = BOOTREQUEST; 3093229Spst xid = (int32) getpid(); 3103229Spst bp->bp_xid = (u_int32) htonl(xid); 3113229Spst if (bp_file) 3123229Spst strncpy(bp->bp_file, bp_file, BP_FILE_LEN); 3133229Spst 3143229Spst /* 3153229Spst * Fill in the hardware address (or client IP address) 3163229Spst */ 3173229Spst if (use_hwa) { 3183229Spst struct ifreq *ifr; 3193229Spst 3203229Spst ifr = getif(s, &sin_server.sin_addr); 3213229Spst if (!ifr) { 3223229Spst printf("No interface for %s\n", servername); 3233229Spst exit(1); 3243229Spst } 32513572Spst if (getether(ifr->ifr_name, (char*)eaddr)) { 3263229Spst printf("Can not get ether addr for %s\n", ifr->ifr_name); 3273229Spst exit(1); 3283229Spst } 3293229Spst /* Copy Ethernet address into request packet. */ 3303229Spst bp->bp_htype = 1; 3313229Spst bp->bp_hlen = 6; 3323229Spst bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); 3333229Spst } else { 3343229Spst /* Fill in the client IP address. */ 3353229Spst hep = gethostbyname(hostname); 3363229Spst if (!hep) { 3373229Spst printf("Can not get my IP address\n"); 3383229Spst exit(1); 3393229Spst } 3403229Spst bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); 3413229Spst } 3423229Spst 3433229Spst /* 3443229Spst * Copy in the default vendor data. 3453229Spst */ 3463229Spst bcopy((char*)&vend_magic, bp->bp_vend, 4); 3473229Spst if (vend_magic) 3483229Spst bp->bp_vend[4] = TAG_END; 3493229Spst 3503229Spst /* 3513229Spst * Read in the "options" part of the request. 3523229Spst * This also determines the size of the packet. 3533229Spst */ 3543229Spst snaplen = sizeof(*bp); 3553229Spst if (vendor_file) { 3563229Spst int fd = open(vendor_file, 0); 3573229Spst if (fd < 0) { 3583229Spst perror(vendor_file); 3593229Spst exit(1); 3603229Spst } 3613229Spst /* Compute actual space for options. */ 3623229Spst n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; 3633229Spst n = read(fd, bp->bp_vend, n); 3643229Spst close(fd); 3653229Spst if (n < 0) { 3663229Spst perror(vendor_file); 3673229Spst exit(1); 3683229Spst } 3693229Spst printf("read %d bytes of vendor template\n", n); 3703229Spst if (n > BP_VEND_LEN) { 3713229Spst printf("warning: extended options in use (len > %d)\n", 3723229Spst BP_VEND_LEN); 3733229Spst snaplen += (n - BP_VEND_LEN); 3743229Spst } 3753229Spst } 3763229Spst /* 3773229Spst * Set globals needed by print_bootp 3783229Spst * (called by send_request) 3793229Spst */ 3803229Spst packetp = (unsigned char *) eaddr; 3813229Spst snapend = (unsigned char *) sndbuf + snaplen; 3823229Spst 3833229Spst /* Send a request once per second while waiting for replies. */ 3843229Spst recvcnt = 0; 3853229Spst bp->bp_secs = secs = 0; 3863229Spst send_request(s); 3873229Spst while (1) { 3883229Spst struct timeval tv; 3893229Spst int readfds; 3903229Spst 3913229Spst tv.tv_sec = WAITSECS; 3923229Spst tv.tv_usec = 0L; 3933229Spst readfds = (1 << s); 3943229Spst n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv); 3953229Spst if (n < 0) { 3963229Spst perror("select"); 3973229Spst break; 3983229Spst } 3993229Spst if (n == 0) { 4003229Spst /* 4013229Spst * We have not received a response in the last second. 4023229Spst * If we have ever received any responses, exit now. 4033229Spst * Otherwise, bump the "wait time" field and re-send. 4043229Spst */ 4053229Spst if (recvcnt > 0) 4063229Spst exit(0); 4073229Spst secs += WAITSECS; 4083229Spst if (secs > MAXWAIT) 4093229Spst break; 4103229Spst bp->bp_secs = htons(secs); 4113229Spst send_request(s); 4123229Spst continue; 4133229Spst } 4143229Spst fromlen = sizeof(sin_from); 4153229Spst n = recvfrom(s, rcvbuf, BUFLEN, 0, 4163229Spst (struct sockaddr *) &sin_from, &fromlen); 4173229Spst if (n <= 0) { 4183229Spst continue; 4193229Spst } 4203229Spst if (n < sizeof(struct bootp)) { 4213229Spst printf("received short packet\n"); 4223229Spst continue; 4233229Spst } 4243229Spst recvcnt++; 4253229Spst 4263229Spst /* Print the received packet. */ 4273229Spst printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); 4283229Spst /* set globals needed by bootp_print() */ 4293229Spst snaplen = n; 4303229Spst snapend = (unsigned char *) rcvbuf + snaplen; 4313229Spst bootp_print(rcvbuf, n, sin_from.sin_port, 0); 4323229Spst putchar('\n'); 4333229Spst /* 4343229Spst * This no longer exits immediately after receiving 4353229Spst * one response because it is useful to know if the 4363229Spst * client might get multiple responses. This code 4373229Spst * will now listen for one second after a response. 4383229Spst */ 4393229Spst } 4403229Spst fprintf(stderr, "no response from %s\n", servername); 4413229Spst exit(1); 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