Pass a constructor callback to create TNCCS server instances while dispatching
[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 <processing/jobs/callback_job.h>
22
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 typedef struct private_pt_tls_dispatcher_t private_pt_tls_dispatcher_t;
28
29 /**
30 * Private data of an pt_tls_dispatcher_t object.
31 */
32 struct private_pt_tls_dispatcher_t {
33
34 /**
35 * Public pt_tls_dispatcher_t interface.
36 */
37 pt_tls_dispatcher_t public;
38
39 /**
40 * Listening socket
41 */
42 int fd;
43
44 /**
45 * Server identity
46 */
47 identification_t *server;
48
49 /**
50 * TNCCS protocol handler constructor
51 */
52 tnccs_t*(*create)();
53 };
54
55 /**
56 * Open listening server socket
57 */
58 static bool open_socket(private_pt_tls_dispatcher_t *this, host_t *host)
59 {
60 this->fd = socket(AF_INET, SOCK_STREAM, 0);
61 if (this->fd == -1)
62 {
63 DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
64 return FALSE;
65 }
66 if (bind(this->fd, host->get_sockaddr(host),
67 *host->get_sockaddr_len(host)) == -1)
68 {
69 DBG1(DBG_TNC, "binding to PT-TLS socket failed: %s", strerror(errno));
70 return FALSE;
71 }
72 if (listen(this->fd, 5) == -1)
73 {
74 DBG1(DBG_TNC, "listen on PT-TLS socket failed: %s", strerror(errno));
75 return FALSE;
76 }
77 return TRUE;
78 }
79
80 /**
81 * Handle a single PT-TLS client connection
82 */
83 static job_requeue_t handle(pt_tls_server_t *connection)
84 {
85 while (TRUE)
86 {
87 switch (connection->handle(connection))
88 {
89 case NEED_MORE:
90 continue;
91 case FAILED:
92 case SUCCESS:
93 default:
94 break;
95 }
96 break;
97 }
98 return JOB_REQUEUE_NONE;
99 }
100
101 /**
102 * Clean up connection state
103 */
104 static void cleanup(pt_tls_server_t *connection)
105 {
106 int fd;
107
108 fd = connection->get_fd(connection);
109 connection->destroy(connection);
110 close(fd);
111 }
112
113 METHOD(pt_tls_dispatcher_t, dispatch, void,
114 private_pt_tls_dispatcher_t *this, tnccs_t*(*create)())
115 {
116 while (TRUE)
117 {
118 pt_tls_server_t *connection;
119 tnccs_t *tnccs;
120 bool old;
121 int fd;
122
123 old = thread_cancelability(TRUE);
124 fd = accept(this->fd, NULL, NULL);
125 thread_cancelability(old);
126 if (fd == -1)
127 {
128 DBG1(DBG_TNC, "accepting PT-TLS failed: %s", strerror(errno));
129 continue;
130 }
131
132 tnccs = create();
133 if (!tnccs)
134 {
135 close(fd);
136 continue;
137 }
138 connection = pt_tls_server_create(this->server, fd, tnccs);
139 if (!connection)
140 {
141 close(fd);
142 continue;
143 }
144 lib->processor->queue_job(lib->processor,
145 (job_t*)callback_job_create_with_prio((callback_job_cb_t)handle,
146 connection, (void*)cleanup,
147 (callback_job_cancel_t)return_false,
148 JOB_PRIO_CRITICAL));
149 }
150 }
151
152 METHOD(pt_tls_dispatcher_t, destroy, void,
153 private_pt_tls_dispatcher_t *this)
154 {
155 if (this->fd != -1)
156 {
157 close(this->fd);
158 }
159 this->server->destroy(this->server);
160 free(this);
161 }
162
163 /**
164 * See header
165 */
166 pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
167 identification_t *id)
168 {
169 private_pt_tls_dispatcher_t *this;
170
171 INIT(this,
172 .public = {
173 .dispatch = _dispatch,
174 .destroy = _destroy,
175 },
176 .server = id,
177 .fd = -1,
178 );
179
180 if (!open_socket(this, address))
181 {
182 address->destroy(address);
183 destroy(this);
184 return NULL;
185 }
186 address->destroy(address);
187
188 return &this->public;
189 }