Porting Toolkit to Symbian. It should work while some sporadic
[silc.git] / lib / silcutil / silcsocketstream.c
1 /*
2
3   silcsocketstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2007 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 SilcBool 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 lookup 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 || !schedule) {
156     SILC_LOG_ERROR(("Missing arguments to silc_socket_tcp_stream_create"));
157     if (callback)
158       callback(SILC_SOCKET_ERROR, NULL, context);
159     return NULL;
160   }
161
162   stream = silc_calloc(1, sizeof(*stream));
163   if (!stream) {
164     if (callback)
165       callback(SILC_SOCKET_NO_MEMORY, NULL, context);
166     return NULL;
167   }
168
169   SILC_LOG_DEBUG(("Creating TCP socket stream %p, sock %lu", stream, sock));
170
171   stream->ops = &silc_socket_stream_ops;
172   stream->sock = sock;
173   stream->schedule = schedule;
174   stream->connected = TRUE;
175
176   l = silc_calloc(1, sizeof(*l));
177   if (!l) {
178     silc_free(stream);
179     if (callback)
180       callback(SILC_SOCKET_NO_MEMORY, NULL, context);
181     return NULL;
182   }
183
184   l->stream = stream;
185   l->callback = callback;
186   l->context = context;
187   l->require_fqdn = require_fqdn;
188
189   if (lookup) {
190     /* Start asynchronous IP, hostname and port lookup process */
191     l->op = silc_async_alloc(silc_socket_host_lookup_abort, NULL, l);
192     if (!l->op) {
193       silc_free(stream);
194       silc_free(l);
195       if (callback)
196         callback(SILC_SOCKET_ERROR, NULL, context);
197       return NULL;
198     }
199
200     /* Lookup in thread */
201     SILC_LOG_DEBUG(("Starting async host lookup"));
202     silc_thread_create(silc_socket_host_lookup_start, l, FALSE);
203     return l->op;
204   } else {
205     /* No lookup */
206     l->status = SILC_SOCKET_OK;
207     silc_socket_host_lookup_finish(schedule,
208                                    silc_schedule_get_context(schedule),
209                                    0, 0, l);
210     return NULL;
211   }
212 }
213
214 /* Creates UDP socket stream */
215
216 SilcStream silc_socket_udp_stream_create(SilcSocket sock, SilcBool ipv6,
217                                          SilcBool connected,
218                                          SilcSchedule schedule)
219 {
220   SilcSocketStream stream;
221
222   stream = silc_calloc(1, sizeof(*stream));
223   if (!stream)
224     return NULL;
225
226   SILC_LOG_DEBUG(("Creating UDP socket stream %p", stream));
227
228   stream->ops = &silc_socket_udp_stream_ops;
229   stream->sock = sock;
230   stream->schedule = schedule;
231   stream->ipv6 = ipv6;
232   stream->connected = connected;
233
234   return (SilcStream)stream;
235 }
236
237 /* Returns TRUE if the stream is UDP stream */
238
239 SilcBool silc_socket_stream_is_udp(SilcStream stream, SilcBool *connected)
240 {
241   SilcSocketStream socket_stream = stream;
242
243   if (!SILC_IS_SOCKET_STREAM_UDP(socket_stream))
244     return FALSE;
245
246   if (connected)
247     *connected = socket_stream->connected;
248
249   return TRUE;
250 }
251
252 /* Returns socket stream information */
253
254 SilcBool silc_socket_stream_get_info(SilcStream stream,
255                                      SilcSocket *sock, const char **hostname,
256                                      const char **ip, SilcUInt16 *port)
257 {
258   SilcSocketStream socket_stream = stream;
259
260   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
261       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
262     return FALSE;
263
264   if (sock)
265     *sock = socket_stream->sock;
266   if (port) {
267     if (!socket_stream->port)
268       return FALSE;
269     *port = socket_stream->port;
270   }
271   if (ip) {
272     if (!socket_stream->ip)
273       return FALSE;
274     *ip = socket_stream->ip;
275   }
276   if (hostname) {
277     if (!socket_stream->hostname)
278       return FALSE;
279     *hostname = socket_stream->hostname;
280   }
281
282   return TRUE;
283 }
284
285 /* Set socket information */
286
287 SilcBool silc_socket_stream_set_info(SilcStream stream,
288                                      const char *hostname,
289                                      const char *ip, SilcUInt16 port)
290 {
291   SilcSocketStream socket_stream = stream;
292
293   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
294       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
295     return FALSE;
296
297   if (hostname) {
298     silc_free(socket_stream->hostname);
299     socket_stream->hostname = strdup(hostname);
300     if (!socket_stream->hostname)
301       return FALSE;
302   }
303   if (ip) {
304     silc_free(socket_stream->ip);
305     socket_stream->ip = strdup(ip);
306     if (!socket_stream->ip)
307       return FALSE;
308     if (!socket_stream->hostname) {
309       socket_stream->hostname = strdup(ip);
310       if (!socket_stream->hostname)
311         return FALSE;
312     }
313   }
314   if (port)
315     socket_stream->port = port;
316
317   return TRUE;
318 }
319
320 /* Return socket errno */
321
322 int silc_socket_stream_get_error(SilcStream stream)
323 {
324   SilcSocketStream socket_stream = stream;
325
326   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
327       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
328     return 0;
329
330   return socket_stream->sock_error;
331 }
332
333 /* Set QoS for socket stream */
334
335 SilcBool silc_socket_stream_set_qos(SilcStream stream,
336                                     SilcUInt32 read_rate,
337                                     SilcUInt32 read_limit_bytes,
338                                     SilcUInt32 limit_sec,
339                                     SilcUInt32 limit_usec)
340 {
341   SilcSocketStream socket_stream = stream;
342
343   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
344       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
345     return FALSE;
346
347   SILC_LOG_DEBUG(("Setting QoS for socket stream"));
348
349   if (socket_stream->qos && !read_rate && !read_limit_bytes &&
350       !limit_sec && !limit_usec) {
351     silc_schedule_task_del_by_context(socket_stream->schedule,
352                                       socket_stream->qos);
353     silc_free(socket_stream->qos);
354     socket_stream->qos = NULL;
355     return TRUE;
356   }
357
358   if (!socket_stream->qos) {
359     socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
360     if (!socket_stream->qos)
361       return FALSE;
362   }
363
364   socket_stream->qos->read_rate = read_rate;
365   socket_stream->qos->read_limit_bytes = read_limit_bytes;
366   socket_stream->qos->limit_sec = limit_sec;
367   socket_stream->qos->limit_usec = limit_usec;
368   memset(&socket_stream->qos->next_limit, 0,
369          sizeof(socket_stream->qos->next_limit));
370   socket_stream->qos->cur_rate = 0;
371   socket_stream->qos->sock = socket_stream;
372
373   socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
374   if (!socket_stream->qos->buffer)
375     return FALSE;
376
377   return TRUE;
378 }
379
380 /* Return associated scheduler */
381
382 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
383 {
384   SilcSocketStream socket_stream = stream;
385
386   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
387       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
388     return NULL;
389
390   return socket_stream->schedule;
391 }
392
393 /* SILC Socket Stream ops.  Functions are implemented under the
394    platform specific subdirectories. */
395 const SilcStreamOps silc_socket_stream_ops =
396 {
397   silc_socket_stream_read,
398   silc_socket_stream_write,
399   silc_socket_stream_close,
400   silc_socket_stream_destroy,
401   silc_socket_stream_notifier,
402   silc_socket_stream_get_schedule,
403 };
404 const SilcStreamOps silc_socket_udp_stream_ops =
405 {
406   silc_socket_udp_stream_read,
407   silc_socket_udp_stream_write,
408   silc_socket_stream_close,
409   silc_socket_stream_destroy,
410   silc_socket_stream_notifier,
411   silc_socket_stream_get_schedule,
412 };