From e11c02c8f1591fef64200137c24598fba9d488a9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 1 Jul 2013 14:47:11 +0200 Subject: [PATCH] whitelist: use a stream service to accept client connections Use SOCK_STREAM, as we don't have SOCK_SEQPACKET on TCP. To have network transparency, the message now uses network byte order. --- src/libcharon/plugins/whitelist/whitelist.c | 93 ++++++++++-- .../plugins/whitelist/whitelist_control.c | 132 ++++-------------- .../plugins/whitelist/whitelist_msg.h | 2 +- 3 files changed, 106 insertions(+), 121 deletions(-) diff --git a/src/libcharon/plugins/whitelist/whitelist.c b/src/libcharon/plugins/whitelist/whitelist.c index 0a3a344593..f5fa6f60f1 100644 --- a/src/libcharon/plugins/whitelist/whitelist.c +++ b/src/libcharon/plugins/whitelist/whitelist.c @@ -18,45 +18,102 @@ #include #include #include +#include #include #include #include +#include /** * Connect to the daemon, return FD */ static int make_connection() { - struct sockaddr_un addr; - int fd; + union { + struct sockaddr_un un; + struct sockaddr_in in; + struct sockaddr sa; + } addr; + int fd, len; - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, WHITELIST_SOCKET); + if (getenv("TCP_PORT")) + { + addr.in.sin_family = AF_INET; + addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.in.sin_port = htons(atoi(getenv("TCP_PORT"))); + len = sizeof(addr.in); + } + else + { + addr.un.sun_family = AF_UNIX; + strcpy(addr.un.sun_path, WHITELIST_SOCKET); - fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + len = offsetof(struct sockaddr_un, sun_path) + strlen(addr.un.sun_path); + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); if (fd < 0) { fprintf(stderr, "opening socket failed: %s\n", strerror(errno)); return -1; } - if (connect(fd, (struct sockaddr *)&addr, - offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)) < 0) + if (connect(fd, &addr.sa, len) < 0) { - fprintf(stderr, "connecting to %s failed: %s\n", - WHITELIST_SOCKET, strerror(errno)); + fprintf(stderr, "connecting failed: %s\n", strerror(errno)); close(fd); return -1; } return fd; } +static int read_all(int fd, void *buf, size_t len) +{ + ssize_t ret, done = 0; + + while (done < len) + { + ret = read(fd, buf, len - done); + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + if (ret < 0) + { + return -1; + } + done += ret; + buf += ret; + } + return len; +} + +static int write_all(int fd, void *buf, size_t len) +{ + ssize_t ret, done = 0; + + while (done < len) + { + ret = write(fd, buf, len - done); + if (ret == -1 && errno == EINTR) + { /* interrupted, try again */ + continue; + } + if (ret < 0) + { + return -1; + } + done += ret; + buf += ret; + } + return len; +} + /** * Send a single message */ static int send_msg(int type, char *id) { whitelist_msg_t msg = { - .type = type, + .type = htonl(type), }; int fd; @@ -66,7 +123,7 @@ static int send_msg(int type, char *id) return 2; } snprintf(msg.id, sizeof(msg.id), "%s", id); - if (send(fd, &msg, sizeof(msg), 0) != sizeof(msg)) + if (write_all(fd, &msg, sizeof(msg)) != sizeof(msg)) { fprintf(stderr, "writing to socket failed: %s\n", strerror(errno)); close(fd); @@ -74,9 +131,15 @@ static int send_msg(int type, char *id) } if (type == WHITELIST_LIST) { - while (recv(fd, &msg, sizeof(msg), 0) == sizeof(msg)) + while (1) { - if (msg.type != WHITELIST_LIST) + if (read_all(fd, &msg, sizeof(msg)) != sizeof(msg)) + { + fprintf(stderr, "reading failed: %s\n", strerror(errno)); + close(fd); + return 2; + } + if (ntohl(msg.type) != WHITELIST_LIST) { break; } @@ -94,7 +157,7 @@ static int send_msg(int type, char *id) static int send_batch(int type, char *file) { whitelist_msg_t msg = { - .type = type, + .type = htonl(type), }; FILE *f = stdin; int fd, len; @@ -125,7 +188,7 @@ static int send_batch(int type, char *file) { msg.id[len-1] = '\0'; } - if (send(fd, &msg, sizeof(msg), 0) != sizeof(msg)) + if (write_all(fd, &msg, sizeof(msg)) != sizeof(msg)) { fprintf(stderr, "writing to socket failed: %s\n", strerror(errno)); if (f != stdin) diff --git a/src/libcharon/plugins/whitelist/whitelist_control.c b/src/libcharon/plugins/whitelist/whitelist_control.c index b90b62ac1e..c3f7ac40eb 100644 --- a/src/libcharon/plugins/whitelist/whitelist_control.c +++ b/src/libcharon/plugins/whitelist/whitelist_control.c @@ -23,8 +23,6 @@ #include #include -#include -#include #include "whitelist_msg.h" @@ -46,65 +44,28 @@ struct private_whitelist_control_t { whitelist_listener_t *listener; /** - * Whitelist unix socket file descriptor + * Whitelist stream service */ - int socket; + stream_service_t *service; }; /** - * Open whitelist unix socket + * Dispatch a received message */ -static bool open_socket(private_whitelist_control_t *this) +static bool on_accept(private_whitelist_control_t *this, stream_t *stream) { - struct sockaddr_un addr; - mode_t old; - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, WHITELIST_SOCKET); + identification_t *id, *current; + enumerator_t *enumerator; + whitelist_msg_t msg; - this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (this->socket == -1) + if (!stream->read_all(stream, &msg, sizeof(msg))) { - DBG1(DBG_CFG, "creating whitelist socket failed"); return FALSE; } - unlink(addr.sun_path); - old = umask(~(S_IRWXU | S_IRWXG)); - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) - { - DBG1(DBG_CFG, "binding whitelist socket failed: %s", strerror(errno)); - close(this->socket); - return FALSE; - } - umask(old); - if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) - { - DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s", - strerror(errno)); - } - if (listen(this->socket, 10) < 0) - { - DBG1(DBG_CFG, "listening on whitelist socket failed: %s", strerror(errno)); - close(this->socket); - unlink(addr.sun_path); - return FALSE; - } - return TRUE; -} -/** - * Dispatch a received message - */ -static void dispatch(private_whitelist_control_t *this, - int fd, whitelist_msg_t *msg) -{ - identification_t *id, *current; - enumerator_t *enumerator; - - msg->id[sizeof(msg->id)-1] = 0; - id = identification_create_from_string(msg->id); - switch (msg->type) + msg.id[sizeof(msg.id) - 1] = 0; + id = identification_create_from_string(msg.id); + switch (ntohl(msg.type)) { case WHITELIST_ADD: this->listener->add(this->listener, id); @@ -118,8 +79,8 @@ static void dispatch(private_whitelist_control_t *this, { if (current->matches(current, id)) { - snprintf(msg->id, sizeof(msg->id), "%Y", current); - if (send(fd, msg, sizeof(*msg), 0) != sizeof(*msg)) + snprintf(msg.id, sizeof(msg.id), "%Y", current); + if (!stream->write_all(stream, &msg, sizeof(msg))) { DBG1(DBG_CFG, "listing whitelist failed"); break; @@ -127,9 +88,9 @@ static void dispatch(private_whitelist_control_t *this, } } enumerator->destroy(enumerator); - msg->type = WHITELIST_END; - memset(msg->id, 0, sizeof(msg->id)); - send(fd, msg, sizeof(*msg), 0); + msg.type = htonl(WHITELIST_END); + memset(msg.id, 0, sizeof(msg.id)); + stream->write_all(stream, &msg, sizeof(msg)); break; case WHITELIST_FLUSH: this->listener->flush(this->listener, id); @@ -145,58 +106,14 @@ static void dispatch(private_whitelist_control_t *this, break; } id->destroy(id); -} - -/** - * Accept whitelist control connections, dispatch - */ -static job_requeue_t receive(private_whitelist_control_t *this) -{ - struct sockaddr_un addr; - int fd, len = sizeof(addr); - whitelist_msg_t msg; - bool oldstate; - - oldstate = thread_cancelability(TRUE); - fd = accept(this->socket, (struct sockaddr*)&addr, &len); - thread_cancelability(oldstate); - - if (fd != -1) - { - while (TRUE) - { - oldstate = thread_cancelability(TRUE); - len = recv(fd, &msg, sizeof(msg), 0); - thread_cancelability(oldstate); - if (len == sizeof(msg)) - { - dispatch(this, fd, &msg); - } - else - { - if (len != 0) - { - DBG1(DBG_CFG, "receiving whitelist msg failed: %s", - strerror(errno)); - } - break; - } - } - close(fd); - } - else - { - DBG1(DBG_CFG, "accepting whitelist connection failed: %s", - strerror(errno)); - } - return JOB_REQUEUE_FAIR; + return FALSE; } METHOD(whitelist_control_t, destroy, void, private_whitelist_control_t *this) { - close(this->socket); + this->service->destroy(this->service); free(this); } @@ -206,6 +123,7 @@ METHOD(whitelist_control_t, destroy, void, whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener) { private_whitelist_control_t *this; + char *uri; INIT(this, .public = { @@ -214,15 +132,19 @@ whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener) .listener = listener, ); - if (!open_socket(this)) + uri = lib->settings->get_str(lib->settings, + "%s.plugins.whitelist.socket", "unix://" WHITELIST_SOCKET, + charon->name); + this->service = lib->streams->create_service(lib->streams, uri, 10); + if (!this->service) { + DBG1(DBG_CFG, "creating whitelist socket failed"); free(this); return NULL; } - lib->processor->queue_job(lib->processor, - (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this, - NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + this->service->on_accept(this->service, (stream_service_cb_t)on_accept, + this, JOB_PRIO_CRITICAL, 0); return &this->public; } diff --git a/src/libcharon/plugins/whitelist/whitelist_msg.h b/src/libcharon/plugins/whitelist/whitelist_msg.h index 65b922996b..595fb6ffb6 100644 --- a/src/libcharon/plugins/whitelist/whitelist_msg.h +++ b/src/libcharon/plugins/whitelist/whitelist_msg.h @@ -53,6 +53,6 @@ struct whitelist_msg_t { int type; /** null terminated identity */ char id[128]; -}; +} __attribute__((packed)); #endif /** WHITELIST_MSG_H_ @}*/ -- 2.47.2