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.
This commit is contained in:
Martin Willi
2013-07-18 16:00:29 +02:00
parent 091d0afa21
commit e11c02c8f1
3 changed files with 107 additions and 122 deletions
+78 -15
View File
@@ -18,45 +18,102 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
/**
* 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)
@@ -23,8 +23,6 @@
#include <errno.h>
#include <daemon.h>
#include <threading/thread.h>
#include <processing/jobs/callback_job.h>
#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
*/
static bool open_socket(private_whitelist_control_t *this)
{
struct sockaddr_un addr;
mode_t old;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, WHITELIST_SOCKET);
this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (this->socket == -1)
{
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)
static bool on_accept(private_whitelist_control_t *this, stream_t *stream)
{
identification_t *id, *current;
enumerator_t *enumerator;
whitelist_msg_t msg;
msg->id[sizeof(msg->id)-1] = 0;
id = identification_create_from_string(msg->id);
switch (msg->type)
if (!stream->read_all(stream, &msg, sizeof(msg)))
{
return FALSE;
}
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;
}
@@ -53,6 +53,6 @@ struct whitelist_msg_t {
int type;
/** null terminated identity */
char id[128];
};
} __attribute__((packed));
#endif /** WHITELIST_MSG_H_ @}*/