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