/* * Send one packet. */ static int send_one_packet(radclient_t *radclient) { assert(radclient->done == 0); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a response. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) { sleep_time = (int) timeout; } /* * Haven't sent the packet yet. Initialize it. */ if (radclient->request->id == -1) { int i, rcode; assert(radclient->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: radclient->request->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, radclient->request, NULL); if (rcode < 0) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_tcp_client_socket(NULL, &server_ipaddr, server_port); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (!mysockfd) { fprintf(stderr, "radclient: Can't open new socket\n"); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &server_ipaddr, server_port, NULL)) { fprintf(stderr, "radclient: Can't add new socket\n"); exit(1); } goto retry; } if (rcode == 0) { done = 0; sleep_time = 0; return 0; } assert(radclient->request->id != -1); assert(radclient->request->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) radclient->request->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (radclient->password[0] != '\0') { VALUE_PAIR *vp; if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD, 0)) != NULL) { strlcpy(vp->vp_strvalue, radclient->password, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD, 0)) != NULL) { int already_hex = 0; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = 1; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { strlcpy(vp->vp_strvalue, radclient->password, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); rad_chap_encode(radclient->request, vp->vp_octets, fr_rand() & 0xff, vp); vp->length = 17; } } else if (pairfind(radclient->request->vps, PW_MSCHAP_PASSWORD, 0) != NULL) { mschapv1_encode(&radclient->request->vps, radclient->password); } else if (fr_debug_flag) { printf("WARNING: No password in the request\n"); } } radclient->timestamp = time(NULL); radclient->tries = 1; radclient->resend++; /* * Duplicate found. Serious error! */ if (!fr_packet_list_insert(pl, &radclient->request)) { assert(0 == 1); } #ifdef WITH_TCP /* * WTF? */ if (client_port == 0) { client_ipaddr = radclient->request->src_ipaddr; client_port = radclient->request->src_port; } #endif } else { /* radclient->request->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - radclient->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - radclient->timestamp))) { sleep_time = now - radclient->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (radclient->tries == retries) { assert(radclient->request->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, radclient->request); fprintf(stderr, "radclient: no response from server for ID %d socket %d\n", radclient->request->id, radclient->request->sockfd); deallocate_id(radclient); /* * Normally we mark it "done" when we've received * the response, but this is a special case. */ if (radclient->resend == resend_count) { radclient->done = 1; } totallost++; return -1; } /* * We are trying later. */ radclient->timestamp = now; radclient->tries++; } /* * Send the packet. */ if (rad_send(radclient->request, NULL, secret) < 0) { fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n", radclient->request->id, fr_strerror()); } if (fr_debug_flag > 2) print_hex(radclient->request); return 0; }
/* * 1 == ID was allocated & assigned * 0 == couldn't allocate ID. * * Note that this ALSO assigns a socket to use, and updates * packet->request->src_ipaddr && packet->request->src_port * * In multi-threaded systems, the calls to id_alloc && id_free * should be protected by a mutex. This does NOT have to be * the same mutex as the one protecting the insert/find/yank * calls! * * We assume that the packet has dst_ipaddr && dst_port * already initialized. We will use those to find an * outgoing socket. The request MAY also have src_ipaddr set. * * We also assume that the sender doesn't care which protocol * should be used. */ bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, RADIUS_PACKET **request_p, void **pctx) { int i, fd, start_i; int src_any = 0; fr_packet_socket_t *ps; RADIUS_PACKET *request = *request_p; if ((request->dst_ipaddr.af == AF_UNSPEC) || (request->dst_port == 0)) { fr_strerror_printf("No destination address/port specified"); return false; } #ifndef WITH_TCP if ((proto != 0) && (proto != IPPROTO_UDP)) { fr_strerror_printf("Invalid destination protocol"); return false; } #endif /* * Special case: unspec == "don't care" */ if (request->src_ipaddr.af == AF_UNSPEC) { memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr)); request->src_ipaddr.af = request->dst_ipaddr.af; } src_any = fr_inaddr_any(&request->src_ipaddr); if (src_any < 0) { fr_strerror_printf("Can't check src_ipaddr"); return false; } /* * MUST specify a destination address. */ if (fr_inaddr_any(&request->dst_ipaddr) != 0) { fr_strerror_printf("Must specify a dst_ipaddr"); return false; } fd = -1; start_i = fr_rand() & SOCKOFFSET_MASK; /* * Pick a random socket which has a free ID */ #define ID_i ((i + start_i) & SOCKOFFSET_MASK) for (i = 0; i < MAX_SOCKETS; i++) { if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */ ps = &(pl->sockets[ID_i]); /* * This socket is marked as "don't use for new * packets". But we can still receive packets * that are outstanding. */ if (ps->dont_use) continue; /* * All IDs are allocated: ignore it. */ if (ps->num_outgoing == 256) continue; #ifdef WITH_TCP if (ps->proto != proto) continue; #endif /* * Address families don't match, skip it. */ if (ps->src_ipaddr.af != request->dst_ipaddr.af) continue; /* * MUST match dst port, if we have one. */ if ((ps->dst_port != 0) && (ps->dst_port != request->dst_port)) continue; /* * MUST match requested src port, if one has been given. */ if ((request->src_port != 0) && (ps->src_port != request->src_port)) continue; /* * We're sourcing from *, and they asked for a * specific source address: ignore it. */ if (ps->src_any && !src_any) continue; /* * We're sourcing from a specific IP, and they * asked for a source IP that isn't us: ignore * it. */ if (!ps->src_any && !src_any && (fr_ipaddr_cmp(&request->src_ipaddr, &ps->src_ipaddr) != 0)) continue; /* * UDP sockets are allowed to match * destination IPs exactly, OR a socket * with destination * is allowed to match * any requested destination. * * TCP sockets must match the destination * exactly. They *always* have dst_any=0, * so the first check always matches. */ if (!ps->dst_any && (fr_ipaddr_cmp(&request->dst_ipaddr, &ps->dst_ipaddr) != 0)) continue; /* * We have a socket with less than 256 outgoing * connections. There MUST be an ID free. */ fd = i; break; } /* * Ask the caller to allocate a new ID. */ if (fd < 0) { fr_strerror_printf("Failed finding socket, caller must allocate a new one"); return false; } /* * Set the ID, source IP, and source port. */ request->id = ps->ids[ps->ids_index]; request->sockfd = ps->sockfd; request->src_ipaddr = ps->src_ipaddr; request->src_port = ps->src_port; /* * If we managed didn't insert it, undo the work above. */ if (!fr_packet_list_insert(pl, request_p)) { request->id = -1; request->sockfd = -1; request->src_ipaddr.af = AF_UNSPEC; request->src_port = 0; return false; } /* * We did insert it. Update the counters. */ if (pctx) *pctx = ps->ctx; ps->num_outgoing++; pl->num_outgoing++; ps->ids_index++; /* * Refill the ID array with random IDs. */ if (ps->ids_index == 256) { for (i = 0; i < 256; i++) { ps->ids[fr_rand() & 0xff] = i; } ps->ids_index = 0; } return true; }