|
@@ -4,9 +4,13 @@
|
|
|
#include <stdlib.h>
|
|
|
#include <strings.h>
|
|
|
#include <unistd.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <error.h>
|
|
|
#include <errno.h>
|
|
|
+#include <time.h>
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
+#include <sys/select.h>
|
|
|
#include <sys/socket.h>
|
|
|
#include <netdb.h>
|
|
|
|
|
@@ -16,7 +20,9 @@
|
|
|
#include "qa/qa.h"
|
|
|
#include "qa/qa_sock.h"
|
|
|
|
|
|
-#define TIMEOUT 5
|
|
|
+#define TIMEOUT_SEC 1
|
|
|
+#define TIMEOUT_USEC 0
|
|
|
+
|
|
|
#define SOCKET_PROTOCOL 0
|
|
|
#define INVALID_SOCKET (-1)
|
|
|
|
|
@@ -77,11 +83,9 @@ int host_port(char *uri, char **host, char **service)
|
|
|
int init_client(const char *host, const char *port)
|
|
|
{
|
|
|
int s, i;
|
|
|
+ fd_set socket_fds;
|
|
|
struct addrinfo *result, *rp;
|
|
|
- struct timeval timeout = {
|
|
|
- .tv_sec = TIMEOUT,
|
|
|
- .tv_usec = 0
|
|
|
- };
|
|
|
+ struct timeval timeout;
|
|
|
|
|
|
if ((i=getaddrinfo(host, port, NULL, &result))) {
|
|
|
BIO_printf(bio_err, "Error: %s\n", gai_strerror(i));
|
|
@@ -98,17 +102,28 @@ int init_client(const char *host, const char *port)
|
|
|
i = 0;
|
|
|
i = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*) &i, sizeof(i));
|
|
|
if (i < 0) return -1;
|
|
|
- i = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO,
|
|
|
- (char *) &timeout, sizeof(struct timeval));
|
|
|
- if (i < 0) return -1;
|
|
|
}
|
|
|
|
|
|
+ //Set the socket to non-blocking
|
|
|
+ int flags = fcntl(s, F_GETFL, 0);
|
|
|
+ fcntl(s, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
|
- if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) break;
|
|
|
- }
|
|
|
+ connect(s, rp->ai_addr, rp->ai_addrlen);
|
|
|
+ if (errno != EINPROGRESS) {
|
|
|
+ close(s);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- if (!rp) return -1;
|
|
|
+ FD_ZERO(&socket_fds);
|
|
|
+ FD_SET(s, &socket_fds);
|
|
|
+ timeout.tv_sec = TIMEOUT_SEC;
|
|
|
+ timeout.tv_usec = TIMEOUT_USEC;
|
|
|
+ i = select(s+1, NULL, &socket_fds, NULL, &timeout);
|
|
|
+ if (i > 0) break;
|
|
|
|
|
|
+ close(s);
|
|
|
+ }
|
|
|
+ if (!rp) return -1;
|
|
|
return s;
|
|
|
}
|
|
|
|
|
@@ -169,11 +184,11 @@ 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;
|
|
|
+ int attempts;
|
|
|
+ int err;
|
|
|
+ fd_set socket_fds;
|
|
|
+ struct timeval timeout;
|
|
|
|
|
|
/* parse input address */
|
|
|
if (!host_port(address, &host, &port)) goto error;
|
|
@@ -182,25 +197,36 @@ struct qa_connection* qa_connection_new(char* address)
|
|
|
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());
|
|
|
+ c->ctx = SSL_CTX_new(TLSv1_client_method());
|
|
|
if (!c->ctx) goto error;
|
|
|
/* create the ssl session, disabling certificate verification */
|
|
|
SSL_CTX_set_verify(c->ctx, SSL_VERIFY_NONE, verify_callback);
|
|
|
c->ssl = SSL_new(c->ctx);
|
|
|
+ SSL_set_connect_state(c->ssl);
|
|
|
+
|
|
|
if (!c->ssl) goto error;
|
|
|
/* open the socket over ssl */
|
|
|
c->socket = init_client(host, port);
|
|
|
- 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;
|
|
|
+ if (c->socket == -1) goto error;
|
|
|
+ if (!SSL_set_fd(c->ssl, c->socket)) goto error;
|
|
|
+
|
|
|
+ FD_ZERO(&socket_fds);
|
|
|
+ FD_SET(c->socket, &socket_fds);
|
|
|
+ for(attempts = 10; attempts; attempts--) {
|
|
|
+ err = SSL_do_handshake(c->ssl);
|
|
|
+ // err = SSL_connect(c->ssl);
|
|
|
+ if (err == 1) return c;
|
|
|
+
|
|
|
+ err = SSL_get_error(c->ssl, err);
|
|
|
+ timeout.tv_sec = TIMEOUT_SEC;
|
|
|
+ timeout.tv_usec = TIMEOUT_USEC;
|
|
|
+ if (err == SSL_ERROR_WANT_WRITE)
|
|
|
+ select(c->socket+1, NULL, &socket_fds, NULL, &timeout);
|
|
|
+ else if (err == SSL_ERROR_WANT_READ)
|
|
|
+ select(c->socket+1, &socket_fds, NULL, NULL, &timeout);
|
|
|
+ else
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
|
|
|
error:
|
|
|
/* XXX. add checks for errno, and the ssl error stack (ssl_get_error) */
|
|
@@ -220,7 +246,7 @@ struct qa_connection* qa_connection_new(char* address)
|
|
|
*/
|
|
|
X509* get_remote_cert(char *address)
|
|
|
{
|
|
|
- X509 *crt;
|
|
|
+ X509 *crt = NULL;
|
|
|
qa_connection_t *c;
|
|
|
|
|
|
c = qa_connection_new(address);
|