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