rename sock_tcp to tcp_{transport,client}, fix irc_net_connect to use log_err/tcp_connect
authorTero Marttila <terom@fixme.fi>
Thu, 07 May 2009 02:17:20 +0300
changeset 177 a74b55104fb9
parent 176 6750d50ee8cd
child 178 3d357d055d67
rename sock_tcp to tcp_{transport,client}, fix irc_net_connect to use log_err/tcp_connect
src/irc_net_connect.c
src/sock_tcp.c
src/sock_tcp.h
src/sock_tcp_internal.h
src/tcp.c
src/tcp.h
src/tcp_client.c
src/tcp_internal.h
src/tcp_transport.c
--- a/src/irc_net_connect.c	Thu May 07 02:13:50 2009 +0300
+++ b/src/irc_net_connect.c	Thu May 07 02:17:20 2009 +0300
@@ -1,6 +1,7 @@
 
 #include "irc_net_internal.h"
-#include "sock_tcp.h"
+#include "tcp.h"
+#include "sock_ssl.h"
 #include "log.h"
 
 #include <time.h>
@@ -80,7 +81,7 @@
     
     // yay
     if (irc_net_connected(net, transport, &err))
-        log_err_info(&err, "irc_net_connected");
+        log_error(&err, "irc_net_connected");
 }
 
 static void irc_net_on_connect_error (transport_t *transport, const error_t *conn_err, void *arg)
@@ -92,10 +93,10 @@
     transport_destroy(transport);
 
     // attempt reconnect later
-    log_err_info(conn_err, "connect failed");
+    log_error(conn_err, "connect failed");
     
     if (irc_net_connect(net, false, &err))
