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