--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/socket.c Sun Jul 06 23:33:24 2008 +0300
@@ -0,0 +1,184 @@
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+
+#include "socket.h"
+#include "common.h"
+
+#define _SOCKOP_SOCKET 0x01
+#define _SOCKOP_ENDPOINT_BIND 0x02
+#define _SOCKOP_ENDPOINT_CONNECT 0x04
+#define _SOCKOP_LISTEN 0x08
+#define _SOCKOP_FLAGS_PASSIVE 0x10
+#define _SOCKOP_FCNTL_NONBLOCK 0x20
+
+enum socket_op {
+ /*
+ * Just resolve the address, don't create a socket
+ */
+ SOCKOP_RESOLVE = _SOCKOP_FLAGS_PASSIVE,
+
+ /*
+ * Create a socket whose local end is bound to the given endpoint, and listening
+ */
+ SOCKOP_LISTEN = _SOCKOP_FLAGS_PASSIVE | _SOCKOP_SOCKET | _SOCKOP_ENDPOINT_BIND | _SOCKOP_LISTEN,
+
+ /*
+ * Create a socket whose remote end is connected to the given endpoint, and connected
+ */
+ SOCKOP_CONNECT = _SOCKOP_SOCKET | _SOCKOP_ENDPOINT_CONNECT,
+
+ /*
+ * Create a socket and initiate a nonblocking connect to the given endpoint
+ */
+ SOCKOP_CONNECT_ASYNC = _SOCKOP_SOCKET | _SOCKOP_FCNTL_NONBLOCK | _SOCKOP_ENDPOINT_CONNECT,
+};
+
+static int _socket_do (struct config_endpoint *endpoint, int *sock, int sock_type, struct sockaddr_storage *addr, enum socket_op sockop);
+
+/*
+ * Create a socket of the given type that's listening on the given endpoint
+ */
+int socket_listen (struct config_endpoint *endpoint, int sock_type) {
+ int sock = -1;
+ struct sockaddr_storage addr;
+
+ // and then _socket_do
+ if (_socket_do(endpoint, &sock, sock_type, &addr, SOCKOP_LISTEN))
+ return -1;
+
+ // just return the socket, discard addr
+ return sock;
+}
+
+/*
+ * Initiate an async connect to the given endpoint for the given socket type. This should work for
+ * PF_INET and PF_LOCAL sockets, and in both cases, socket writeability should indicate that the
+ * connect succeeded.
+ *
+ * XXX: Currently it looks up the endpoint each time - the working addrinfo should be cached
+ */
+int socket_connect_async (struct config_endpoint *endpoint, int sock_type) {
+ int sock = -1;
+ struct sockaddr_storage addr;
+
+ // and then _socket_do
+ if (_socket_do(endpoint, &sock, sock_type, &addr, SOCKOP_CONNECT_ASYNC))
+ return -1;
+
+ // return the socket
+ return sock;
+}
+
+
+/*
+ * Do something to apply an endpoint to a socket
+ */
+static int _socket_do (struct config_endpoint *endpoint, int *sock, int sock_type, struct sockaddr_storage *addr, enum socket_op sockop) {
+ struct addrinfo *res = NULL, *info, _fake_res;
+ struct sockaddr_un _fake_addr_un;
+
+ if (endpoint->family == PF_UNIX) {
+ // getaddrinfo doesn't handle PF_UNIX, so we need to build a fake result
+
+ // build the sockaddr_un
+ _fake_addr_un.sun_family = endpoint->family;
+ memcpy(_fake_addr_un.sun_path, endpoint->af.local.path, sizeof(_fake_addr_un.sun_path));
+
+ // build the fake addrinfo res
+ _fake_res.ai_flags = 0;
+ _fake_res.ai_family = PF_UNIX;
+ _fake_res.ai_socktype = sock_type;
+ _fake_res.ai_protocol = 0;
+ _fake_res.ai_addrlen = SUN_LEN(&_fake_addr_un);
+ _fake_res.ai_addr = (struct sockaddr *) &_fake_addr_un;
+ _fake_res.ai_canonname = (char *) endpoint->af.local.path; /* XXX: not const */
+ _fake_res.ai_next = NULL;
+
+ res = &_fake_res;
+
+ } else if (endpoint->family == PF_INET) {
+ // use the real getaddrinfo
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_socktype = sock_type;
+ hints.ai_protocol = 0;
+ hints.ai_flags = (sockop & _SOCKOP_FLAGS_PASSIVE ? AI_PASSIVE : 0) | AI_CANONNAME;
+
+ int err;
+
+ // check that we have a service, doesn't make any sense without
+ if (!endpoint->af.inet.port)
+ ERROR("no service specified");
+
+ if ((err = getaddrinfo(endpoint->af.inet.addr, endpoint->af.inet.port, &hints, &res)) != 0)
+ ERROR("getaddrinfo: %s#%s: %s", endpoint->af.inet.addr, endpoint->af.inet.port, gai_strerror(err));
+
+ } else {
+ ERROR("invalid endpoint");
+ }
+
+ for (info = res; info; info = info->ai_next) {
+ if (sockop & _SOCKOP_SOCKET && (*sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) == -1) {
+ PWARNING("socket(%d, %s)", info->ai_family, info->ai_canonname);
+ continue;
+ }
+
+ if (sockop & _SOCKOP_FCNTL_NONBLOCK && fcntl(*sock, F_SETFL, O_NONBLOCK) == -1) {
+ PWARNING("fcntl(%d, %s, F_SETFL O_NONBLOCK)", info->ai_family, info->ai_canonname);
+ close(*sock); *sock = -1;
+ continue;
+ }
+
+ if (sockop & _SOCKOP_ENDPOINT_BIND && bind(*sock, info->ai_addr, info->ai_addrlen) == -1) {
+ PWARNING("bind(%d, %s)", info->ai_family, info->ai_canonname);
+ close(*sock); *sock = -1;
+ continue;
+ }
+
+ if (sockop & _SOCKOP_LISTEN && listen(*sock, SOCKET_LISTEN_BACKLOG) == -1) {
+ PWARNING("listen(%d, %s)", info->ai_family, info->ai_canonname);
+ close(*sock); *sock = -1;
+ continue;
+ }
+
+ if (sockop & _SOCKOP_ENDPOINT_CONNECT && connect(*sock, info->ai_addr, info->ai_addrlen) == -1) {
+ if (sockop & _SOCKOP_FCNTL_NONBLOCK && errno == EINPROGRESS) {
+ /* to be expected */
+
+ } else {
+ PWARNING("listen(%d, %s)", info->ai_family, info->ai_canonname);
+ close(*sock); *sock = -1;
+ continue;
+ }
+ }
+
+ // copy the succesfull address over
+ memcpy(addr, info->ai_addr, info->ai_addrlen);
+
+ // exit the loop with a valid socket
+ assert(*sock != -1);
+ break;
+ }
+
+ if (*sock == -1)
+ ERROR("no working results from getaddrinfo: %s#%s", endpoint->af.inet.addr, endpoint->af.inet.port);
+
+ return 0;
+
+error:
+ if (res != 0 && res != &_fake_res)
+ freeaddrinfo(res);
+
+ return -1;
+}
+