Patches from "Alan Curry" <pacman-radius@cqc.com>
[freeradius.git] / src / main / radclient.c
1 /*
2  * radclient    General radius packet debug tool.
3  *
4  * Version:     $Id$
5  *
6  */
7 static const char rcsid[] = "$Id$";
8
9 #include        "autoconf.h"
10
11 #include        <sys/types.h>
12 #include        <stdio.h>
13 #include        <stdlib.h>
14 #include        <unistd.h>
15 #include        <string.h>
16 #include        <ctype.h>
17 #include        <netdb.h>
18 #include        <sys/socket.h>
19 #include        <netinet/in.h>
20 #include        <errno.h>
21
22 #if HAVE_SYS_SELECT_H
23 #  include      <sys/select.h>
24 #endif
25
26 #if HAVE_ERRNO_H
27 #  include      <errno.h>
28 #endif
29
30 #if HAVE_GETOPT_H
31 #  include      <getopt.h>
32 #endif
33
34 #include        "conf.h"
35 #include        "libradius.h"
36 #include        "radpaths.h"
37
38 static int              retries = 10;
39 static float            timeout = 3;
40 static const char       *secret = "secret";
41 static int              do_output = 1;
42
43 /*
44  *      Read valuepairs from the fp up to End-Of-File.
45  */
46 static VALUE_PAIR *readvp(FILE *fp)
47 {
48         char            buf[128];
49         int             last_token;
50         char            *p;
51         VALUE_PAIR      *vp;
52         VALUE_PAIR      *list;
53         int             error = 0;
54
55         list = NULL;
56
57         while (!error && fgets(buf, sizeof(buf), fp) != NULL) {
58
59                 p = buf;
60                 do {
61                         if ((vp = pairread(&p, &last_token)) == NULL) {
62                                 librad_perror("radclient:");
63                                 error = 1;
64                                 break;
65                         }
66                         pairadd(&list, vp);
67                 } while (last_token == T_COMMA);
68         }
69         return error ? NULL: list;
70 }
71
72 static void usage(void)
73 {
74         fprintf(stderr, "Usage: radclient [-c count] [-d raddb] [-f file] [-r retries] [-t timeout]\n           [-i id] [-qvx] server acct|auth <secret>\n");
75         
76         fprintf(stderr, " -c count    Send 'count' packets.\n");
77         fprintf(stderr, " -d raddb    Set dictionary directory.\n");
78         fprintf(stderr, " -f file     Read packets from file, not stdin.\n");
79         fprintf(stderr, " -r retries  If timeout, retry sending the packet 'retires' times.\n");
80         fprintf(stderr, " -t timeout  Wait 'timeout' seconds before retrying.\n");
81         fprintf(stderr, " -i id       Set request id to 'id'.  Values may be 0..255\n");
82         fprintf(stderr, " -q          Do not print anything out.\n");
83         fprintf(stderr, " -v          Show program version information.\n");
84         fprintf(stderr, " -x          Debugging mode.\n");
85
86         exit(1);
87 }
88
89 static int getport(const char *name)
90 {
91         struct  servent         *svp;
92
93         svp = getservbyname (name, "udp");
94         if (!svp) {
95                 return 0;
96         }
97
98         return ntohs(svp->s_port);
99 }
100
101 static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
102 {
103         int i;
104         struct timeval  tv;
105
106         for (i = 0; i < retries; i++) {
107                 fd_set          rdfdesc;
108
109                 rad_send(req, secret);
110
111                 /* And wait for reply, timing out as necessary */
112                 FD_ZERO(&rdfdesc);
113                 FD_SET(req->sockfd, &rdfdesc);
114
115                 tv.tv_sec = (int)timeout;
116                 tv.tv_usec = 1000000 * (timeout - (int)timeout);
117
118                 /* Something's wrong if we don't get exactly one fd. */
119                 if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) {
120                         continue;
121                 }
122
123                 *rep = rad_recv(req->sockfd);
124                 if (*rep != NULL) {
125                         break;
126                 } else {        /* NULL: couldn't receive the packet */
127                         librad_perror("radclient:");
128                         exit(1);
129                 }
130         }
131
132         /* No response or no data read (?) */
133         if (i == retries) {
134                 fprintf(stderr, "radclient: no response from server\n");
135                 exit(1);
136         }
137
138         if (rad_decode(*rep, req, secret) != 0) {
139                 librad_perror("rad_decode");
140                 exit(1);
141         }
142
143         /* libradius debug already prints out the value pairs for us */
144         if (!librad_debug && do_output) {
145                 printf("Received response ID %d, code %d, length = %d\n",
146                        (*rep)->id, (*rep)->code, (*rep)->data_len);
147                 vp_printlist(stdout, (*rep)->vps);
148         }
149
150         return 0;
151 }
152
153 int main(int argc, char **argv)
154 {
155         RADIUS_PACKET   *req;
156         RADIUS_PACKET   *rep = NULL;
157         char            *p;
158         int             c;
159         int             port = 0;
160         const char      *radius_dir = RADDBDIR;
161         char            *filename = NULL;
162         FILE            *fp;
163         int             count = 1;
164         int             loop;
165         char            password[256];
166         VALUE_PAIR      *vp;
167         int             id;
168
169         id = ((int)getpid() & 0xff);
170
171         while ((c = getopt(argc, argv, "c:d:f:hi:qt:r:xv")) != EOF) switch(c) {
172                 case 'c':
173                         if (!isdigit(*optarg)) usage();
174                         count = atoi(optarg);
175                         break;
176                 case 'd':
177                         radius_dir = optarg;
178                         break;
179                 case 'f':
180                         filename = optarg;
181                         break;
182                 case 'q':
183                         do_output = 0;
184                         break;
185                 case 'x':
186                         librad_debug = 1;
187                         break;
188                 case 'r':
189                         if (!isdigit(*optarg)) usage();
190                         retries = atoi(optarg);
191                         break;
192                 case 'i':
193                         if (!isdigit(*optarg)) usage();
194                         id = atoi(optarg);
195                         if ((id < 0) || (id > 255)) {
196                                 usage();
197                         }
198                         break;
199                 case 't':
200                         if (!isdigit(*optarg)) usage();
201                         timeout = atof(optarg);
202                         break;
203                 case 'v':
204                         printf("radclient: $Id$ built on " __DATE__ "\n");
205                         exit(0);
206                         break;
207                 case 'h':
208                 default:
209                         usage();
210                         break;
211         }
212         argc -= (optind - 1);
213         argv += (optind - 1);
214
215         if (argc < 4) {
216                 usage();
217         }
218
219         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
220                 librad_perror("radclient");
221                 return 1;
222         }
223
224         if ((req = rad_alloc(1)) == NULL) {
225                 librad_perror("radclient");
226                 exit(1);
227         }
228
229         req->id = id;
230
231         /*
232          *      Strip port from hostname if needed.
233          */
234         if ((p = strchr(argv[1], ':')) != NULL) {
235                 *p++ = 0;
236                 port = atoi(p);
237         }
238
239         /*
240          *      See what kind of request we want to send.
241          */
242         if (strcmp(argv[2], "auth") == 0) {
243                 if (port == 0) port = getport("radius");
244                 if (port == 0) port = PW_AUTH_UDP_PORT;
245                 req->code = PW_AUTHENTICATION_REQUEST;
246         } else if (strcmp(argv[2], "acct") == 0) {
247                 if (port == 0) port = getport("radacct");
248                 if (port == 0) port = PW_ACCT_UDP_PORT;
249                 req->code = PW_ACCOUNTING_REQUEST;
250         } else if (isdigit(argv[2][0])) {
251                 if (port == 0) port = PW_AUTH_UDP_PORT;
252                 port = atoi(argv[2]);
253         } else {
254                 usage();
255         }
256
257         /*
258          *      Resolve hostname.
259          */
260         req->dst_port = port;
261         req->dst_ipaddr = ip_getaddr(argv[1]);
262         if (req->dst_ipaddr == 0) {
263                 librad_perror("radclient: %s: ", argv[1]);
264                 exit(1);
265         }
266
267         /*
268          *      Add the secret.
269          */
270         if (argv[3]) secret = argv[3];
271
272         /*
273          *      Read valuepairs.
274          *      Maybe read them, from stdin, if there's no
275          *      filename, or if the filename is '-'.
276          */
277         if (filename && (strcmp(filename, "-") != 0)) {
278                 fp = fopen(filename, "r");
279                 if (!fp) {
280                         fprintf(stderr, "radclient: Error opening %s: %s\n",
281                                 filename, strerror(errno));
282                         exit(1);
283                 }
284         } else {
285                 fp = stdin;
286         }
287
288         if ((req->vps = readvp(fp)) == NULL) {
289                 exit(1);
290         }
291
292         /*
293          *      Send request.
294          */
295         if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
296                 perror("radclient: socket: ");
297                 exit(1);
298         }
299
300         vp = pairfind(req->vps, PW_PASSWORD);
301         if (vp) {
302           strNcpy(password, (char *)vp->strvalue, sizeof(vp->length));
303         } else {
304           *password = '\0';
305         }
306
307         /*
308          *      Loop, sending the packet N times.
309          */
310         for (loop = 0; loop < count; loop++) {
311                 req->id++;
312
313                 /*
314                  *      If we've already sent a packet, free up the old
315                  *      one, and ensure that the next packet has a unique
316                  *      ID and authentication vector.
317                  */
318                 if (req->data) {
319                         free(req->data);
320                         req->data = NULL;
321                         if (*password != '\0') {
322                           vp = pairfind(req->vps, PW_PASSWORD);
323                           if (vp) {
324                             strNcpy((char *)vp->strvalue, password,
325                                     sizeof(password));
326                             vp->length = strlen(password);
327                           }
328                         }
329                         
330                         librad_md5_calc(req->vector, req->vector,
331                                         sizeof(req->vector));
332                 }
333                 send_packet(req, &rep);
334                 rad_free(rep);
335                 rep = NULL;
336         }
337
338         return 0;
339 }
340