/*
 *	Send one packet.
 */
static int send_one_packet(rc_request_t *request)
{
	assert(request->done == false);

	/*
	 *	Remember when we have to wake up, to re-send the
	 *	request, of we didn't receive a reply.
	 */
	if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout;

	/*
	 *	Haven't sent the packet yet.  Initialize it.
	 */
	if (request->packet->id == -1) {
		int i;
		bool rcode;

		assert(request->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:
		request->packet->src_ipaddr.af = server_ipaddr.af;
		rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL);
		if (!rcode) {
			int mysockfd;

#ifdef WITH_TCP
			if (proto) {
				mysockfd = fr_socket_client_tcp(NULL,
								&request->packet->dst_ipaddr,
								request->packet->dst_port, false);
			} else
#endif
			mysockfd = fr_socket(&client_ipaddr, 0);
			if (mysockfd < 0) {
				ERROR("Failed opening socket");
				exit(1);
			}
			if (!fr_packet_list_socket_add(pl, mysockfd, ipproto,
						       &request->packet->dst_ipaddr,
						       request->packet->dst_port, NULL)) {
				ERROR("Can't add new socket");
				exit(1);
			}
			goto retry;
		}

		assert(request->packet->id != -1);
		assert(request->packet->data == NULL);

		for (i = 0; i < 4; i++) {
			((uint32_t *) request->packet->vector)[i] = fr_rand();
		}

		/*
		 *	Update the password, so it can be encrypted with the
		 *	new authentication vector.
		 */
		if (request->password) {
			VALUE_PAIR *vp;

			if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY)) != NULL) {
				fr_pair_value_strcpy(vp, request->password->vp_strvalue);

			} else if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_CHAP_PASSWORD, TAG_ANY)) != NULL) {
				uint8_t buffer[17];

				fr_radius_encode_chap_password(buffer, request->packet, fr_rand() & 0xff, request->password);
				fr_pair_value_memcpy(vp, buffer, 17);

			} else if (fr_pair_find_by_num(request->packet->vps, 0, PW_MS_CHAP_PASSWORD, TAG_ANY) != NULL) {
				mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue);

			} else {
				DEBUG("WARNING: No password in the request");
			}
		}

		request->timestamp = time(NULL);
		request->tries = 1;
		request->resend++;

	} else {		/* request->packet->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 - request->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 - request->timestamp))) {
				sleep_time = now - request->timestamp;
			}
			return 0;
		}

		/*
		 *	We're not trying later, maybe the packet is done.
		 */
		if (request->tries == retries) {
			assert(request->packet->id >= 0);

			/*
			 *	Delete the request from the tree of
			 *	outstanding requests.
			 */
			fr_packet_list_yank(pl, request->packet);

			REDEBUG("No reply from server for ID %d socket %d",
				request->packet->id, request->packet->sockfd);
			deallocate_id(request);

			/*
			 *	Normally we mark it "done" when we've received
			 *	the reply, but this is a special case.
			 */
			if (request->resend == resend_count) {
				request->done = true;
			}
			stats.lost++;
			return -1;
		}

		/*
		 *	We are trying later.
		 */
		request->timestamp = now;
		request->tries++;
	}

	/*
	 *	Send the packet.
	 */
	if (fr_radius_send(request->packet, NULL, secret) < 0) {
		REDEBUG("Failed to send packet for ID %d", request->packet->id);
		deallocate_id(request);
		request->done = true;
		return -1;
	}

	fr_packet_header_print(fr_log_fp, request->packet, false);
	if (fr_debug_lvl > 0) fr_pair_list_fprint(fr_log_fp, request->packet->vps);

	return 0;
}
/*
 *	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, TAG_ANY)) != NULL) {
                pairstrcpy(vp, radclient->password);

            } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != 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) {
                    uint8_t *p;
                    size_t len, len2;

                    len = len2 = strlen(radclient->password);
                    if (len2 < 17) len2 = 17;

                    p = talloc_zero_array(vp, uint8_t, len2);

                    memcpy(p, radclient->password, len);

                    rad_chap_encode(radclient->request,
                                    p,
                                    fr_rand() & 0xff, vp);
                    vp->vp_octets = p;
                    vp->length = 17;
                }
            } else if (pairfind(radclient->request->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY) != NULL) {
                mschapv1_encode(radclient->request,
                                &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;
}