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