xref: /netbsd-src/usr.sbin/bootp/bootptest/bootptest.c (revision 3fe138c1461e710931a50b66f08982c5f52c371f)
1 /*	$NetBSD: bootptest.c,v 1.4 1998/01/09 08:09:03 perry Exp $	*/
2 
3 /*
4  * bootptest.c - Test out a bootp server.
5  *
6  * This simple program was put together from pieces taken from
7  * various places, including the CMU BOOTP client and server.
8  * The packet printing routine is from the Berkeley "tcpdump"
9  * program with some enhancements I added.  The print-bootp.c
10  * file was shared with my copy of "tcpdump" and therefore uses
11  * some unusual utility routines that would normally be provided
12  * by various parts of the tcpdump program.  Gordon W. Ross
13  *
14  * Boilerplate:
15  *
16  * This program includes software developed by the University of
17  * California, Lawrence Berkeley Laboratory and its contributors.
18  * (See the copyright notice in print-bootp.c)
19  *
20  * The remainder of this program is public domain.  You may do
21  * whatever you like with it except claim that you wrote it.
22  *
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
24  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  *
27  * HISTORY:
28  *
29  * 12/02/93 Released version 1.4 (with bootp-2.3.2)
30  * 11/05/93 Released version 1.3
31  * 10/14/93 Released version 1.2
32  * 10/11/93 Released version 1.1
33  * 09/28/93 Released version 1.0
34  * 09/93 Original developed by Gordon W. Ross <gwr@mc.com>
35  */
36 
37 char *usage = "bootptest [-h] server-name [vendor-data-template-file]";
38 
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/ioctl.h>
42 #include <sys/file.h>
43 #include <sys/time.h>
44 #include <sys/stat.h>
45 
46 #include <net/if.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>			/* inet_ntoa */
49 
50 #include <stdlib.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <errno.h>
55 #include <ctype.h>
56 #include <netdb.h>
57 #include <assert.h>
58 
59 #include "bootp.h"
60 #include "bootptest.h"
61 #include "getif.h"
62 #include "patchlevel.h"
63 
64 #define LOG_ERR 1
65 #define BUFLEN 1024
66 #define WAITSECS 1
67 #define MAXWAIT  10
68 
69 int vflag = 1;
70 int tflag = 0;
71 int thiszone;
72 char *progname;
73 unsigned char *packetp;
74 unsigned char *snapend;
75 int snaplen;
76 
77 
78 /*
79  * IP port numbers for client and server obtained from /etc/services
80  */
81 
82 u_short bootps_port, bootpc_port;
83 
84 
85 /*
86  * Internet socket and interface config structures
87  */
88 
89 struct sockaddr_in sin_server;	/* where to send requests */
90 struct sockaddr_in sin_client;	/* for bind and listen */
91 struct sockaddr_in sin_from;	/* Packet source */
92 u_char eaddr[16];				/* Ethernet address */
93 
94 /*
95  * General
96  */
97 
98 int debug = 1;					/* Debugging flag (level) */
99 char hostname[64];
100 char *sndbuf;					/* Send packet buffer */
101 char *rcvbuf;					/* Receive packet buffer */
102 
103 /*
104  * Vendor magic cookies for CMU and RFC1048
105  */
106 
107 unsigned char vm_cmu[4] = VM_CMU;
108 unsigned char vm_rfc1048[4] = VM_RFC1048;
109 short secs;						/* How long client has waited */
110 
111 char *get_errmsg();
112 extern void bootp_print();
113 
114 /*
115  * Initialization such as command-line processing is done, then
116  * the receiver loop is started.  Die when interrupted.
117  */
118 
119 main(argc, argv)
120 	int argc;
121 	char **argv;
122 {
123 	struct bootp *bp;
124 	struct servent *sep;
125 	struct hostent *hep;
126 
127 	char *servername = NULL;
128 	char *vendor_file = NULL;
129 	char *bp_file = NULL;
130 	int s;				/* Socket file descriptor */
131 	int n, tolen, fromlen, recvcnt;
132 	int use_hwa = 0;
133 	int32 vend_magic;
134 	int32 xid;
135 
136 	progname = strrchr(argv[0], '/');
137 	if (progname)
138 		progname++;
139 	else
140 		progname = argv[0];
141 	argc--;
142 	argv++;
143 
144 	if (debug)
145 		printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL);
146 
147 	/*
148 	 * Verify that "struct bootp" has the correct official size.
149 	 * (Catch evil compilers that do struct padding.)
150 	 */
151 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
152 
153 	sndbuf = malloc(BUFLEN);
154 	rcvbuf = malloc(BUFLEN);
155 	if (!sndbuf || !rcvbuf) {
156 		printf("malloc failed\n");
157 		exit(1);
158 	}
159 
160 	/* default magic number */
161 	bcopy(vm_rfc1048, (char*)&vend_magic, 4);
162 
163 	/* Handle option switches. */
164 	while (argc > 0) {
165 		if (argv[0][0] != '-')
166 			break;
167 		switch (argv[0][1]) {
168 
169 		case 'f':				/* File name to reqest. */
170 			if (argc < 2)
171 				goto error;
172 			argc--; argv++;
173 			bp_file = *argv;
174 			break;
175 
176 		case 'h':				/* Use hardware address. */
177 			use_hwa = 1;
178 			break;
179 
180 		case 'm':				/* Magic number value. */
181 			if (argc < 2)
182 				goto error;
183 			argc--; argv++;
184 			vend_magic = inet_addr(*argv);
185 			break;
186 
187 		error:
188 		default:
189 			puts(usage);
190 			exit(1);
191 
192 		}
193 		argc--;
194 		argv++;
195 	}
196 
197 	/* Get server name (or address) for query. */
198 	if (argc > 0) {
199 		servername = *argv;
200 		argc--;
201 		argv++;
202 	}
203 	/* Get optional vendor-data-template-file. */
204 	if (argc > 0) {
205 		vendor_file = *argv;
206 		argc--;
207 		argv++;
208 	}
209 	if (!servername) {
210 		printf("missing server name.\n");
211 		puts(usage);
212 		exit(1);
213 	}
214 	/*
215 	 * Create a socket.
216 	 */
217 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
218 		perror("socket");
219 		exit(1);
220 	}
221 	/*
222 	 * Get server's listening port number
223 	 */
224 	sep = getservbyname("bootps", "udp");
225 	if (sep) {
226 		bootps_port = ntohs((u_short) sep->s_port);
227 	} else {
228 		fprintf(stderr, "udp/bootps: unknown service -- using port %d\n",
229 				IPPORT_BOOTPS);
230 		bootps_port = (u_short) IPPORT_BOOTPS;
231 	}
232 
233 	/*
234 	 * Set up server socket address (for send)
235 	 */
236 	if (servername) {
237 		if (inet_aton(servername, &sin_server.sin_addr) == 0) {
238 			hep = gethostbyname(servername);
239 			if (!hep) {
240 				fprintf(stderr, "%s: unknown host\n", servername);
241 				exit(1);
242 			}
243 			memcpy(&sin_server.sin_addr, hep->h_addr,
244 			    sizeof(sin_server.sin_addr));
245 		}
246 	} else {
247 		/* Get broadcast address */
248 		/* XXX - not yet */
249 		sin_server.sin_addr.s_addr = INADDR_ANY;
250 	}
251 	sin_server.sin_family = AF_INET;
252 	sin_server.sin_port = htons(bootps_port);
253 
254 	/*
255 	 * Get client's listening port number
256 	 */
257 	sep = getservbyname("bootpc", "udp");
258 	if (sep) {
259 		bootpc_port = ntohs(sep->s_port);
260 	} else {
261 		fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n",
262 				IPPORT_BOOTPC);
263 		bootpc_port = (u_short) IPPORT_BOOTPC;
264 	}
265 
266 	/*
267 	 * Set up client socket address (for listen)
268 	 */
269 	sin_client.sin_family = AF_INET;
270 	sin_client.sin_port = htons(bootpc_port);
271 	sin_client.sin_addr.s_addr = INADDR_ANY;
272 
273 	/*
274 	 * Bind client socket to BOOTPC port.
275 	 */
276 	if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) {
277 		perror("bind BOOTPC port");
278 		if (errno == EACCES)
279 			fprintf(stderr, "You need to run this as root\n");
280 		exit(1);
281 	}
282 	/*
283 	 * Build a request.
284 	 */
285 	bp = (struct bootp *) sndbuf;
286 	bzero(bp, sizeof(*bp));
287 	bp->bp_op = BOOTREQUEST;
288 	xid = (int32) getpid();
289 	bp->bp_xid = (u_int32) htonl(xid);
290 	if (bp_file)
291 		strncpy(bp->bp_file, bp_file, BP_FILE_LEN);
292 
293 	/*
294 	 * Fill in the hardware address (or client IP address)
295 	 */
296 	if (use_hwa) {
297 		struct ifreq *ifr;
298 
299 		ifr = getif(s, &sin_server.sin_addr);
300 		if (!ifr) {
301 			printf("No interface for %s\n", servername);
302 			exit(1);
303 		}
304 		if (getether(ifr->ifr_name, eaddr)) {
305 			printf("Can not get ether addr for %s\n", ifr->ifr_name);
306 			exit(1);
307 		}
308 		/* Copy Ethernet address into request packet. */
309 		bp->bp_htype = 1;
310 		bp->bp_hlen = 6;
311 		bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen);
312 	} else {
313 		/* Fill in the client IP address. */
314 		gethostname(hostname, sizeof(hostname));
315 		hep = gethostbyname(hostname);
316 		if (!hep) {
317 			printf("Can not get my IP address\n");
318 			exit(1);
319 		}
320 		bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length);
321 	}
322 
323 	/*
324 	 * Copy in the default vendor data.
325 	 */
326 	bcopy((char*)&vend_magic, bp->bp_vend, 4);
327 	if (vend_magic)
328 		bp->bp_vend[4] = TAG_END;
329 
330 	/*
331 	 * Read in the "options" part of the request.
332 	 * This also determines the size of the packet.
333 	 */
334 	snaplen = sizeof(*bp);
335 	if (vendor_file) {
336 		int fd = open(vendor_file, 0);
337 		if (fd < 0) {
338 			perror(vendor_file);
339 			exit(1);
340 		}
341 		/* Compute actual space for options. */
342 		n = BUFLEN - sizeof(*bp) + BP_VEND_LEN;
343 		n = read(fd, bp->bp_vend, n);
344 		close(fd);
345 		if (n < 0) {
346 			perror(vendor_file);
347 			exit(1);
348 		}
349 		printf("read %d bytes of vendor template\n", n);
350 		if (n > BP_VEND_LEN) {
351 			printf("warning: extended options in use (len > %d)\n",
352 				   BP_VEND_LEN);
353 			snaplen += (n - BP_VEND_LEN);
354 		}
355 	}
356 	/*
357 	 * Set globals needed by print_bootp
358 	 * (called by send_request)
359 	 */
360 	packetp = (unsigned char *) eaddr;
361 	snapend = (unsigned char *) sndbuf + snaplen;
362 
363 	/* Send a request once per second while waiting for replies. */
364 	recvcnt = 0;
365 	bp->bp_secs = secs = 0;
366 	send_request(s);
367 	while (1) {
368 		struct timeval tv;
369 		int readfds;
370 
371 		tv.tv_sec = WAITSECS;
372 		tv.tv_usec = 0L;
373 		readfds = (1 << s);
374 		n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv);
375 		if (n < 0) {
376 			perror("select");
377 			break;
378 		}
379 		if (n == 0) {
380 			/*
381 			 * We have not received a response in the last second.
382 			 * If we have ever received any responses, exit now.
383 			 * Otherwise, bump the "wait time" field and re-send.
384 			 */
385 			if (recvcnt > 0)
386 				exit(0);
387 			secs += WAITSECS;
388 			if (secs > MAXWAIT)
389 				break;
390 			bp->bp_secs = htons(secs);
391 			send_request(s);
392 			continue;
393 		}
394 		fromlen = sizeof(sin_from);
395 		n = recvfrom(s, rcvbuf, BUFLEN, 0,
396 					 (struct sockaddr *) &sin_from, &fromlen);
397 		if (n <= 0) {
398 			continue;
399 		}
400 		if (n < sizeof(struct bootp)) {
401 			printf("received short packet\n");
402 			continue;
403 		}
404 		recvcnt++;
405 
406 		/* Print the received packet. */
407 		printf("Recvd from %s", inet_ntoa(sin_from.sin_addr));
408 		/* set globals needed by bootp_print() */
409 		snaplen = n;
410 		snapend = (unsigned char *) rcvbuf + snaplen;
411 		bootp_print(rcvbuf, n, sin_from.sin_port, 0);
412 		putchar('\n');
413 		/*
414 		 * This no longer exits immediately after receiving
415 		 * one response because it is useful to know if the
416 		 * client might get multiple responses.  This code
417 		 * will now listen for one second after a response.
418 		 */
419 	}
420 	fprintf(stderr, "no response from %s\n", servername);
421 	exit(1);
422 }
423 
424 send_request(s)
425 	int s;
426 {
427 	/* Print the request packet. */
428 	printf("Sending to %s", inet_ntoa(sin_server.sin_addr));
429 	bootp_print(sndbuf, snaplen, sin_from.sin_port, 0);
430 	putchar('\n');
431 
432 	/* Send the request packet. */
433 	if (sendto(s, sndbuf, snaplen, 0,
434 			   (struct sockaddr *) &sin_server,
435 			   sizeof(sin_server)) < 0)
436 	{
437 		perror("sendto server");
438 		exit(1);
439 	}
440 }
441 
442 /*
443  * Print out a filename (or other ascii string).
444  * Return true if truncated.
445  */
446 int
447 printfn(s, ep)
448 	register u_char *s, *ep;
449 {
450 	register u_char c;
451 
452 	putchar('"');
453 	while (c = *s++) {
454 		if (s > ep) {
455 			putchar('"');
456 			return (1);
457 		}
458 		if (!isascii(c)) {
459 			c = toascii(c);
460 			putchar('M');
461 			putchar('-');
462 		}
463 		if (!isprint(c)) {
464 			c ^= 0x40;			/* DEL to ?, others to alpha */
465 			putchar('^');
466 		}
467 		putchar(c);
468 	}
469 	putchar('"');
470 	return (0);
471 }
472 
473 /*
474  * Convert an IP addr to a string.
475  * (like inet_ntoa, but ina is a pointer)
476  */
477 char *
478 ipaddr_string(ina)
479 	struct in_addr *ina;
480 {
481 	static char b[24];
482 	u_char *p;
483 
484 	p = (u_char *) ina;
485 	sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
486 	return (b);
487 }
488 
489 /*
490  * Local Variables:
491  * tab-width: 4
492  * c-indent-level: 4
493  * c-argdecl-indent: 4
494  * c-continued-statement-offset: 4
495  * c-continued-brace-offset: -4
496  * c-label-offset: -4
497  * c-brace-offset: 0
498  * End:
499  */
500