/* * 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 . * * 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 #include #include #include #include #include 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; }