5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 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.
20 #include "silcincludes.h"
22 #define SILC_IS_SOCKET_STREAM(s) (s->ops == &silc_socket_stream_ops)
24 const SilcStreamOps silc_socket_stream_ops;
26 /* Platform specific functions */
27 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
29 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
31 bool silc_socket_stream_close(SilcStream stream);
32 void silc_socket_stream_destroy(SilcStream stream);
34 /* Internal async host lookup context. */
36 SilcSocketStream stream;
37 SilcSocketStreamStatus status;
38 SilcSocketStreamCallback callback;
39 SilcAsyncOperation op;
41 unsigned int require_fqdn : 1;
42 unsigned int aborted : 1;
43 } *SilcSocketHostLookup;
45 /* The IO process callback that calls the notifier callback to upper
48 SILC_TASK_CALLBACK(silc_socket_stream_io)
50 SilcSocketStream stream = context;
52 if (!stream->notifier)
57 stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
61 stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
69 /* Finishing timeout callback that will actually call the user specified
70 host lookup callback. This is executed back in the calling thread and
71 not in the lookup thread. */
73 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
75 SilcSocketHostLookup lookup = context;
76 SilcSocketStream stream = lookup->stream;
78 if (lookup->aborted) {
79 SILC_LOG_DEBUG(("Socket stream creation was aborted"));
80 silc_net_close_connection(stream->sock);
81 silc_free(stream->ip);
82 silc_free(stream->hostname);
88 if (lookup->status != SILC_SOCKET_OK) {
89 SILC_LOG_DEBUG(("Socket stream failed"));
90 silc_net_close_connection(stream->sock);
91 silc_free(stream->ip);
92 silc_free(stream->hostname);
94 stream = lookup->stream = NULL;
97 /* Add the socket to scheduler */
99 silc_schedule_task_add_fd(stream->schedule, stream->sock,
100 silc_socket_stream_io, stream);
102 /* Initially set socket for reading */
103 silc_schedule_set_listen_fd(stream->schedule, stream->sock,
104 SILC_TASK_READ, FALSE);
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;
166 /* Creates socket stream */
169 silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
170 SilcSchedule schedule,
171 SilcSocketStreamCallback callback,
174 SilcSocketStream stream;
175 SilcSocketHostLookup l;
177 stream = silc_calloc(1, sizeof(*stream));
180 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
184 SILC_LOG_DEBUG(("Creating new socket stream %p", stream));
186 stream->ops = &silc_socket_stream_ops;
188 stream->schedule = schedule;
190 l = silc_calloc(1, sizeof(*l));
194 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
199 l->callback = callback;
200 l->context = context;
201 l->require_fqdn = require_fqdn;
204 /* Start asynchronous IP, hostname and port lookup process */
205 l->op = silc_async_alloc(silc_socket_host_lookup_abort, NULL, l);
210 callback(SILC_SOCKET_ERROR, NULL, context);
214 /* Lookup in thread */
215 SILC_LOG_DEBUG(("Starting async host lookup"));
216 silc_thread_create(silc_socket_host_lookup_start, l, FALSE);
220 l->status = SILC_SOCKET_OK;
221 silc_socket_host_lookup_finish(schedule,
222 silc_schedule_get_context(schedule),
228 /* Returns socket stream information */
230 bool silc_socket_stream_get_info(SilcStream stream,
231 int *sock, const char **hostname,
232 const char **ip, SilcUInt16 *port)
234 SilcSocketStream socket_stream = stream;
236 if (!SILC_IS_SOCKET_STREAM(socket_stream))
240 *sock = socket_stream->sock;
242 *hostname = socket_stream->hostname;
244 *ip = socket_stream->ip;
246 *port = socket_stream->port;
251 /* Set socket information */
253 bool silc_socket_stream_set_info(SilcStream stream,
254 const char *hostname,
255 const char *ip, SilcUInt16 port)
257 SilcSocketStream socket_stream = stream;
259 if (!SILC_IS_SOCKET_STREAM(socket_stream))
263 silc_free(socket_stream->hostname);
264 socket_stream->hostname = strdup(hostname);
265 if (!socket_stream->hostname)
269 silc_free(socket_stream->ip);
270 socket_stream->ip = strdup(ip);
271 if (!socket_stream->ip)
275 socket_stream->port = port;
280 /* Return socket errno */
282 int silc_socket_stream_get_error(SilcStream stream)
284 SilcSocketStream socket_stream = stream;
286 if (!SILC_IS_SOCKET_STREAM(socket_stream))
289 return socket_stream->sock_error;
292 /* Set QoS for socket stream */
294 bool silc_socket_stream_set_qos(SilcStream stream,
295 SilcUInt32 read_rate,
296 SilcUInt32 read_limit_bytes,
297 SilcUInt32 limit_sec,
298 SilcUInt32 limit_usec)
300 SilcSocketStream socket_stream = stream;
302 if (!SILC_IS_SOCKET_STREAM(socket_stream))
305 SILC_LOG_DEBUG(("Setting QoS for socket stream"));
307 if (socket_stream->qos && !read_rate && !read_limit_bytes &&
308 !limit_sec && !limit_usec) {
309 silc_schedule_task_del_by_context(socket_stream->schedule,
311 silc_free(socket_stream->qos);
312 socket_stream->qos = NULL;
316 if (!socket_stream->qos) {
317 socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
318 if (!socket_stream->qos)
322 socket_stream->qos->read_rate = read_rate;
323 socket_stream->qos->read_limit_bytes = read_limit_bytes;
324 socket_stream->qos->limit_sec = limit_sec;
325 socket_stream->qos->limit_usec = limit_usec;
326 memset(&socket_stream->qos->next_limit, 0,
327 sizeof(socket_stream->qos->next_limit));
328 socket_stream->qos->cur_rate = 0;
329 socket_stream->qos->sock = socket_stream;
331 socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
332 if (!socket_stream->qos->buffer)
340 bool silc_socket_stream_close(SilcStream stream)
342 SilcSocketStream socket_stream = stream;
344 if (!SILC_IS_SOCKET_STREAM(socket_stream))
347 silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
348 silc_net_close_connection(socket_stream->sock);
353 /* Destroys the stream */
355 void silc_socket_stream_destroy(SilcStream stream)
357 SilcSocketStream socket_stream = stream;
359 if (!SILC_IS_SOCKET_STREAM(socket_stream))
362 silc_socket_stream_close(socket_stream);
363 silc_free(socket_stream->ip);
364 silc_free(socket_stream->hostname);
366 if (socket_stream->qos) {
367 silc_schedule_task_del_by_context(socket_stream->schedule,
369 if (socket_stream->qos->buffer) {
370 memset(socket_stream->qos->buffer, 0,
371 socket_stream->qos->read_limit_bytes);
372 silc_free(socket_stream->qos->buffer);
374 silc_free(socket_stream->qos);
377 silc_free(socket_stream);
380 /* Sets stream notification callback for the stream */
382 void silc_socket_stream_notifier(SilcStream stream,
383 SilcStreamNotifier callback,
386 SilcSocketStream socket_stream = stream;
388 if (!SILC_IS_SOCKET_STREAM(socket_stream))
391 SILC_LOG_DEBUG(("Setting stream notifier callback"));
393 socket_stream->notifier = callback;
394 socket_stream->notifier_context = context;
397 /* SILC Socket Stream ops. Functions are implemented under the
398 platform specific subdirectories. */
399 const SilcStreamOps silc_socket_stream_ops =
401 silc_socket_stream_read,
402 silc_socket_stream_write,
403 silc_socket_stream_close,
404 silc_socket_stream_destroy,
405 silc_socket_stream_notifier,