--- a/src/irc_net.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/irc_net.c Thu Apr 16 01:20:09 2009 +0300
@@ -271,7 +271,7 @@
// trap errors
if (conn_err)
- FATAL_ERROR(conn_err, "async TCP connect failed");
+ FATAL_ERROR(conn_err, "async connect failed");
// ok, yay
if (irc_net_connected(net, sock, &err))
@@ -294,26 +294,22 @@
TAILQ_INIT(&net->channels);
LIST_INIT(&net->users);
- // non-async socket connect?
- if (info->raw_sock || info->use_ssl) {
- // create the socket
- if (info->raw_sock) {
- log_debug("connected using raw socket: %p", info->raw_sock);
+ if (info->raw_sock) {
+ // direct sock_stream connection
+ log_debug("connected using raw socket: %p", info->raw_sock);
- sock = info->raw_sock;
-
- } else if (info->use_ssl) {
- log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
-
- // XXX: over-simplified blocking connect
- if (sock_ssl_connect(&sock, info->hostname, info->service, err))
- goto error;
- }
+ sock = info->raw_sock;
// then create the conn right away
if (irc_net_connected(net, sock, err))
goto error;
+ } else if (info->use_ssl) {
+ log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
+
+ if (sock_ssl_connect_async(&sock, info->hostname, info->service, &irc_net_on_connect, net, err))
+ goto error;
+
} else {
log_debug("connecting to [%s]:%s", info->hostname, info->service);
--- a/src/log.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/log.c Thu Apr 16 01:20:09 2009 +0300
@@ -114,7 +114,7 @@
va_end(vargs);
}
-void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...)
+void _log_err_code (enum log_level level, err_t err, const char *func, const char *format, ...)
{
va_list vargs;
@@ -124,7 +124,7 @@
va_end(vargs);
}
-void _log_err_info (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
+void _log_err (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
{
va_list vargs;
--- a/src/log.h Sun Apr 12 23:27:15 2009 +0300
+++ b/src/log.h Thu Apr 16 01:20:09 2009 +0300
@@ -70,13 +70,13 @@
/**
* Log a message with the given level, appending the formatted error code name
*/
-void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...)
+void _log_err_code (enum log_level level, err_t err, const char *func, const char *format, ...)
__attribute__ ((format (printf, 4, 5)));
/**
* Log a message with the given level, appending the formatted error message
*/
-void _log_err_info (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
+void _log_err (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
__attribute__ ((format (printf, 4, 5)));
/**
@@ -87,11 +87,16 @@
/**
* Shorthand for _log_*
+ *
+ * XXX: kill log_err_info, add log_err_code
*/
-#define log_err(err, ...) _log_err(LOG_ERROR, err, __func__, __VA_ARGS__)
-#define log_err_info(err_info, ...) _log_err_info(LOG_ERROR, err_info, __func__, __VA_ARGS__)
+#define log_err(err_code, ...) _log_err_code(LOG_ERROR, err_code, __func__, __VA_ARGS__)
+#define log_err_info(err_info, ...) _log_err(LOG_ERROR, err_info, __func__, __VA_ARGS__)
#define log_perr(...) _log_perr(LOG_ERROR, __func__, __VA_ARGS__)
+#define log_warn_err_code(err_code, ...) _log_err_code(LOG_WARN, err_code, __func__, __VA_ARGS__)
+#define log_warn_err(err_info, ...) _log_err(LOG_WARN, err_info, __func__, __VA_ARGS__)
+
/**
* log_fatal + exit failure
*/
@@ -100,12 +105,12 @@
/**
* log_err + exit failure
*/
-#define FATAL_ERR(err_code, ...) do { _log_err(LOG_FATAL, err_code, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
+#define FATAL_ERR(err_code, ...) do { _log_err_code(LOG_FATAL, err_code, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
/**
* log_err_info + exit failure
*/
-#define FATAL_ERROR(err_info, ...) do { _log_err_info(LOG_FATAL, err_info, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
+#define FATAL_ERROR(err_info, ...) do { _log_err(LOG_FATAL, err_info, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
/**
* log_perr + exit failure
--- a/src/nexus.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/nexus.c Thu Apr 16 01:20:09 2009 +0300
@@ -154,7 +154,7 @@
while (opt) {
char *flag = strsep(&opt, ":");
- if (strcmp(flag, "ssl"))
+ if (strcmp(flag, "ssl") == 0)
info.use_ssl = true;
else
--- a/src/sock.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock.c Thu Apr 16 01:20:09 2009 +0300
@@ -20,13 +20,15 @@
return SUCCESS;
}
-void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type)
+void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type, sock_stream_connect_cb cb_func, void *cb_arg)
{
// be strict
assert(sock->type == NULL);
- // store type
+ // store
sock->type = type;
+ sock->conn_cb_func = cb_func;
+ sock->conn_cb_arg = cb_arg;
}
int sock_stream_read (struct sock_stream *sock, void *buf, size_t len)
@@ -92,6 +94,22 @@
sock->cb_info->on_read(sock, sock->cb_arg);
}
+void sock_stream_invoke_conn_cb (struct sock_stream *sock, struct error_info *err, bool direct)
+{
+ if (!direct && sock->type->methods._conn_cb) {
+ // invoke indirectly
+ sock->type->methods._conn_cb(sock, err);
+
+ } else {
+ sock_stream_connect_cb cb_func = sock->conn_cb_func;
+
+ // invoke directly
+ sock->conn_cb_func = NULL;
+ cb_func(sock, err, sock->conn_cb_arg);
+ sock->conn_cb_arg = NULL;
+ }
+}
+
void sock_stream_release (struct sock_stream *sock)
{
sock->type->methods.release(sock);
--- a/src/sock.h Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock.h Thu Apr 16 01:20:09 2009 +0300
@@ -91,18 +91,19 @@
sock_stream_connect_cb cb_func, void *cb_arg, struct error_info *err);
/**
- * A simple blocking SSL connect to the given host/service. The connected/handshake'd SSL socket is returned via
- * *sock_ptr. In case of errors, additional error information is stored in *err.
- *
+ * Start a non-blocking SSL connect/handshake to the given host/service. The socket will not yet be connected when the
+ * function returns, but rather, the eventual redyness/failure of the connect/handshake will be indicated later using
+ * the given \a cb_func.
+ *
* @param sock_ptr the new sock_stream
* @param host the hostname to connect to
* @param service the TCP service name (i.e. port) to connect to
* @param err returned error info
*
- * XXX: blocking
* XXX: doesn't do any certificate verification.
*/
-err_t sock_ssl_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err);
+err_t sock_ssl_connect_async (struct sock_stream **sock_ptr, const char *host, const char *service,
+ sock_stream_connect_cb cb_func, void *cb_arg, struct error_info *err);
/**
* A read-only "socket" based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
--- a/src/sock_fifo.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock_fifo.c Thu Apr 16 01:20:09 2009 +0300
@@ -133,7 +133,7 @@
return SET_ERROR(err, ERR_STRDUP);
// init
- sock_stream_init(FIFO_BASE(fifo), &fifo_stream_type);
+ sock_stream_init(FIFO_BASE(fifo), &fifo_stream_type, NULL, NULL);
sock_fd_init(FIFO_FD(fifo), -1);
// open the fifo
--- a/src/sock_gnutls.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock_gnutls.c Thu Apr 16 01:20:09 2009 +0300
@@ -1,9 +1,121 @@
#include "sock_gnutls.h"
+// XXX: remove
+#include "log.h"
+
#include <stdlib.h>
#include <err.h>
+/**
+ * Register for events based on the session's gnutls_record_get_direction().
+ */
+static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, struct error_info *err)
+{
+ int ret;
+
+ // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
+ // operation, so we can use that to determine what events to register
+ switch ((ret = gnutls_record_get_direction(sock->session))) {
+ case 0:
+ // read more data
+ sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_READ);
+ break;
+
+ case 1:
+ // write buffer full
+ sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_WRITE);
+ break;
+
+ default:
+ // random error
+ RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
+ }
+
+ // ok... wait
+ return SUCCESS;
+}
+
+/**
+ * Our handshake driver. This will execute the next gnutls_handshake step, handling E_AGAIN.
+ *
+ * This updates the sock_gnutls::handshake state internally, as used by sock_gnutls_event_handler.
+ *
+ * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
+ */
+static int sock_gnutls_handshake (struct sock_gnutls *sock, struct error_info *err)
+{
+ int ret;
+
+ // perform the handshake
+ if ((ret = gnutls_handshake(sock->session)) < 0 && ret != GNUTLS_E_AGAIN)
+ JUMP_SET_ERROR_EXTRA(err, ERR_GNUTLS_HANDSHAKE, ret);
+
+ // complete?
+ if (ret == 0) {
+ // update state
+ sock->handshake = false;
+
+ // handshake done
+ return 1;
+
+ } else {
+ // set state, isn't really needed every time, but easier this way
+ sock->handshake = true;
+
+ // re-enable the event for the next iteration
+ return sock_gnutls_ev_enable(sock, err);
+ }
+
+error:
+ return -ERROR_CODE(err);
+}
+
+/**
+ * Our SOCK_STREAM event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
+ *
+ * XXX: this is ugly. This sock_stream-level separation doesn't really work that well.
+ */
+static void sock_gnutls_event_handler (int fd, short what, void *arg)
+{
+ struct sock_gnutls *sock = arg;
+ struct error_info err;
+
+ (void) fd;
+ (void) what;
+
+ // are we in the handshake cycle?
+ if (sock->handshake) {
+ RESET_ERROR(&err);
+
+ if (sock_gnutls_handshake(sock, &err) == 0) {
+ // wait for the next handshake step
+
+ } else if (SOCK_GNUTLS_BASE(sock)->conn_cb_func) {
+ // the async connect process has now completed, either succesfully or not
+ // invoke the user connect callback directly
+ sock_stream_invoke_conn_cb(SOCK_GNUTLS_BASE(sock), ERROR_CODE(&err) ? &err : NULL, true);
+
+ } else {
+ // re-handshake completed, so continue with the sock_stream_callbacks, so the user can call sock_gnutls_read/write
+
+ if (ERROR_CODE(&err))
+ // XXX: bad, since we can't report this directly... we need to let the user call _read/write, and get
+ // the error from there
+ log_warn_err(&err, "sock_gnutls_handshake failed");
+
+ // continue where we left off
+ sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask);
+ }
+
+ } else {
+ // normal sock_stream operation
+ // gnutls might be able to proceed now, so ask user to try what didn't work before now, using the mask given to
+ // event_enable().
+ sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask);
+ }
+}
+
static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
{
struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
@@ -61,65 +173,26 @@
return SUCCESS;
}
-static void sock_gnutls_event_handler (int fd, short what, void *arg)
-{
- struct sock_gnutls *sock = arg;
-
- (void) fd;
- (void) what;
-
- // gnutls might be able to proceed now, so ask user to try what didn't work before now, using the mask given to
- // event_enable().
- sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask);
-}
-
static err_t sock_gnutls_event_init (struct sock_stream *base_sock)
{
struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
- err_t err;
+ (void) sock;
- // set nonblocking
- if ((err = sock_fd_set_nonblock(SOCK_GNUTLS_FD(sock), true)))
- return err;
-
- // add ourselves as the event handler
- if ((err = sock_fd_init_ev(SOCK_GNUTLS_FD(sock), &sock_gnutls_event_handler, sock)))
- return err;
-
- // ok
+ // already setup, ok
return SUCCESS;
}
static err_t sock_gnutls_event_enable (struct sock_stream *base_sock, short mask)
{
struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
- int ret;
// store the ev_mask. We don't care about it here, because we assume that event_enable is only called once read or
// write, respectively, return zero. This is really the only case we can handle with gnutls.
sock->ev_mask = mask;
-
- // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
- // operation, so we can use that to determine what events to register
- switch ((ret = gnutls_record_get_direction(sock->session))) {
- case 0:
- // read more data
- sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_READ);
- break;
-
- case 1:
- // write buffer full
- sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_WRITE);
- break;
-
- default:
- // random error
- RETURN_SET_ERROR_EXTRA(SOCK_GNUTLS_ERR(sock), ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
- }
- // ok... wait
- return SUCCESS;
+ // then wait for the event
+ return sock_gnutls_ev_enable(sock, SOCK_GNUTLS_ERR(sock));
}
static void sock_gnutls_release (struct sock_stream *base_sock)
@@ -130,6 +203,38 @@
sock_gnutls_destroy(sock);
}
+/**
+ * Our sock_tcp-invoked connect handler
+ */
+static void sock_gnutls_on_connect (struct sock_stream *base_sock, struct error_info *tcp_err)
+{
+ struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
+ struct error_info err;
+
+ // trap errors to let the user handle them directly
+ if (tcp_err)
+ JUMP_SET_ERROR_INFO(&err, tcp_err);
+
+ // bind default transport functions (recv/send) to use the TCP fd
+ gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd);
+
+ // add ourselves as the event handler
+ if ((ERROR_CODE(&err) = sock_fd_init_ev(SOCK_GNUTLS_FD(sock), &sock_gnutls_event_handler, sock)))
+ goto error;
+
+ // start handshake
+ if (sock_gnutls_handshake(sock, &err))
+ // this should complete with SUCCESS if it returns >0
+ goto error;
+
+ // ok, so we wait...
+ return;
+
+error:
+ // tell the user
+ SOCK_GNUTLS_BASE(sock)->conn_cb_func(SOCK_GNUTLS_BASE(sock), &err, SOCK_GNUTLS_BASE(sock)->conn_cb_arg);
+}
+
/*
* Our sock_stream_Type
*/
@@ -140,6 +245,7 @@
.event_init = &sock_gnutls_event_init,
.event_enable = &sock_gnutls_event_enable,
.release = &sock_gnutls_release,
+ ._conn_cb = &sock_gnutls_on_connect,
},
};
@@ -185,7 +291,8 @@
return SUCCESS;
}
-err_t sock_ssl_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err)
+err_t sock_ssl_connect_async (struct sock_stream **sock_ptr, const char *host, const char *service,
+ sock_stream_connect_cb cb_func, void *cb_arg, struct error_info *err)
{
struct sock_gnutls *sock = NULL;
struct sock_gnutls_client_ctx *ctx = &_sock_gnutls_client_ctx;
@@ -195,7 +302,7 @@
return SET_ERROR(err, ERR_CALLOC);
// initialize base
- sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type);
+ sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type, cb_func, cb_arg);
// initialize client session
if ((ERROR_EXTRA(err) = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0)
@@ -210,17 +317,10 @@
JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
// TCP connect
- if (sock_tcp_connect_blocking(SOCK_GNUTLS_TCP(sock), host, service, err))
+ if (sock_tcp_connect_async_begin(SOCK_GNUTLS_TCP(sock), host, service, err))
goto error;
- // bind default transport functions (recv/send) to use the TCP fd
- gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd);
-
- // perform the handshake
- if ((ERROR_EXTRA(err) = gnutls_handshake(sock->session)) < 0)
- JUMP_SET_ERROR(err, ERR_GNUTLS_HANDSHAKE);
-
- // done
+ // done, wait for the connect to complete
*sock_ptr = SOCK_GNUTLS_BASE(sock);
return SUCCESS;
--- a/src/sock_gnutls.h Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock_gnutls.h Thu Apr 16 01:20:09 2009 +0300
@@ -54,6 +54,9 @@
/** The current event_enable mask */
int ev_mask;
+
+ /** Are we running a handshake? */
+ bool handshake;
};
/**
--- a/src/sock_internal.h Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock_internal.h Thu Apr 16 01:20:09 2009 +0300
@@ -7,6 +7,7 @@
* internal sock_* interface
*/
#include "sock.h"
+#include <stdbool.h>
/**
* General state for all sock_stream's
@@ -40,6 +41,9 @@
/** Release all resources and free the sock_stream */
void (*release) (struct sock_stream *sock);
+
+ /** The type's connect_cb handler, defaults to just invoke conn_cb_func */
+ void (*_conn_cb) (struct sock_stream *sock, struct error_info *err);
};
/**
@@ -93,8 +97,10 @@
*
* @param sock the new sock_stream
* @param type the sock_stream_type defining the implementation used
+ * @param cb_func the optional connect_async callback function
+ * @param cb_arg the optional context argument for cb_func
*/
-void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type);
+void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type, sock_stream_connect_cb cb_func, void *cb_arg);
/**
* Invoke the appropriate callbacks for the given EV_* bitmask.
@@ -104,4 +110,13 @@
*/
void sock_stream_invoke_callbacks (struct sock_stream *sock, short what);
+/**
+ * Invoke the sock_stream_conn_cb callback with the given error param.
+ *
+ * This invokes the sock_stream_methods::_conn_cb if present and \a direct is not given, otherwise the callback directly
+ *
+ * @param direct force the conn_cb to be called directly
+ */
+void sock_stream_invoke_conn_cb (struct sock_stream *sock, struct error_info *err, bool direct);
+
#endif /* SOCK_INTERNAL_H */
--- a/src/sock_tcp.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock_tcp.c Thu Apr 16 01:20:09 2009 +0300
@@ -29,14 +29,14 @@
},
};
-static err_t sock_tcp_alloc (struct sock_tcp **sock_ptr)
+static err_t sock_tcp_alloc (struct sock_tcp **sock_ptr, sock_stream_connect_cb cb_func, void *cb_arg)
{
// alloc
if ((*sock_ptr = calloc(1, sizeof(**sock_ptr))) == NULL)
return ERR_CALLOC;
// initialize base with sock_tcp_type
- sock_stream_init(SOCK_TCP_BASE(*sock_ptr), &sock_tcp_type);
+ sock_stream_init(SOCK_TCP_BASE(*sock_ptr), &sock_tcp_type, cb_func, cb_arg);
// init without any fd
sock_fd_init(SOCK_TCP_FD(*sock_ptr), -1);
@@ -107,7 +107,7 @@
sock_fd_deinit_ev(SOCK_TCP_FD(sock));
// ok, run callback
- sock_base->conn_cb_func(sock_base, err, sock_base->conn_cb_arg);
+ sock_stream_invoke_conn_cb(sock_base, err, false);
}
/**
@@ -304,7 +304,7 @@
struct sock_tcp *sock;
// allocate
- if ((ERROR_CODE(err) = sock_tcp_alloc(&sock)))
+ if ((ERROR_CODE(err) = sock_tcp_alloc(&sock, NULL, NULL)))
return ERROR_CODE(err);
// connect
@@ -329,13 +329,9 @@
{
struct sock_tcp *sock;
- // allocate
- if ((ERROR_CODE(err) = sock_tcp_alloc(&sock)))
+ // allocate and init
+ if ((ERROR_CODE(err) = sock_tcp_alloc(&sock, cb_func, cb_arg)))
return ERROR_CODE(err);
-
- // store the callbacks
- SOCK_TCP_BASE(sock)->conn_cb_func = cb_func;
- SOCK_TCP_BASE(sock)->conn_cb_arg = cb_arg;
// connect
if (sock_tcp_connect_async_begin(sock, host, service, err))
--- a/src/sock_test.c Sun Apr 12 23:27:15 2009 +0300
+++ b/src/sock_test.c Thu Apr 16 01:20:09 2009 +0300
@@ -167,7 +167,7 @@
assert((sock = calloc(1, sizeof(*sock))));
// initialize base with our sock_stream_type
- sock_stream_init(SOCK_TEST_BASE(sock), &sock_test_type);
+ sock_stream_init(SOCK_TEST_BASE(sock), &sock_test_type, NULL, NULL);
// ok
return sock;