5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2007 - 2008 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcclient.h"
22 #include "client_internal.h"
24 /************************** Types and definitions ***************************/
26 /* Listener context */
27 struct SilcClientListenerStruct {
28 SilcClient client; /* Client */
29 SilcSchedule schedule; /* Scheduler */
30 SilcClientConnectCallback callback; /* Connection callback */
31 void *context; /* User context */
32 SilcClientConnectionParams params; /* Connection parameters */
33 SilcPublicKey public_key; /* Responder public key */
34 SilcPrivateKey private_key; /* Responder private key */
35 SilcNetListener tcp_listener; /* TCP listener */
36 SilcPacketStream udp_listener; /* UDP listener */
39 /************************ Static utility functions **************************/
41 /* Called after application has verified remote host's public key. */
43 static void silc_client_listener_verify_key_cb(SilcBool success, void *context)
45 SilcVerifyKeyContext verify = context;
47 /* Call the completion callback back to the SKE */
48 verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
49 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
50 verify->completion_context);
55 /* Verify remote host's public key. */
58 silc_client_listener_verify_key(SilcSKE ske,
59 SilcPublicKey public_key,
61 SilcSKEVerifyCbCompletion completion,
62 void *completion_context)
64 SilcClientConnection conn = context;
65 SilcClient client = conn->client;
66 SilcVerifyKeyContext verify;
68 /* If we provided repository for SKE and we got here the key was not
69 found from the repository. */
70 if (conn->internal->params.repository &&
71 !conn->internal->params.verify_notfound) {
72 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
77 SILC_LOG_DEBUG(("Verify remote public key"));
79 verify = silc_calloc(1, sizeof(*verify));
81 completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
86 verify->completion = completion;
87 verify->completion_context = completion_context;
89 /* Verify public key in application */
90 client->internal->ops->verify_public_key(client, conn,
91 SILC_CONN_CLIENT, public_key,
92 silc_client_listener_verify_key_cb,
96 /* Key exchange protocol completion callback. */
98 static void silc_client_listener_completion(SilcSKE ske,
100 SilcSKESecurityProperties prop,
101 SilcSKEKeyMaterial keymat,
102 SilcSKERekeyMaterial rekey,
105 SilcClientConnection conn = context;
106 SilcCipher send_key, receive_key;
107 SilcHmac hmac_send, hmac_receive;
109 SILC_LOG_DEBUG(("Key exchange completed"));
111 if (status != SILC_SKE_STATUS_OK) {
112 /* Key exchange failed */
113 conn->callback(conn->client, conn,
114 status == SILC_SKE_STATUS_TIMEOUT ?
115 SILC_CLIENT_CONN_ERROR_TIMEOUT :
116 SILC_CLIENT_CONN_ERROR_KE, conn->internal->error,
117 conn->internal->disconnect_message,
118 conn->callback_context);
122 /* Allocate the cipher and HMAC contexts */
123 if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
124 &hmac_send, &hmac_receive, &conn->internal->hash)) {
125 conn->callback(conn->client, conn,
126 SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
127 conn->callback_context);
131 /* Set the keys into the packet stream. After this call packets will be
132 encrypted with these keys. */
133 if (!silc_packet_set_keys(conn->stream, send_key, receive_key, hmac_send,
134 hmac_receive, FALSE)) {
135 conn->callback(conn->client, conn,
136 SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
137 conn->callback_context);
141 /* Key exchange successful */
142 conn->callback(conn->client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
143 conn->callback_context);
146 /* Starts key agreement as responder. */
149 silc_client_listener_new_connection(SilcClientListener listener,
150 SilcPacketStream stream)
152 SilcClient client = listener->client;
153 SilcClientConnection conn;
154 SilcSKEParamsStruct params;
155 const char *hostname = NULL, *ip = NULL;
158 /* Get remote information */
159 silc_socket_stream_get_info(silc_packet_stream_get_stream(stream),
160 NULL, &hostname, &ip, &port);
162 listener->callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL,
164 silc_packet_stream_destroy(stream);
170 /* Add new connection */
171 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, FALSE,
173 listener->public_key,
174 listener->private_key,
175 (char *)hostname, port,
176 listener->callback, listener->context);
178 listener->callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL,
180 silc_packet_stream_destroy(stream);
183 conn->stream = stream;
184 conn->socket_stream = silc_packet_stream_get_stream(stream);
185 silc_socket_stream_get_info(conn->socket_stream, &conn->sock, NULL,
187 conn->internal->schedule = listener->schedule;
188 silc_packet_set_context(conn->stream, conn);
190 SILC_LOG_DEBUG(("Processing new incoming connection %p", conn));
193 conn->internal->ske =
194 silc_ske_alloc(client->rng, conn->internal->schedule,
195 listener->params.repository, listener->public_key,
196 listener->private_key, listener);
197 if (!conn->internal->ske) {
198 conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
199 conn->callback_context);
203 /* Set SKE parameters */
204 params.version = client->internal->silc_client_version;
205 params.flags = SILC_SKE_SP_FLAG_MUTUAL;
206 if (listener->params.udp) {
207 params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
208 params.session_port = listener->params.local_port;
211 silc_ske_set_callbacks(conn->internal->ske, silc_client_listener_verify_key,
212 silc_client_listener_completion, conn);
214 /* Start key exchange as responder */
215 conn->internal->op = silc_ske_responder(conn->internal->ske,
216 conn->stream, ¶ms);
217 if (!conn->internal->op)
218 conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
219 conn->callback_context);
222 /* TCP network listener callback. Accepts new key agreement connection.
223 Responder function. */
225 static void silc_client_listener_tcp_accept(SilcResult status,
229 SilcClientListener listener = context;
230 SilcPacketStream packet_stream;
232 SILC_LOG_DEBUG(("New incoming TCP connection"));
234 /* Create packet stream */
236 silc_packet_stream_create(listener->client->internal->packet_engine,
237 listener->schedule, stream);
238 if (!packet_stream) {
239 silc_stream_destroy(stream);
243 /* Process session */
244 silc_client_listener_new_connection(listener, packet_stream);
247 /* UDP network listener callback. Accepts new key agreement session.
248 Responder function. */
250 static SilcBool silc_client_udp_accept(SilcPacketEngine engine,
251 SilcPacketStream stream,
253 void *callback_context,
254 void *stream_context)
256 SilcClientListener listener = callback_context;
257 SilcPacketStream packet_stream;
261 SILC_LOG_DEBUG(("New incoming UDP connection"));
263 /* We want only key exchange packet. Eat other packets so that default
264 packet callback doesn't get them. */
265 if (packet->type != SILC_PACKET_KEY_EXCHANGE) {
266 silc_packet_free(packet);
270 /* Create packet stream for this remote UDP session */
271 if (!silc_packet_get_sender(packet, &ip, &port)) {
272 silc_packet_free(packet);
275 packet_stream = silc_packet_stream_add_remote(stream, ip, port, packet);
276 if (!packet_stream) {
277 silc_packet_free(packet);
281 /* Process session */
282 silc_client_listener_new_connection(listener, packet_stream);
286 /* Packet stream callbacks */
287 static SilcPacketCallbacks silc_client_listener_stream_cb =
289 silc_client_udp_accept, NULL, NULL
292 /***************************** Listner routines *****************************/
294 /* Adds network listener. The `callback' will be called after new conection
295 has arrived and key exchange protocol has been completed. */
298 silc_client_listener_add(SilcClient client,
299 SilcSchedule schedule,
300 SilcClientConnectionParams *params,
301 SilcPublicKey public_key,
302 SilcPrivateKey private_key,
303 SilcClientConnectCallback callback,
306 SilcClientListener listener;
309 if (!client || !schedule ||
310 !params || (!params->local_ip && !params->bind_ip))
313 SILC_LOG_DEBUG(("Adding new listener"));
315 listener = silc_calloc(1, sizeof(*listener));
318 listener->client = client;
319 listener->schedule = schedule;
320 listener->callback = callback;
321 listener->context = context;
322 listener->params = *params;
323 listener->public_key = public_key;
324 listener->private_key = private_key;
326 /* Create network listener */
329 stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
330 params->local_ip, params->local_port,
332 listener->udp_listener =
333 silc_packet_stream_create(client->internal->packet_engine,
335 if (!listener->udp_listener) {
336 client->internal->ops->say(
337 client, NULL, SILC_CLIENT_MESSAGE_ERROR,
338 "Cannot create UDP listener on %s on port %d: %s",
339 params->bind_ip ? params->bind_ip :
340 params->local_ip, params->local_port,
341 silc_errno_string(silc_errno));
342 silc_client_listener_free(listener);
344 silc_stream_destroy(stream);
347 silc_packet_stream_link(listener->udp_listener,
348 &silc_client_listener_stream_cb, listener,
349 1000000, SILC_PACKET_ANY, -1);
351 if (!params->local_port) {
352 /* Get listener port */
354 silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
355 listener->params.local_port = silc_net_get_local_port(sock);
359 listener->tcp_listener =
360 silc_net_tcp_create_listener(params->bind_ip ?
361 (const char **)¶ms->bind_ip :
362 (const char **)¶ms->local_ip,
363 1, params->local_port, TRUE, FALSE,
364 schedule, silc_client_listener_tcp_accept,
366 if (!listener->tcp_listener) {
367 client->internal->ops->say(
368 client, NULL, SILC_CLIENT_MESSAGE_ERROR,
369 "Cannot create listener on %s on port %d: %s",
370 params->bind_ip ? params->bind_ip :
371 params->local_ip, params->local_port,
372 silc_errno_string(silc_errno));
374 silc_client_listener_free(listener);
378 if (!params->local_port) {
379 /* Get listener port */
381 ports = silc_net_listener_get_port(listener->tcp_listener, NULL);
382 listener->params.local_port = ports[0];
387 SILC_LOG_DEBUG(("Bound listener to %s:%d",
388 params->bind_ip ? params->bind_ip : params->local_ip,
389 listener->params.local_port));
394 /* Close and free listner */
396 void silc_client_listener_free(SilcClientListener listener)
398 if (listener->tcp_listener)
399 silc_net_close_listener(listener->tcp_listener);
400 silc_packet_stream_destroy(listener->udp_listener);
404 /* Return listner bound port */
406 SilcUInt16 silc_client_listener_get_local_port(SilcClientListener listener)
408 return listener->params.local_port;