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 *hostname = socket_stream->hostname;
269 *ip = socket_stream->ip;
271 *port = socket_stream->port;
276 /* Set socket information */
278 SilcBool silc_socket_stream_set_info(SilcStream stream,
279 const char *hostname,
280 const char *ip, SilcUInt16 port)
282 SilcSocketStream socket_stream = stream;
284 if (!SILC_IS_SOCKET_STREAM(socket_stream))
288 silc_free(socket_stream->hostname);
289 socket_stream->hostname = strdup(hostname);
290 if (!socket_stream->hostname)
294 silc_free(socket_stream->ip);
295 socket_stream->ip = strdup(ip);
296 if (!socket_stream->ip)
300 socket_stream->port = port;
305 /* Return socket errno */
307 int silc_socket_stream_get_error(SilcStream stream)
309 SilcSocketStream socket_stream = stream;
311 if (!SILC_IS_SOCKET_STREAM(socket_stream))
314 return socket_stream->sock_error;
317 /* Set QoS for socket stream */
319 SilcBool silc_socket_stream_set_qos(SilcStream stream,
320 SilcUInt32 read_rate,
321 SilcUInt32 read_limit_bytes,
322 SilcUInt32 limit_sec,
323 SilcUInt32 limit_usec)
325 SilcSocketStream socket_stream = stream;
327 if (!SILC_IS_SOCKET_STREAM(socket_stream))
330 SILC_LOG_DEBUG(("Setting QoS for socket stream"));
332 if (socket_stream->qos && !read_rate && !read_limit_bytes &&
333 !limit_sec && !limit_usec) {
334 silc_schedule_task_del_by_context(socket_stream->schedule,
336 silc_free(socket_stream->qos);
337 socket_stream->qos = NULL;
341 if (!socket_stream->qos) {
342 socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
343 if (!socket_stream->qos)
347 socket_stream->qos->read_rate = read_rate;
348 socket_stream->qos->read_limit_bytes = read_limit_bytes;
349 socket_stream->qos->limit_sec = limit_sec;
350 socket_stream->qos->limit_usec = limit_usec;
351 memset(&socket_stream->qos->next_limit, 0,
352 sizeof(socket_stream->qos->next_limit));
353 socket_stream->qos->cur_rate = 0;
354 socket_stream->qos->sock = socket_stream;
356 socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
357 if (!socket_stream->qos->buffer)
365 SilcBool silc_socket_stream_close(SilcStream stream)
367 SilcSocketStream socket_stream = stream;
369 if (!SILC_IS_SOCKET_STREAM(socket_stream))
372 silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
373 silc_net_close_connection(socket_stream->sock);
378 /* Destroys the stream */
380 void silc_socket_stream_destroy(SilcStream stream)
382 SilcSocketStream socket_stream = stream;
384 if (!SILC_IS_SOCKET_STREAM(socket_stream))
387 silc_socket_stream_close(socket_stream);
388 silc_free(socket_stream->ip);
389 silc_free(socket_stream->hostname);
390 silc_schedule_task_del_by_fd(socket_stream->schedule, socket_stream->sock);
392 if (socket_stream->qos) {
393 silc_schedule_task_del_by_context(socket_stream->schedule,
395 if (socket_stream->qos->buffer) {
396 memset(socket_stream->qos->buffer, 0,
397 socket_stream->qos->read_limit_bytes);
398 silc_free(socket_stream->qos->buffer);
400 silc_free(socket_stream->qos);
403 silc_schedule_wakeup(socket_stream->schedule);
405 silc_free(socket_stream);
408 /* Sets stream notification callback for the stream */
410 void silc_socket_stream_notifier(SilcStream stream,
411 SilcSchedule schedule,
412 SilcStreamNotifier callback,
415 SilcSocketStream socket_stream = stream;
417 if (!SILC_IS_SOCKET_STREAM(socket_stream))
420 SILC_LOG_DEBUG(("Setting stream notifier callback"));
422 socket_stream->notifier = callback;
423 socket_stream->notifier_context = context;
424 socket_stream->schedule = schedule;
426 if (socket_stream->notifier) {
427 /* Add the socket to scheduler. Safe to call if already added. */
428 silc_schedule_task_add_fd(socket_stream->schedule, socket_stream->sock,
429 silc_socket_stream_io, socket_stream);
431 /* Initially set socket for reading */
432 silc_schedule_set_listen_fd(socket_stream->schedule, socket_stream->sock,
433 SILC_TASK_READ, FALSE);
434 silc_schedule_wakeup(socket_stream->schedule);
436 /* Unschedule the socket */
437 silc_schedule_unset_listen_fd(socket_stream->schedule,
438 socket_stream->sock);
439 silc_schedule_task_del_by_fd(socket_stream->schedule,
440 socket_stream->sock);
441 silc_schedule_wakeup(socket_stream->schedule);
445 /* Return associated scheduler */
447 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
449 SilcSocketStream socket_stream = stream;
451 if (!SILC_IS_SOCKET_STREAM(socket_stream))
454 return socket_stream->schedule;
457 /* SILC Socket Stream ops. Functions are implemented under the
458 platform specific subdirectories. */
459 const SilcStreamOps silc_socket_stream_ops =
461 silc_socket_stream_read,
462 silc_socket_stream_write,
463 silc_socket_stream_close,
464 silc_socket_stream_destroy,
465 silc_socket_stream_notifier,
466 silc_socket_stream_get_schedule,
468 const SilcStreamOps silc_socket_udp_stream_ops =
470 silc_socket_udp_stream_read,
471 silc_socket_udp_stream_write,
472 silc_socket_stream_close,
473 silc_socket_stream_destroy,
474 silc_socket_stream_notifier,
475 silc_socket_stream_get_schedule,