-        log_err_info(&err, "unable to reconnect");
+        log_error(&err, "unable to reconnect");
 }
 
 static const struct transport_callbacks irc_net_transport_callbacks = {
@@ -147,7 +148,7 @@
         log_debug("connecting to [%s]:%s", info->hostname, info->service);
             
         // begin async connect
-        if (sock_tcp_connect(&transport_info, &transport, info->hostname, info->service, err))
+        if (tcp_connect(&transport_info, &transport, info->hostname, info->service, err))
             goto error;
         
         net->connecting = true;
@@ -176,7 +177,7 @@
 
     // execute it?
     if (irc_net_connect(net, true, &err))
-        log_err_info(&err, "unable to reconnect");
+        log_error(&err, "unable to reconnect");
 }
 
 /**
@@ -201,7 +202,7 @@
     if (now) {
         if (irc_net_do_connect(net, err))
             // log error and continue below with schedule_reconnect
-            log_err_info(err, "reconnect failed");
+            log_error(err, "reconnect failed");
 
         else
             // connecting, done
--- a/src/sock_tcp.c	Thu May 07 02:13:50 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,337 +0,0 @@
-
-#include "sock_tcp_internal.h"
-#include "sock_internal.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <string.h>
-
-/**
- * Start connecting to the given address in a non-blocking fashion. Returns any errors that immediately crop up,
- * otherwise eventually calls sock_tcp_connect_done().
- */
-static err_t sock_tcp_connect_addr (struct sock_tcp *sock, struct addrinfo *addr, error_t *err);
-
-
-
-/**
- * Our transport_methods
- */
-static void _sock_tcp_destroy (transport_t *transport)
-{
-    struct sock_tcp *sock = transport_check(transport, &sock_tcp_type);
-    
-    // proxy
-    sock_tcp_destroy(sock);
-}
-
-/*
- * Our transport_type
- */
-struct transport_type sock_tcp_type = {
-    .parent                 = &transport_fd_type,
-    .methods                = {
-        .read               = transport_fd_methods_read,
-        .write              = transport_fd_methods_write,
-        .events             = transport_fd_methods_events,
-        .destroy            = _sock_tcp_destroy,
-    },
-};
-
-/**
- * Create a new socket() using the given addr's family/socktype/protocol, and update our transport_fd state.
- */
-static err_t sock_tcp_create_socket (struct sock_tcp *sock, struct addrinfo *addr, error_t *err)
-{
-    int fd;
-
-    // call socket()
-    if ((fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0)
-        RETURN_SET_ERROR_ERRNO(err, ERR_SOCKET);
-
-    // ok, update transport_fd
-    transport_fd_set(SOCK_TCP_FD(sock), fd);
-
-
-    return SUCCESS;
-}
-
-/**
- * Read the socket's error code, if any.
- *
- * The read error number is stored in err->extra on SUCCESS, unless reading the error fails, in which case
- * err contains the normal error info.
- *
- * @return error code on failure
- */
-static err_t sock_tcp_read_error (struct sock_tcp *sock, error_t *err)
-{
-    int optval;
-    socklen_t optlen;
-
-    RESET_ERROR(err);
-
-    // init params
-    optval = 0;
-    optlen = sizeof(optval);
-
-    // read error code
-    if (getsockopt(SOCK_TCP_FD(sock)->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen))
-        RETURN_SET_ERROR_ERRNO(err, ERR_GETSOCKOPT);
-    
-    // sanity-check optlen... not sure if this is sensible
-    if (optlen != sizeof(optval))
-        RETURN_SET_ERROR_EXTRA(err, ERR_GETSOCKOPT, EINVAL);
-    
-    // then store the system error code
-    ERROR_EXTRA(err) = optval;
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Attempt to connect to the given addrinfo, or the next one, if that fails, etc.
- *
- * This does not call transport_connected().
- */
-static err_t sock_tcp_connect_continue (struct sock_tcp *sock, struct addrinfo *addr, struct error_info *err)
-{
-    if (!addr)
-        // no more addresses left to try
-        return SET_ERROR(err, ERR_GETADDRINFO_EMPTY);
-
-    // try and connect to each one until we find one that works
-    do {
-        // attempt to start connect
-        if (sock_tcp_connect_addr(sock, addr, err) == SUCCESS)
-            break;
-
-        // log a warning on the failed connect
-        log_warn("sock_tcp_connect_addr(%s): %s", addr->ai_canonname, error_msg(err));
-
-    } while ((addr = addr->ai_next));
-    
-
-    if (addr)
-        // we succesfully did a sock_tcp_connect_addr on valid address
-        return SUCCESS;
-
-    else
-        // all of the connect_async_addr's failed, return the last error
-        return ERROR_CODE(err);
-    
-}
-
-/**
- * Cleanup our resolver state and any connect callbacks after a connect
- */
-static void sock_tcp_connect_cleanup (struct sock_tcp *sock)
-{
-    // free the addrinfo
-    freeaddrinfo(sock->async_res); 
-    sock->async_res = sock->async_cur = NULL;
-    
-    // remove our event handler
-    transport_fd_clear(SOCK_TCP_FD(sock));
-}
-
-/**
- * Our async connect operation has completed, clean up, set up state for event-based operation with user callbacks, and
- * invoke transport_connected().
- *
- * The given \a err should be NULL for successful completion, or the error for failures.
- */
-static void sock_tcp_connect_done (struct sock_tcp *sock, struct error_info *conn_err)
-{
-    error_t err;
-
-    // cleanup
-    sock_tcp_connect_cleanup(sock);
-
-    if (conn_err)
-        // passthrough errors
-        JUMP_SET_ERROR_INFO(&err, conn_err);
-    
-    // set up for default transport event-based operation
-    if ((ERROR_CODE(&err) = transport_fd_defaults(SOCK_TCP_FD(sock))))
-        goto error;
-
-    // ok, no error
-
-error:                
-    // pass on to transport
-    transport_connected(SOCK_TCP_TRANSPORT(sock), IS_ERROR(&err) ? &err : NULL, false);
-}
-
-/**
- * Our async connect callback
- */
-static void sock_tcp_on_connect (struct transport_fd *fd, short what, void *arg)
-{
-    struct sock_tcp *sock = arg;
-    struct error_info err;
-    err_t tmp;
-
-    // XXX: timeouts
-    (void) what;
-    
-    // read socket error code
-    if (sock_tcp_read_error(sock, &err))
-        goto error;
-
-    // did the connect fail?
-    if (ERROR_EXTRA(&err))
-        JUMP_SET_ERROR(&err, ERR_CONNECT);
-    
-    // done, success
-    return sock_tcp_connect_done(sock, NULL);
-
-error:
-    // close the socket
-    if ((tmp = transport_fd_close(fd)))
-        log_warn("error closing socket after connect error: %s", error_name(tmp));
-
-    // log a warning
-    log_warn("connect to '%s' failed: %s", sock->async_cur->ai_canonname, error_msg(&err));
-
-    // try the next one or fail completely
-    if (sock_tcp_connect_continue(sock, sock->async_cur->ai_next, &err))
-        sock_tcp_connect_done(sock, &err);
-}
-
-static err_t sock_tcp_connect_addr (struct sock_tcp *sock, struct addrinfo *addr, error_t *err)
-{
-    int ret;
-    err_t tmp;
-
-    // first, create the socket
-    if (sock_tcp_create_socket(sock, addr, err))
-        return ERROR_CODE(err);
-
-    // then, set it up as nonblocking
-    if ((ERROR_CODE(err) = transport_fd_nonblock(SOCK_TCP_FD(sock), true)))
-        goto error;
-
-    // then, initiate the connect
-    if ((ret = connect(SOCK_TCP_FD(sock)->fd, addr->ai_addr, addr->ai_addrlen)) < 0 && errno != EINPROGRESS) 
-        JUMP_SET_ERROR_ERRNO(err, ERR_CONNECT);
-    
-    if (ret < 0) {
-        // set the "current" address in case it fails and we need to try the next one
-        sock->async_cur = addr;
-
-        // ok, connect started, setup our completion callback
-        if ((ERROR_CODE(err) = transport_fd_setup(SOCK_TCP_FD(sock), sock_tcp_on_connect, sock)))
-            goto error;
-    
-        // enable for write
-        if ((ERROR_CODE(err) = transport_fd_enable(SOCK_TCP_FD(sock), TRANSPORT_WRITE)))
-            goto error;
-
-    } else {
-        // oops... blocking connect - fail to avoid confusion
-        // XXX: come up with a better error name to use
-        // XXX: support non-async connects as well
-        JUMP_SET_ERROR_EXTRA(err, ERR_CONNECT, EINPROGRESS);
-    }
-    
-    // ok
-    return SUCCESS;
-
-error:
-    // close the stuff we did open
-    if ((tmp = transport_fd_close(SOCK_TCP_FD(sock))))
-        log_warn("error closing socket after connect error: %s", error_name(tmp));
-
-    return ERROR_CODE(err);
-}
-
-/**
- * External interface
- */
-void sock_tcp_init (struct sock_tcp *sock)
-{
-    struct event_base *ev_base = _sock_stream_ctx.ev_base;
-
-    // init without any fd
-    transport_fd_init(SOCK_TCP_FD(sock), ev_base, TRANSPORT_FD_INVALID);
-
-}
-
-err_t sock_tcp_connect_async (struct sock_tcp *sock, const char *hostname, const char *service, error_t *err)
-{
-    struct addrinfo hints;
-    int ret;
-
-    // build hints
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = AF_UNSPEC;
-    hints.ai_socktype = SOCK_STREAM;
-
-    // resolve (blocking)
-    if ((ret = getaddrinfo(hostname, service, &hints, &sock->async_res)))
-        RETURN_SET_ERROR_EXTRA(err, ERR_GETADDRINFO, ret);
-    
-    // start connecting on the first result
-    if (sock_tcp_connect_continue(sock, sock->async_res, err))
-        goto error;
-
-    // ok
-    return SUCCESS;
-
-error:
-    // cleanup
-    if (sock->async_res)
-        sock_tcp_connect_cleanup(sock);
-    
-    return ERROR_CODE(err);
-}
-
-void sock_tcp_destroy (struct sock_tcp *sock)
-{
-    // cleanup our stuff
-    if (sock->async_res)
-        sock_tcp_connect_cleanup(sock);
-    
-    // destroy lower level
-    transport_fd_destroy(SOCK_TCP_FD(sock)); 
-}
-
-/**
- * Public interface
- */
-err_t sock_tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
-        const char *host, const char *service, error_t *err)
-{
-    struct sock_tcp *sock;
- 
-    // alloc
-    if ((sock = calloc(1, sizeof(*sock))) == NULL)
-        return ERR_CALLOC;
-    
-    // initialize transport
-    transport_init(SOCK_TCP_TRANSPORT(sock), &sock_tcp_type, info);
-    
-    // init our state
-    sock_tcp_init(sock);
- 
-    // connect    
-    if (sock_tcp_connect_async(sock, host, service, err))
-        goto error;
-
-    // good
-    *transport_ptr = SOCK_TCP_TRANSPORT(sock);
-
-    return 0;
-
-error:
-    // cleanup
-    sock_tcp_destroy(sock);
-        
-    // return error code
-    return ERROR_CODE(err);
-}
-
--- a/src/sock_tcp.h	Thu May 07 02:13:50 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-#ifndef SOCK_TCP_H
-#define SOCK_TCP_H
-
-/**
- * @file
- *
- * TCP transport implementation.
- *
- * XXX: provide some TCP-specific type/functions?
- */
-#include "transport.h"
-
-/**
- * Connect the given transport via TCP to the given host/service. The transport will not be ready for use until the
- * transport_callbacks::on_connect callback has been invoked.
- *
- * XXX: blocking DNS resolution
- *
- * @param info the transport setup info
- * @param transport_ptr returned transport
- * @param host the hostname to connect to
- * @param service the service name (i.e. port) to connect to
- * @param err returned error info
- */
-err_t sock_tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
-        const char *host, const char *service, error_t *err);
-
-#endif
--- a/src/sock_tcp_internal.h	Thu May 07 02:13:50 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-#ifndef SOCK_TCP_INTERNAL_H
-#define SOCK_TCP_INTERNAL_H
-
-/**
- * @file
- *
- * Internal interface of the sock_tcp transport implementation.
- */
-#include "sock_tcp.h"
-#include "transport_fd.h"
-#include <netdb.h>
-
-/**
- * Our transport type struct
- */
-extern struct transport_type sock_tcp_type;
-
-/**
- * TCP transport state
- */
-struct sock_tcp {
-    /** Base fd-based transport state */
-    struct transport_fd base_fd;
-    
-    /** The resolver state for the async connect process */
-    struct addrinfo *async_res, *async_cur;
-};
-
-/**
- * Get a transport_fd pointer from a sock_tcp pointer
- */
-#define SOCK_TCP_FD(sock_ptr) (&(sock_ptr)->base_fd)
-
-/**
- * Get a transport pointer from a sock_tcp pointer
- */
-#define SOCK_TCP_TRANSPORT(sock_ptr) TRANSPORT_FD_BASE(SOCK_TCP_FD(sock_ptr))
-
-/**
- * Initialize the sock_tcp state
- */
-void sock_tcp_init (struct sock_tcp *sock);
-
-/**
- * Attempt to connect asyncronously to the given hostname/service. Once a connection has been established, this will
- * call transport_connected(), so you can register transport_methods::_connected if layering on top of TCP.
- *
- * In case of errors while starting the async connect process, an error code will be returned. If the connect fails
- * later on, transport_connected() will be called with the error code.
- *
- * The sock must have been initialized using sock_tcp_init().
- *
- * @param sock the unconnected TCP socket to connect with
- * @param hostname the hostname to resolve
- * @param service the service to connect to
- * @param err returned error info for immediate errors
- */
-err_t sock_tcp_connect_async (struct sock_tcp *sock, const char *hostname, const char *service, error_t *err);
-
-/**
- * Destroy the sock_tcp's state, including the transport_fd state.
- */
-void sock_tcp_destroy (struct sock_tcp *sock);
-
-#endif /* SOCK_TCP_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tcp.c	Thu May 07 02:17:20 2009 +0300
@@ -0,0 +1,43 @@
+#include "tcp_internal.h"
+
+int tcp_sock_create (const struct addrinfo *addr, error_t *err)
+{
+    int sock;
+
+    // create a new socket using addr->ai_family/socktype/protocol
+    if ((sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0)
+        JUMP_SET_ERROR_ERRNO(err, ERR_SOCKET);
+
+    return sock;
+
+error:
+    return -ERROR_CODE(err);    
+}
+
+err_t tcp_sock_error (evutil_socket_t sock, error_t *err)
+{
+    int optval;
+    socklen_t optlen;
+
+    RESET_ERROR(err);
+
+    // init params
+    optval = 0;
+    optlen = sizeof(optval);
+
+    // read error code
+    if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen))
+        RETURN_SET_ERROR_ERRNO(err, ERR_GETSOCKOPT);
+    
+    // sanity-check optlen... not sure if this is sensible
+    if (optlen != sizeof(optval))
+        RETURN_SET_ERROR_EXTRA(err, ERR_GETSOCKOPT, EINVAL);
+    
+    // then store the system error code
+    ERROR_EXTRA(err) = optval;
+
+    // ok
+    return SUCCESS;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tcp.h	Thu May 07 02:17:20 2009 +0300
@@ -0,0 +1,35 @@
+#ifndef TCP_H
+#define TCP_H
+
+/**
+ * @file
+ *
+ * TCP transport/service implementation.
+ *
+ * XXX: provide some TCP-specific type/functions?
+ */
+#include "transport.h"
+#include "service.h"
+
+/**
+ * Connect the given transport via TCP to the given host/service. The transport will not be ready for use until the
+ * transport_callbacks::on_connect callback has been invoked.
+ *
+ * XXX: blocking DNS resolution
+ *
+ * @param info the transport setup info
+ * @param transport_ptr returned transport
+ * @param host the hostname to connect to
+ * @param service the service name (i.e. port) to connect to
+ * @param err returned error info
+ */
+err_t tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *host, const char *service, error_t *err);
+
+/**
+ * Create a passive/listening TCP socket on the given interface/port (NULL to pick automatically).
+ */
+err_t tcp_listen (const struct service_info *info, service_t **service_ptr, 
+        const char *interface, const char *service, error_t *err);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tcp_client.c	Thu May 07 02:17:20 2009 +0300
@@ -0,0 +1,278 @@
+#include "tcp_internal.h"
+#include "log.h"
+
+/*
+ * Our transport methods
+ */
+static void tcp_client__deinit (transport_t *transport)
+{
+    struct tcp_client *client = transport_check(transport, &tcp_client_type);
+    
+    // proxy
+    tcp_client_deinit(client);
+}
+
+/*
+ * Our transport_type
+ */
+const struct transport_type tcp_client_type = {
+    .base_type = {
+        .parent             = &tcp_transport_type.base_type,
+    },
+    .methods                = {
+        .read               = transport_fd__read,
+        .write              = transport_fd__write,
+        .events             = transport_fd__events,
+        .deinit             = tcp_client__deinit,
+    },
+};
+
+/*
+ * Forward-declare 
+ */
+static void tcp_client_on_connect (struct transport_fd *fd, short what, void *arg);
+
+/*
+ * Function implementations
+ */
+void tcp_client_init (struct tcp_client *client)
+{
+    tcp_transport_init(&client->base_trans, -1);
+    
+    resolve_result_init(&client->rr);
+}
+
+/*
+ * Start connecting to the given address in a non-blocking fashion. Returns any errors that immediately crop up,
+ * otherwise eventually calls tcp_client_connect_done().
+ */
+static err_t tcp_client_connect_addr (struct tcp_client *client, struct addrinfo *addr, error_t *err)
+{
+    struct transport_fd *_fd = &client->base_trans.base_fd;
+    int ret;
+    evutil_socket_t sock;
+    err_t tmp;
+
+    // first, create the socket
+    if ((sock = tcp_sock_create(addr, err)) < 0)
+        return ERROR_CODE(err);
+
+    // set it as our sock
+    if ((ERROR_CODE(err) = transport_fd_set(_fd, sock)))
+        goto error;
+
+    // then, set it up as nonblocking
+    if ((ERROR_CODE(err) = transport_fd_nonblock(_fd, true)))
+        goto error;
+
+    // then, initiate the connect
+    if ((ret = connect(sock, addr->ai_addr, addr->ai_addrlen)) < 0 && errno != EINPROGRESS) 
+        JUMP_SET_ERROR_ERRNO(err, ERR_CONNECT);
+    
+    if (ret < 0) {
+        // ok, connect started, setup our completion callback
+        if ((ERROR_CODE(err) = transport_fd_setup(_fd, tcp_client_on_connect, client)))
+            goto error;
+    
+        // enable for write
+        if ((ERROR_CODE(err) = transport_fd_enable(_fd, TRANSPORT_WRITE)))
+            goto error;
+
+    } else {
+        // oops... blocking connect - fail to avoid confusion
+        // XXX: come up with a better error name to use
+        // XXX: support non-async connects as well
+        JUMP_SET_ERROR_EXTRA(err, ERR_CONNECT, EINPROGRESS);
+    }
+    
+    // ok
+    return SUCCESS;
+
+error:
+    // close the stuff we did open
+    if ((tmp = transport_fd_close(_fd)))
+        log_warn("error closing socket after connect error: %s", error_name(tmp));
+
+    return ERROR_CODE(err);
+}
+
+
+/*
+ * Attempt to connect to the next addrinfo, or the next one, if that fails, etc.
+ *
+ * This does not call transport_connected().
+ */
+static err_t tcp_client_connect_continue (struct tcp_client *client, error_t *err)
+{
+    struct addrinfo *addr;
+
+    // try and connect to each one until we find one that works
+    while ((addr = resolve_result_next(&client->rr))) {
+        // attempt to start connect
+        if (tcp_client_connect_addr(client, addr, err) == SUCCESS)
+            break;
+
+        // log a warning on the failed connect
+        log_warn_error(err, "%s", resolve_addr_text(addr));
+    }
+    
+
+    if (addr)
+        // we succesfully did a tcp_client_connect_addr on valid address
+        return SUCCESS;
+
+    else
+        // all of the connect_async_addr's failed, return the last error
+        return ERROR_CODE(err);
+}
+
+/*
+ * Cleanup our resolver state and any connect callbacks after a connect
+ */
+static void tcp_client_connect_cleanup (struct tcp_client *client)
+{
+    // drop the resolver stuff
+    resolve_result_deinit(&client->rr);
+    
+    // remove our event handler
+    transport_fd_clear(&client->base_trans.base_fd);
+}
+
+/*
+ * Our async connect operation has completed, clean up, set up state for event-based operation with user callbacks, and
+ * invoke transport_connected().
+ *
+ * The given \a err should be NULL for successful completion, or the error for failures.
+ */
+static void tcp_client_connect_done (struct tcp_client *client, error_t *conn_err)
+{
+    error_t err;
+
+    // cleanup
+    tcp_client_connect_cleanup(client);
+
+    if (conn_err)
+        JUMP_SET_ERROR_INFO(&err, conn_err);
+
+    // let the transport handle the rest
+    if (tcp_transport_connected(&client->base_trans, &err))
+        goto error;
+    
+    // ok
+    return;
+
+error:    
+    // pass the error on to transport
+    transport_connected(&client->base_trans.base_fd.base, &err, false);
+}
+
+/*
+ * Our async connect callback
+ */
+static void tcp_client_on_connect (struct transport_fd *fd, short what, void *arg)
+{
+    struct tcp_client *client = arg;
+    error_t err;
+    err_t tmp;
+
+    // XXX: timeouts
+    (void) what;
+    
+    // read socket error code
+    if (tcp_sock_error(client->base_trans.base_fd.fd, &err))
+        goto error;
+
+    // did the connect fail?
+    if (ERROR_EXTRA(&err))
+        JUMP_SET_ERROR(&err, ERR_CONNECT);
+    
+    // done, success
+    return tcp_client_connect_done(client, NULL);
+
+error:
+    // close the socket
+    if ((tmp = transport_fd_close(fd)))
+        log_warn("error closing socket after connect error: %s", error_name(tmp));
+
+    // log a warning
+    log_warn_error(&err, "connect to %s failed", "???");
+
+    // try the next one or fail completely
+    if (tcp_client_connect_continue(client, &err))
+        tcp_client_connect_done(client, &err);
+}
+
+err_t tcp_client_connect_async (struct tcp_client *client, const char *hostname, const char *service, error_t *err)
+{
+    // do the resolving
+    if (resolve_addr(&client->rr, hostname, service, SOCK_STREAM, 0, err))
+        return ERROR_CODE(err);
+
+    // start connecting with the first result
+    if (tcp_client_connect_continue(client, err))
+        goto error;
+
+    // ok
+    return SUCCESS;
+
+error:
+    // cleanup
+    resolve_result_deinit(&client->rr);
+    
+    return ERROR_CODE(err);
+}
+
+void tcp_client_deinit (struct tcp_client *client)
+{
+    // cleanup our stuff
+    resolve_result_deinit(&client->rr);
+    
+    // deinit lower transport
+    tcp_transport_deinit(&client->base_trans);
+}
+
+/*
+ * Deinit and free, not using the transport interface
+ */
+static void tcp_client_destroy (struct tcp_client *client)
+{
+    tcp_client_deinit(client);
+
+    free(client);
+}
+
+/*
+ * Public interface
+ */
+err_t tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *host, const char *service, error_t *err)
+{
+    struct tcp_client *client;
+ 
+    // alloc
+    if ((client = calloc(1, sizeof(*client))) == NULL)
+        return ERR_CALLOC;
+
+    // init transport
+    transport_init(&client->base_trans.base_fd.base, &tcp_client_type, info);
+    
+    // init our state
+    tcp_client_init(client);
+ 
+    // begin connect
+    if (tcp_client_connect_async(client, host, service, err))
+        goto error;
+
+    // good
+    *transport_ptr = &client->base_trans.base_fd.base;
+
+    return 0;
+
+error:
+    // cleanup
+    tcp_client_destroy(client);
+        
+    // return error code
+    return ERROR_CODE(err);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tcp_internal.h	Thu May 07 02:17:20 2009 +0300
@@ -0,0 +1,170 @@
+#ifndef TCP_INTERNAL_H
+#define TCP_INTERNAL_H
+
+/**
+ * @file
+ *
+ * Internal TCP interface for implementations
+ */
+#include "tcp.h"
+#include "resolve.h"
+#include "transport_fd.h"
+#include "transport_internal.h"
+#include "service_internal.h"
+#include "error.h"
+
+#include <event2/event.h>
+#include <event2/util.h>
+
+/**
+ * Create a new socket() using the given addr's family/socktype/protocol and return it.
+ *
+ * In case of errors, this returns -err_t
+ *
+ * @param addr the addrinfo to create the socket for
+ * @param err returned error info
+ * @return new fd on success, -err_t on error
+ */
+int tcp_sock_create (const struct addrinfo *addr, error_t *err);
+
+/**
+ * Return the socket's current error code via err->extra.
+ *
+ * In case getting the socket error code itself fails, this will return normal error code/info.
+ *
+ * Otherwise, this will return SUCCESS, with the errno value stored in err->extra.
+ */
+err_t tcp_sock_error (evutil_socket_t sock, error_t *err);
+
+
+/**
+ * TCP transport type
+ */
+extern const struct transport_type tcp_transport_type;
+
+/**
+ * Base TCP transport 
+ *
+ * XXX: currently just the same as transport_fd, but this will probably change
+ */
+struct tcp_transport {
+    /** Base FD-based implementation */
+    struct transport_fd base_fd;
+};
+
+/**
+ * Initialize the tcp_transport state.
+ *
+ * This initializes the transport_fd base using the global sock_ctx::ev_base and the given socket.
+ */
+void tcp_transport_init (struct tcp_transport *trans, evutil_socket_t sock);
+
+/**
+ * Create a new tcp_transport with the given sock.
+ *
+ * For convenience, this will also make the sock nonblocking.
+ *
+ * In case of errors, this will the socket.
+ *
+ * @param trans_ptr returned tcp_transport
+ * @param info the transport user settings
+ * @param sock the unused TCP socket
+ * @param err returned error info
+ */
+err_t tcp_transport_create (struct tcp_transport **trans_ptr, const struct transport_info *info, evutil_socket_t sock, error_t *err);
+
+/**
+ * The transport as now connected, this sets up the intitial user settings, and invokes the callback.
+ *
+ * XXX: this does an 'indirect' call to transport_connected().
+ *
+ * @param err returned error info
+ */
+err_t tcp_transport_connected (struct tcp_transport *trans, error_t *err);
+
+/**
+ * Deinitialize the transport state, terminating the connection and releasing resources.
+ */
+void tcp_transport_deinit (struct tcp_transport *trans);
+
+/**
+ * Deinitialize and free the given tcp_transport
+ */
+void tcp_transport_destroy (struct tcp_transport *trans);
+
+/**
+ * TCP client transport type
+ */
+extern const struct transport_type tcp_client_type;
+
+/**
+ * TCP client state
+ */
+struct tcp_client {
+    /** Base transport stuff */
+    struct tcp_transport base_trans;
+
+    /** The resolver lookup result for the async connect process */
+    struct resolve_result rr;
+};
+
+/**
+ * Initialize the tcp_client state
+ */
+void tcp_client_init (struct tcp_client *client);
+
+/**
+ * Attempt to connect asyncronously to the given hostname/service. Once a connection has been established, this will
+ * call transport_connected(), so you can register transport_methods::_connected if layering on top of TCP.
+ *
+ * In case of errors while starting the async connect process, an error code will be returned. If the connect fails
+ * later on, transport_connected() will be called with the error code.
+ *
+ * The sock must have been initialized using sock_tcp_init().
+ *
+ * @param client the unconnected TCP client socket to connect with
+ * @param hostname the hostname to resolve
+ * @param service the service to connect to
+ * @param err returned error info for immediate errors
+ */
+err_t tcp_client_connect_async (struct tcp_client *client, const char *hostname, const char *service, error_t *err);
+
+/**
+ * Deinitialize the tcp_client's state, including the tcp_transport state.
+ */
+void tcp_client_deinit (struct tcp_client *client);
+
+
+
+/**
+ * TCP service type
+ */
+extern const struct service_type tcp_server_type;
+
+/**
+ * TCP service state
+ */
+struct tcp_server {
+    /** Base service state */
+    struct service base_service;
+    
+    /** The input event with our listen() socket */
+    struct event *ev;
+};
+
+/**
+ * The listen() backlog
+ */
+#define TCP_SERVER_BACKLOG 5
+
+/**
+ * Open the listening socket on the given interface/service.
+ */
+err_t tcp_server_listen (struct tcp_server *serv, const char *interface, const char *service, error_t *err);
+
+/**
+ * Release the tcp_server's state, and cleanup the struct.
+ */
+void tcp_server_deinit (struct tcp_server *serv);
+
+#endif /* TCP_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tcp_transport.c	Thu May 07 02:17:20 2009 +0300
@@ -0,0 +1,86 @@
+#include "tcp_internal.h"
+#include "sock_internal.h"
+
+#include <unistd.h>
+
+/*
+ * Our transport_type
+ */
+const struct transport_type tcp_transport_type = {
+    .base_type = {
+        .parent             = &transport_fd_type.base_type,
+    },
+    .methods                = {
+        .read               = transport_fd__read,
+        .write              = transport_fd__write,
+        .events             = transport_fd__events,
+        .deinit             = transport_fd__deinit,
+    },
+};
+
+void tcp_transport_init (struct tcp_transport *trans, evutil_socket_t sock)
+{
+    struct event_base *ev_base = _sock_stream_ctx.ev_base;
+
+    transport_fd_init(&trans->base_fd, ev_base, sock);
+}
+
+err_t tcp_transport_create (struct tcp_transport **trans_ptr, const struct transport_info *info, evutil_socket_t sock, error_t *err)
+{
+    struct tcp_transport *trans;
+
+    // alloc
+    if ((trans = calloc(1, sizeof(*trans))) == NULL)
+        JUMP_SET_ERROR(err, ERR_MEM);
+
+    // init transport
+    transport_init(&trans->base_fd.base, &tcp_transport_type, info);
+
+    // init ourselves
+    tcp_transport_init(trans, sock);
+
+    // setup the socket?
+    if (sock >= 0) {
+        // make it non-blocking
+        if ((ERROR_CODE(err) = transport_fd_nonblock(&trans->base_fd, true)))
+            goto error;
+    }
+
+    // ok
+    *trans_ptr = trans;
+
+    return SUCCESS;
+
+error:
+    // cleanup
+    if (trans)
+        tcp_transport_deinit(trans);
+    else
+        EVUTIL_CLOSESOCKET(sock);
+
+    return ERROR_CODE(err);
+}
+
+err_t tcp_transport_connected (struct tcp_transport *trans, error_t *err)
+{
+    // set up for default transport event-based operation
+    if ((ERROR_CODE(err) = transport_fd_defaults(&trans->base_fd)))
+        return ERROR_CODE(err);
+
+    // ok
+    transport_connected(&trans->base_fd.base, NULL, false);
+
+    return SUCCESS;
+}
+
+void tcp_transport_deinit (struct tcp_transport *trans)
+{
+    transport_fd_deinit(&trans->base_fd);
+}
+
+void tcp_transport_destroy (struct tcp_transport *trans)
+{
+    tcp_transport_deinit(trans);
+
+    free(trans);
+}