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)
26 const SilcStreamOps silc_socket_stream_ops;
27 const SilcStreamOps silc_socket_udp_stream_ops;
29 /* Platform specific functions */
30 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
32 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
34 SilcBool silc_socket_stream_close(SilcStream stream);
35 void silc_socket_stream_destroy(SilcStream stream);
36 int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf,
38 int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,
41 /* Internal async host lookup context. */
43 SilcSocketStream stream;
44 SilcSocketStreamStatus status;
45 SilcSocketStreamCallback callback;
46 SilcAsyncOperation op;
48 unsigned int require_fqdn : 1;
49 unsigned int aborted : 1;
50 } *SilcSocketHostLookup;
53 /************************ Static utility functions **************************/
55 /* The IO process callback that calls the notifier callback to upper
58 SILC_TASK_CALLBACK(silc_socket_stream_io)
60 SilcSocketStream stream = context;
62 if (!stream->notifier)
67 stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
71 stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
79 /* Finishing timeout callback that will actually call the user specified
80 host lookup callback. This is executed back in the calling thread and
81 not in the lookup thread. */
83 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
85 SilcSocketHostLookup lookup = context;
86 SilcSocketStream stream = lookup->stream;
88 if (lookup->aborted) {
89 SILC_LOG_DEBUG(("Socket stream creation was aborted"));
90 silc_net_close_connection(stream->sock);
91 silc_free(stream->ip);
92 silc_free(stream->hostname);
98 if (lookup->status != SILC_SOCKET_OK) {
99 SILC_LOG_DEBUG(("Socket stream failed"));
100 silc_net_close_connection(stream->sock);
101 silc_free(stream->ip);
102 silc_free(stream->hostname);
104 stream = lookup->stream = NULL;
107 /* Return the created socket stream to the caller */
108 if (lookup->callback)
109 lookup->callback(lookup->status, stream, lookup->context);
112 silc_async_free(lookup->op);
116 /* The thread function that performs the actual lookup. */
118 static void *silc_socket_host_lookup_start(void *context)
120 SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
121 SilcSocketStream stream = lookup->stream;
122 SilcSchedule schedule = stream->schedule;
124 stream->port = silc_net_get_remote_port(stream->sock);
126 silc_net_check_host_by_sock(stream->sock, &stream->hostname, &stream->ip);
128 lookup->status = SILC_SOCKET_UNKNOWN_IP;
132 if (!stream->hostname && lookup->require_fqdn) {
133 lookup->status = SILC_SOCKET_UNKNOWN_HOST;
137 if (!stream->hostname) {
138 stream->hostname = strdup(stream->ip);
139 if (!stream->hostname) {
140 lookup->status = SILC_SOCKET_NO_MEMORY;
145 lookup->status = SILC_SOCKET_OK;
148 silc_schedule_task_add_timeout(schedule, silc_socket_host_lookup_finish,
150 silc_schedule_wakeup(schedule);
154 /* Abort callback for stream creation. */
156 static void silc_socket_host_lookup_abort(SilcAsyncOperation op,
159 SilcSocketHostLookup lookup = context;
161 /* The host lookup is done in thread. We'll let it finish in its own
162 good time and handle the abortion after it finishes. */
163 lookup->aborted = TRUE;
167 /******************************* Public API *********************************/
169 /* Creates TCP socket stream */
172 silc_socket_tcp_stream_create(int sock, SilcBool lookup,
173 SilcBool require_fqdn,
174 SilcSchedule schedule,
175 SilcSocketStreamCallback callback,
178 SilcSocketStream stream;
179 SilcSocketHostLookup l;
181 stream = silc_calloc(1, sizeof(*stream));
184 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
188 SILC_LOG_DEBUG(("Creating TCP socket stream %p", stream));
190 stream->ops = &silc_socket_stream_ops;
192 stream->schedule = schedule;
194 l = silc_calloc(1, sizeof(*l));
198 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
203 l->callback = callback;
204 l->context = context;
205 l->require_fqdn = require_fqdn;
208 /* Start asynchronous IP, hostname and port lookup process */
209 l->op = silc_async_alloc(silc_socket_host_lookup_abort, NULL, l);
214 callback(SILC_SOCKET_ERROR, NULL, context);
218 /* Lookup in thread */
219 SILC_LOG_DEBUG(("Starting async host lookup"));
220 silc_thread_create(silc_socket_host_lookup_start, l, FALSE);
224 l->status = SILC_SOCKET_OK;
225 silc_socket_host_lookup_finish(schedule,
226 silc_schedule_get_context(schedule),
232 /* Creates UDP socket stream */
234 SilcStream silc_socket_udp_stream_create(int sock, SilcBool ipv6,
235 SilcSchedule schedule)
237 SilcSocketStream stream;
239 stream = silc_calloc(1, sizeof(*stream));
243 SILC_LOG_DEBUG(("Creating UDP socket stream %p", stream));
245 stream->ops = &silc_socket_udp_stream_ops;
247 stream->schedule = schedule;
250 return (SilcStream)stream;
253 /* Returns socket stream information */
255 SilcBool silc_socket_stream_get_info(SilcStream stream,
256 int *sock, const char **hostname,
257 const char **ip, SilcUInt16 *port)
259 SilcSocketStream socket_stream = stream;
261 if (!SILC_IS_SOCKET_STREAM(socket_stream))
265 *sock = socket_stream->sock;
267 if (!socket_stream->hostname)
269 *hostname = socket_stream->hostname;
272 if (!socket_stream->ip)
274 *ip = socket_stream->ip;
277 if (!socket_stream->port)
279 *port = socket_stream->port;
285 /* Set socket information */
287 SilcBool silc_socket_stream_set_info(SilcStream stream,
288 const char *hostname,
289 const char *ip, SilcUInt16 port)
291 SilcSocketStream socket_stream = stream;
293 if (!SILC_IS_SOCKET_STREAM(socket_stream))
297 silc_free(socket_stream->hostname);
298 socket_stream->hostname = strdup(hostname);
299 if (!socket_stream->hostname)
303 silc_free(socket_stream->ip);
304 socket_stream->ip = strdup(ip);
305 if (!socket_stream->ip)
307 if (!socket_stream->hostname) {
308 socket_stream->hostname = strdup(ip);
309 if (!socket_stream->hostname)
314 socket_stream->port = port;
319 /* Return socket errno */
321 int silc_socket_stream_get_error(SilcStream stream)
323 SilcSocketStream socket_stream = stream;
325 if (!SILC_IS_SOCKET_STREAM(socket_stream))
328 return socket_stream->sock_error;
331 /* Set QoS for socket stream */
333 SilcBool silc_socket_stream_set_qos(SilcStream stream,
334 SilcUInt32 read_rate,
335 SilcUInt32 read_limit_bytes,
336 SilcUInt32 limit_sec,
337 SilcUInt32 limit_usec)
339 SilcSocketStream socket_stream = stream;
341 if (!SILC_IS_SOCKET_STREAM(socket_stream))
344 SILC_LOG_DEBUG(("Setting QoS for socket stream"));
346 if (socket_stream->qos && !read_rate && !read_limit_bytes &&
347 !limit_sec && !limit_usec) {
348 silc_schedule_task_del_by_context(socket_stream->schedule,
350 silc_free(socket_stream->qos);
351 socket_stream->qos = NULL;
355 if (!socket_stream->qos) {
356 socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
357 if (!socket_stream->qos)
361 socket_stream->qos->read_rate = read_rate;
362 socket_stream->qos->read_limit_bytes = read_limit_bytes;
363 socket_stream->qos->limit_sec = limit_sec;
364 socket_stream->qos->limit_usec = limit_usec;
365 memset(&socket_stream->qos->next_limit, 0,
366 sizeof(socket_stream->qos->next_limit));
367 socket_stream->qos->cur_rate = 0;
368 socket_stream->qos->sock = socket_stream;
370 socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
371 if (!socket_stream->qos->buffer)
379 SilcBool silc_socket_stream_close(SilcStream stream)
381 SilcSocketStream socket_stream = stream;
383 if (!SILC_IS_SOCKET_STREAM(socket_stream))
386 silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
387 silc_net_close_connection(socket_stream->sock);
392 /* Destroys the stream */
394 void silc_socket_stream_destroy(SilcStream stream)
396 SilcSocketStream socket_stream = stream;
398 if (!SILC_IS_SOCKET_STREAM(socket_stream))
401 silc_socket_stream_close(socket_stream);
402 silc_free(socket_stream->ip);
403 silc_free(socket_stream->hostname);
404 silc_schedule_task_del_by_fd(socket_stream->schedule, socket_stream->sock);
406 if (socket_stream->qos) {
407 silc_schedule_task_del_by_context(socket_stream->schedule,
409 if (socket_stream->qos->buffer) {
410 memset(socket_stream->qos->buffer, 0,
411 socket_stream->qos->read_limit_bytes);
412 silc_free(socket_stream->qos->buffer);
414 silc_free(socket_stream->qos);
417 silc_schedule_wakeup(socket_stream->schedule);
419 silc_free(socket_stream);
422 /* Sets stream notification callback for the stream */
424 void silc_socket_stream_notifier(SilcStream stream,
425 SilcSchedule schedule,
426 SilcStreamNotifier callback,
429 SilcSocketStream socket_stream = stream;
431 if (!SILC_IS_SOCKET_STREAM(socket_stream))
434 SILC_LOG_DEBUG(("Setting stream notifier callback"));
436 socket_stream->notifier = callback;
437 socket_stream->notifier_context = context;
438 socket_stream->schedule = schedule;
440 if (socket_stream->notifier) {
441 /* Add the socket to scheduler. Safe to call if already added. */
442 silc_schedule_task_add_fd(socket_stream->schedule, socket_stream->sock,
443 silc_socket_stream_io, socket_stream);
445 /* Initially set socket for reading */
446 silc_schedule_set_listen_fd(socket_stream->schedule, socket_stream->sock,
447 SILC_TASK_READ, FALSE);
448 silc_schedule_wakeup(socket_stream->schedule);
450 /* Unschedule the socket */
451 silc_schedule_unset_listen_fd(socket_stream->schedule,
452 socket_stream->sock);
453 silc_schedule_task_del_by_fd(socket_stream->schedule,
454 socket_stream->sock);
455 silc_schedule_wakeup(socket_stream->schedule);
459 /* Return associated scheduler */
461 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
463 SilcSocketStream socket_stream = stream;
465 if (!SILC_IS_SOCKET_STREAM(socket_stream))
468 return socket_stream->schedule;
471 /* SILC Socket Stream ops. Functions are implemented under the
472 platform specific subdirectories. */
473 const SilcStreamOps silc_socket_stream_ops =
475 silc_socket_stream_read,
476 silc_socket_stream_write,
477 silc_socket_stream_close,
478 silc_socket_stream_destroy,
479 silc_socket_stream_notifier,
480 silc_socket_stream_get_schedule,
482 const SilcStreamOps silc_socket_udp_stream_ops =
484 silc_socket_udp_stream_read,
485 silc_socket_udp_stream_write,
486 silc_socket_stream_close,
487 silc_socket_stream_destroy,
488 silc_socket_stream_notifier,
489 silc_socket_stream_get_schedule,