error-notify: use a stream service to accept client connections
As TCP does not have SOCK_SEQPACKET, we now use SOCK_STREAM for the error-notify socket. To have network transparency, the message now uses network byte order.
This commit is contained in:
@@ -16,46 +16,89 @@
|
||||
#include "error_notify_msg.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/**
|
||||
* Connect to the daemon, return FD
|
||||
*/
|
||||
static int make_connection()
|
||||
{
|
||||
union {
|
||||
struct sockaddr_un un;
|
||||
struct sockaddr_in in;
|
||||
struct sockaddr sa;
|
||||
} addr;
|
||||
int fd, len;
|
||||
|
||||
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, ERROR_NOTIFY_SOCKET);
|
||||
|
||||
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, &addr.sa, len) < 0)
|
||||
{
|
||||
fprintf(stderr, "connecting failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example of a simple notification listener
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
error_notify_msg_t msg;
|
||||
int s;
|
||||
int s, len, total;
|
||||
void *pos;
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, ERROR_NOTIFY_SOCKET);
|
||||
|
||||
s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
s = make_connection();
|
||||
if (s < 0)
|
||||
{
|
||||
fprintf(stderr, "opening socket failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
fprintf(stderr, "connect failed: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return 1;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
if (read(s, &msg, sizeof(msg)) != sizeof(msg))
|
||||
total = 0;
|
||||
pos = &msg;
|
||||
|
||||
while (total < sizeof(msg))
|
||||
{
|
||||
fprintf(stderr, "read failed: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return 1;
|
||||
len = read(s, pos, sizeof(msg) - total);
|
||||
if (len < 0)
|
||||
{
|
||||
fprintf(stderr, "read failed: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return 1;
|
||||
}
|
||||
total += len;
|
||||
pos += len;
|
||||
}
|
||||
printf("%d %s %s %s %s\n",
|
||||
msg.type, msg.name, msg.id, msg.ip, msg.str);
|
||||
ntohl(msg.type), msg.name, msg.id, msg.ip, msg.str);
|
||||
}
|
||||
close(s);
|
||||
return 0;
|
||||
|
||||
@@ -56,80 +56,80 @@ METHOD(listener_t, alert, bool,
|
||||
switch (alert)
|
||||
{
|
||||
case ALERT_RADIUS_NOT_RESPONDING:
|
||||
msg.type = ERROR_NOTIFY_RADIUS_NOT_RESPONDING;
|
||||
msg.type = htonl(ERROR_NOTIFY_RADIUS_NOT_RESPONDING);
|
||||
snprintf(msg.str, sizeof(msg.str),
|
||||
"a RADIUS request message timed out");
|
||||
break;
|
||||
case ALERT_LOCAL_AUTH_FAILED:
|
||||
msg.type = ERROR_NOTIFY_LOCAL_AUTH_FAILED;
|
||||
msg.type = htonl(ERROR_NOTIFY_LOCAL_AUTH_FAILED);
|
||||
snprintf(msg.str, sizeof(msg.str),
|
||||
"creating local authentication data failed");
|
||||
break;
|
||||
case ALERT_PEER_AUTH_FAILED:
|
||||
msg.type = ERROR_NOTIFY_PEER_AUTH_FAILED;
|
||||
msg.type = htonl(ERROR_NOTIFY_PEER_AUTH_FAILED);
|
||||
snprintf(msg.str, sizeof(msg.str), "peer authentication failed");
|
||||
break;
|
||||
case ALERT_PARSE_ERROR_HEADER:
|
||||
msg.type = ERROR_NOTIFY_PARSE_ERROR_HEADER;
|
||||
msg.type = htonl(ERROR_NOTIFY_PARSE_ERROR_HEADER);
|
||||
message = va_arg(args, message_t*);
|
||||
snprintf(msg.str, sizeof(msg.str), "parsing IKE header from "
|
||||
"%#H failed", message->get_source(message));
|
||||
break;
|
||||
case ALERT_PARSE_ERROR_BODY:
|
||||
msg.type = ERROR_NOTIFY_PARSE_ERROR_BODY;
|
||||
msg.type = htonl(ERROR_NOTIFY_PARSE_ERROR_BODY);
|
||||
message = va_arg(args, message_t*);
|
||||
snprintf(msg.str, sizeof(msg.str), "parsing IKE message from "
|
||||
"%#H failed", message->get_source(message));
|
||||
break;
|
||||
case ALERT_RETRANSMIT_SEND_TIMEOUT:
|
||||
msg.type = ERROR_NOTIFY_RETRANSMIT_SEND_TIMEOUT;
|
||||
msg.type = htonl(ERROR_NOTIFY_RETRANSMIT_SEND_TIMEOUT);
|
||||
snprintf(msg.str, sizeof(msg.str),
|
||||
"IKE message retransmission timed out");
|
||||
break;
|
||||
case ALERT_HALF_OPEN_TIMEOUT:
|
||||
msg.type = ERROR_NOTIFY_HALF_OPEN_TIMEOUT;
|
||||
msg.type = htonl(ERROR_NOTIFY_HALF_OPEN_TIMEOUT);
|
||||
snprintf(msg.str, sizeof(msg.str), "IKE_SA timed out before it "
|
||||
"could be established");
|
||||
break;
|
||||
case ALERT_PROPOSAL_MISMATCH_IKE:
|
||||
msg.type = ERROR_NOTIFY_PROPOSAL_MISMATCH_IKE;
|
||||
msg.type = htonl(ERROR_NOTIFY_PROPOSAL_MISMATCH_IKE);
|
||||
list = va_arg(args, linked_list_t*);
|
||||
snprintf(msg.str, sizeof(msg.str), "the received IKE_SA poposals "
|
||||
"did not match: %#P", list);
|
||||
break;
|
||||
case ALERT_PROPOSAL_MISMATCH_CHILD:
|
||||
msg.type = ERROR_NOTIFY_PROPOSAL_MISMATCH_CHILD;
|
||||
msg.type = htonl(ERROR_NOTIFY_PROPOSAL_MISMATCH_CHILD);
|
||||
list = va_arg(args, linked_list_t*);
|
||||
snprintf(msg.str, sizeof(msg.str), "the received CHILD_SA poposals "
|
||||
"did not match: %#P", list);
|
||||
break;
|
||||
case ALERT_TS_MISMATCH:
|
||||
msg.type = ERROR_NOTIFY_TS_MISMATCH;
|
||||
msg.type = htonl(ERROR_NOTIFY_TS_MISMATCH);
|
||||
list = va_arg(args, linked_list_t*);
|
||||
list2 = va_arg(args, linked_list_t*);
|
||||
snprintf(msg.str, sizeof(msg.str), "the received traffic selectors "
|
||||
"did not match: %#R=== %#R", list, list2);
|
||||
break;
|
||||
case ALERT_INSTALL_CHILD_SA_FAILED:
|
||||
msg.type = ERROR_NOTIFY_INSTALL_CHILD_SA_FAILED;
|
||||
msg.type = htonl(ERROR_NOTIFY_INSTALL_CHILD_SA_FAILED);
|
||||
snprintf(msg.str, sizeof(msg.str), "installing IPsec SA failed");
|
||||
break;
|
||||
case ALERT_INSTALL_CHILD_POLICY_FAILED:
|
||||
msg.type = ERROR_NOTIFY_INSTALL_CHILD_POLICY_FAILED;
|
||||
msg.type = htonl(ERROR_NOTIFY_INSTALL_CHILD_POLICY_FAILED);
|
||||
snprintf(msg.str, sizeof(msg.str), "installing IPsec policy failed");
|
||||
break;
|
||||
case ALERT_UNIQUE_REPLACE:
|
||||
msg.type = ERROR_NOTIFY_UNIQUE_REPLACE;
|
||||
msg.type = htonl(ERROR_NOTIFY_UNIQUE_REPLACE);
|
||||
snprintf(msg.str, sizeof(msg.str),
|
||||
"replaced old IKE_SA due to uniqueness policy");
|
||||
break;
|
||||
case ALERT_UNIQUE_KEEP:
|
||||
msg.type = ERROR_NOTIFY_UNIQUE_KEEP;
|
||||
msg.type = htonl(ERROR_NOTIFY_UNIQUE_KEEP);
|
||||
snprintf(msg.str, sizeof(msg.str), "keep existing in favor of "
|
||||
"rejected new IKE_SA due to uniqueness policy");
|
||||
break;
|
||||
case ALERT_VIP_FAILURE:
|
||||
msg.type = ERROR_NOTIFY_VIP_FAILURE;
|
||||
msg.type = htonl(ERROR_NOTIFY_VIP_FAILURE);
|
||||
list = va_arg(args, linked_list_t*);
|
||||
if (list->get_first(list, (void**)&host) == SUCCESS)
|
||||
{
|
||||
@@ -143,7 +143,7 @@ METHOD(listener_t, alert, bool,
|
||||
}
|
||||
break;
|
||||
case ALERT_AUTHORIZATION_FAILED:
|
||||
msg.type = ERROR_NOTIFY_AUTHORIZATION_FAILED;
|
||||
msg.type = htonl(ERROR_NOTIFY_AUTHORIZATION_FAILED);
|
||||
snprintf(msg.str, sizeof(msg.str), "an authorization plugin "
|
||||
"prevented establishment of an IKE_SA");
|
||||
break;
|
||||
|
||||
@@ -61,6 +61,6 @@ struct error_notify_msg_t {
|
||||
char id[128];
|
||||
/** peer address and port, if known */
|
||||
char ip[60];
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /** ERROR_NOTIFY_MSG_H_ @}*/
|
||||
|
||||
@@ -43,12 +43,12 @@ struct private_error_notify_socket_t {
|
||||
error_notify_socket_t public;
|
||||
|
||||
/**
|
||||
* Unix socket file descriptor
|
||||
* Service accepting connections
|
||||
*/
|
||||
int socket;
|
||||
stream_service_t *service;
|
||||
|
||||
/**
|
||||
* List of connected clients, as uintptr_t FD
|
||||
* List of connected clients, as stream_t
|
||||
*/
|
||||
linked_list_t *connected;
|
||||
|
||||
@@ -58,48 +58,6 @@ struct private_error_notify_socket_t {
|
||||
mutex_t *mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open error notify unix socket
|
||||
*/
|
||||
static bool open_socket(private_error_notify_socket_t *this)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
mode_t old;
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, ERROR_NOTIFY_SOCKET);
|
||||
|
||||
this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (this->socket == -1)
|
||||
{
|
||||
DBG1(DBG_CFG, "creating notify 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 notify 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 notify socket permissions failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
if (listen(this->socket, 10) < 0)
|
||||
{
|
||||
DBG1(DBG_CFG, "listening on notify socket failed: %s", strerror(errno));
|
||||
close(this->socket);
|
||||
unlink(addr.sun_path);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(error_notify_socket_t, has_listeners, bool,
|
||||
private_error_notify_socket_t *this)
|
||||
{
|
||||
@@ -116,23 +74,21 @@ METHOD(error_notify_socket_t, notify, void,
|
||||
private_error_notify_socket_t *this, error_notify_msg_t *msg)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
uintptr_t fd;
|
||||
stream_t *stream;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
enumerator = this->connected->create_enumerator(this->connected);
|
||||
while (enumerator->enumerate(enumerator, (void*)&fd))
|
||||
while (enumerator->enumerate(enumerator, &stream))
|
||||
{
|
||||
while (send(fd, msg, sizeof(*msg), 0) <= 0)
|
||||
if (!stream->write_all(stream, msg, sizeof(*msg)))
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EINTR:
|
||||
continue;
|
||||
case ECONNRESET:
|
||||
case EPIPE:
|
||||
/* disconnect, remove this listener */
|
||||
this->connected->remove_at(this->connected, enumerator);
|
||||
close(fd);
|
||||
stream->destroy(stream);
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_CFG, "sending notify failed: %s", strerror(errno));
|
||||
@@ -146,45 +102,23 @@ METHOD(error_notify_socket_t, notify, void,
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept client connections, dispatch
|
||||
* Accept client connections
|
||||
*/
|
||||
static job_requeue_t accept_(private_error_notify_socket_t *this)
|
||||
static bool on_accept(private_error_notify_socket_t *this, stream_t *stream)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int fd, len;
|
||||
bool oldstate;
|
||||
this->mutex->lock(this->mutex);
|
||||
this->connected->insert_last(this->connected, stream);
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
len = sizeof(addr);
|
||||
oldstate = thread_cancelability(TRUE);
|
||||
fd = accept(this->socket, (struct sockaddr*)&addr, &len);
|
||||
thread_cancelability(oldstate);
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
this->connected->insert_last(this->connected, (void*)(uintptr_t)fd);
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "accepting notify connection failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(error_notify_socket_t, destroy, void,
|
||||
private_error_notify_socket_t *this)
|
||||
{
|
||||
uintptr_t fd;
|
||||
|
||||
while (this->connected->remove_last(this->connected, (void*)&fd) == SUCCESS)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
this->connected->destroy(this->connected);
|
||||
DESTROY_IF(this->service);
|
||||
this->connected->destroy_offset(this->connected, offsetof(stream_t, destroy));
|
||||
this->mutex->destroy(this->mutex);
|
||||
close(this->socket);
|
||||
free(this);
|
||||
}
|
||||
|
||||
@@ -194,6 +128,7 @@ METHOD(error_notify_socket_t, destroy, void,
|
||||
error_notify_socket_t *error_notify_socket_create()
|
||||
{
|
||||
private_error_notify_socket_t *this;
|
||||
char *uri;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
@@ -205,15 +140,18 @@ error_notify_socket_t *error_notify_socket_create()
|
||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
if (!open_socket(this))
|
||||
uri = lib->settings->get_str(lib->settings,
|
||||
"%s.plugins.error-notify.socket", "unix://" ERROR_NOTIFY_SOCKET,
|
||||
charon->name);
|
||||
this->service = lib->streams->create_service(lib->streams, uri, 10);
|
||||
if (!this->service)
|
||||
{
|
||||
free(this);
|
||||
DBG1(DBG_CFG, "creating duplicheck socket failed");
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create_with_prio((callback_job_cb_t)accept_, 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, 1);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user