5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 - 2006 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.
22 /************************** Types and definitions ***************************/
24 #define SILC_IS_SOCKET_STREAM(s) (s->ops == &silc_socket_stream_ops)
25 #define SILC_IS_SOCKET_STREAM_UDP(s) (s->ops == &silc_socket_udp_stream_ops)
27 const SilcStreamOps silc_socket_stream_ops;
28 const SilcStreamOps silc_socket_udp_stream_ops;
30 /* Platform specific functions */
31 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
33 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
35 SilcBool silc_socket_stream_close(SilcStream stream);
36 void silc_socket_stream_destroy(SilcStream stream);
37 int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf,
39 int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,
42 /* Internal async host lookup context. */
44 SilcSocketStream stream;
45 SilcSocketStreamStatus status;
46 SilcSocketStreamCallback callback;
47 SilcAsyncOperation op;
49 unsigned int require_fqdn : 1;
50 unsigned int aborted : 1;
51 } *SilcSocketHostLookup;
54 /************************ Static utility functions **************************/
56 /* The IO process callback that calls the notifier callback to upper
59 SILC_TASK_CALLBACK(silc_socket_stream_io)
61 SilcSocketStream stream = context;
63 if (!stream->notifier)
68 stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
72 stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
80 /* Finishing timeout callback that will actually call the user specified
81 host lookup callback. This is executed back in the calling thread and
82 not in the lookup thread. */
84 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
86 SilcSocketHostLookup lookup = context;
87 SilcSocketStream stream = lookup->stream;
89 if (lookup->aborted) {
90 SILC_LOG_DEBUG(("Socket stream creation was aborted"));
91 silc_net_close_connection(stream->sock);
92 silc_free(stream->ip);
93 silc_free(stream->hostname);
99 if (lookup->status != SILC_SOCKET_OK) {
100 SILC_LOG_DEBUG(("Socket stream failed"));
101 silc_net_close_connection(stream->sock);
102 silc_free(stream->ip);
103 silc_free(stream->hostname);
105 stream = lookup->stream = NULL;
108 /* Return the created socket stream to the caller */
109 if (lookup->callback)
110 lookup->callback(lookup->status, stream, lookup->context);
113 silc_async_free(lookup->op);
117 /* The thread function that performs the actual lookup. */
119 static void *silc_socket_host_lookup_start(void *context)
121 SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
122 SilcSocketStream stream = lookup->stream;
123 SilcSchedule schedule = stream->schedule;
125 stream->port = silc_net_get_remote_port(stream->sock);
127 silc_net_check_host_by_sock(stream->sock, &stream->hostname, &stream->ip);
129 lookup->status = SILC_SOCKET_UNKNOWN_IP;
133 if (!stream->hostname && lookup->require_fqdn) {
134 lookup->status = SILC_SOCKET_UNKNOWN_HOST;
138 if (!stream->hostname) {
139 stream->hostname = strdup(stream->ip);
140 if (!stream->hostname) {
141 lookup->status = SILC_SOCKET_NO_MEMORY;
146 lookup->status = SILC_SOCKET_OK;
149 silc_schedule_task_add_timeout(schedule, silc_socket_host_lookup_finish,
151 silc_schedule_wakeup(schedule);
155 /* Abort callback for stream creation. */
157 static void silc_socket_host_lookup_abort(SilcAsyncOperation op,
160 SilcSocketHostLookup lookup = context;
162 /* The host lookup is done in thread. We'll let it finish in its own
163 good time and handle the abortion after it finishes. */
164 lookup->aborted = TRUE;
168 /******************************* Public API *********************************/
170 /* Creates TCP socket stream */
173 silc_socket_tcp_stream_create(int sock, SilcBool lookup,
174 SilcBool require_fqdn,
175 SilcSchedule schedule,
176 SilcSocketStreamCallback callback,
179 SilcSocketStream stream;
180 SilcSocketHostLookup l;
182 stream = silc_calloc(1, sizeof(*stream));
185 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
189 SILC_LOG_DEBUG(("Creating TCP socket stream %p", stream));
191 stream->ops = &silc_socket_stream_ops;
193 stream->schedule = schedule;
195 l = silc_calloc(1, sizeof(*l));
199 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
204 l->callback = callback;
205 l->context = context;
206 l->require_fqdn = require_fqdn;
209 /* Start asynchronous IP, hostname and port lookup process */
210 l->op = silc_async_alloc(silc_socket_host_lookup_abort, NULL, l);
215 callback(SILC_SOCKET_ERROR, NULL, context);
219 /* Lookup in thread */
220 SILC_LOG_DEBUG(("Starting async host lookup"));
221 silc_thread_create(silc_socket_host_lookup_start, l, FALSE);
225 l->status = SILC_SOCKET_OK;
226 silc_socket_host_lookup_finish(schedule,
227 silc_schedule_get_context(schedule),
233 /* Creates UDP socket stream */
235 SilcStream silc_socket_udp_stream_create(int sock, SilcBool ipv6,
237 SilcSchedule schedule)
239 SilcSocketStream stream;
241 stream = silc_calloc(1, sizeof(*stream));
245 SILC_LOG_DEBUG(("Creating UDP socket stream %p", stream));
247 stream->ops = &silc_socket_udp_stream_ops;
249 stream->schedule = schedule;
251 stream->connected = connected;
253 return (SilcStream)stream;
256 /* Returns TRUE if the stream is UDP stream */
258 SilcBool silc_socket_stream_is_udp(SilcStream stream, SilcBool *connected)
260 SilcSocketStream socket_stream = stream;
262 if (!SILC_IS_SOCKET_STREAM_UDP(socket_stream))
266 *connected = socket_stream->connected;
271 /* Returns socket stream information */
273 SilcBool silc_socket_stream_get_info(SilcStream stream,
274 int *sock, const char **hostname,
275 const char **ip, SilcUInt16 *port)
277 SilcSocketStream socket_stream = stream;
279 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
280 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
284 *sock = socket_stream->sock;
286 if (!socket_stream->hostname)
288 *hostname = socket_stream->hostname;
291 if (!socket_stream->ip)
293 *ip = socket_stream->ip;
296 if (!socket_stream->port)
298 *port = socket_stream->port;
304 /* Set socket information */
306 SilcBool silc_socket_stream_set_info(SilcStream stream,
307 const char *hostname,
308 const char *ip, SilcUInt16 port)
310 SilcSocketStream socket_stream = stream;
312 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
313 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
317 silc_free(socket_stream->hostname);
318 socket_stream->hostname = strdup(hostname);
319 if (!socket_stream->hostname)
323 silc_free(socket_stream->ip);
324 socket_stream->ip = strdup(ip);
325 if (!socket_stream->ip)
327 if (!socket_stream->hostname) {
328 socket_stream->hostname = strdup(ip);
329 if (!socket_stream->hostname)
334 socket_stream->port = port;
339 /* Return socket errno */
341 int silc_socket_stream_get_error(SilcStream stream)
343 SilcSocketStream socket_stream = stream;
345 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
346 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
349 return socket_stream->sock_error;
352 /* Set QoS for socket stream */
354 SilcBool silc_socket_stream_set_qos(SilcStream stream,
355 SilcUInt32 read_rate,
356 SilcUInt32 read_limit_bytes,
357 SilcUInt32 limit_sec,
358 SilcUInt32 limit_usec)
360 SilcSocketStream socket_stream = stream;
362 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
363 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
366 SILC_LOG_DEBUG(("Setting QoS for socket stream"));
368 if (socket_stream->qos && !read_rate && !read_limit_bytes &&
369 !limit_sec && !limit_usec) {
370 silc_schedule_task_del_by_context(socket_stream->schedule,
372 silc_free(socket_stream->qos);
373 socket_stream->qos = NULL;
377 if (!socket_stream->qos) {
378 socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
379 if (!socket_stream->qos)
383 socket_stream->qos->read_rate = read_rate;
384 socket_stream->qos->read_limit_bytes = read_limit_bytes;
385 socket_stream->qos->limit_sec = limit_sec;
386 socket_stream->qos->limit_usec = limit_usec;
387 memset(&socket_stream->qos->next_limit, 0,
388 sizeof(socket_stream->qos->next_limit));
389 socket_stream->qos->cur_rate = 0;
390 socket_stream->qos->sock = socket_stream;
392 socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
393 if (!socket_stream->qos->buffer)
401 SilcBool silc_socket_stream_close(SilcStream stream)
403 SilcSocketStream socket_stream = stream;
405 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
406 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
409 silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
410 silc_net_close_connection(socket_stream->sock);
415 /* Destroys the stream */
417 void silc_socket_stream_destroy(SilcStream stream)
419 SilcSocketStream socket_stream = stream;
421 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
422 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
425 silc_socket_stream_close(socket_stream);
426 silc_free(socket_stream->ip);
427 silc_free(socket_stream->hostname);
428 silc_schedule_task_del_by_fd(socket_stream->schedule, socket_stream->sock);
430 if (socket_stream->qos) {
431 silc_schedule_task_del_by_context(socket_stream->schedule,
433 if (socket_stream->qos->buffer) {
434 memset(socket_stream->qos->buffer, 0,
435 socket_stream->qos->read_limit_bytes);
436 silc_free(socket_stream->qos->buffer);
438 silc_free(socket_stream->qos);
441 silc_schedule_wakeup(socket_stream->schedule);
443 silc_free(socket_stream);
446 /* Sets stream notification callback for the stream */
448 void silc_socket_stream_notifier(SilcStream stream,
449 SilcSchedule schedule,
450 SilcStreamNotifier callback,
453 SilcSocketStream socket_stream = stream;
455 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
456 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
459 SILC_LOG_DEBUG(("Setting stream notifier callback"));
461 socket_stream->notifier = callback;
462 socket_stream->notifier_context = context;
463 socket_stream->schedule = schedule;
465 if (socket_stream->notifier) {
466 /* Add the socket to scheduler. Safe to call if already added. */
467 silc_schedule_task_add_fd(socket_stream->schedule, socket_stream->sock,
468 silc_socket_stream_io, socket_stream);
470 /* Initially set socket for reading */
471 silc_schedule_set_listen_fd(socket_stream->schedule, socket_stream->sock,
472 SILC_TASK_READ, FALSE);
473 silc_schedule_wakeup(socket_stream->schedule);
475 /* Unschedule the socket */
476 silc_schedule_unset_listen_fd(socket_stream->schedule,
477 socket_stream->sock);
478 silc_schedule_task_del_by_fd(socket_stream->schedule,
479 socket_stream->sock);
480 silc_schedule_wakeup(socket_stream->schedule);
484 /* Return associated scheduler */
486 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
488 SilcSocketStream socket_stream = stream;
490 if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
491 !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
494 return socket_stream->schedule;
497 /* SILC Socket Stream ops. Functions are implemented under the
498 platform specific subdirectories. */
499 const SilcStreamOps silc_socket_stream_ops =
501 silc_socket_stream_read,
502 silc_socket_stream_write,
503 silc_socket_stream_close,
504 silc_socket_stream_destroy,
505 silc_socket_stream_notifier,
506 silc_socket_stream_get_schedule,
508 const SilcStreamOps silc_socket_udp_stream_ops =
510 silc_socket_udp_stream_read,
511 silc_socket_udp_stream_write,
512 silc_socket_stream_close,
513 silc_socket_stream_destroy,
514 silc_socket_stream_notifier,
515 silc_socket_stream_get_schedule,