--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/error.h Sun Feb 22 06:44:16 2009 +0200
@@ -0,0 +1,76 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+/*
+ * Error-handling functions
+ */
+#include <errno.h>
+
+/*
+ * Type used for error codes is an explicitly *unsigned* int, meaning that error codes themselves are positive.
+ * Negative error codes also exist in some places, and they are just a negative err_t.
+ */
+typedef unsigned int err_t;
+
+/*
+ * List of defined error codes, organized mostly by function name
+ */
+enum error_code {
+ ERR_CALLOC = 0x000100,
+
+ // network resolver errors
+ ERR_GETADDRINFO = 0x000200,
+ ERR_GETADDRINFO_EMPTY = 0x000201, /* No valid results */
+
+ // low-level network errors
+ ERR_SOCKET = 0x000301,
+ ERR_CONNECT = 0x000302,
+
+ // low-level IO errors
+ ERR_READ = 0x000401,
+ ERR_WRITE = 0x000402,
+};
+
+/*
+ * An error code and associated extra infos
+ */
+struct error_info {
+ /* The base error code */
+ err_t code;
+
+ /* Additional detail info, usually some third-part error code */
+ unsigned int extra;
+};
+
+/** No error, evaulates as logical false */
+#define SUCCESS (0)
+
+/* Evaulates to error_info.code as lvalue */
+#define ERROR_CODE(err_info) ((err_info).code)
+
+/* Evaulates to error_info.extra as lvalue */
+#define ERROR_EXTRA(err_info) ((err_info).extra)
+
+/* Set error_info.code to SUCCESS, evaulates as zero */
+#define RESET_ERROR(err_info) ((err_info).code = SUCCESS)
+
+/* Compare error_info.code != 0 */
+#define IS_ERROR(err_info) (!!(err_info).code)
+
+/* Set error_info.code, but leave err_extra as-is. Evaluates to err_code */
+#define SET_ERROR(err_info, err_code) ((err_info).code = (err_code))
+
+/* Set error_info.code/extra. XXX: should evaluate to err_code */
+#define _SET_ERROR_EXTRA(err_info, err_code, err_extra) (err_info).code = (err_code); err_info.extra = (err_extra)
+#define SET_ERROR_EXTRA(err_info, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info, err_code, err_extra); } while (0)
+
+/* Set error.info.code to err_code, and .extra to errno. XXX: should evaulate to err_code */
+#define _SET_ERROR_ERRNO(err_info, err_code) _SET_ERROR_EXTRA(err_info, err_code, errno);
+#define SET_ERROR_ERRNO(err_info, err_code) SET_ERROR_EXTRA(err_info, err_code, errno);
+
+/* Ss above, but also return err_code from func. XXX: use 'return SET_ERROR...' instead */
+#define RETURN_SET_ERROR(err_info, err_code) do { _SET_ERROR(err_info, err_code); return (err_code); } while (0)
+#define RETURN_SET_ERROR_EXTRA(err_info, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info, err_code, err_extra); return (err_code); } while (0)
+#define RETURN_SET_ERROR_ERRNO(err_info, err_code) do { _SET_ERROR_ERRNO(err_info, err_code); return (err_code); } while (0)
+
+#endif
--- a/src/sock.c Sun Feb 22 05:27:29 2009 +0200
+++ b/src/sock.c Sun Feb 22 06:44:16 2009 +0200
@@ -2,21 +2,41 @@
#include "sock_internal.h"
#include "sock_gnutls.h"
-void sock_init (void)
+#include <assert.h>
+
+err_t sock_init (void)
{
- // XXX: just call directly for now
- sock_gnutls_init();
+ err_t err;
+
+ // XXX: just call these all directly for now
+
+ if ((err = sock_gnutls_init()))
+ return err;
}
-int sock_stream_read (struct sock_stream *sock, void *buf, size_t len)
+void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type)
+{
+ // be strict
+ assert(sock->type == NULL);
+
+ // store type
+ sock->type = type;
+}
+
+err_t sock_stream_read (struct sock_stream *sock, void *buf, size_t len)
{
// proxy off to method handler
return sock->type->methods.read(sock, buf, len);
}
-int sock_stream_write (struct sock_stream *sock, const void *buf, size_t len)
+err_t sock_stream_write (struct sock_stream *sock, const void *buf, size_t len)
{
// proxy off to method handler
return sock->type->methods.write(sock, buf, len);
}
+void sock_stream_error (struct sock_stream *sock, struct error_info *err)
+{
+ // copy from SOCK_ER
+ *err = SOCK_ERR(sock);
+}
--- a/src/sock.h Sun Feb 22 05:27:29 2009 +0200
+++ b/src/sock.h Sun Feb 22 06:44:16 2009 +0200
@@ -4,6 +4,7 @@
/*
* Low-level socket-related functions
*/
+#include "error.h"
#include <sys/types.h>
/*
@@ -12,14 +13,19 @@
struct sock_stream;
/*
- * A simple blocking TCP connect to the given host/service, using getaddrinfo.
+ * Initialize the socket module's global state. Call this before calling any other sock_* functions.
+ */
+err_t sock_init (void);
+
+/*
+ * A simple blocking TCP connect to the given host/service, using getaddrinfo. The connected socket is returned via
+ * *sock_ptr. In case of errors, additional error information is stored in *err
+ *
+ * @return zero on success, nonzero on error
*
* XXX: blocking
- * XXX: exits on error
- *
- * Returns the socket handle.
*/
-struct sock_stream *sock_tcp_connect (const char *host, const char *service);
+err_t sock_tcp_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err);
/*
* A simple blocking SSL connect to the given host/service.
@@ -28,19 +34,19 @@
* XXX: doesn't do any certificate verification.
* XXX: exits on error
*
- * Returns the socket handle.
+ * Returns the socket handle, or NULL on errors.
*/
struct sock_stream *sock_ssl_connect (const char *host, const char *service);
/*
- * Initialize the socket module's global state
- */
-void sock_init (void);
-
-/*
* The generic read/write API for stream sockets.
*/
-int sock_stream_read (struct sock_stream *sock, void *buf, size_t len);
-int sock_stream_write (struct sock_stream *sock, const void *buf, size_t len);
+err_t sock_stream_read (struct sock_stream *sock, void *buf, size_t len);
+err_t sock_stream_write (struct sock_stream *sock, const void *buf, size_t len);
+
+/**
+ * Get last err_info for \a sock, returned via \a *err.
+ */
+void sock_stream_error (struct sock_stream *sock, struct error_info *err);
#endif
--- a/src/sock_gnutls.c Sun Feb 22 05:27:29 2009 +0200
+++ b/src/sock_gnutls.c Sun Feb 22 06:44:16 2009 +0200
@@ -12,7 +12,7 @@
errx(1, "%s: %s", func, gnutls_strerror(_err));
}
-static int sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t len)
+static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t len)
{
struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
@@ -20,7 +20,7 @@
return gnutls_record_recv(sock->session, buf, len);
}
-static int sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t len)
+static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t len)
{
struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
@@ -51,8 +51,7 @@
gnutls_certificate_allocate_credentials(&ctx->xcred);
}
-// XXX: errors
-void sock_gnutls_init (void)
+err_t sock_gnutls_init (void)
{
int _err;
@@ -62,6 +61,9 @@
// init _sock_gnutls_ctx
sock_gnutls_client_ctx_anon(&_sock_gnutls_client_ctx);
+
+ // done
+ return SUCCESS;
}
--- a/src/sock_gnutls.h Sun Feb 22 05:27:29 2009 +0200
+++ b/src/sock_gnutls.h Sun Feb 22 06:44:16 2009 +0200
@@ -35,10 +35,11 @@
#define SOCK_GNUTLS_BASE(sock_ptr) (&(sock_ptr)->base_tcp.base)
#define SOCK_GNUTLS_TCP(sock_ptr) (&(sock_ptr)->base_tcp)
+#define SOCK_GNUTLS_ERR(sock_ptr) SOCK_ERR(SOCK_GNUTLS_BASE(sock_ptr))
/*
* Initialize the global gnutls state
*/
-void sock_gnutls_init (void);
+err_t sock_gnutls_init (void);
#endif /* SOCK_GNUTLS_H */
--- a/src/sock_internal.h Sun Feb 22 05:27:29 2009 +0200
+++ b/src/sock_internal.h Sun Feb 22 06:44:16 2009 +0200
@@ -10,10 +10,10 @@
/* method table */
struct sock_stream_methods {
/* Normal read(2) */
- int (*read) (struct sock_stream *sock, void *buf, size_t len);
+ err_t (*read) (struct sock_stream *sock, void *buf, size_t len);
/* Normal write(2) */
- int (*write) (struct sock_stream *sock, const void *buf, size_t len);
+ err_t (*write) (struct sock_stream *sock, const void *buf, size_t len);
} methods;
};
@@ -25,9 +25,21 @@
* as appropriate.
*/
struct sock_stream {
+ /* The sock_stream_type for this socket */
struct sock_stream_type *type;
+
+ /* Last error info */
+ struct error_info err;
};
#define SOCK_FROM_BASE(sock, type) ((type*) sock)
+#define SOCK_ERR(sock) ((sock)->err)
+
+/*
+ * Initialize a sock_stream with the given sock_stream_type.
+ *
+ * The sock_stream should be initialized to zero. It is a bug to call this twice.
+ */
+void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type);
#endif /* SOCK_INTERNAL_H */
--- a/src/sock_tcp.c Sun Feb 22 05:27:29 2009 +0200
+++ b/src/sock_tcp.c Sun Feb 22 06:44:16 2009 +0200
@@ -7,66 +7,82 @@
#include <netdb.h>
#include <unistd.h>
#include <string.h>
-#include <err.h>
+#include <assert.h>
/*
- * Our sock_stream_type.methods.read implementation
+ * Our sock_stream_methods.read method
*/
-static int sock_tcp_read (struct sock_stream *base_sock, void *buf, size_t len)
+static err_t sock_tcp_read (struct sock_stream *base_sock, void *buf, size_t len)
{
struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
+ int ret;
+
+ // map directly to read(2)
+ if ((ret = read(sock->fd, buf, len)) < 0)
+ // errno
+ RETURN_SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_READ);
- return read(sock->fd, buf, len);
+ else
+ // bytes read
+ return ret;
}
/*
- * Our sock_stream_type.methods.write implementation
+ * Our sock_stream_methods.write method
*/
-static int sock_tcp_write (struct sock_stream *base_sock, const void *buf, size_t len)
+static err_t sock_tcp_write (struct sock_stream *base_sock, const void *buf, size_t len)
{
struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
+ int ret;
+
+ // map directly to write(2)
+ if ((ret = write(sock->fd, buf, len)) < 0)
+ // errno
+ RETURN_SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_WRITE);
- return write(sock->fd, buf, len);
+ else
+ // bytes read
+ return ret;
}
/*
* Our sock_stream_type
- *
- * XXX: move to sock_tcp.h
*/
struct sock_stream_type sock_tcp_type = {
.methods.read = &sock_tcp_read,
.methods.write = &sock_tcp_write,
};
-struct sock_tcp* sock_tcp_alloc (void)
+err_t sock_tcp_alloc (struct sock_tcp **sock_ptr)
{
- struct sock_tcp *sock;
-
// alloc
- if ((sock = calloc(1, sizeof(*sock))) == NULL)
- errx(1, "calloc");
+ if ((*sock_ptr = calloc(1, sizeof(**sock_ptr))) == NULL)
+ return ERR_CALLOC;
- // initialize base
- sock->base.type = &sock_tcp_type;
+ // initialize base with sock_tcp_type
+ sock_stream_init(SOCK_TCP_BASE(*sock_ptr), &sock_tcp_type);
// done
- return sock;
+ return SUCCESS;
}
-int sock_tcp_init_fd (struct sock_tcp *sock, int fd)
+err_t sock_tcp_init_fd (struct sock_tcp *sock, int fd)
{
+ // valid fd -XXX: err instead?
+ assert(fd >= 0);
+
// initialize
sock->fd = fd;
// done
- return 0;
+ return SUCCESS;
}
-int sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service)
+err_t sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service)
{
struct addrinfo hints, *res, *r;
- int _err;
+ int err;
+ RESET_ERROR(SOCK_TCP_ERR(sock));
// hints
memset(&hints, 0, sizeof(hints));
@@ -74,40 +90,85 @@
hints.ai_socktype = SOCK_STREAM;
// resolve
- if ((_err = getaddrinfo(hostname, service, &hints, &res)))
- errx(1, "getaddrinfo: %s", gai_strerror(_err));
+ if ((err = getaddrinfo(hostname, service, &hints, &res)))
+ RETURN_SET_ERROR_EXTRA(SOCK_TCP_ERR(sock), ERR_GETADDRINFO, err);
- // use
+ // try each result in turn
for (r = res; r; r = r->ai_next) {
- // XXX: wrong
- if ((sock->fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0)
- err(1, "socket");
+ // create the socket
+ if ((sock->fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0) {
+ // remember error
+ SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_SOCKET);
- if (connect(sock->fd, r->ai_addr, r->ai_addrlen))
- err(1, "connect");
+ // skip to next one
+ continue;
+ }
+
+ // connect to remote address
+ if (connect(sock->fd, r->ai_addr, r->ai_addrlen)) {
+ // remember error
+ SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_CONNECT);
+
+ // close/invalidate socket
+ close(sock->fd);
+ sock->fd = -1;
+ // skip to next one
+ continue;
+ }
+
+ // valid socket, use this
break;
}
- // ensure we got some valid socket
- if (sock->fd < 0)
- errx(1, "no valid socket");
+ // ensure we got some valid socket, else return last error code
+ if (sock->fd < 0) {
+ // did we hit some error?
+ if (IS_ERROR(SOCK_TCP_ERR(sock)))
+ // return last error
+ return ERROR_CODE(SOCK_TCP_ERR(sock));
+
+ else
+ // no results
+ return SET_ERROR(SOCK_TCP_ERR(sock), ERR_GETADDRINFO_EMPTY);
+ }
// ok, done
return 0;
}
-// XXX: error handling
-struct sock_stream* sock_tcp_connect (const char *host, const char *service)
+err_t sock_tcp_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err_info)
{
struct sock_tcp *sock;
+ err_t err;
// allocate
- sock = sock_tcp_alloc();
+ if ((err = sock_tcp_alloc(&sock)))
+ return err;
// connect
- sock_tcp_init_connect(sock, host, service);
+ if ((err = sock_tcp_init_connect(sock, host, service))) {
+ // set *err_info
+ *err_info = SOCK_TCP_ERR(sock);
- // done
- return SOCK_TCP_BASE(sock);
+ // cleanup
+ sock_tcp_release(sock);
+
+ // return error code
+ return err;
+ }
+
+ // good
+ *sock_ptr = SOCK_TCP_BASE(sock);
+
+ return 0;
}
+
+void sock_tcp_release (struct sock_tcp *sock)
+{
+ // must not be connected
+ assert(sock->fd < 0);
+
+ // free
+ free(sock);
+}
--- a/src/sock_tcp.h Sun Feb 22 05:27:29 2009 +0200
+++ b/src/sock_tcp.h Sun Feb 22 06:44:16 2009 +0200
@@ -18,20 +18,26 @@
};
#define SOCK_TCP_BASE(sock_ptr) (&(sock_ptr)->base)
+#define SOCK_TCP_ERR(sock_ptr) SOCK_ERR(SOCK_TCP_BASE(sock_ptr))
/*
* Allocate a new blank sock_tcp with a correctly initialized base
*/
-struct sock_tcp* sock_tcp_alloc (void);
+err_t sock_tcp_alloc (struct sock_tcp **sock_ptr);
/*
* Initialize a blank sock_tcp with a given already-existing fd
*/
-int sock_tcp_init_fd (struct sock_tcp *sock, int fd);
+err_t sock_tcp_init_fd (struct sock_tcp *sock, int fd);
/*
* Initialize a blank sock_tcp by connecting
*/
-int sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service);
+err_t sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service);
+
+/*
+ * Release a non-connected sock_tcp
+ */
+void sock_tcp_release (struct sock_tcp *sock);
#endif /* SOCK_TCP_H */