Merge branch 'ike-dscp'
[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 * Peer identity
51 */
52 identification_t *peer;
53
54 /**
55 * TNCCS protocol handler constructor
56 */
57 pt_tls_tnccs_constructor_t *create;
58 };
59
60 /**
61 * Open listening server socket
62 */
63 static bool open_socket(private_pt_tls_dispatcher_t *this, host_t *host)
64 {
65 this->fd = socket(AF_INET, SOCK_STREAM, 0);
66 if (this->fd == -1)
67 {
68 DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
69 return FALSE;
70 }
71 if (bind(this->fd, host->get_sockaddr(host),
72 *host->get_sockaddr_len(host)) == -1)
73 {
74 DBG1(DBG_TNC, "binding to PT-TLS socket failed: %s", strerror(errno));
75 return FALSE;
76 }
77 if (listen(this->fd, 5) == -1)
78 {
79 DBG1(DBG_TNC, "listen on PT-TLS socket failed: %s", strerror(errno));
80 return FALSE;
81 }
82 return TRUE;
83 }
84
85 /**
86 * Handle a single PT-TLS client connection
87 */
88 static job_requeue_t handle(pt_tls_server_t *connection)
89 {
90 while (TRUE)
91 {
92 switch (connection->handle(connection))
93 {
94 case NEED_MORE:
95 continue;
96 case FAILED:
97 case SUCCESS:
98 default:
99 break;
100 }
101 break;
102 }
103 return JOB_REQUEUE_NONE;
104 }
105
106 /**
107 * Clean up connection state
108 */
109 static void cleanup(pt_tls_server_t *connection)
110 {
111 int fd;
112
113 fd = connection->get_fd(connection);
114 connection->destroy(connection);
115 close(fd);
116 }
117
118 METHOD(pt_tls_dispatcher_t, dispatch, void,
119 private_pt_tls_dispatcher_t *this,
120 pt_tls_tnccs_constructor_t *create)
121 {
122 while (TRUE)
123 {
124 pt_tls_server_t *connection;
125 tnccs_t *tnccs;
126 bool old;
127 int fd;
128
129 old = thread_cancelability(TRUE);
130 fd = accept(this->fd, NULL, NULL);
131 thread_cancelability(old);
132 if (fd == -1)
133 {
134 DBG1(DBG_TNC, "accepting PT-TLS failed: %s", strerror(errno));
135 continue;
136 }
137
138 tnccs = create(this->server, this->peer);
139 if (!tnccs)
140 {
141 close(fd);
142 continue;
143 }
144 connection = pt_tls_server_create(this->server, fd, tnccs);
145 if (!connection)
146 {
147 close(fd);
148 continue;
149 }
150 lib->processor->queue_job(lib->processor,
151 (job_t*)callback_job_create_with_prio((callback_job_cb_t)handle,
152 connection, (void*)cleanup,
153 (callback_job_cancel_t)return_false,
154 JOB_PRIO_CRITICAL));
155 }
156 }
157
158 METHOD(pt_tls_dispatcher_t, destroy, void,
159 private_pt_tls_dispatcher_t *this)
160 {
161 if (this->fd != -1)
162 {
163 close(this->fd);
164 }
165 this->server->destroy(this->server);
166 this->peer->destroy(this->peer);
167 free(this);
168 }
169
170 /**
171 * See header
172 */
173 pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
174 identification_t *id)
175 {
176 private_pt_tls_dispatcher_t *this;
177
178 INIT(this,
179 .public = {
180 .dispatch = _dispatch,
181 .destroy = _destroy,
182 },
183 .server = id,
184 /* we currently don't authenticate the peer, use %any identity */
185 .peer = identification_create_from_encoding(ID_ANY, chunk_empty),
186 .fd = -1,
187 );
188
189 if (!open_socket(this, address))
190 {
191 address->destroy(address);
192 destroy(this);
193 return NULL;
194 }
195 address->destroy(address);
196
197 return &this->public;
198 }