bootptest.c revision 1.4 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 (at) 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