Browse Source

Timeouts.

Filling remote certificate retrieval with timeout, at tcp level, plus ssl level.
Note: for some reasons, sites like `baidu.com` still fail after an incredibly
long timeout, but that's the same with the browser, afais. (wtf)
Michele Orrù 11 years ago
parent
commit
17b43ae625
1 changed files with 40 additions and 17 deletions
  1. 40 17
      src/qa_sock.c

+ 40 - 17
src/qa_sock.c

@@ -16,6 +16,7 @@
 #include "qa/qa.h"
 #include "qa/qa_sock.h"
 
+#define TIMEOUT 5
 #define SOCKET_PROTOCOL 0
 #define INVALID_SOCKET  (-1)
 
@@ -64,18 +65,23 @@ int host_port(char *uri, char **host, char **service)
   return 1;
 }
 
+
+/**
+ * \brief Instantiate a new TCP connection.
+ *
+ * Attempt to create tcp connection with  with the remote host `host`
+ * - eventually resolved - over port `port`.
+ * \return -1 on failure, the socket file descriptor otherwise.
+ *
+ */
 int init_client(const char *host, const char *port)
 {
-  struct addrinfo hints;
+  int s, i;
   struct addrinfo *result, *rp;
-  int s;
-  int i;
-
-
-  hints.ai_family = AF_UNSPEC;
-  hints.ai_socktype = SOCK_STREAM;
-  hints.ai_flags = 0;
-  hints.ai_protocol = 0;
+  struct timeval timeout = {
+    .tv_sec = TIMEOUT,
+    .tv_usec = 0
+  };
 
   if ((i=getaddrinfo(host, port, NULL, &result))) {
     BIO_printf(bio_err, "Error: %s\n", gai_strerror(i));
@@ -91,11 +97,16 @@ int init_client(const char *host, const char *port)
     if (rp->ai_protocol == SOCK_STREAM) {
        i = 0;
        i = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*) &i, sizeof(i));
-       if (i < 0) exit(EXIT_FAILURE);
+       if (i < 0) return -1;
+       i = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO,
+                      (char *) &timeout, sizeof(struct timeval));
+       if (i < 0) return -1;
     }
 
+
     if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) break;
   }
+
   if (!rp) return -1;
 
   return s;
@@ -106,8 +117,9 @@ int init_client(const char *host, const char *port)
  */
 typedef struct qa_connection {
   int socket;   /**< socket file descriptor. */
-  SSL* ssl;     /**< ssl handler for this connection. */
-  SSL_CTX* ctx; /**< ssl context used in this connection. */
+  SSL *ssl;     /**< ssl handler for this connection. */
+  BIO  *sbio;
+  SSL_CTX *ctx; /**< ssl context used in this connection. */
 } qa_connection_t;
 
 
@@ -124,6 +136,8 @@ static void qa_connection_free(struct qa_connection* c)
 {
   if (!c) return;
 
+  if (c->sbio)
+    BIO_free(c->sbio);
   if (c->socket != -1)
     close(c->socket);
   if (c->ssl) {
@@ -155,13 +169,17 @@ static int verify_callback(int ok, X509_STORE_CTX* ctx)
 struct qa_connection* qa_connection_new(char* address)
 {
   struct qa_connection* c = NULL;
+  struct timeval timeout = {
+    .tv_sec = TIMEOUT,
+    .tv_usec = 0
+  };
   char *host, *port;
 
   /* parse input address */
   if (!host_port(address, &host, &port)) goto error;
   if (!port) port = "https";
 
-  c = malloc(sizeof(struct qa_connection));
+  c = calloc(1, sizeof(struct qa_connection));
   if (!c) goto error;
   /* set up context, and protocol versions */
   c->ctx = SSL_CTX_new(SSLv23_client_method());
@@ -172,10 +190,15 @@ struct qa_connection* qa_connection_new(char* address)
   if (!c->ssl) goto error;
   /* open the socket over ssl */
   c->socket = init_client(host, port);
-  if (c->socket == -1)                goto error;
-  if (!SSL_set_fd(c->ssl, c->socket)) goto error;
-  if (SSL_connect(c->ssl) != 1)       goto error;
-
+  c->sbio = BIO_new_dgram(c->socket, BIO_NOCLOSE);
+  // BIO_ctrl(c->sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout);
+  BIO_ctrl(c->sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
+
+  if (c->socket == -1)
+    goto error;
+  SSL_set_bio(c->ssl, c->sbio, c->sbio);
+  if (SSL_connect(c->ssl) != 1)
+    goto error;
   SSL_set_connect_state(c->ssl);
   return c;