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