Path: blob/main/libexec/bootpd/tools/bootptest/bootptest.c
34875 views
/*1* bootptest.c - Test out a bootp server.2*3* This simple program was put together from pieces taken from4* various places, including the CMU BOOTP client and server.5* The packet printing routine is from the Berkeley "tcpdump"6* program with some enhancements I added. The print-bootp.c7* file was shared with my copy of "tcpdump" and therefore uses8* some unusual utility routines that would normally be provided9* by various parts of the tcpdump program. Gordon W. Ross10*11* Boilerplate:12*13* This program includes software developed by the University of14* California, Lawrence Berkeley Laboratory and its contributors.15* (See the copyright notice in print-bootp.c)16*17* The remainder of this program is public domain. You may do18* whatever you like with it except claim that you wrote it.19*20* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED21* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF22* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.23*24* HISTORY:25*26* 12/02/93 Released version 1.4 (with bootp-2.3.2)27* 11/05/93 Released version 1.328* 10/14/93 Released version 1.229* 10/11/93 Released version 1.130* 09/28/93 Released version 1.031* 09/93 Original developed by Gordon W. Ross <[email protected]>32*33*/3435#include <sys/cdefs.h>36char *usage = "bootptest [-h] server-name [vendor-data-template-file]";3738#include <sys/types.h>39#include <sys/socket.h>40#include <sys/ioctl.h>41#include <sys/file.h>42#include <sys/time.h>43#include <sys/stat.h>44#include <sys/utsname.h>4546#include <net/if.h>47#include <netinet/in.h>48#include <arpa/inet.h> /* inet_ntoa */4950#ifndef NO_UNISTD51#include <unistd.h>52#endif5354#include <err.h>55#include <stdlib.h>56#include <signal.h>57#include <stdio.h>58#include <string.h>59#include <errno.h>60#include <ctype.h>61#include <netdb.h>62#include <assert.h>6364#include "bootp.h"65#include "bootptest.h"66#include "getif.h"67#include "getether.h"6869#include "patchlevel.h"7071static void send_request(int s);7273#define LOG_ERR 174#define BUFLEN 102475#define WAITSECS 176#define MAXWAIT 107778int vflag = 1;79int tflag = 0;80int thiszone;81char *progname;82unsigned char *packetp;83unsigned char *snapend;84int snaplen;858687/*88* IP port numbers for client and server obtained from /etc/services89*/9091u_short bootps_port, bootpc_port;929394/*95* Internet socket and interface config structures96*/9798struct sockaddr_in sin_server; /* where to send requests */99struct sockaddr_in sin_client; /* for bind and listen */100struct sockaddr_in sin_from; /* Packet source */101u_char eaddr[16]; /* Ethernet address */102103/*104* General105*/106107int debug = 1; /* Debugging flag (level) */108char *sndbuf; /* Send packet buffer */109char *rcvbuf; /* Receive packet buffer */110111struct utsname my_uname;112char *hostname;113114/*115* Vendor magic cookies for CMU and RFC1048116*/117118unsigned char vm_cmu[4] = VM_CMU;119unsigned char vm_rfc1048[4] = VM_RFC1048;120short secs; /* How long client has waited */121122/*123* Initialization such as command-line processing is done, then124* the receiver loop is started. Die when interrupted.125*/126127int128main(int argc, char **argv)129{130struct bootp *bp;131struct servent *sep;132struct hostent *hep;133134char *servername = NULL;135char *vendor_file = NULL;136char *bp_file = NULL;137int32 server_addr; /* inet addr, network order */138int s; /* Socket file descriptor */139int n, fromlen, recvcnt;140int use_hwa = 0;141int32 vend_magic;142int32 xid;143144progname = strrchr(argv[0], '/');145if (progname)146progname++;147else148progname = argv[0];149argc--;150argv++;151152if (debug)153printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL);154155/*156* Verify that "struct bootp" has the correct official size.157* (Catch evil compilers that do struct padding.)158*/159assert(sizeof(struct bootp) == BP_MINPKTSZ);160161if (uname(&my_uname) < 0)162errx(1, "can't get hostname");163hostname = my_uname.nodename;164165sndbuf = malloc(BUFLEN);166rcvbuf = malloc(BUFLEN);167if (!sndbuf || !rcvbuf) {168printf("malloc failed\n");169exit(1);170}171172/* default magic number */173bcopy(vm_rfc1048, (char*)&vend_magic, 4);174175/* Handle option switches. */176while (argc > 0) {177if (argv[0][0] != '-')178break;179switch (argv[0][1]) {180181case 'f': /* File name to request. */182if (argc < 2)183goto error;184argc--; argv++;185bp_file = *argv;186break;187188case 'h': /* Use hardware address. */189use_hwa = 1;190break;191192case 'm': /* Magic number value. */193if (argc < 2)194goto error;195argc--; argv++;196vend_magic = inet_addr(*argv);197break;198199error:200default:201puts(usage);202exit(1);203204}205argc--;206argv++;207}208209/* Get server name (or address) for query. */210if (argc > 0) {211servername = *argv;212argc--;213argv++;214}215/* Get optional vendor-data-template-file. */216if (argc > 0) {217vendor_file = *argv;218argc--;219argv++;220}221if (!servername) {222printf("missing server name.\n");223puts(usage);224exit(1);225}226/*227* Create a socket.228*/229if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {230perror("socket");231exit(1);232}233/*234* Get server's listening port number235*/236sep = getservbyname("bootps", "udp");237if (sep) {238bootps_port = ntohs((u_short) sep->s_port);239} else {240warnx("bootps/udp: unknown service -- using port %d",241IPPORT_BOOTPS);242bootps_port = (u_short) IPPORT_BOOTPS;243}244245/*246* Set up server socket address (for send)247*/248if (servername) {249if (isdigit(servername[0]))250server_addr = inet_addr(servername);251else {252hep = gethostbyname(servername);253if (!hep)254errx(1, "%s: unknown host", servername);255bcopy(hep->h_addr, &server_addr, sizeof(server_addr));256}257} else {258/* Get broadcast address */259/* XXX - not yet */260server_addr = INADDR_ANY;261}262sin_server.sin_family = AF_INET;263sin_server.sin_port = htons(bootps_port);264sin_server.sin_addr.s_addr = server_addr;265266/*267* Get client's listening port number268*/269sep = getservbyname("bootpc", "udp");270if (sep) {271bootpc_port = ntohs(sep->s_port);272} else {273warnx("bootpc/udp: unknown service -- using port %d",274IPPORT_BOOTPC);275bootpc_port = (u_short) IPPORT_BOOTPC;276}277278/*279* Set up client socket address (for listen)280*/281sin_client.sin_family = AF_INET;282sin_client.sin_port = htons(bootpc_port);283sin_client.sin_addr.s_addr = INADDR_ANY;284285/*286* Bind client socket to BOOTPC port.287*/288if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) {289if (errno == EACCES) {290warn("bind BOOTPC port");291errx(1, "you need to run this as root");292}293else294err(1, "bind BOOTPC port");295}296/*297* Build a request.298*/299bp = (struct bootp *) sndbuf;300bzero(bp, sizeof(*bp));301bp->bp_op = BOOTREQUEST;302xid = (int32) getpid();303bp->bp_xid = (u_int32) htonl(xid);304if (bp_file)305strncpy(bp->bp_file, bp_file, BP_FILE_LEN);306307/*308* Fill in the hardware address (or client IP address)309*/310if (use_hwa) {311struct ifreq *ifr;312313ifr = getif(s, &sin_server.sin_addr);314if (!ifr) {315printf("No interface for %s\n", servername);316exit(1);317}318if (getether(ifr->ifr_name, (char*)eaddr)) {319printf("Can not get ether addr for %s\n", ifr->ifr_name);320exit(1);321}322/* Copy Ethernet address into request packet. */323bp->bp_htype = 1;324bp->bp_hlen = 6;325bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen);326} else {327/* Fill in the client IP address. */328hep = gethostbyname(hostname);329if (!hep) {330printf("Can not get my IP address\n");331exit(1);332}333bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length);334}335336/*337* Copy in the default vendor data.338*/339bcopy((char*)&vend_magic, bp->bp_vend, 4);340if (vend_magic)341bp->bp_vend[4] = TAG_END;342343/*344* Read in the "options" part of the request.345* This also determines the size of the packet.346*/347snaplen = sizeof(*bp);348if (vendor_file) {349int fd = open(vendor_file, 0);350if (fd < 0) {351perror(vendor_file);352exit(1);353}354/* Compute actual space for options. */355n = BUFLEN - sizeof(*bp) + BP_VEND_LEN;356n = read(fd, bp->bp_vend, n);357close(fd);358if (n < 0) {359perror(vendor_file);360exit(1);361}362printf("read %d bytes of vendor template\n", n);363if (n > BP_VEND_LEN) {364printf("warning: extended options in use (len > %d)\n",365BP_VEND_LEN);366snaplen += (n - BP_VEND_LEN);367}368}369/*370* Set globals needed by print_bootp371* (called by send_request)372*/373packetp = (unsigned char *) eaddr;374snapend = (unsigned char *) sndbuf + snaplen;375376/* Send a request once per second while waiting for replies. */377recvcnt = 0;378bp->bp_secs = secs = 0;379send_request(s);380while (1) {381struct timeval tv;382int readfds;383384tv.tv_sec = WAITSECS;385tv.tv_usec = 0L;386readfds = (1 << s);387n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv);388if (n < 0) {389perror("select");390break;391}392if (n == 0) {393/*394* We have not received a response in the last second.395* If we have ever received any responses, exit now.396* Otherwise, bump the "wait time" field and re-send.397*/398if (recvcnt > 0)399exit(0);400secs += WAITSECS;401if (secs > MAXWAIT)402break;403bp->bp_secs = htons(secs);404send_request(s);405continue;406}407fromlen = sizeof(sin_from);408n = recvfrom(s, rcvbuf, BUFLEN, 0,409(struct sockaddr *) &sin_from, &fromlen);410if (n <= 0) {411continue;412}413if (n < sizeof(struct bootp)) {414printf("received short packet\n");415continue;416}417recvcnt++;418419/* Print the received packet. */420printf("Recvd from %s", inet_ntoa(sin_from.sin_addr));421/* set globals needed by bootp_print() */422snaplen = n;423snapend = (unsigned char *) rcvbuf + snaplen;424bootp_print((struct bootp *)rcvbuf, n, sin_from.sin_port, 0);425putchar('\n');426/*427* This no longer exits immediately after receiving428* one response because it is useful to know if the429* client might get multiple responses. This code430* will now listen for one second after a response.431*/432}433errx(1, "no response from %s", servername);434}435436static void437send_request(int s)438{439/* Print the request packet. */440printf("Sending to %s", inet_ntoa(sin_server.sin_addr));441bootp_print((struct bootp *)sndbuf, snaplen, sin_from.sin_port, 0);442putchar('\n');443444/* Send the request packet. */445if (sendto(s, sndbuf, snaplen, 0,446(struct sockaddr *) &sin_server,447sizeof(sin_server)) < 0)448{449perror("sendto server");450exit(1);451}452}453454/*455* Print out a filename (or other ascii string).456* Return true if truncated.457*/458int459printfn(u_char *s, u_char *ep)460{461u_char c;462463putchar('"');464while ((c = *s++) != '\0') {465if (s > ep) {466putchar('"');467return (1);468}469if (!isascii(c)) {470c = toascii(c);471putchar('M');472putchar('-');473}474if (!isprint(c)) {475c ^= 0x40; /* DEL to ?, others to alpha */476putchar('^');477}478putchar(c);479}480putchar('"');481return (0);482}483484/*485* Convert an IP addr to a string.486* (like inet_ntoa, but ina is a pointer)487*/488char *489ipaddr_string(struct in_addr *ina)490{491static char b[24];492u_char *p;493494p = (u_char *) ina;495snprintf(b, sizeof(b), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);496return (b);497}498499/*500* Local Variables:501* tab-width: 4502* c-indent-level: 4503* c-argdecl-indent: 4504* c-continued-statement-offset: 4505* c-continued-brace-offset: -4506* c-label-offset: -4507* c-brace-offset: 0508* End:509*/510511512