New build path variable
[freeradius.git] / src / main / dhclient.c
1 /*
2  * dhclient.c   General radius packet debug tool.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2010  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/libradius.h>
29 #include <freeradius-devel/conf.h>
30 #include <freeradius-devel/radpaths.h>
31 #include <freeradius-devel/dhcp.h>
32
33 #ifdef WITH_DHCP
34
35 #include <ctype.h>
36
37 #ifdef HAVE_GETOPT_H
38 #       include <getopt.h>
39 #endif
40
41 #include <assert.h>
42
43 static int success = 0;
44 static int retries = 3;
45 static float timeout = 5;
46
47 static int server_port = 0;
48 static int packet_code = 0;
49 static fr_ipaddr_t server_ipaddr;
50 static int resend_count = 1;
51
52 static fr_ipaddr_t client_ipaddr;
53 static int client_port = 0;
54
55 static int sockfd;
56
57 static int sleep_time = -1;
58
59 static RADIUS_PACKET *request = NULL;
60 static RADIUS_PACKET *reply = NULL;
61
62 #define DHCP_CHADDR_LEN (16)
63 #define DHCP_SNAME_LEN  (64)
64 #define DHCP_FILE_LEN   (128)
65 #define DHCP_VEND_LEN   (308)
66 #define DHCP_OPTION_MAGIC_NUMBER (0x63825363)
67
68 static void NEVER_RETURNS usage(void)
69 {
70         fprintf(stderr, "Usage: dhclient [options] server[:port] <command>\n");
71
72         fprintf(stderr, "  <command>    One of discover, request\n");
73         fprintf(stderr, "  -c count    Send each packet 'count' times.\n");
74         fprintf(stderr, "  -d raddb    Set dictionary directory.\n");
75         fprintf(stderr, "  -f file     Read packets from file, not stdin.\n");
76         fprintf(stderr, "  -r retries  If timeout, retry sending the packet 'retries' times.\n");
77         fprintf(stderr, "  -v          Show program version information.\n");
78         fprintf(stderr, "  -x          Debugging mode.\n");
79
80         exit(1);
81 }
82
83
84 /*
85  *      Initialize the request.
86  */
87 static int request_init(const char *filename)
88 {
89         FILE *fp;
90         VALUE_PAIR *vp;
91         int filedone = 0;
92
93         /*
94          *      Determine where to read the VP's from.
95          */
96         if (filename) {
97                 fp = fopen(filename, "r");
98                 if (!fp) {
99                         fprintf(stderr, "dhclient: Error opening %s: %s\n",
100                                 filename, strerror(errno));
101                         return 0;
102                 }
103         } else {
104                 fp = stdin;
105         }
106
107         request = rad_alloc(0);
108
109         /*
110          *      Read the VP's.
111          */
112         request->vps = readvp2(fp, &filedone, "dhclient:");
113         if (!request->vps) {
114                 rad_free(&request);
115                 if (fp != stdin) fclose(fp);
116                 return 1;
117         }
118
119         /*
120          *      Fix / set various options
121          */
122         for (vp = request->vps; vp != NULL; vp = vp->next) {
123                 switch (vp->attribute) {
124                 default:
125                         break;
126                         
127                         /*
128                          *      Allow it to set the packet type in
129                          *      the attributes read from the file.
130                          */
131                 case PW_PACKET_TYPE:
132                         request->code = vp->vp_integer;
133                         break;
134                         
135                 case PW_PACKET_DST_PORT:
136                         request->dst_port = (vp->vp_integer & 0xffff);
137                         break;
138                         
139                 case PW_PACKET_DST_IP_ADDRESS:
140                         request->dst_ipaddr.af = AF_INET;
141                         request->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
142                         break;
143                         
144                 case PW_PACKET_DST_IPV6_ADDRESS:
145                         request->dst_ipaddr.af = AF_INET6;
146                         request->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
147                         break;
148                         
149                 case PW_PACKET_SRC_PORT:
150                         request->src_port = (vp->vp_integer & 0xffff);
151                         break;
152                         
153                 case PW_PACKET_SRC_IP_ADDRESS:
154                         request->src_ipaddr.af = AF_INET;
155                         request->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
156                         break;
157                         
158                 case PW_PACKET_SRC_IPV6_ADDRESS:
159                         request->src_ipaddr.af = AF_INET6;
160                         request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
161                         break;
162                 } /* switch over the attribute */
163                         
164         } /* loop over the VP's we read in */
165
166         if (fp != stdin) fclose(fp);
167
168         /*
169          *      And we're done.
170          */
171         return 1;
172 }
173
174 static const char *dhcp_header_names[] = {
175         "DHCP-Opcode",
176         "DHCP-Hardware-Type",
177         "DHCP-Hardware-Address-Length",
178         "DHCP-Hop-Count",
179         "DHCP-Transaction-Id",
180         "DHCP-Number-of-Seconds",
181         "DHCP-Flags",
182         "DHCP-Client-IP-Address",
183         "DHCP-Your-IP-Address",
184         "DHCP-Server-IP-Address",
185         "DHCP-Gateway-IP-Address",
186         "DHCP-Client-Hardware-Address",
187         "DHCP-Server-Host-Name",
188         "DHCP-Boot-Filename",
189
190         NULL
191 };
192
193 static int dhcp_header_sizes[] = {
194         1, 1, 1, 1,
195         4, 2, 2, 4,
196         4, 4, 4,
197         DHCP_CHADDR_LEN,
198         DHCP_SNAME_LEN,
199         DHCP_FILE_LEN
200 };
201
202
203 static void print_hex(RADIUS_PACKET *packet)
204 {
205         int i, j;
206         const uint8_t *p, *a;
207
208         if (!packet->data) return;
209
210         if (packet->data_len < 244) {
211                 printf("Huh?\n");
212                 return;
213         }
214
215         printf("----------------------------------------------------------------------\n");
216         fflush(stdout);
217
218         p = packet->data;
219         for (i = 0; i < 14; i++) {
220                 printf("%s = 0x", dhcp_header_names[i]);
221                 for (j = 0; j < dhcp_header_sizes[i]; j++) {
222                         printf("%02x", p[j]);
223                         
224                 }
225                 printf("\n");
226                 p += dhcp_header_sizes[i];
227         }
228
229         /*
230          *      Magic number
231          */
232         printf("%02x %02x %02x %02x\n",
233                p[0], p[1], p[2], p[3]);
234         p += 4;
235
236         while (p < (packet->data + packet->data_len)) {
237
238                 if (*p == 0) break;
239                 if (*p == 255) break; /* end of options signifier */
240                 if ((p + 2) > (packet->data + packet->data_len)) break;
241
242                 printf("%02x  %02x  ", p[0], p[1]);
243                 a = p + 2;
244                 
245                 for (i = 0; i < p[1]; i++) {
246                         if ((i > 0) && ((i & 0x0f) == 0x00))
247                                 printf("\t\t");
248                         printf("%02x ", a[i]);
249                         if ((i & 0x0f) == 0x0f) printf("\n");
250                 }
251                 
252                 if ((p[1] & 0x0f) != 0x00) printf("\n");
253                 
254                 p += p[1] + 2;
255         }
256         printf("\n----------------------------------------------------------------------\n");
257         fflush(stdout);
258 }
259
260 int main(int argc, char **argv)
261 {
262         char *p;
263         int c;
264         const char *radius_dir = RADDBDIR;
265         const char *filename = NULL;
266
267         fr_debug_flag = 0;
268
269         while ((c = getopt(argc, argv, "d:f:hr:t:vx")) != EOF) switch(c) {
270                 case 'd':
271                         radius_dir = optarg;
272                         break;
273                 case 'f':
274                         filename = optarg;
275                         break;
276                 case 'r':
277                         if (!isdigit((int) *optarg))
278                                 usage();
279                         retries = atoi(optarg);
280                         if ((retries == 0) || (retries > 1000)) usage();
281                         break;
282                 case 't':
283                         if (!isdigit((int) *optarg))
284                                 usage();
285                         timeout = atof(optarg);
286                         break;
287                 case 'v':
288                         printf("dhclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
289                         exit(0);
290                         break;
291                 case 'x':
292                         fr_debug_flag++;
293                         fr_log_fp = stdout;
294                         break;
295                 case 'h':
296                 default:
297                         usage();
298                         break;
299         }
300         argc -= (optind - 1);
301         argv += (optind - 1);
302
303         if (argc < 2) usage();
304
305         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
306                 fr_perror("dhclient");
307                 return 1;
308         }
309
310         /*
311          *      Resolve hostname.
312          */
313         server_ipaddr.af = AF_INET;
314         if (strcmp(argv[1], "-") != 0) {
315                 const char *hostname = argv[1];
316                 const char *portname = argv[1];
317                 char buffer[256];
318
319                 if (*argv[1] == '[') { /* IPv6 URL encoded */
320                         p = strchr(argv[1], ']');
321                         if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
322                                 usage();
323                         }
324
325                         memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
326                         buffer[p - argv[1] - 1] = '\0';
327
328                         hostname = buffer;
329                         portname = p + 1;
330
331                 }
332                 p = strchr(portname, ':');
333                 if (p && (strchr(p + 1, ':') == NULL)) {
334                         *p = '\0';
335                         portname = p + 1;
336                 } else {
337                         portname = NULL;
338                 }
339
340                 if (ip_hton(hostname, AF_INET, &server_ipaddr) < 0) {
341                         fprintf(stderr, "dhclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
342                         exit(1);
343                 }
344
345                 /*
346                  *      Strip port from hostname if needed.
347                  */
348                 if (portname) server_port = atoi(portname);
349         }
350
351         /*
352          *      See what kind of request we want to send.
353          */
354         if (strcmp(argv[2], "discover") == 0) {
355                 if (server_port == 0) server_port = 67;
356                 packet_code = PW_DHCP_DISCOVER;
357
358         } else if (strcmp(argv[2], "request") == 0) {
359                 if (server_port == 0) server_port = 67;
360                 packet_code = PW_DHCP_REQUEST;
361
362         } else if (isdigit((int) argv[2][0])) {
363                 if (server_port == 0) server_port = 67;
364                 packet_code = atoi(argv[2]);
365         } else {
366                 fprintf(stderr, "Unknown packet type %s\n", argv[2]);
367                 usage();
368         }
369
370         request_init(filename);
371
372         /*
373          *      No data read.  Die.
374          */
375         if (!request || !request->vps) {
376                 fprintf(stderr, "dhclient: Nothing to send.\n");
377                 exit(1);
378         }
379         request->code = packet_code;
380
381         /*
382          *      Bind to the first specified IP address and port.
383          *      This means we ignore later ones.
384          */
385         if (request->src_ipaddr.af == AF_UNSPEC) {
386                 memset(&client_ipaddr, 0, sizeof(client_ipaddr));
387                 client_ipaddr.af = server_ipaddr.af;
388                 client_port = 0;
389         } else {
390                 client_ipaddr = request->src_ipaddr;
391                 client_port = request->src_port;
392         }
393         sockfd = fr_socket(&client_ipaddr, client_port);
394         if (sockfd < 0) {
395                 fprintf(stderr, "dhclient: socket: %s\n", fr_strerror());
396                 exit(1);
397         }
398
399         request->sockfd = sockfd;
400         if (request->src_ipaddr.af == AF_UNSPEC) {
401                 request->src_ipaddr = client_ipaddr;
402                 request->src_port = client_port;
403         }
404         if (request->dst_ipaddr.af == AF_UNSPEC) {
405                 request->dst_ipaddr = server_ipaddr;
406                 request->dst_port = server_port;
407         }
408
409         /*
410          *      Encode the packet
411          */
412         if (fr_dhcp_encode(request, NULL) < 0) {
413                 fprintf(stderr, "dhclient: failed encoding: %s\n",
414                         fr_strerror());
415                 exit(1);
416         }
417         if (fr_debug_flag) print_hex(request);
418         
419         if (fr_dhcp_send(request) < 0) {
420                 fprintf(stderr, "dhclient: failed sending: %s\n",
421                         strerror(errno));
422                 exit(1);
423         }
424
425         reply = fr_dhcp_recv(sockfd);
426         if (!reply) {
427                 fprintf(stderr, "dhclient: no reply\n");
428                 exit(1);
429         }
430         if (fr_debug_flag) print_hex(reply);
431
432         if (fr_dhcp_decode(reply) < 0) {
433                 fprintf(stderr, "dhclient: failed decoding\n");
434                 return 1;
435         }
436
437         dict_free();
438
439         if (success) return 0;
440
441         return 1;
442 }
443
444 #endif  /* WITH_DHCP */