libradius: Use poll(2) to wait for RADIUS responses
authorMartin Willi <martin@revosec.ch>
Wed, 5 Nov 2014 15:28:42 +0000 (16:28 +0100)
committerMartin Willi <martin@revosec.ch>
Fri, 21 Nov 2014 10:16:48 +0000 (11:16 +0100)
src/libradius/radius_socket.c

index f432151..fe9cf3c 100644 (file)
@@ -129,7 +129,7 @@ METHOD(radius_socket_t, request, radius_message_t*,
        private_radius_socket_t *this, radius_message_t *request)
 {
        chunk_t data;
-       int i, *fd;
+       int i, *fd, retransmit = 0;
        u_int16_t port;
        rng_t *rng = NULL;
 
@@ -166,64 +166,59 @@ METHOD(radius_socket_t, request, radius_message_t*,
        for (i = 2; i <= 5; i++)
        {
                radius_message_t *response;
-               bool retransmit = FALSE;
-               struct timeval tv;
                char buf[4096];
-               fd_set fds;
                int res;
+               struct pollfd pfd = {
+                       .fd = *fd,
+                       .events = POLLIN,
+               };
 
+               if (retransmit)
+               {
+                       DBG1(DBG_CFG, "retransmitting RADIUS %N (attempt %d)",
+                                radius_message_code_names, request->get_code(request),
+                                retransmit);
+               }
                if (send(*fd, data.ptr, data.len, 0) != data.len)
                {
                        DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
                        return NULL;
                }
-               tv.tv_sec = i;
-               tv.tv_usec = 0;
-
-               while (TRUE)
+               res = poll(&pfd, 1, i * 1000);
+               if (res < 0)
                {
-                       FD_ZERO(&fds);
-                       FD_SET(*fd, &fds);
-                       res = select((*fd) + 1, &fds, NULL, NULL, &tv);
-                       /* TODO: updated tv to time not waited. Linux does this for us. */
-                       if (res < 0)
-                       {       /* failed */
-                               DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
-                                        strerror(errno));
-                               break;
-                       }
-                       if (res == 0)
-                       {       /* timeout */
-                               DBG1(DBG_CFG, "retransmitting RADIUS message");
-                               retransmit = TRUE;
-                               break;
-                       }
-                       res = recv(*fd, buf, sizeof(buf), MSG_DONTWAIT);
-                       if (res <= 0)
-                       {
-                               DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
-                                        strerror(errno));
-                               break;
-                       }
-                       response = radius_message_parse(chunk_create(buf, res));
-                       if (response)
-                       {
-                               if (response->verify(response,
-                                                       request->get_authenticator(request), this->secret,
-                                                       this->hasher, this->signer))
-                               {
-                                       return response;
-                               }
-                               response->destroy(response);
-                       }
-                       DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
+                       DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
+                                strerror(errno));
+                       return NULL;
+               }
+               if (res == 0)
+               {       /* timeout */
+                       retransmit++;
+                       continue;
                }
-               if (!retransmit)
+               res = recv(*fd, buf, sizeof(buf), MSG_DONTWAIT);
+               if (res <= 0)
                {
-                       break;
+                       DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
+                                strerror(errno));
+                       return NULL;
                }
+               response = radius_message_parse(chunk_create(buf, res));
+               if (response)
+               {
+                       if (response->verify(response,
+                                               request->get_authenticator(request), this->secret,
+                                               this->hasher, this->signer))
+                       {
+                               return response;
+                       }
+                       response->destroy(response);
+               }
+               DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
+               return NULL;
        }
-       DBG1(DBG_CFG, "RADIUS server is not responding");
+       DBG1(DBG_CFG, "RADIUS %N timed out after %d retransmits",
+                radius_message_code_names, request->get_code(request), retransmit - 1);
        return NULL;
 }