-/* exactly one of client and server must be non-NULL */
-/* return who we received from in *client or *server */
-/* return from in sa if not NULL */
-unsigned char *radudpget(int s, struct client **client, struct server **server, uint16_t *port) {
- int cnt, len;
- unsigned char buf[4], *rad = NULL;
- struct sockaddr_storage from;
- struct sockaddr *fromcopy;
- socklen_t fromlen = sizeof(from);
- struct clsrvconf *p;
- struct list_node *node;
- fd_set readfds;
- struct client *c = NULL;
- struct timeval now;
-
- for (;;) {
- if (rad) {
- free(rad);
- rad = NULL;
- }
- FD_ZERO(&readfds);
- FD_SET(s, &readfds);
- if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
- continue;
- cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
- if (cnt == -1) {
- debug(DBG_WARN, "radudpget: recv failed");
- continue;
- }
-
- p = client
- ? find_clconf(handle, (struct sockaddr *)&from, NULL)
- : find_srvconf(handle, (struct sockaddr *)&from, NULL);
- if (!p) {
- debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from));
- recv(s, buf, 4, 0);
- continue;
- }
-
- len = RADLEN(buf);
- if (len < 20) {
- debug(DBG_WARN, "radudpget: length too small");
- recv(s, buf, 4, 0);
- continue;
- }
-
- rad = malloc(len);
- if (!rad) {
- debug(DBG_ERR, "radudpget: malloc failed");
- recv(s, buf, 4, 0);
- continue;
- }
-
- cnt = recv(s, rad, len, MSG_TRUNC);
- debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from));
-
- if (cnt < len) {
- debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
- continue;
- }
- if (cnt > len)
- debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
-
- if (client) {
- *client = NULL;
- pthread_mutex_lock(p->lock);
- for (node = list_first(p->clients); node;) {
- c = (struct client *)node->data;
- node = list_next(node);
- if (s != c->sock)
- continue;
- gettimeofday(&now, NULL);
- if (!*client && addr_equal((struct sockaddr *)&from, c->addr)) {
- c->expiry = now.tv_sec + 60;
- *client = c;
- }
- if (c->expiry >= now.tv_sec)
- continue;
-
- debug(DBG_DBG, "radudpget: removing expired client (%s)", addr2string(c->addr));
- removeudpclientfromreplyq(c);
- c->replyq = NULL; /* stop removeclient() from removing common udp replyq */
- removelockedclient(c);
- break;
+/* Callback for conn->wev and conn->rev. FIXME: Rename.
+
+ USER_DATA contains connection for EV_READ and a packet for
+ EV_WRITE. This is because we don't have a connect/establish entry
+ point at the user level -- send implies connect so when we're
+ connected we need the packet to send. */
+static void
+_evcb (evutil_socket_t fd, short what, void *user_data)
+{
+ int err;
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
+
+ rs_debug (("%s: fd=%d what =", __func__, fd));
+ if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT -- shouldn't happen!"));
+ if (what & EV_READ) rs_debug ((" READ"));
+ if (what & EV_WRITE) rs_debug ((" WRITE"));
+ rs_debug (("\n"));
+
+ assert (pkt);
+ assert (pkt->conn);
+ if (what & EV_READ)
+ {
+ /* Read a single UDP packet and stick it in USER_DATA. */
+ /* TODO: Verify that unsolicited packets are dropped. */
+ ssize_t r = 0;
+
+ assert (pkt->rpkt->data);
+
+ r = compat_recv (fd, pkt->rpkt->data, RS_MAX_PACKET_LEN, MSG_TRUNC);
+ if (r == -1)
+ {
+ int sockerr = evutil_socket_geterror (pkt->conn->fd);
+ if (sockerr == EAGAIN)
+ {
+ /* FIXME: Really shouldn't happen since we've been told
+ that fd is readable! */
+ rs_debug (("%s: EAGAIN reading UDP packet -- wot?\n"));
+ goto err_out;