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,2014 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/radclient.h>
28 #include <freeradius-devel/radpaths.h>
29 #include <freeradius-devel/conf.h>
38 typedef struct REQUEST REQUEST; /* to shut up warnings about mschap.h */
43 static int retries = 3;
44 static float timeout = 5;
45 static char const *secret = NULL;
46 static bool do_output = true;
48 static rc_stats_t stats;
50 static uint16_t server_port = 0;
51 static int packet_code = PW_CODE_UNDEFINED;
52 static fr_ipaddr_t server_ipaddr;
53 static int resend_count = 1;
54 static bool done = true;
55 static bool print_filename = false;
57 static fr_ipaddr_t client_ipaddr;
58 static uint16_t client_port = 0;
61 static int last_used_id = -1;
64 static char const *proto = NULL;
66 static int ipproto = IPPROTO_UDP;
68 static rbtree_t *filename_tree = NULL;
69 static fr_packet_list_t *pl = NULL;
71 static int sleep_time = -1;
73 static rc_request_t *request_head = NULL;
74 static rc_request_t *rc_request_tail = NULL;
76 static char const *radclient_version = "radclient version " RADIUSD_VERSION_STRING
77 #ifdef RADIUSD_VERSION_COMMIT
78 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
80 ", built on " __DATE__ " at " __TIME__;
82 static void NEVER_RETURNS usage(void)
84 fprintf(stderr, "Usage: radclient [options] server[:port] <command> [<secret>]\n");
86 fprintf(stderr, " <command> One of auth, acct, status, coa, disconnect or auto.\n");
87 fprintf(stderr, " -4 Use IPv4 address of server\n");
88 fprintf(stderr, " -6 Use IPv6 address of server.\n");
89 fprintf(stderr, " -c <count> Send each packet 'count' times.\n");
90 fprintf(stderr, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n");
91 fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
92 fprintf(stderr, " -f <file>[:<file>] Read packets from file, not stdin.\n");
93 fprintf(stderr, " If a second file is provided, it will be used to verify responses\n");
94 fprintf(stderr, " -F Print the file name, packet number and reply code.\n");
95 fprintf(stderr, " -h Print usage help information.\n");
96 fprintf(stderr, " -i <id> Set request id to 'id'. Values may be 0..255\n");
97 fprintf(stderr, " -n <num> Send N requests/s\n");
98 fprintf(stderr, " -p <num> Send 'num' packets from a file in parallel.\n");
99 fprintf(stderr, " -q Do not print anything out.\n");
100 fprintf(stderr, " -r <retries> If timeout, retry sending the packet 'retries' times.\n");
101 fprintf(stderr, " -s Print out summary information of auth results.\n");
102 fprintf(stderr, " -S <file> read secret from file, not command line.\n");
103 fprintf(stderr, " -t <timeout> Wait 'timeout' seconds before retrying (may be a floating point number).\n");
104 fprintf(stderr, " -v Show program version information.\n");
105 fprintf(stderr, " -x Debugging mode.\n");
108 fprintf(stderr, " -P <proto> Use proto (tcp or udp) for transport.\n");
114 static const FR_NAME_NUMBER request_types[] = {
115 { "auth", PW_CODE_ACCESS_REQUEST },
116 { "challenge", PW_CODE_ACCESS_CHALLENGE },
117 { "acct", PW_CODE_ACCOUNTING_REQUEST },
118 { "status", PW_CODE_STATUS_SERVER },
119 { "disconnect", PW_CODE_DISCONNECT_REQUEST },
120 { "coa", PW_CODE_COA_REQUEST },
121 { "auto", PW_CODE_UNDEFINED },
127 * Free a radclient struct, which may (or may not)
128 * already be in the list.
130 static int _rc_request_free(rc_request_t *request)
132 rc_request_t *prev, *next;
134 prev = request->prev;
135 next = request->next;
138 assert(request_head != request);
140 } else if (request_head) {
141 assert(request_head == request);
146 assert(rc_request_tail != request);
148 } else if (rc_request_tail) {
149 assert(rc_request_tail == request);
150 rc_request_tail = prev;
156 static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
157 char const *password)
161 VALUE_PAIR *challenge, *reply;
164 fr_pair_delete_by_num(&packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
165 fr_pair_delete_by_num(&packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
167 challenge = fr_pair_afrom_num(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT);
172 fr_pair_add(request, challenge);
173 challenge->vp_length = 8;
174 challenge->vp_octets = p = talloc_array(challenge, uint8_t, challenge->vp_length);
175 for (i = 0; i < challenge->vp_length; i++) {
179 reply = fr_pair_afrom_num(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
184 fr_pair_add(request, reply);
185 reply->vp_length = 50;
186 reply->vp_octets = p = talloc_array(reply, uint8_t, reply->vp_length);
187 memset(p, 0, reply->vp_length);
189 p[1] = 0x01; /* NT hash */
191 if (mschap_ntpwdhash(nthash, password) < 0) {
195 smbdes_mschap(nthash, challenge->vp_octets, p + 26);
200 static int getport(char const *name)
204 svp = getservbyname(name, "udp");
207 return ntohs(svp->s_port);
211 * Set a port from the request type if we don't already have one
213 static void radclient_get_port(PW_CODE type, uint16_t *port)
217 case PW_CODE_ACCESS_REQUEST:
218 case PW_CODE_ACCESS_CHALLENGE:
219 case PW_CODE_STATUS_SERVER:
220 if (*port == 0) *port = getport("radius");
221 if (*port == 0) *port = PW_AUTH_UDP_PORT;
224 case PW_CODE_ACCOUNTING_REQUEST:
225 if (*port == 0) *port = getport("radacct");
226 if (*port == 0) *port = PW_ACCT_UDP_PORT;
229 case PW_CODE_DISCONNECT_REQUEST:
230 if (*port == 0) *port = PW_POD_UDP_PORT;
233 case PW_CODE_COA_REQUEST:
234 if (*port == 0) *port = PW_COA_UDP_PORT;
237 case PW_CODE_UNDEFINED:
238 if (*port == 0) *port = 0;
244 * Resolve a port to a request type
246 static PW_CODE radclient_get_code(uint16_t port)
249 * getport returns 0 if the service doesn't exist
250 * so we need to return early, to avoid incorrect
253 if (port == 0) return PW_CODE_UNDEFINED;
255 if ((port == getport("radius")) || (port == PW_AUTH_UDP_PORT) || (port == PW_AUTH_UDP_PORT_ALT)) {
256 return PW_CODE_ACCESS_REQUEST;
258 if ((port == getport("radacct")) || (port == PW_ACCT_UDP_PORT) || (port == PW_ACCT_UDP_PORT_ALT)) {
259 return PW_CODE_ACCOUNTING_REQUEST;
261 if (port == PW_COA_UDP_PORT) return PW_CODE_COA_REQUEST;
262 if (port == PW_POD_UDP_PORT) return PW_CODE_DISCONNECT_REQUEST;
264 return PW_CODE_UNDEFINED;
268 static bool already_hex(VALUE_PAIR *vp)
272 if (!vp || (vp->da->type != PW_TYPE_OCTETS)) return true;
275 * If it's 17 octets, it *might* be already encoded.
276 * Or, it might just be a 17-character password (maybe UTF-8)
277 * Check it for non-printable characters. The odds of ALL
278 * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
279 * or 1/(2^51), which is pretty much zero.
281 for (i = 0; i < vp->vp_length; i++) {
282 if (vp->vp_octets[i] < 32) {
292 * Initialize a radclient data structure and add it to
293 * the global linked list.
295 static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
297 FILE *packets, *filters = NULL;
301 rc_request_t *request;
302 bool packets_done = false;
305 assert(files->packets != NULL);
308 * Determine where to read the VP's from.
310 if (strcmp(files->packets, "-") != 0) {
311 packets = fopen(files->packets, "r");
313 ERROR("Error opening %s: %s", files->packets, strerror(errno));
318 * Read in the pairs representing the expected response.
320 if (files->filters) {
321 filters = fopen(files->filters, "r");
323 ERROR("Error opening %s: %s", files->filters, strerror(errno));
334 * Loop until the file is done.
340 request = talloc_zero(ctx, rc_request_t);
342 ERROR("Out of memory");
346 request->packet = rad_alloc(request, true);
347 if (!request->packet) {
348 ERROR("Out of memory");
353 request->packet->src_ipaddr = client_ipaddr;
354 request->packet->src_port = client_port;
355 request->packet->dst_ipaddr = server_ipaddr;
356 request->packet->dst_port = server_port;
357 request->packet->proto = ipproto;
360 request->files = files;
361 request->packet->id = -1; /* allocate when sending */
362 request->num = num++;
365 * Read the request VP's.
367 if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, packets, &packets_done) < 0) {
370 if ((files->packets[0] == '-') && (files->packets[1] == '\0')) {
373 input = files->packets;
376 REDEBUG("Error parsing \"%s\"", input);
383 if (!request->packet->vps) {
384 talloc_free(request);
389 * Read in filter VP's.
394 if (fr_pair_list_afrom_file(request, &request->filter, filters, &filters_done) < 0) {
395 REDEBUG("Error parsing \"%s\"", files->filters);
399 if (filters_done && !packets_done) {
400 REDEBUG("Differing number of packets/filters in %s:%s "
401 "(too many requests))", files->packets, files->filters);
405 if (!filters_done && packets_done) {
406 REDEBUG("Differing number of packets/filters in %s:%s "
407 "(too many filters))", files->packets, files->filters);
412 * xlat expansions aren't supported here
414 for (vp = fr_cursor_init(&cursor, &request->filter);
416 vp = fr_cursor_next(&cursor)) {
417 if (vp->type == VT_XLAT) {
419 vp->vp_strvalue = vp->value.xlat;
420 vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
423 if (vp->da->vendor == 0 ) switch (vp->da->attr) {
424 case PW_RESPONSE_PACKET_TYPE:
426 fr_cursor_remove(&cursor); /* so we don't break the filter */
427 request->filter_code = vp->vp_integer;
436 * This allows efficient list comparisons later
438 fr_pair_list_sort(&request->filter, fr_pair_cmp_by_da_tag);
442 * Process special attributes
444 for (vp = fr_cursor_init(&cursor, &request->packet->vps);
446 vp = fr_cursor_next(&cursor)) {
448 * Double quoted strings get marked up as xlat expansions,
449 * but we don't support that in request.
451 if (vp->type == VT_XLAT) {
453 vp->vp_strvalue = vp->value.xlat;
454 vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
457 if (!vp->da->vendor) switch (vp->da->attr) {
462 * Allow it to set the packet type in
463 * the attributes read from the file.
466 request->packet->code = vp->vp_integer;
469 case PW_RESPONSE_PACKET_TYPE:
470 request->filter_code = vp->vp_integer;
473 case PW_PACKET_DST_PORT:
474 request->packet->dst_port = (vp->vp_integer & 0xffff);
477 case PW_PACKET_DST_IP_ADDRESS:
478 request->packet->dst_ipaddr.af = AF_INET;
479 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
480 request->packet->dst_ipaddr.prefix = 32;
483 case PW_PACKET_DST_IPV6_ADDRESS:
484 request->packet->dst_ipaddr.af = AF_INET6;
485 request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
486 request->packet->dst_ipaddr.prefix = 128;
489 case PW_PACKET_SRC_PORT:
490 if ((vp->vp_integer < 1024) ||
491 (vp->vp_integer > 65535)) {
492 ERROR("Invalid value '%u' for Packet-Src-Port", vp->vp_integer);
495 request->packet->src_port = (vp->vp_integer & 0xffff);
498 case PW_PACKET_SRC_IP_ADDRESS:
499 request->packet->src_ipaddr.af = AF_INET;
500 request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
501 request->packet->src_ipaddr.prefix = 32;
504 case PW_PACKET_SRC_IPV6_ADDRESS:
505 request->packet->src_ipaddr.af = AF_INET6;
506 request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
507 request->packet->src_ipaddr.prefix = 128;
510 case PW_DIGEST_REALM:
511 case PW_DIGEST_NONCE:
512 case PW_DIGEST_METHOD:
515 case PW_DIGEST_ALGORITHM:
516 case PW_DIGEST_BODY_DIGEST:
517 case PW_DIGEST_CNONCE:
518 case PW_DIGEST_NONCE_COUNT:
519 case PW_DIGEST_USER_NAME:
525 p = talloc_array(vp, uint8_t, vp->vp_length + 2);
527 memcpy(p + 2, vp->vp_octets, vp->vp_length);
528 p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
530 p[1] = vp->vp_length;
532 da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
534 ERROR("Out of memory");
540 * Re-do fr_pair_value_memsteal ourselves,
541 * because we play games with
542 * vp->da, and fr_pair_value_memsteal goes
543 * to GREAT lengths to sanitize
544 * and fix and change and
545 * double-check the various
548 memcpy(&q, &vp->vp_octets, sizeof(q));
551 vp->vp_octets = talloc_steal(vp, p);
559 * Cache this for later.
561 case PW_CLEARTEXT_PASSWORD:
562 request->password = vp;
566 * Keep a copy of the the password attribute.
568 case PW_CHAP_PASSWORD:
570 * If it's already hex, do nothing.
572 if ((vp->vp_length == 17) &&
573 (already_hex(vp))) break;
576 * CHAP-Password is octets, so it may not be zero terminated.
578 request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password",
580 fr_pair_value_bstrncpy(request->password, vp->vp_strvalue, vp->vp_length);
583 case PW_USER_PASSWORD:
584 case PW_MS_CHAP_PASSWORD:
585 request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password",
586 vp->vp_strvalue, T_OP_EQ);
589 case PW_RADCLIENT_TEST_NAME:
590 request->name = vp->vp_strvalue;
593 } /* loop over the VP's we read in */
596 * Use the default set on the command line
598 if (request->packet->code == PW_CODE_UNDEFINED) request->packet->code = packet_code;
601 * Default to the filename
603 if (!request->name) request->name = request->files->packets;
606 * Automatically set the response code from the request code
607 * (if one wasn't already set).
609 if (request->filter_code == PW_CODE_UNDEFINED) {
610 switch (request->packet->code) {
611 case PW_CODE_ACCESS_REQUEST:
612 request->filter_code = PW_CODE_ACCESS_ACCEPT;
615 case PW_CODE_ACCOUNTING_REQUEST:
616 request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
619 case PW_CODE_COA_REQUEST:
620 request->filter_code = PW_CODE_COA_ACK;
623 case PW_CODE_DISCONNECT_REQUEST:
624 request->filter_code = PW_CODE_DISCONNECT_ACK;
627 case PW_CODE_STATUS_SERVER:
628 switch (radclient_get_code(request->packet->dst_port)) {
629 case PW_CODE_ACCESS_REQUEST:
630 request->filter_code = PW_CODE_ACCESS_ACCEPT;
633 case PW_CODE_ACCOUNTING_REQUEST:
634 request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
638 request->filter_code = PW_CODE_UNDEFINED;
643 case PW_CODE_UNDEFINED:
644 REDEBUG("Both Packet-Type and Response-Packet-Type undefined, specify at least one, "
645 "or a well known RADIUS port");
649 REDEBUG("Can't determine expected Response-Packet-Type for Packet-Type %i",
650 request->packet->code);
654 * Automatically set the request code from the response code
655 * (if one wasn't already set).
657 } else if (request->packet->code == PW_CODE_UNDEFINED) {
658 switch (request->filter_code) {
659 case PW_CODE_ACCESS_ACCEPT:
660 case PW_CODE_ACCESS_REJECT:
661 request->packet->code = PW_CODE_ACCESS_REQUEST;
664 case PW_CODE_ACCOUNTING_RESPONSE:
665 request->packet->code = PW_CODE_ACCOUNTING_REQUEST;
668 case PW_CODE_DISCONNECT_ACK:
669 case PW_CODE_DISCONNECT_NAK:
670 request->packet->code = PW_CODE_DISCONNECT_REQUEST;
673 case PW_CODE_COA_ACK:
674 case PW_CODE_COA_NAK:
675 request->packet->code = PW_CODE_COA_REQUEST;
679 REDEBUG("Can't determine expected Packet-Type for Response-Packet-Type %i",
680 request->filter_code);
686 * Automatically set the dst port (if one wasn't already set).
688 if (request->packet->dst_port == 0) {
689 radclient_get_port(request->packet->code, &request->packet->dst_port);
690 if (request->packet->dst_port == 0) {
691 REDEBUG("Can't determine destination port");
697 * Add it to the tail of the list.
700 assert(rc_request_tail == NULL);
701 request_head = request;
702 request->prev = NULL;
704 assert(rc_request_tail->next == NULL);
705 rc_request_tail->next = request;
706 request->prev = rc_request_tail;
708 rc_request_tail = request;
709 request->next = NULL;
712 * Set the destructor so it removes itself from the
713 * request list when freed. We don't set this until
714 * the packet is actually in the list, else we trigger
715 * the asserts in the free callback.
717 talloc_set_destructor(request, _rc_request_free);
718 } while (!packets_done); /* loop until the file is done. */
720 if (packets != stdin) fclose(packets);
721 if (filters) fclose(filters);
729 talloc_free(request);
731 if (packets != stdin) fclose(packets);
732 if (filters) fclose(filters);
739 * Sanity check each argument.
741 static int radclient_sane(rc_request_t *request)
743 if (request->packet->dst_port == 0) {
744 request->packet->dst_port = server_port;
746 if (request->packet->dst_ipaddr.af == AF_UNSPEC) {
747 if (server_ipaddr.af == AF_UNSPEC) {
748 ERROR("No server was given, and request %" PRIu64 " in file %s did not contain "
749 "Packet-Dst-IP-Address", request->num, request->files->packets);
752 request->packet->dst_ipaddr = server_ipaddr;
754 if (request->packet->code == 0) {
755 if (packet_code == -1) {
756 ERROR("Request was \"auto\", and request %" PRIu64 " in file %s did not contain Packet-Type",
757 request->num, request->files->packets);
760 request->packet->code = packet_code;
762 request->packet->sockfd = -1;
769 * For request handling.
771 static int filename_cmp(void const *one, void const *two)
775 rc_file_pair_t const *a = one;
776 rc_file_pair_t const *b = two;
778 cmp = strcmp(a->packets, b->packets);
779 if (cmp != 0) return cmp;
781 return strcmp(a->filters, b->filters);
784 static int filename_walk(UNUSED void *context, void *data)
786 rc_file_pair_t *files = data;
789 * Read request(s) from the file.
791 if (!radclient_init(files, files)) return -1; /* stop walking */
798 * Deallocate packet ID, etc.
800 static void deallocate_id(rc_request_t *request)
802 if (!request || !request->packet ||
803 (request->packet->id < 0)) {
808 * One more unused RADIUS ID.
810 fr_packet_list_id_free(pl, request->packet, true);
813 * If we've already sent a packet, free up the old one,
814 * and ensure that the next packet has a unique
815 * authentication vector.
817 if (request->packet->data) TALLOC_FREE(request->packet->data);
818 if (request->reply) rad_free(&request->reply);
824 static int send_one_packet(rc_request_t *request)
826 assert(request->done == false);
829 * Remember when we have to wake up, to re-send the
830 * request, of we didn't receive a reply.
832 if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout;
835 * Haven't sent the packet yet. Initialize it.
837 if (request->packet->id == -1) {
841 assert(request->reply == NULL);
844 * Didn't find a free packet ID, we're not done,
845 * we don't sleep, and we stop trying to process
849 request->packet->src_ipaddr.af = server_ipaddr.af;
850 rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL);
856 mysockfd = fr_socket_client_tcp(NULL,
857 &request->packet->dst_ipaddr,
858 request->packet->dst_port, false);
861 mysockfd = fr_socket(&client_ipaddr, 0);
863 ERROR("Failed opening socket");
866 if (!fr_packet_list_socket_add(pl, mysockfd, ipproto,
867 &request->packet->dst_ipaddr,
868 request->packet->dst_port, NULL)) {
869 ERROR("Can't add new socket");
875 assert(request->packet->id != -1);
876 assert(request->packet->data == NULL);
878 for (i = 0; i < 4; i++) {
879 ((uint32_t *) request->packet->vector)[i] = fr_rand();
883 * Update the password, so it can be encrypted with the
884 * new authentication vector.
886 if (request->password) {
889 if ((vp = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
890 fr_pair_value_strcpy(vp, request->password->vp_strvalue);
892 } else if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
895 rad_chap_encode(request->packet, buffer, fr_rand() & 0xff, request->password);
896 fr_pair_value_memcpy(vp, buffer, 17);
898 } else if (fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) {
899 mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue);
902 DEBUG("WARNING: No password in the request");
906 request->timestamp = time(NULL);
910 } else { /* request->packet->id >= 0 */
911 time_t now = time(NULL);
914 * FIXME: Accounting packets are never retried!
915 * The Acct-Delay-Time attribute is updated to
916 * reflect the delay, and the packet is re-sent
921 * Not time for a retry, do so.
923 if ((now - request->timestamp) < timeout) {
925 * When we walk over the tree sending
926 * packets, we update the minimum time
929 if ((sleep_time == -1) ||
930 (sleep_time > (now - request->timestamp))) {
931 sleep_time = now - request->timestamp;
937 * We're not trying later, maybe the packet is done.
939 if (request->tries == retries) {
940 assert(request->packet->id >= 0);
943 * Delete the request from the tree of
944 * outstanding requests.
946 fr_packet_list_yank(pl, request->packet);
948 REDEBUG("No reply from server for ID %d socket %d",
949 request->packet->id, request->packet->sockfd);
950 deallocate_id(request);
953 * Normally we mark it "done" when we've received
954 * the reply, but this is a special case.
956 if (request->resend == resend_count) {
957 request->done = true;
964 * We are trying later.
966 request->timestamp = now;
973 if (rad_send(request->packet, NULL, secret) < 0) {
974 REDEBUG("Failed to send packet for ID %d", request->packet->id);
975 deallocate_id(request);
976 request->done = true;
980 fr_packet_header_print(fr_log_fp, request->packet, false);
981 if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps);
987 * Receive one packet, maybe.
989 static int recv_one_packet(int wait_time)
993 rc_request_t *request;
994 RADIUS_PACKET *reply, **packet_p;
997 /* And wait for reply, timing out as necessary */
1000 max_fd = fr_packet_list_fd_set(pl, &set);
1001 if (max_fd < 0) exit(1); /* no sockets to listen on! */
1003 tv.tv_sec = (wait_time <= 0) ? 0 : wait_time;
1007 * No packet was received.
1009 if (select(max_fd, &set, NULL, NULL, &tv) <= 0) return 0;
1012 * Look for the packet.
1014 reply = fr_packet_list_recv(pl, &set);
1016 ERROR("Received bad packet");
1019 * If the packet is bad, we close the socket.
1020 * I'm not sure how to do that now, so we just
1025 return -1; /* bad packet */
1029 * We don't use udpfromto. So if we bind to "*", we want
1030 * to find replies sent to 192.0.2.4. Therefore, we
1031 * force all replies to have the one address we know
1032 * about, no matter what real address they were sent to.
1034 * This only works if were not using any of the
1035 * Packet-* attributes, or running with 'auto'.
1037 reply->dst_ipaddr = client_ipaddr;
1038 reply->dst_port = client_port;
1043 * TCP sockets don't use recvmsg(), and thus don't get
1044 * the source IP/port. However, since they're TCP, we
1045 * know what the source IP/port is, because that's where
1048 if (ipproto == IPPROTO_TCP) {
1049 reply->src_ipaddr = server_ipaddr;
1050 reply->src_port = server_port;
1054 packet_p = fr_packet_list_find_byreply(pl, reply);
1056 ERROR("Received reply to request we did not send. (id=%d socket %d)",
1057 reply->id, reply->sockfd);
1059 return -1; /* got reply to packet we didn't send */
1061 request = fr_packet2myptr(rc_request_t, packet, packet_p);
1064 * Fails the signature validation: not a real reply.
1065 * FIXME: Silently drop it and listen for another packet.
1067 if (rad_verify(reply, request->packet, secret) < 0) {
1068 REDEBUG("Reply verification failed");
1070 goto packet_done; /* shared secret is incorrect */
1073 if (print_filename) {
1074 RDEBUG("%s response code %d", request->files->packets, reply->code);
1077 deallocate_id(request);
1078 request->reply = reply;
1082 * If this fails, we're out of memory.
1084 if (rad_decode(request->reply, request->packet, secret) != 0) {
1085 REDEBUG("Reply decode failed");
1090 fr_packet_header_print(fr_log_fp, request->reply, true);
1091 if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->reply->vps);
1094 * Increment counters...
1096 switch (request->reply->code) {
1097 case PW_CODE_ACCESS_ACCEPT:
1098 case PW_CODE_ACCOUNTING_RESPONSE:
1099 case PW_CODE_COA_ACK:
1100 case PW_CODE_DISCONNECT_ACK:
1104 case PW_CODE_ACCESS_CHALLENGE:
1112 * If we had an expected response code, check to see if the
1113 * packet matched that.
1115 if ((request->filter_code != PW_CODE_UNDEFINED) && (request->reply->code != request->filter_code)) {
1116 if (is_radius_code(request->reply->code)) {
1117 REDEBUG("%s: Expected %s got %s", request->name, fr_packet_codes[request->filter_code],
1118 fr_packet_codes[request->reply->code]);
1120 REDEBUG("%s: Expected %u got %i", request->name, request->filter_code,
1121 request->reply->code);
1125 * Check if the contents of the packet matched the filter
1127 } else if (!request->filter) {
1130 VALUE_PAIR const *failed[2];
1132 fr_pair_list_sort(&request->reply->vps, fr_pair_cmp_by_da_tag);
1133 if (fr_pair_validate(failed, request->filter, request->reply->vps)) {
1134 RDEBUG("%s: Response passed filter", request->name);
1137 fr_pair_validate_debug(request, failed);
1138 REDEBUG("%s: Response for failed filter", request->name);
1143 if (request->resend == resend_count) {
1144 request->done = true;
1148 rad_free(&request->reply);
1149 rad_free(&reply); /* may be NULL */
1154 int main(int argc, char **argv)
1157 char const *radius_dir = RADDBDIR;
1158 char const *dict_dir = DICTDIR;
1159 char filesecret[256];
1161 int do_summary = false;
1165 int force_af = AF_UNSPEC;
1168 * It's easier having two sets of flags to set the
1169 * verbosity of library calls and the verbosity of
1176 if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
1177 fr_perror("radclient");
1182 talloc_set_log_stderr();
1184 filename_tree = rbtree_create(NULL, filename_cmp, NULL, 0);
1185 if (!filename_tree) {
1187 ERROR("Out of memory");
1191 while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:n:p:qr:sS:t:vx"
1195 )) != EOF) switch (c) {
1201 force_af = AF_INET6;
1205 if (!isdigit((int) *optarg))
1207 resend_count = atoi(optarg);
1215 radius_dir = optarg;
1221 rc_file_pair_t *files;
1223 files = talloc(talloc_autofree_context(), rc_file_pair_t);
1224 if (!files) goto oom;
1226 p = strchr(optarg, ':');
1228 files->packets = talloc_strndup(files, optarg, p - optarg);
1229 if (!files->packets) goto oom;
1230 files->filters = p + 1;
1232 files->packets = optarg;
1233 files->filters = NULL;
1235 rbtree_insert(filename_tree, (void *) files);
1240 print_filename = true;
1243 case 'i': /* currently broken */
1244 if (!isdigit((int) *optarg))
1246 last_used_id = atoi(optarg);
1247 if ((last_used_id < 0) || (last_used_id > 255)) {
1253 persec = atoi(optarg);
1254 if (persec <= 0) usage();
1258 * Note that sending MANY requests in
1259 * parallel can over-run the kernel
1260 * queues, and Linux will happily discard
1261 * packets. So even if the server responds,
1262 * the client may not see the reply.
1265 parallel = atoi(optarg);
1266 if (parallel <= 0) usage();
1272 if (strcmp(proto, "tcp") != 0) {
1273 if (strcmp(proto, "udp") == 0) {
1279 ipproto = IPPROTO_TCP;
1287 fr_log_fp = NULL; /* no output from you, either! */
1291 if (!isdigit((int) *optarg)) usage();
1292 retries = atoi(optarg);
1293 if ((retries == 0) || (retries > 1000)) usage();
1303 fp = fopen(optarg, "r");
1305 ERROR("Error opening %s: %s", optarg, fr_syserror(errno));
1308 if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
1309 ERROR("Error reading %s: %s", optarg, fr_syserror(errno));
1314 /* truncate newline */
1315 p = filesecret + strlen(filesecret) - 1;
1316 while ((p >= filesecret) &&
1322 if (strlen(filesecret) < 2) {
1323 ERROR("Secret in %s is too short", optarg);
1326 secret = filesecret;
1331 if (!isdigit((int) *optarg))
1333 timeout = atof(optarg);
1338 DEBUG("%s", radclient_version);
1349 argc -= (optind - 1);
1350 argv += (optind - 1);
1352 if ((argc < 3) || ((secret == NULL) && (argc < 4))) {
1353 ERROR("Insufficient arguments");
1357 * Mismatch between the binary and the libraries it depends on
1359 if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
1360 fr_perror("radclient");
1364 if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
1365 fr_perror("radclient");
1369 if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
1370 fr_perror("radclient");
1373 fr_strerror(); /* Clear the error buffer */
1376 * Get the request type
1378 if (!isdigit((int) argv[2][0])) {
1379 packet_code = fr_str2int(request_types, argv[2], -2);
1380 if (packet_code == -2) {
1381 ERROR("Unrecognised request type \"%s\"", argv[2]);
1385 packet_code = atoi(argv[2]);
1391 if (strcmp(argv[1], "-") != 0) {
1392 if (fr_pton_port(&server_ipaddr, &server_port, argv[1], -1, force_af, true) < 0) {
1393 ERROR("%s", fr_strerror());
1398 * Work backwards from the port to determine the packet type
1400 if (packet_code == PW_CODE_UNDEFINED) packet_code = radclient_get_code(server_port);
1402 radclient_get_port(packet_code, &server_port);
1407 if (argv[3]) secret = argv[3];
1410 * If no '-f' is specified, we're reading from stdin.
1412 if (rbtree_num_elements(filename_tree) == 0) {
1413 rc_file_pair_t *files;
1415 files = talloc_zero(talloc_autofree_context(), rc_file_pair_t);
1416 files->packets = "-";
1417 if (!radclient_init(files, files)) {
1423 * Walk over the list of filenames, creating the requests.
1425 if (rbtree_walk(filename_tree, RBTREE_IN_ORDER, filename_walk, NULL) != 0) {
1426 ERROR("Failed parsing input files");
1431 * No packets read. Die.
1433 if (!request_head) {
1434 ERROR("Nothing to send");
1439 * Bind to the first specified IP address and port.
1440 * This means we ignore later ones.
1442 if (request_head->packet->src_ipaddr.af == AF_UNSPEC) {
1443 memset(&client_ipaddr, 0, sizeof(client_ipaddr));
1444 client_ipaddr.af = server_ipaddr.af;
1446 client_ipaddr = request_head->packet->src_ipaddr;
1449 client_port = request_head->packet->src_port;
1453 sockfd = fr_socket_client_tcp(NULL, &server_ipaddr, server_port, false);
1456 sockfd = fr_socket(&client_ipaddr, client_port);
1458 ERROR("Error opening socket");
1462 pl = fr_packet_list_create(1);
1464 ERROR("Out of memory");
1468 if (!fr_packet_list_socket_add(pl, sockfd, ipproto, &server_ipaddr,
1469 server_port, NULL)) {
1470 ERROR("Out of memory");
1475 * Walk over the list of packets, sanity checking
1478 for (this = request_head; this != NULL; this = this->next) {
1479 this->packet->src_ipaddr = client_ipaddr;
1480 this->packet->src_port = client_port;
1481 if (radclient_sane(this) != 0) {
1487 * Walk over the packets to send, until
1490 * FIXME: This currently busy-loops until it receives
1491 * all of the packets. It should really have some sort of
1492 * send packet, get time to wait, select for time, etc.
1498 char const *filename = NULL;
1504 * Walk over the packets, sending them.
1507 for (this = request_head; this != NULL; this = next) {
1511 * If there's a packet to receive,
1512 * receive it, but don't wait for a
1518 * This packet is done. Delete it.
1526 * Packets from multiple '-f' are sent
1529 * Packets from one file are sent in
1530 * series, unless '-p' is specified, in
1531 * which case N packets from each file
1532 * are sent in parallel.
1534 if (this->files->packets != filename) {
1535 filename = this->files->packets;
1543 * Send the current packet.
1545 if (send_one_packet(this) < 0) {
1551 * Wait a little before sending
1552 * the next packet, if told to.
1558 * Don't sleep elsewhere.
1567 tv.tv_usec = 1000000/persec;
1571 * Sleep for milliseconds,
1574 * If we get an error or
1575 * a signal, treat it like
1578 select(0, NULL, NULL, NULL, &tv);
1582 * If we haven't sent this packet
1583 * often enough, we're not done,
1584 * and we shouldn't sleep.
1586 if (this->resend < resend_count) {
1590 } else { /* haven't sent this packet, we're not done */
1591 assert(this->done == false);
1592 assert(this->reply == NULL);
1598 * Still have outstanding requests.
1600 if (fr_packet_list_num_elements(pl) > 0) {
1607 * Nothing to do until we receive a request, so
1608 * sleep until then. Once we receive one packet,
1609 * we go back, and walk through the whole list again,
1610 * sending more packets (if necessary), and updating
1613 if (!done && (sleep_time > 0)) {
1614 recv_one_packet(sleep_time);
1618 rbtree_free(filename_tree);
1619 fr_packet_list_free(pl);
1620 while (request_head) TALLOC_FREE(request_head);
1624 DEBUG("Packet summary:\n"
1625 "\tAccepted : %" PRIu64 "\n"
1626 "\tRejected : %" PRIu64 "\n"
1627 "\tLost : %" PRIu64 "\n"
1628 "\tPassed filter : %" PRIu64 "\n"
1629 "\tFailed filter : %" PRIu64,
1638 if ((stats.lost > 0) || (stats.failed > 0)) {