Add a libpttls providing NEA PT-TLS / TNC IF-T for TLS transport layer
[strongswan.git] / src / libpttls / pt_tls_dispatcher.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "pt_tls_dispatcher.h"
17 #include "pt_tls_server.h"
18
19 #include <threading/thread.h>
20 #include <utils/debug.h>
21 #include <networking/host.h>
22 #include <processing/jobs/callback_job.h>
23
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 typedef struct private_pt_tls_dispatcher_t private_pt_tls_dispatcher_t;
29
30 /**
31 * Private data of an pt_tls_dispatcher_t object.
32 */
33 struct private_pt_tls_dispatcher_t {
34
35 /**
36 * Public pt_tls_dispatcher_t interface.
37 */
38 pt_tls_dispatcher_t public;
39
40 /**
41 * Listening socket
42 */
43 int fd;
44
45 /**
46 * Server identity
47 */
48 identification_t *server;
49 };
50
51 /**
52 * Open listening server socket
53 */
54 static bool open_socket(private_pt_tls_dispatcher_t *this,
55 char *server, u_int16_t port)
56 {
57 host_t *host;
58
59 this->fd = socket(AF_INET, SOCK_STREAM, 0);
60 if (this->fd == -1)
61 {
62 DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
63 return FALSE;
64 }
65 host = host_create_from_dns(server, AF_UNSPEC, port);
66 if (!host)
67 {
68 return FALSE;
69 }
70 if (bind(this->fd, host->get_sockaddr(host),
71 *host->get_sockaddr_len(host)) == -1)
72 {
73 DBG1(DBG_TNC, "binding to PT-TLS socket failed: %s", strerror(errno));
74 return FALSE;
75 }
76 if (listen(this->fd, 5) == -1)
77 {
78 DBG1(DBG_TNC, "listen on PT-TLS socket failed: %s", strerror(errno));
79 return FALSE;
80 }
81 return TRUE;
82 }
83
84 /**
85 * Handle a single PT-TLS client connection
86 */
87 static job_requeue_t handle(pt_tls_server_t *connection)
88 {
89 while (TRUE)
90 {
91 switch (connection->handle(connection))
92 {
93 case NEED_MORE:
94 continue;
95 case FAILED:
96 case SUCCESS:
97 default:
98 break;
99 }
100 break;
101 }
102 return JOB_REQUEUE_NONE;
103 }
104
105 /**
106 * Clean up connection state
107 */
108 static void cleanup(pt_tls_server_t *connection)
109 {
110 int fd;
111
112 fd = connection->get_fd(connection);
113 connection->destroy(connection);
114 close(fd);
115 }
116
117 METHOD(pt_tls_dispatcher_t, dispatch, void,
118 private_pt_tls_dispatcher_t *this)
119 {
120 while (TRUE)
121 {
122 pt_tls_server_t *connection;
123 bool old;
124 int fd;
125
126 old = thread_cancelability(TRUE);
127 fd = accept(this->fd, NULL, NULL);
128 thread_cancelability(old);
129 if (fd == -1)
130 {
131 DBG1(DBG_TNC, "accepting PT-TLS failed: %s", strerror(errno));
132 continue;
133 }
134
135 connection = pt_tls_server_create(this->server, fd);
136 if (!connection)
137 {
138 close(fd);
139 continue;
140 }
141 lib->processor->queue_job(lib->processor,
142 (job_t*)callback_job_create_with_prio((callback_job_cb_t)handle,
143 connection, (void*)cleanup,
144 (callback_job_cancel_t)return_false,
145 JOB_PRIO_CRITICAL));
146 }
147 }
148
149 METHOD(pt_tls_dispatcher_t, destroy, void,
150 private_pt_tls_dispatcher_t *this)
151 {
152 if (this->fd != -1)
153 {
154 close(this->fd);
155 }
156 this->server->destroy(this->server);
157 free(this);
158 }
159
160 /**
161 * See header
162 */
163 pt_tls_dispatcher_t *pt_tls_dispatcher_create(char *server, u_int16_t port)
164 {
165 private_pt_tls_dispatcher_t *this;
166
167 INIT(this,
168 .public = {
169 .dispatch = _dispatch,
170 .destroy = _destroy,
171 },
172 .server = identification_create_from_string(server),
173 .fd = -1,
174 );
175
176 if (!open_socket(this, server, port))
177 {
178 destroy(this);
179 return NULL;
180 }
181
182 return &this->public;
183 }