Added preliminary Symbian support.
[silc.git] / lib / silcutil / silcsocketstream.c
1 /*
2
3   silcsocketstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2006 Pekka Riikonen
8
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.
12
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.
17
18 */
19
20 #include "silc.h"
21
22 /************************** Types and definitions ***************************/
23
24 /* Stream operation functions (platform specific) */
25 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
26                             SilcUInt32 buf_len);
27 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
28                              SilcUInt32 data_len);
29 SilcBool silc_socket_stream_close(SilcStream stream);
30 void silc_socket_stream_destroy(SilcStream stream);
31 int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf,
32                                 SilcUInt32 buf_len);
33 int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,
34                                  SilcUInt32 data_len);
35 SilcBool silc_socket_stream_close(SilcStream stream);
36 void silc_socket_stream_destroy(SilcStream stream);
37 void silc_socket_stream_notifier(SilcStream stream,
38                                  SilcSchedule schedule,
39                                  SilcStreamNotifier callback,
40                                  void *context);
41 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream);
42
43 /* Internal async host lookup context. */
44 typedef struct {
45   SilcSocketStream stream;
46   SilcSocketStreamStatus status;
47   SilcSocketStreamCallback callback;
48   SilcAsyncOperation op;
49   void *context;
50   unsigned int require_fqdn : 1;
51   unsigned int aborted      : 1;
52 } *SilcSocketHostLookup;
53
54
55 /************************ Static utility functions **************************/
56
57 /* Finishing timeout callback that will actually call the user specified
58    host lookup callback.  This is executed back in the calling thread and
59    not in the lookup thread. */
60
61 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
62 {
63   SilcSocketHostLookup lookup = context;
64   SilcSocketStream stream = lookup->stream;
65
66   if (lookup->aborted) {
67     SILC_LOG_DEBUG(("Socket stream creation was aborted"));
68     stream->schedule = NULL;
69     silc_socket_stream_destroy(stream);
70     silc_free(lookup);
71     return;
72   }
73
74   if (lookup->status != SILC_SOCKET_OK) {
75     SILC_LOG_DEBUG(("Socket stream failed"));
76     stream->schedule = NULL;
77     silc_socket_stream_destroy(stream);
78     stream = lookup->stream = NULL;
79   }
80
81   /* Return the created socket stream to the caller */
82   if (lookup->callback)
83     lookup->callback(lookup->status, stream, lookup->context);
84
85   if (lookup->op)
86     silc_async_free(lookup->op);
87   silc_free(lookup);
88 }
89
90 /* The thread function that performs the actual lookup. */
91
92 static void *silc_socket_host_lookup_start(void *context)
93 {
94   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
95   SilcSocketStream stream = lookup->stream;
96   SilcSchedule schedule = stream->schedule;
97
98   stream->port = silc_net_get_remote_port(stream->sock);
99
100   silc_net_check_host_by_sock(stream->sock, &stream->hostname, &stream->ip);
101   if (!stream->ip) {
102     lookup->status = SILC_SOCKET_UNKNOWN_IP;
103     goto out;
104   }
105
106   if (!stream->hostname && lookup->require_fqdn) {
107     lookup->status = SILC_SOCKET_UNKNOWN_HOST;
108     goto out;
109   }
110
111   if (!stream->hostname) {
112     stream->hostname = strdup(stream->ip);
113     if (!stream->hostname) {
114       lookup->status = SILC_SOCKET_NO_MEMORY;
115       goto out;
116     }
117   }
118
119   lookup->status = SILC_SOCKET_OK;
120
121  out:
122   silc_schedule_task_add_timeout(schedule, silc_socket_host_lookup_finish,
123                                  lookup, 0, 0);
124   silc_schedule_wakeup(schedule);
125   return NULL;
126 }
127
128 /* Abort callback for stream creation. */
129
130 static void silc_socket_host_lookup_abort(SilcAsyncOperation op,
131                                           void *context)
132 {
133   SilcSocketHostLookup lookup = context;
134
135   /* The host lookup is done in thread.  We'll let it finish in its own
136      good time and handle the abortion after it finishes. */
137   lookup->aborted = TRUE;
138 }
139
140
141 /******************************* Public API *********************************/
142
143 /* Creates TCP socket stream */
144
145 SilcAsyncOperation
146 silc_socket_tcp_stream_create(SilcSocket sock, SilcBool lookup,
147                               SilcBool require_fqdn,
148                               SilcSchedule schedule,
149                               SilcSocketStreamCallback callback,
150                               void *context)
151 {
152   SilcSocketStream stream;
153   SilcSocketHostLookup l;
154
155   if (!sock) {
156     if (callback)
157       callback(SILC_SOCKET_ERROR, NULL, context);
158     return NULL;
159   }
160
161   stream = silc_calloc(1, sizeof(*stream));
162   if (!stream) {
163     if (callback)
164       callback(SILC_SOCKET_NO_MEMORY, NULL, context);
165     return NULL;
166   }
167
168   SILC_LOG_DEBUG(("Creating TCP socket stream %p", stream));
169
170   stream->ops = &silc_socket_stream_ops;
171   stream->sock = sock;
172   stream->schedule = schedule;
173
174   l = silc_calloc(1, sizeof(*l));
175   if (!l) {
176     silc_free(stream);
177     if (callback)
178       callback(SILC_SOCKET_NO_MEMORY, NULL, context);
179     return NULL;
180   }
181
182   l->stream = stream;
183   l->callback = callback;
184   l->context = context;
185   l->require_fqdn = require_fqdn;
186
187   if (lookup) {
188     /* Start asynchronous IP, hostname and port lookup process */
189     l->op = silc_async_alloc(silc_socket_host_lookup_abort, NULL, l);
190     if (!l->op) {
191       silc_free(stream);
192       silc_free(l);
193       if (callback)
194         callback(SILC_SOCKET_ERROR, NULL, context);
195       return NULL;
196     }
197
198     /* Lookup in thread */
199     SILC_LOG_DEBUG(("Starting async host lookup"));
200     silc_thread_create(silc_socket_host_lookup_start, l, FALSE);
201     return l->op;
202   } else {
203     /* No lookup */
204     l->status = SILC_SOCKET_OK;
205     silc_socket_host_lookup_finish(schedule,
206                                    silc_schedule_get_context(schedule),
207                                    0, 0, l);
208     return NULL;
209   }
210 }
211
212 /* Creates UDP socket stream */
213
214 SilcStream silc_socket_udp_stream_create(SilcSocket sock, SilcBool ipv6,
215                                          SilcBool connected,
216                                          SilcSchedule schedule)
217 {
218   SilcSocketStream stream;
219
220   stream = silc_calloc(1, sizeof(*stream));
221   if (!stream)
222     return NULL;
223
224   SILC_LOG_DEBUG(("Creating UDP socket stream %p", stream));
225
226   stream->ops = &silc_socket_udp_stream_ops;
227   stream->sock = sock;
228   stream->schedule = schedule;
229   stream->ipv6 = ipv6;
230   stream->connected = connected;
231
232   return (SilcStream)stream;
233 }
234
235 /* Returns TRUE if the stream is UDP stream */
236
237 SilcBool silc_socket_stream_is_udp(SilcStream stream, SilcBool *connected)
238 {
239   SilcSocketStream socket_stream = stream;
240
241   if (!SILC_IS_SOCKET_STREAM_UDP(socket_stream))
242     return FALSE;
243
244   if (connected)
245     *connected = socket_stream->connected;
246
247   return TRUE;
248 }
249
250 /* Returns socket stream information */
251
252 SilcBool silc_socket_stream_get_info(SilcStream stream,
253                                      SilcSocket *sock, const char **hostname,
254                                      const char **ip, SilcUInt16 *port)
255 {
256   SilcSocketStream socket_stream = stream;
257
258   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
259       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
260     return FALSE;
261
262   if (sock)
263     *sock = socket_stream->sock;
264   if (hostname) {
265     if (!socket_stream->hostname)
266       return FALSE;
267     *hostname = socket_stream->hostname;
268   }
269   if (ip) {
270     if (!socket_stream->ip)
271       return FALSE;
272     *ip = socket_stream->ip;
273   }
274   if (port) {
275     if (!socket_stream->port)
276       return FALSE;
277     *port = socket_stream->port;
278   }
279
280   return TRUE;
281 }
282
283 /* Set socket information */
284
285 SilcBool silc_socket_stream_set_info(SilcStream stream,
286                                      const char *hostname,
287                                      const char *ip, SilcUInt16 port)
288 {
289   SilcSocketStream socket_stream = stream;
290
291   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
292       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
293     return FALSE;
294
295   if (hostname) {
296     silc_free(socket_stream->hostname);
297     socket_stream->hostname = strdup(hostname);
298     if (!socket_stream->hostname)
299       return FALSE;
300   }
301   if (ip) {
302     silc_free(socket_stream->ip);
303     socket_stream->ip = strdup(ip);
304     if (!socket_stream->ip)
305       return FALSE;
306     if (!socket_stream->hostname) {
307       socket_stream->hostname = strdup(ip);
308       if (!socket_stream->hostname)
309         return FALSE;
310     }
311   }
312   if (port)
313     socket_stream->port = port;
314
315   return TRUE;
316 }
317
318 /* Return socket errno */
319
320 int silc_socket_stream_get_error(SilcStream stream)
321 {
322   SilcSocketStream socket_stream = stream;
323
324   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
325       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
326     return 0;
327
328   return socket_stream->sock_error;
329 }
330
331 /* Set QoS for socket stream */
332
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)
338 {
339   SilcSocketStream socket_stream = stream;
340
341   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
342       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
343     return FALSE;
344
345   SILC_LOG_DEBUG(("Setting QoS for socket stream"));
346
347   if (socket_stream->qos && !read_rate && !read_limit_bytes &&
348       !limit_sec && !limit_usec) {
349     silc_schedule_task_del_by_context(socket_stream->schedule,
350                                       socket_stream->qos);
351     silc_free(socket_stream->qos);
352     socket_stream->qos = NULL;
353     return TRUE;
354   }
355
356   if (!socket_stream->qos) {
357     socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
358     if (!socket_stream->qos)
359       return FALSE;
360   }
361
362   socket_stream->qos->read_rate = read_rate;
363   socket_stream->qos->read_limit_bytes = read_limit_bytes;
364   socket_stream->qos->limit_sec = limit_sec;
365   socket_stream->qos->limit_usec = limit_usec;
366   memset(&socket_stream->qos->next_limit, 0,
367          sizeof(socket_stream->qos->next_limit));
368   socket_stream->qos->cur_rate = 0;
369   socket_stream->qos->sock = socket_stream;
370
371   socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
372   if (!socket_stream->qos->buffer)
373     return FALSE;
374
375   return TRUE;
376 }
377
378 /* Return associated scheduler */
379
380 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
381 {
382   SilcSocketStream socket_stream = stream;
383
384   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
385       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
386     return NULL;
387
388   return socket_stream->schedule;
389 }
390
391 /* SILC Socket Stream ops.  Functions are implemented under the
392    platform specific subdirectories. */
393 const SilcStreamOps silc_socket_stream_ops =
394 {
395   silc_socket_stream_read,
396   silc_socket_stream_write,
397   silc_socket_stream_close,
398   silc_socket_stream_destroy,
399   silc_socket_stream_notifier,
400   silc_socket_stream_get_schedule,
401 };
402 const SilcStreamOps silc_socket_udp_stream_ops =
403 {
404   silc_socket_udp_stream_read,
405   silc_socket_udp_stream_write,
406   silc_socket_stream_close,
407   silc_socket_stream_destroy,
408   silc_socket_stream_notifier,
409   silc_socket_stream_get_schedule,
410 };