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