5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2007 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->internal->schedule = listener->schedule;
185 silc_packet_set_context(conn->stream, conn);
187 SILC_LOG_DEBUG(("Processing new incoming connection %p", conn));
190 conn->internal->ske =
191 silc_ske_alloc(client->rng, conn->internal->schedule,
192 listener->params.repository, listener->public_key,
193 listener->private_key, listener);
194 if (!conn->internal->ske) {
195 conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
196 conn->callback_context);
200 /* Set SKE parameters */
201 params.version = client->internal->silc_client_version;
202 params.flags = SILC_SKE_SP_FLAG_MUTUAL;
203 if (listener->params.udp) {
204 params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
205 params.session_port = listener->params.local_port;
208 silc_ske_set_callbacks(conn->internal->ske, silc_client_listener_verify_key,
209 silc_client_listener_completion, conn);
211 /* Start key exchange as responder */
212 conn->internal->op = silc_ske_responder(conn->internal->ske,
213 conn->stream, ¶ms);
214 if (!conn->internal->op)
215 conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
216 conn->callback_context);
219 /* TCP network listener callback. Accepts new key agreement connection.
220 Responder function. */
222 static void silc_client_listener_tcp_accept(SilcResult status,
226 SilcClientListener listener = context;
227 SilcPacketStream packet_stream;
229 SILC_LOG_DEBUG(("New incoming TCP connection"));
231 /* Create packet stream */
233 silc_packet_stream_create(listener->client->internal->packet_engine,
234 listener->schedule, stream);
235 if (!packet_stream) {
236 silc_stream_destroy(stream);
240 /* Process session */
241 silc_client_listener_new_connection(listener, packet_stream);
244 /* UDP network listener callback. Accepts new key agreement session.
245 Responder function. */
247 static SilcBool silc_client_udp_accept(SilcPacketEngine engine,
248 SilcPacketStream stream,
250 void *callback_context,
251 void *stream_context)
253 SilcClientListener listener = callback_context;
254 SilcPacketStream packet_stream;
258 SILC_LOG_DEBUG(("New incoming UDP connection"));
260 /* We want only key exchange packet. Eat other packets so that default
261 packet callback doesn't get them. */
262 if (packet->type != SILC_PACKET_KEY_EXCHANGE) {
263 silc_packet_free(packet);
267 /* Create packet stream for this remote UDP session */
268 if (!silc_packet_get_sender(packet, &ip, &port)) {
269 silc_packet_free(packet);
272 packet_stream = silc_packet_stream_add_remote(stream, ip, port, packet);
273 if (!packet_stream) {
274 silc_packet_free(packet);
278 /* Process session */
279 silc_client_listener_new_connection(listener, packet_stream);
283 /* Packet stream callbacks */
284 static SilcPacketCallbacks silc_client_listener_stream_cb =
286 silc_client_udp_accept, NULL, NULL
289 /***************************** Listner routines *****************************/
291 /* Adds network listener. The `callback' will be called after new conection
292 has arrived and key exchange protocol has been completed. */
295 silc_client_listener_add(SilcClient client,
296 SilcSchedule schedule,
297 SilcClientConnectionParams *params,
298 SilcPublicKey public_key,
299 SilcPrivateKey private_key,
300 SilcClientConnectCallback callback,
303 SilcClientListener listener;
306 if (!client || !schedule ||
307 !params || (!params->local_ip && !params->bind_ip))
310 SILC_LOG_DEBUG(("Adding new listener"));
312 listener = silc_calloc(1, sizeof(*listener));
315 listener->client = client;
316 listener->schedule = schedule;
317 listener->callback = callback;
318 listener->context = context;
319 listener->params = *params;
320 listener->public_key = public_key;
321 listener->private_key = private_key;
323 /* Create network listener */
326 stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
327 params->local_ip, params->local_port,
329 listener->udp_listener =
330 silc_packet_stream_create(client->internal->packet_engine,
332 if (!listener->udp_listener) {
333 client->internal->ops->say(
334 client, NULL, SILC_CLIENT_MESSAGE_ERROR,
335 "Cannot create UDP listener on %s on port %d: %s",
336 params->bind_ip ? params->bind_ip :
337 params->local_ip, params->local_port, strerror(errno));
338 silc_client_listener_free(listener);
340 silc_stream_destroy(stream);
343 silc_packet_stream_link(listener->udp_listener,
344 &silc_client_listener_stream_cb, listener,
345 1000000, SILC_PACKET_ANY, -1);
347 if (!params->local_port) {
348 /* Get listener port */
350 silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
351 listener->params.local_port = silc_net_get_local_port(sock);
355 listener->tcp_listener =
356 silc_net_tcp_create_listener(params->bind_ip ?
357 (const char **)¶ms->bind_ip :
358 (const char **)¶ms->local_ip,
359 1, params->local_port, TRUE, FALSE,
360 schedule, silc_client_listener_tcp_accept,
362 if (!listener->tcp_listener) {
363 client->internal->ops->say(
364 client, NULL, SILC_CLIENT_MESSAGE_ERROR,
365 "Cannot create listener on %s on port %d: %s",
366 params->bind_ip ? params->bind_ip :
367 params->local_ip, params->local_port, strerror(errno));
369 silc_client_listener_free(listener);
373 if (!params->local_port) {
374 /* Get listener port */
376 ports = silc_net_listener_get_port(listener->tcp_listener, NULL);
377 listener->params.local_port = ports[0];
382 SILC_LOG_DEBUG(("Bound listener to %s:%d",
383 params->bind_ip ? params->bind_ip : params->local_ip,
384 listener->params.local_port));
389 /* Close and free listner */
391 void silc_client_listener_free(SilcClientListener listener)
393 if (listener->tcp_listener)
394 silc_net_close_listener(listener->tcp_listener);
395 silc_packet_stream_destroy(listener->udp_listener);
399 /* Return listner bound port */
401 SilcUInt16 silc_client_listener_get_local_port(SilcClientListener listener)
403 return listener->params.local_port;