strongswan/src/libpttls/pt_tls_dispatcher.c

203 lines
4.0 KiB
C

/*
* Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "pt_tls_dispatcher.h"
#include "pt_tls_server.h"
#include <threading/thread.h>
#include <utils/debug.h>
#include <processing/jobs/callback_job.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
typedef struct private_pt_tls_dispatcher_t private_pt_tls_dispatcher_t;
/**
* Private data of an pt_tls_dispatcher_t object.
*/
struct private_pt_tls_dispatcher_t {
/**
* Public pt_tls_dispatcher_t interface.
*/
pt_tls_dispatcher_t public;
/**
* Listening socket
*/
int fd;
/**
* Client authentication requirements
*/
pt_tls_auth_t auth;
/**
* Server identity
*/
identification_t *server;
/**
* Peer identity
*/
identification_t *peer;
/**
* TNCCS protocol handler constructor
*/
pt_tls_tnccs_constructor_t *create;
};
/**
* Open listening server socket
*/
static bool open_socket(private_pt_tls_dispatcher_t *this, host_t *host)
{
this->fd = socket(AF_INET, SOCK_STREAM, 0);
if (this->fd == -1)
{
DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
return FALSE;
}
if (bind(this->fd, host->get_sockaddr(host),
*host->get_sockaddr_len(host)) == -1)
{
DBG1(DBG_TNC, "binding to PT-TLS socket failed: %s", strerror(errno));
return FALSE;
}
if (listen(this->fd, 5) == -1)
{
DBG1(DBG_TNC, "listen on PT-TLS socket failed: %s", strerror(errno));
return FALSE;
}
return TRUE;
}
/**
* Handle a single PT-TLS client connection
*/
static job_requeue_t handle(pt_tls_server_t *connection)
{
while (TRUE)
{
switch (connection->handle(connection))
{
case NEED_MORE:
continue;
case FAILED:
case SUCCESS:
default:
break;
}
break;
}
return JOB_REQUEUE_NONE;
}
/**
* Clean up connection state
*/
static void cleanup(pt_tls_server_t *connection)
{
int fd;
fd = connection->get_fd(connection);
connection->destroy(connection);
close(fd);
}
METHOD(pt_tls_dispatcher_t, dispatch, void,
private_pt_tls_dispatcher_t *this,
pt_tls_tnccs_constructor_t *create)
{
while (TRUE)
{
pt_tls_server_t *connection;
tnccs_t *tnccs;
bool old;
int fd;
old = thread_cancelability(TRUE);
fd = accept(this->fd, NULL, NULL);
thread_cancelability(old);
if (fd == -1)
{
DBG1(DBG_TNC, "accepting PT-TLS failed: %s", strerror(errno));
continue;
}
tnccs = create(this->server, this->peer);
if (!tnccs)
{
close(fd);
continue;
}
connection = pt_tls_server_create(this->server, fd, this->auth, tnccs);
if (!connection)
{
close(fd);
continue;
}
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create_with_prio((callback_job_cb_t)handle,
connection, (void*)cleanup,
(callback_job_cancel_t)return_false,
JOB_PRIO_CRITICAL));
}
}
METHOD(pt_tls_dispatcher_t, destroy, void,
private_pt_tls_dispatcher_t *this)
{
if (this->fd != -1)
{
close(this->fd);
}
this->server->destroy(this->server);
this->peer->destroy(this->peer);
free(this);
}
/**
* See header
*/
pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
identification_t *id, pt_tls_auth_t auth)
{
private_pt_tls_dispatcher_t *this;
INIT(this,
.public = {
.dispatch = _dispatch,
.destroy = _destroy,
},
.server = id->clone(id),
/* we currently don't authenticate the peer, use %any identity */
.peer = identification_create_from_encoding(ID_ANY, chunk_empty),
.fd = -1,
.auth = auth,
);
if (!open_socket(this, address))
{
destroy(this);
return NULL;
}
return &this->public;
}