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.
22 /************************** Types and definitions ***************************/
24 #define SILC_IS_SOCKET_STREAM(s) (s->ops == &silc_socket_stream_ops)
26 const SilcStreamOps silc_socket_stream_ops;
28 /* Platform specific functions */
29 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
31 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
33 SilcBool silc_socket_stream_close(SilcStream stream);
34 void silc_socket_stream_destroy(SilcStream stream);
36 /* Internal async host lookup context. */
38 SilcSocketStream stream;
39 SilcSocketStreamStatus status;
40 SilcSocketStreamCallback callback;
41 SilcAsyncOperation op;
43 unsigned int require_fqdn : 1;
44 unsigned int aborted : 1;
45 } *SilcSocketHostLookup;
48 /************************ Static utility functions **************************/
50 /* The IO process callback that calls the notifier callback to upper
53 SILC_TASK_CALLBACK(silc_socket_stream_io)
55 SilcSocketStream stream = context;
57 if (!stream->notifier)
62 stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
66 stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
74 /* Finishing timeout callback that will actually call the user specified
75 host lookup callback. This is executed back in the calling thread and
76 not in the lookup thread. */
78 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
80 SilcSocketHostLookup lookup = context;
81 SilcSocketStream stream = lookup->stream;
83 if (lookup->aborted) {
84 SILC_LOG_DEBUG(("Socket stream creation was aborted"));
85 silc_net_close_connection(stream->sock);
86 silc_free(stream->ip);
87 silc_free(stream->hostname);
93 if (lookup->status != SILC_SOCKET_OK) {
94 SILC_LOG_DEBUG(("Socket stream failed"));
95 silc_net_close_connection(stream->sock);
96 silc_free(stream->ip);
97 silc_free(stream->hostname);
99 stream = lookup->stream = NULL;
102 /* Return the created socket stream to the caller */
103 if (lookup->callback)
104 lookup->callback(lookup->status, stream, lookup->context);
107 silc_async_free(lookup->op);
111 /* The thread function that performs the actual lookup. */
113 static void *silc_socket_host_lookup_start(void *context)
115 SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
116 SilcSocketStream stream = lookup->stream;
117 SilcSchedule schedule = stream->schedule;
119 stream->port = silc_net_get_remote_port(stream->sock);
121 silc_net_check_host_by_sock(stream->sock, &stream->hostname, &stream->ip);
123 lookup->status = SILC_SOCKET_UNKNOWN_IP;
127 if (!stream->hostname && lookup->require_fqdn) {
128 lookup->status = SILC_SOCKET_UNKNOWN_HOST;
132 if (!stream->hostname) {
133 stream->hostname = strdup(stream->ip);
134 if (!stream->hostname) {
135 lookup->status = SILC_SOCKET_NO_MEMORY;
140 lookup->status = SILC_SOCKET_OK;
143 silc_schedule_task_add_timeout(schedule, silc_socket_host_lookup_finish,
145 silc_schedule_wakeup(schedule);
149 /* Abort callback for stream creation. */
151 static void silc_socket_host_lookup_abort(SilcAsyncOperation op,
154 SilcSocketHostLookup lookup = context;
156 /* The host lookup is done in thread. We'll let it finish in its own
157 good time and handle the abortion after it finishes. */
158 lookup->aborted = TRUE;
162 /******************************* Public API *********************************/
164 /* Creates socket stream */
167 silc_socket_stream_create(int sock, SilcBool lookup, SilcBool require_fqdn,
168 SilcSchedule schedule,
169 SilcSocketStreamCallback callback,
172 SilcSocketStream stream;
173 SilcSocketHostLookup l;
175 stream = silc_calloc(1, sizeof(*stream));
178 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
182 SILC_LOG_DEBUG(("Creating new socket stream %p", stream));
184 stream->ops = &silc_socket_stream_ops;
186 stream->schedule = schedule;
188 l = silc_calloc(1, sizeof(*l));
192 callback(SILC_SOCKET_NO_MEMORY, NULL, context);
197 l->callback = callback;
198 l->context = context;
199 l->require_fqdn = require_fqdn;
202 /* Start asynchronous IP, hostname and port lookup process */
203 l->op = silc_async_alloc(silc_socket_host_lookup_abort, NULL, l);
208 callback(SILC_SOCKET_ERROR, NULL, context);
212 /* Lookup in thread */
213 SILC_LOG_DEBUG(("Starting async host lookup"));
214 silc_thread_create(silc_socket_host_lookup_start, l, FALSE);
218 l->status = SILC_SOCKET_OK;
219 silc_socket_host_lookup_finish(schedule,
220 silc_schedule_get_context(schedule),
226 /* Returns socket stream information */
228 SilcBool silc_socket_stream_get_info(SilcStream stream,
229 int *sock, const char **hostname,
230 const char **ip, SilcUInt16 *port)
232 SilcSocketStream socket_stream = stream;
234 if (!SILC_IS_SOCKET_STREAM(socket_stream))
238 *sock = socket_stream->sock;
240 *hostname = socket_stream->hostname;
242 *ip = socket_stream->ip;
244 *port = socket_stream->port;
249 /* Set socket information */
251 SilcBool silc_socket_stream_set_info(SilcStream stream,
252 const char *hostname,
253 const char *ip, SilcUInt16 port)
255 SilcSocketStream socket_stream = stream;
257 if (!SILC_IS_SOCKET_STREAM(socket_stream))
261 silc_free(socket_stream->hostname);
262 socket_stream->hostname = strdup(hostname);
263 if (!socket_stream->hostname)
267 silc_free(socket_stream->ip);
268 socket_stream->ip = strdup(ip);
269 if (!socket_stream->ip)
273 socket_stream->port = port;
278 /* Return socket errno */
280 int silc_socket_stream_get_error(SilcStream stream)
282 SilcSocketStream socket_stream = stream;
284 if (!SILC_IS_SOCKET_STREAM(socket_stream))
287 return socket_stream->sock_error;
290 /* Set QoS for socket stream */
292 SilcBool silc_socket_stream_set_qos(SilcStream stream,
293 SilcUInt32 read_rate,
294 SilcUInt32 read_limit_bytes,
295 SilcUInt32 limit_sec,
296 SilcUInt32 limit_usec)
298 SilcSocketStream socket_stream = stream;
300 if (!SILC_IS_SOCKET_STREAM(socket_stream))
303 SILC_LOG_DEBUG(("Setting QoS for socket stream"));
305 if (socket_stream->qos && !read_rate && !read_limit_bytes &&
306 !limit_sec && !limit_usec) {
307 silc_schedule_task_del_by_context(socket_stream->schedule,
309 silc_free(socket_stream->qos);
310 socket_stream->qos = NULL;
314 if (!socket_stream->qos) {
315 socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
316 if (!socket_stream->qos)
320 socket_stream->qos->read_rate = read_rate;
321 socket_stream->qos->read_limit_bytes = read_limit_bytes;
322 socket_stream->qos->limit_sec = limit_sec;
323 socket_stream->qos->limit_usec = limit_usec;
324 memset(&socket_stream->qos->next_limit, 0,
325 sizeof(socket_stream->qos->next_limit));
326 socket_stream->qos->cur_rate = 0;
327 socket_stream->qos->sock = socket_stream;
329 socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
330 if (!socket_stream->qos->buffer)
338 SilcBool silc_socket_stream_close(SilcStream stream)
340 SilcSocketStream socket_stream = stream;
342 if (!SILC_IS_SOCKET_STREAM(socket_stream))
345 silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
346 silc_net_close_connection(socket_stream->sock);
351 /* Destroys the stream */
353 void silc_socket_stream_destroy(SilcStream stream)
355 SilcSocketStream socket_stream = stream;
357 if (!SILC_IS_SOCKET_STREAM(socket_stream))
360 silc_socket_stream_close(socket_stream);
361 silc_free(socket_stream->ip);
362 silc_free(socket_stream->hostname);
363 silc_schedule_task_del_by_fd(socket_stream->schedule, socket_stream->sock);
365 if (socket_stream->qos) {
366 silc_schedule_task_del_by_context(socket_stream->schedule,
368 if (socket_stream->qos->buffer) {
369 memset(socket_stream->qos->buffer, 0,
370 socket_stream->qos->read_limit_bytes);
371 silc_free(socket_stream->qos->buffer);
373 silc_free(socket_stream->qos);
376 silc_free(socket_stream);
379 /* Sets stream notification callback for the stream */
381 void silc_socket_stream_notifier(SilcStream stream,
382 SilcSchedule schedule,
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;
395 socket_stream->schedule = schedule;
397 if (socket_stream->notifier) {
398 /* Add the socket to scheduler. Safe to call if already added. */
399 silc_schedule_task_add_fd(socket_stream->schedule, socket_stream->sock,
400 silc_socket_stream_io, socket_stream);
402 /* Initially set socket for reading */
403 silc_schedule_set_listen_fd(socket_stream->schedule, socket_stream->sock,
404 SILC_TASK_READ, FALSE);
406 /* Unschedule the socket */
407 silc_schedule_unset_listen_fd(socket_stream->schedule,
408 socket_stream->sock);
409 silc_schedule_task_del_by_fd(socket_stream->schedule,
410 socket_stream->sock);
414 /* Return associated scheduler */
416 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
418 SilcSocketStream socket_stream = stream;
420 if (!SILC_IS_SOCKET_STREAM(socket_stream))
423 return socket_stream->schedule;
426 /* SILC Socket Stream ops. Functions are implemented under the
427 platform specific subdirectories. */
428 const SilcStreamOps silc_socket_stream_ops =
430 silc_socket_stream_read,
431 silc_socket_stream_write,
432 silc_socket_stream_close,
433 silc_socket_stream_destroy,
434 silc_socket_stream_notifier,
435 silc_socket_stream_get_schedule,