2 * radclient.c General radius packet debug tool.
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.
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.
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
20 * Copyright 2000,2006 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2000 Alan DeKok <aland@ox.org>
27 #include <freeradius-devel/libradius.h>
28 #include <freeradius-devel/radpaths.h>
29 #include <freeradius-devel/conf.h>
39 typedef struct REQUEST REQUEST; /* to shut up warnings about mschap.h */
44 static int retries = 3;
45 static float timeout = 5;
46 static char const *secret = NULL;
47 static bool do_output = true;
49 typedef struct rc_stats {
50 uint64_t accepted; //!< Requests to which we received a accept
51 uint64_t rejected; //!< Requests to which we received a reject
52 uint64_t lost; //!< Requests to which we received no response
53 uint64_t passed; //!< Requests which passed a filter
54 uint64_t failed; //!< Requests which failed a fitler
57 static rc_stats_t stats;
59 static int server_port = 0;
60 static int packet_code = 0;
61 static fr_ipaddr_t server_ipaddr;
62 static int resend_count = 1;
63 static bool done = true;
64 static bool print_filename = false;
66 static fr_ipaddr_t client_ipaddr;
67 static int client_port = 0;
70 static int last_used_id = -1;
73 char const *proto = NULL;
75 static int ipproto = IPPROTO_UDP;
77 static rbtree_t *filename_tree = NULL;
78 static fr_packet_list_t *pl = NULL;
80 static int sleep_time = -1;
82 typedef struct rc_request rc_request_t;
84 typedef struct rc_file_pair {
85 char const *packets; //!< The file containing the request packet
86 char const *filters; //!< The file containing the definition of the
87 //!< packet we want to match.
94 rc_file_pair_t *files; //!< Request and response file names.
96 int request_number; //!< The number (within the file) of the request were reading.
101 RADIUS_PACKET *packet; //!< The outgoing request.
102 PW_CODE packet_code; //!< The code in the outgoing request.
103 RADIUS_PACKET *reply; //!< The incoming response.
104 VALUE_PAIR *filter; //!< If the reply passes the filter, then the request passes.
105 PW_CODE filter_code; //!< Expected code of the response packet.
109 bool done; //!< Whether the request is complete.
112 static rc_request_t *request_head = NULL;
113 static rc_request_t *rc_request_tail = NULL;
115 char const *radclient_version = "radclient version " RADIUSD_VERSION_STRING
116 #ifdef RADIUSD_VERSION_COMMIT
117 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
119 ", built on " __DATE__ " at " __TIME__;
121 static void NEVER_RETURNS usage(void)
123 fprintf(stderr, "Usage: radclient [options] server[:port] <command> [<secret>]\n");
125 fprintf(stderr, " <command> One of auth, acct, status, coa, or disconnect.\n");
126 fprintf(stderr, " -4 Use IPv4 address of server\n");
127 fprintf(stderr, " -6 Use IPv6 address of server.\n");
128 fprintf(stderr, " -c <count> Send each packet 'count' times.\n");
129 fprintf(stderr, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n");
130 fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
131 fprintf(stderr, " -f <file>[:<file>] Read packets from file, not stdin.\n");
132 fprintf(stderr, " If a second file is provided, it will be used to verify responses\n");
133 fprintf(stderr, " -F Print the file name, packet number and reply code.\n");
134 fprintf(stderr, " -h Print usage help information.\n");
135 fprintf(stderr, " -i <id> Set request id to 'id'. Values may be 0..255\n");
136 fprintf(stderr, " -n <num> Send N requests/s\n");
137 fprintf(stderr, " -p <num> Send 'num' packets from a file in parallel.\n");
138 fprintf(stderr, " -q Do not print anything out.\n");
139 fprintf(stderr, " -r <retries> If timeout, retry sending the packet 'retries' times.\n");
140 fprintf(stderr, " -s Print out summary information of auth results.\n");
141 fprintf(stderr, " -S <file> read secret from file, not command line.\n");
142 fprintf(stderr, " -t <timeout> Wait 'timeout' seconds before retrying (may be a floating point number).\n");
143 fprintf(stderr, " -v Show program version information.\n");
144 fprintf(stderr, " -x Debugging mode.\n");
147 fprintf(stderr, " -P <proto> Use proto (tcp or udp) for transport.\n");
154 * Free a radclient struct, which may (or may not)
155 * already be in the list.
157 static int _rc_request_free(rc_request_t *request)
159 rc_request_t *prev, *next;
161 prev = request->prev;
162 next = request->next;
165 assert(request_head != request);
167 } else if (request_head) {
168 assert(request_head == request);
173 assert(rc_request_tail != request);
175 } else if (rc_request_tail) {
176 assert(rc_request_tail == request);
177 rc_request_tail = prev;
183 static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
184 char const *password)
188 VALUE_PAIR *challenge, *reply;
191 challenge = paircreate(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT);
196 pairadd(request, challenge);
197 challenge->length = 8;
198 challenge->vp_octets = p = talloc_array(challenge, uint8_t, challenge->length);
199 for (i = 0; i < challenge->length; i++) {
203 reply = paircreate(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
208 pairadd(request, reply);
210 reply->vp_octets = p = talloc_array(reply, uint8_t, reply->length);
211 memset(p, 0, reply->length);
213 p[1] = 0x01; /* NT hash */
215 if (mschap_ntpwdhash(nthash, password) < 0) {
219 smbdes_mschap(nthash, challenge->vp_octets, p + 26);
224 * Initialize a radclient data structure and add it to
225 * the global linked list.
227 static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
229 FILE *packets, *filters = NULL;
233 rc_request_t *request;
234 bool packets_done = false;
235 int request_number = 1;
237 assert(files->packets != NULL);
240 * Determine where to read the VP's from.
242 if (strcmp(files->packets, "-") != 0) {
243 packets = fopen(files->packets, "r");
245 fr_perror("radclient: Error opening %s: %s",
246 files->packets, strerror(errno));
251 * Read in the pairs representing the expected response.
253 if (files->filters) {
254 filters = fopen(files->filters, "r");
256 fr_perror("radclient: Error opening %s: %s",
257 files->filters, strerror(errno));
268 * Loop until the file is done.
274 request = talloc_zero(ctx, rc_request_t);
276 fr_perror("radclient: Out of memory");
279 talloc_set_destructor(request, _rc_request_free);
281 request->packet = rad_alloc(request, 1);
282 if (!request->packet) {
283 fr_perror("radclient: Out of memory");
288 request->packet->src_ipaddr = client_ipaddr;
289 request->packet->src_port = client_port;
290 request->packet->dst_ipaddr = server_ipaddr;
291 request->packet->dst_port = server_port;
292 request->packet->proto = ipproto;
295 request->files = files;
296 request->packet->id = -1; /* allocate when sending */
297 request->request_number = request_number++;
300 * Read the request VP's.
302 if (readvp2(&request->packet->vps, request->packet, packets, &packets_done) < 0) {
303 fr_perror("radclient: Error parsing \"%s\"", files->filters);
307 fr_cursor_init(&cursor, &request->filter);
308 vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
310 fr_cursor_remove(&cursor);
311 request->packet_code = vp->vp_integer;
314 request->packet_code = packet_code; /* Use the default set on the command line */
318 * Read in filter VP's.
323 if (readvp2(&request->filter, request, filters, &filters_done) < 0) {
324 fr_perror("radclient: Error parsing \"%s\"", files->filters);
328 if (!request->filter) {
332 if (filters_done && !packets_done) {
333 fr_perror("radclient: Differing number of packets/filters in %s:%s "
334 "(too many requests))", files->packets, files->filters);
338 if (!filters_done && packets_done) {
339 fr_perror("radclient: Differing number of packets/filters in %s:%s "
340 "(too many filters))", files->packets, files->filters);
344 fr_cursor_init(&cursor, &request->filter);
345 vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
347 fr_cursor_remove(&cursor);
348 request->filter_code = vp->vp_integer;
353 * xlat expansions aren't supported here
355 for (vp = fr_cursor_init(&cursor, &request->filter);
357 vp = fr_cursor_next(&cursor)) {
358 if (vp->type == VT_XLAT) {
360 vp->vp_strvalue = vp->value.xlat;
365 * This allows efficient list comparisons later
367 pairsort(&request->filter, attrtagcmp);
371 * Determine the response code from the request (if not already set)
373 if (!request->filter_code) {
374 switch (request->packet_code) {
375 case PW_CODE_AUTHENTICATION_REQUEST:
376 request->filter_code = PW_CODE_AUTHENTICATION_ACK;
379 case PW_CODE_ACCOUNTING_REQUEST:
380 request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
383 case PW_CODE_COA_REQUEST:
384 request->filter_code = PW_CODE_COA_ACK;
387 case PW_CODE_DISCONNECT_REQUEST:
388 request->filter_code = PW_CODE_DISCONNECT_ACK;
397 * Keep a copy of the the User-Password attribute.
399 if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
400 strlcpy(request->password, vp->vp_strvalue,
401 sizeof(request->password));
403 * Otherwise keep a copy of the CHAP-Password attribute.
405 } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
406 strlcpy(request->password, vp->vp_strvalue,
407 sizeof(request->password));
409 } else if ((vp = pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
410 strlcpy(request->password, vp->vp_strvalue,
411 sizeof(request->password));
413 request->password[0] = '\0';
417 * Fix up Digest-Attributes issues
419 for (vp = fr_cursor_init(&cursor, &request->packet->vps);
421 vp = fr_cursor_next(&cursor)) {
423 * Double quoted strings get marked up as xlat expansions,
424 * but we don't support that in request.
426 if (vp->type == VT_XLAT) {
427 vp->vp_strvalue = vp->value.xlat;
428 vp->value.xlat = NULL;
432 if (!vp->da->vendor) switch (vp->da->attr) {
437 * Allow it to set the packet type in
438 * the attributes read from the file.
441 request->packet->code = vp->vp_integer;
444 case PW_PACKET_DST_PORT:
445 request->packet->dst_port = (vp->vp_integer & 0xffff);
448 case PW_PACKET_DST_IP_ADDRESS:
449 request->packet->dst_ipaddr.af = AF_INET;
450 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
453 case PW_PACKET_DST_IPV6_ADDRESS:
454 request->packet->dst_ipaddr.af = AF_INET6;
455 request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
458 case PW_PACKET_SRC_PORT:
459 request->packet->src_port = (vp->vp_integer & 0xffff);
462 case PW_PACKET_SRC_IP_ADDRESS:
463 request->packet->src_ipaddr.af = AF_INET;
464 request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
467 case PW_PACKET_SRC_IPV6_ADDRESS:
468 request->packet->src_ipaddr.af = AF_INET6;
469 request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
472 case PW_DIGEST_REALM:
473 case PW_DIGEST_NONCE:
474 case PW_DIGEST_METHOD:
477 case PW_DIGEST_ALGORITHM:
478 case PW_DIGEST_BODY_DIGEST:
479 case PW_DIGEST_CNONCE:
480 case PW_DIGEST_NONCE_COUNT:
481 case PW_DIGEST_USER_NAME:
487 p = talloc_array(vp, uint8_t, vp->length + 2);
489 memcpy(p + 2, vp->vp_octets, vp->length);
490 p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
494 da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
496 fr_perror("radclient: Out of memory");
502 * Re-do pairmemsteal ourselves,
503 * because we play games with
504 * vp->da, and pairmemsteal goes
505 * to GREAT lengths to sanitize
506 * and fix and change and
507 * double-check the various
510 memcpy(&q, &vp->vp_octets, sizeof(q));
513 vp->vp_octets = talloc_steal(vp, p);
521 } /* loop over the VP's we read in */
524 * Add it to the tail of the list.
527 assert(rc_request_tail == NULL);
528 request_head = request;
529 request->prev = NULL;
531 assert(rc_request_tail->next == NULL);
532 rc_request_tail->next = request;
533 request->prev = rc_request_tail;
535 rc_request_tail = request;
536 request->next = NULL;
538 } while (!packets_done); /* loop until the file is done. */
540 if (packets != stdin) fclose(packets);
541 if (filters) fclose(filters);
549 talloc_free(request);
551 if (packets != stdin) fclose(packets);
552 if (filters) fclose(filters);
559 * Sanity check each argument.
561 static int radclient_sane(rc_request_t *request)
563 if (request->packet->dst_port == 0) {
564 request->packet->dst_port = server_port;
566 if (request->packet->dst_ipaddr.af == AF_UNSPEC) {
567 if (server_ipaddr.af == AF_UNSPEC) {
568 fr_perror("radclient: No server was given, but request %d in file %s "
569 "did not contain Packet-Dst-IP-Address",
570 request->request_number, request->files->packets);
573 request->packet->dst_ipaddr = server_ipaddr;
575 if (request->packet->code == 0) {
576 if (packet_code == -1) {
577 fr_perror("radclient: Request was \"auto\", but request %d in file %s "
578 "did not contain Packet-Type",
579 request->request_number, request->files->packets);
582 request->packet->code = packet_code;
584 request->packet->sockfd = -1;
591 * For request handling.
593 static int filename_cmp(void const *one, void const *two)
597 rc_file_pair_t const *a = one;
598 rc_file_pair_t const *b = two;
600 cmp = strcmp(a->packets, b->packets);
601 if (cmp != 0) return cmp;
603 return strcmp(a->filters, b->filters);
606 static int filename_walk(UNUSED void *context, void *data)
608 rc_file_pair_t *files = data;
611 * Read request(s) from the file.
613 if (!radclient_init(files, files)) {
614 return -1; /* stop walking */
622 * Deallocate packet ID, etc.
624 static void deallocate_id(rc_request_t *request)
626 if (!request || !request->packet ||
627 (request->packet->id < 0)) {
632 * One more unused RADIUS ID.
634 fr_packet_list_id_free(pl, request->packet, true);
637 * If we've already sent a packet, free up the old one,
638 * and ensure that the next packet has a unique
639 * authentication vector.
641 if (request->packet->data) {
642 talloc_free(request->packet->data);
643 request->packet->data = NULL;
646 if (request->reply) rad_free(&request->reply);
650 static void print_hex(RADIUS_PACKET *packet)
654 if (!packet->data) return;
656 printf(" Code:\t\t%u\n", packet->data[0]);
657 printf(" Id:\t\t%u\n", packet->data[1]);
658 printf(" Length:\t%u\n", ((packet->data[2] << 8) |
660 printf(" Vector:\t");
661 for (i = 4; i < 20; i++) {
662 printf("%02x", packet->data[i]);
666 if (packet->data_len > 20) {
671 total = packet->data_len - 20;
672 ptr = packet->data + 20;
678 if (total < 2) { /* too short */
679 printf("%02x\n", *ptr);
683 if (ptr[1] > total) { /* too long */
684 for (i = 0; i < total; i++) {
685 printf("%02x ", ptr[i]);
690 printf("%02x %02x ", ptr[0], ptr[1]);
691 attrlen = ptr[1] - 2;
695 for (i = 0; i < attrlen; i++) {
696 if ((i > 0) && ((i & 0x0f) == 0x00))
698 printf("%02x ", ptr[i]);
699 if ((i & 0x0f) == 0x0f) printf("\n");
702 if ((attrlen & 0x0f) != 0x00) printf("\n");
714 static int send_one_packet(rc_request_t *request)
716 assert(request->done == false);
719 * Remember when we have to wake up, to re-send the
720 * request, of we didn't receive a reply.
722 if ((sleep_time == -1) || (sleep_time > (int) timeout)) {
723 sleep_time = (int) timeout;
727 * Haven't sent the packet yet. Initialize it.
729 if (request->packet->id == -1) {
733 assert(request->reply == NULL);
736 * Didn't find a free packet ID, we're not done,
737 * we don't sleep, and we stop trying to process
741 request->packet->src_ipaddr.af = server_ipaddr.af;
742 rcode = fr_packet_list_id_alloc(pl, ipproto,
743 &request->packet, NULL);
749 mysockfd = fr_tcp_client_socket(NULL,
754 mysockfd = fr_socket(&client_ipaddr, 0);
756 fr_perror("radclient: Can't open new socket: %s",
760 if (!fr_packet_list_socket_add(pl, mysockfd, ipproto,
762 server_port, NULL)) {
763 fr_perror("radclient: Can't add new socket");
769 assert(request->packet->id != -1);
770 assert(request->packet->data == NULL);
772 for (i = 0; i < 4; i++) {
773 ((uint32_t *) request->packet->vector)[i] = fr_rand();
777 * Update the password, so it can be encrypted with the
778 * new authentication vector.
780 if (request->password[0] != '\0') {
783 if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
784 pairstrcpy(vp, request->password);
786 } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
787 bool already_hex = false;
790 * If it's 17 octets, it *might* be already encoded.
791 * Or, it might just be a 17-character password (maybe UTF-8)
792 * Check it for non-printable characters. The odds of ALL
793 * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
794 * or 1/(2^51), which is pretty much zero.
796 if (vp->length == 17) {
797 for (i = 0; i < 17; i++) {
798 if (vp->vp_octets[i] < 32) {
806 * Allow the user to specify ASCII or hex CHAP-Password
812 len = len2 = strlen(request->password);
813 if (len2 < 17) len2 = 17;
815 p = talloc_zero_array(vp, uint8_t, len2);
817 memcpy(p, request->password, len);
819 rad_chap_encode(request->packet,
821 fr_rand() & 0xff, vp);
825 } else if (pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY) != NULL) {
826 mschapv1_encode(request->packet,
827 &request->packet->vps,
829 } else if (fr_debug_flag) {
830 printf("WARNING: No password in the request\n");
834 request->timestamp = time(NULL);
842 if (client_port == 0) {
843 client_ipaddr = request->packet->src_ipaddr;
844 client_port = request->packet->src_port;
848 } else { /* request->packet->id >= 0 */
849 time_t now = time(NULL);
852 * FIXME: Accounting packets are never retried!
853 * The Acct-Delay-Time attribute is updated to
854 * reflect the delay, and the packet is re-sent
859 * Not time for a retry, do so.
861 if ((now - request->timestamp) < timeout) {
863 * When we walk over the tree sending
864 * packets, we update the minimum time
867 if ((sleep_time == -1) ||
868 (sleep_time > (now - request->timestamp))) {
869 sleep_time = now - request->timestamp;
875 * We're not trying later, maybe the packet is done.
877 if (request->tries == retries) {
878 assert(request->packet->id >= 0);
881 * Delete the request from the tree of
882 * outstanding requests.
884 fr_packet_list_yank(pl, request->packet);
886 fr_perror("radclient: no reply from server for ID %d socket %d",
887 request->packet->id, request->packet->sockfd);
888 deallocate_id(request);
891 * Normally we mark it "done" when we've received
892 * the reply, but this is a special case.
894 if (request->resend == resend_count) {
895 request->done = true;
902 * We are trying later.
904 request->timestamp = now;
912 if (rad_send(request->packet, NULL, secret) < 0) {
913 fr_perror("radclient: Failed to send packet for ID %d",
914 request->packet->id);
917 if (fr_debug_flag > 2) print_hex(request->packet);
923 * Receive one packet, maybe.
925 static int recv_one_packet(int wait_time)
929 rc_request_t *request;
930 RADIUS_PACKET *reply, **packet_p;
933 /* And wait for reply, timing out as necessary */
936 max_fd = fr_packet_list_fd_set(pl, &set);
937 if (max_fd < 0) exit(1); /* no sockets to listen on! */
939 if (wait_time <= 0) {
942 tv.tv_sec = wait_time;
947 * No packet was received.
949 if (select(max_fd, &set, NULL, NULL, &tv) <= 0) {
954 * Look for the packet.
957 reply = fr_packet_list_recv(pl, &set);
959 fr_perror("radclient: received bad packet");
962 * If the packet is bad, we close the socket.
963 * I'm not sure how to do that now, so we just
968 return -1; /* bad packet */
972 * udpfromto issues. We may have bound to "*",
973 * and we want to find the replies that are sent to
976 reply->dst_ipaddr = client_ipaddr;
977 reply->dst_port = client_port;
979 reply->src_ipaddr = server_ipaddr;
980 reply->src_port = server_port;
983 if (fr_debug_flag > 2) print_hex(reply);
985 packet_p = fr_packet_list_find_byreply(pl, reply);
987 fr_perror("radclient: received reply to request we did not send. (id=%d socket %d)",
988 reply->id, reply->sockfd);
990 return -1; /* got reply to packet we didn't send */
992 request = fr_packet2myptr(rc_request_t, packet, packet_p);
995 * Fails the signature validation: not a real reply.
996 * FIXME: Silently drop it and listen for another packet.
998 if (rad_verify(reply, request->packet, secret) < 0) {
999 fr_perror("rad_verify");
1001 goto packet_done; /* shared secret is incorrect */
1004 if (print_filename) {
1005 printf("%s:%d %d\n", request->files->packets, request->request_number, reply->code);
1008 deallocate_id(request);
1009 request->reply = reply;
1013 * If this fails, we're out of memory.
1015 if (rad_decode(request->reply, request->packet, secret) != 0) {
1016 fr_perror("rad_decode");
1021 /* libradius debug already prints out the value pairs for us */
1022 if (!fr_debug_flag && do_output) {
1023 printf("Received reply ID %d, code %d, length = %zd\n",
1024 request->reply->id, request->reply->code,
1025 request->reply->data_len);
1026 vp_printlist(stdout, request->reply->vps);
1030 * Increment counters...
1032 if ((request->reply->code == PW_CODE_AUTHENTICATION_ACK) ||
1033 (request->reply->code == PW_CODE_ACCOUNTING_RESPONSE) ||
1034 (request->reply->code == PW_CODE_COA_ACK) ||
1035 (request->reply->code == PW_CODE_DISCONNECT_ACK)) {
1042 * If we had an expected response code, check to see if the
1043 * packet matched that.
1045 if (request->reply->code != request->filter_code) {
1046 if (is_radius_code(request->packet_code)) {
1047 printf("Expected %s got %s\n", fr_packet_codes[request->filter_code],
1048 fr_packet_codes[request->reply->code]);
1050 printf("Expected %u got %i\n", request->filter_code,
1051 request->reply->code);
1055 * Check if the contents of the packet matched the filter
1057 } else if (!request->filter) {
1060 VALUE_PAIR const *failed[2];
1062 pairsort(&request->reply->vps, attrtagcmp);
1063 if (pairvalidate(failed, request->filter, request->reply->vps)) {
1064 printf("Packet passed filter\n");
1067 pairvalidate_debug(request, failed);
1068 fr_perror("Packet failed filter");
1073 if (request->resend == resend_count) {
1074 request->done = true;
1078 rad_free(&request->reply);
1079 rad_free(&reply); /* may be NULL */
1085 static int getport(char const *name)
1087 struct servent *svp;
1089 svp = getservbyname (name, "udp");
1094 return ntohs(svp->s_port);
1097 int main(int argc, char **argv)
1100 char const *radius_dir = RADDBDIR;
1101 char const *dict_dir = DICTDIR;
1102 char filesecret[256];
1104 int do_summary = false;
1108 int force_af = AF_UNSPEC;
1113 if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
1114 fr_perror("radclient");
1119 talloc_set_log_stderr();
1121 filename_tree = rbtree_create(filename_cmp, NULL, 0);
1122 if (!filename_tree) {
1124 fr_perror("radclient: Out of memory");
1128 while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:n:p:qr:sS:t:vx"
1132 )) != EOF) switch(c) {
1137 force_af = AF_INET6;
1140 if (!isdigit((int) *optarg))
1142 resend_count = atoi(optarg);
1148 radius_dir = optarg;
1153 rc_file_pair_t *files;
1155 files = talloc(talloc_autofree_context(), rc_file_pair_t);
1156 if (!files) goto oom;
1158 p = strchr(optarg, ':');
1160 files->packets = talloc_strndup(files, optarg, p - optarg);
1161 if (!files->packets) goto oom;
1162 files->filters = p + 1;
1164 files->packets = optarg;
1166 rbtree_insert(filename_tree, (void *) files);
1170 print_filename = true;
1172 case 'i': /* currently broken */
1173 if (!isdigit((int) *optarg))
1175 last_used_id = atoi(optarg);
1176 if ((last_used_id < 0) || (last_used_id > 255)) {
1182 persec = atoi(optarg);
1183 if (persec <= 0) usage();
1187 * Note that sending MANY requests in
1188 * parallel can over-run the kernel
1189 * queues, and Linux will happily discard
1190 * packets. So even if the server responds,
1191 * the client may not see the reply.
1194 parallel = atoi(optarg);
1195 if (parallel <= 0) usage();
1201 if (strcmp(proto, "tcp") != 0) {
1202 if (strcmp(proto, "udp") == 0) {
1208 ipproto = IPPROTO_TCP;
1216 fr_log_fp = NULL; /* no output from you, either! */
1219 if (!isdigit((int) *optarg))
1221 retries = atoi(optarg);
1222 if ((retries == 0) || (retries > 1000)) usage();
1230 fp = fopen(optarg, "r");
1232 fr_perror("radclient: Error opening %s: %s",
1233 optarg, strerror(errno));
1236 if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
1237 fr_perror("radclient: Error reading %s: %s",
1238 optarg, strerror(errno));
1243 /* truncate newline */
1244 p = filesecret + strlen(filesecret) - 1;
1245 while ((p >= filesecret) &&
1251 if (strlen(filesecret) < 2) {
1252 fr_perror("radclient: Secret in %s is too short", optarg);
1255 secret = filesecret;
1259 if (!isdigit((int) *optarg))
1261 timeout = atof(optarg);
1264 printf("%s\n", radclient_version);
1276 argc -= (optind - 1);
1277 argv += (optind - 1);
1280 ((secret == NULL) && (argc < 4))) {
1285 * Mismatch between the binary and the libraries it depends on
1287 if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
1288 fr_perror("radclient");
1292 if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
1293 fr_perror("radclient");
1297 if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
1298 fr_perror("radclient");
1305 if (force_af == AF_UNSPEC) force_af = AF_INET;
1306 server_ipaddr.af = force_af;
1307 if (strcmp(argv[1], "-") != 0) {
1309 char const *hostname = argv[1];
1310 char const *portname = argv[1];
1313 if (*argv[1] == '[') { /* IPv6 URL encoded */
1314 p = strchr(argv[1], ']');
1315 if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
1319 memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
1320 buffer[p - argv[1] - 1] = '\0';
1326 p = strchr(portname, ':');
1327 if (p && (strchr(p + 1, ':') == NULL)) {
1334 if (ip_hton(hostname, force_af, &server_ipaddr) < 0) {
1335 fr_perror("radclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
1340 * Strip port from hostname if needed.
1342 if (portname) server_port = atoi(portname);
1346 * See what kind of request we want to send.
1348 if (strcmp(argv[2], "auth") == 0) {
1349 if (server_port == 0) server_port = getport("radius");
1350 if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
1351 packet_code = PW_CODE_AUTHENTICATION_REQUEST;
1353 } else if (strcmp(argv[2], "challenge") == 0) {
1354 if (server_port == 0) server_port = getport("radius");
1355 if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
1356 packet_code = PW_CODE_ACCESS_CHALLENGE;
1358 } else if (strcmp(argv[2], "acct") == 0) {
1359 if (server_port == 0) server_port = getport("radacct");
1360 if (server_port == 0) server_port = PW_ACCT_UDP_PORT;
1361 packet_code = PW_CODE_ACCOUNTING_REQUEST;
1364 } else if (strcmp(argv[2], "status") == 0) {
1365 if (server_port == 0) server_port = getport("radius");
1366 if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
1367 packet_code = PW_CODE_STATUS_SERVER;
1369 } else if (strcmp(argv[2], "disconnect") == 0) {
1370 if (server_port == 0) server_port = PW_COA_UDP_PORT;
1371 packet_code = PW_CODE_DISCONNECT_REQUEST;
1373 } else if (strcmp(argv[2], "coa") == 0) {
1374 if (server_port == 0) server_port = PW_COA_UDP_PORT;
1375 packet_code = PW_CODE_COA_REQUEST;
1377 } else if (strcmp(argv[2], "auto") == 0) {
1380 } else if (isdigit((int) argv[2][0])) {
1381 if (server_port == 0) server_port = getport("radius");
1382 if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
1383 packet_code = atoi(argv[2]);
1391 if (argv[3]) secret = argv[3];
1394 * If no '-f' is specified, we're reading from stdin.
1396 if (rbtree_num_elements(filename_tree) == 0) {
1397 rc_file_pair_t *files;
1399 files = talloc_zero(talloc_autofree_context(), rc_file_pair_t);
1400 files->packets = "-";
1401 if (!radclient_init(files, files)) {
1407 * Walk over the list of filenames, creating the requests.
1409 if (rbtree_walk(filename_tree, RBTREE_IN_ORDER, filename_walk, NULL) != 0) {
1410 fr_perror("radclient: Failed parsing input files");
1415 * No packets read. Die.
1417 if (!request_head) {
1418 fr_perror("radclient: Nothing to send");
1423 * Bind to the first specified IP address and port.
1424 * This means we ignore later ones.
1426 if (request_head->packet->src_ipaddr.af == AF_UNSPEC) {
1427 memset(&client_ipaddr, 0, sizeof(client_ipaddr));
1428 client_ipaddr.af = server_ipaddr.af;
1431 client_ipaddr = request_head->packet->src_ipaddr;
1432 client_port = request_head->packet->src_port;
1436 sockfd = fr_tcp_client_socket(NULL, &server_ipaddr, server_port);
1439 sockfd = fr_socket(&client_ipaddr, client_port);
1441 fr_perror("radclient: socket");
1445 pl = fr_packet_list_create(1);
1447 fr_perror("radclient: Out of memory");
1451 if (!fr_packet_list_socket_add(pl, sockfd, ipproto, &server_ipaddr,
1452 server_port, NULL)) {
1453 fr_perror("radclient: Out of memory");
1458 * Walk over the list of packets, sanity checking
1461 for (this = request_head; this != NULL; this = this->next) {
1462 this->packet->src_ipaddr = client_ipaddr;
1463 this->packet->src_port = client_port;
1464 if (radclient_sane(this) != 0) {
1470 * Walk over the packets to send, until
1473 * FIXME: This currently busy-loops until it receives
1474 * all of the packets. It should really have some sort of
1475 * send packet, get time to wait, select for time, etc.
1481 char const *filename = NULL;
1487 * Walk over the packets, sending them.
1490 for (this = request_head; this != NULL; this = next) {
1494 * If there's a packet to receive,
1495 * receive it, but don't wait for a
1501 * This packet is done. Delete it.
1509 * Packets from multiple '-f' are sent
1512 * Packets from one file are sent in
1513 * series, unless '-p' is specified, in
1514 * which case N packets from each file
1515 * are sent in parallel.
1517 if (this->files->packets != filename) {
1518 filename = this->files->packets;
1526 * Send the current packet.
1528 send_one_packet(this);
1531 * Wait a little before sending
1532 * the next packet, if told to.
1538 * Don't sleep elsewhere.
1547 tv.tv_usec = 1000000/persec;
1551 * Sleep for milliseconds,
1554 * If we get an error or
1555 * a signal, treat it like
1558 select(0, NULL, NULL, NULL, &tv);
1562 * If we haven't sent this packet
1563 * often enough, we're not done,
1564 * and we shouldn't sleep.
1566 if (this->resend < resend_count) {
1570 } else { /* haven't sent this packet, we're not done */
1571 assert(this->done == false);
1572 assert(this->reply == NULL);
1578 * Still have outstanding requests.
1580 if (fr_packet_list_num_elements(pl) > 0) {
1587 * Nothing to do until we receive a request, so
1588 * sleep until then. Once we receive one packet,
1589 * we go back, and walk through the whole list again,
1590 * sending more packets (if necessary), and updating
1593 if (!done && (sleep_time > 0)) {
1594 recv_one_packet(sleep_time);
1598 rbtree_free(filename_tree);
1599 fr_packet_list_free(pl);
1600 while (request_head) talloc_free(request_head);
1604 printf("\tAccess-Accepts : %" PRIu64 "\n"
1605 "\tAccess-Rejects : %" PRIu64 "\n"
1606 "\tLost : %" PRIu64 "\n"
1607 "\tPassed filter : %" PRIu64 "\n"
1608 "\tFailed filter : %" PRIu64 "\n",
1617 if ((stats.lost > 0) || (stats.failed > 0)) {