1 /*
2 
3   silcsocketstream.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2005 - 2008 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
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 
SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)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 
silc_socket_host_lookup_start(void * context)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 
silc_socket_host_lookup_abort(SilcAsyncOperation op,void * context)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
silc_socket_tcp_stream_create(SilcSocket sock,SilcBool lookup,SilcBool require_fqdn,SilcSchedule schedule,SilcSocketStreamCallback callback,void * context)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, (long unsigned) 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 
silc_socket_udp_stream_create(SilcSocket sock,SilcBool ipv6,SilcBool connected,SilcSchedule schedule)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 
silc_socket_stream_is_udp(SilcStream stream,SilcBool * connected)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 
silc_socket_stream_get_info(SilcStream stream,SilcSocket * sock,const char ** hostname,const char ** ip,SilcUInt16 * port)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 
silc_socket_stream_set_info(SilcStream stream,const char * hostname,const char * ip,SilcUInt16 port)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 
silc_socket_stream_get_error(SilcStream stream)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 
silc_socket_stream_set_qos(SilcStream stream,SilcUInt32 read_rate,SilcUInt32 read_limit_bytes,SilcUInt32 limit_sec,SilcUInt32 limit_usec)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->buffer);
354     silc_free(socket_stream->qos);
355     socket_stream->qos = NULL;
356     return TRUE;
357   }
358 
359   if (!socket_stream->qos) {
360     socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
361     if (!socket_stream->qos)
362       return FALSE;
363   }
364 
365   socket_stream->qos->read_rate = read_rate;
366   socket_stream->qos->read_limit_bytes = read_limit_bytes;
367   socket_stream->qos->limit_sec = limit_sec;
368   socket_stream->qos->limit_usec = limit_usec;
369   memset(&socket_stream->qos->next_limit, 0,
370 	 sizeof(socket_stream->qos->next_limit));
371   socket_stream->qos->cur_rate = 0;
372   socket_stream->qos->sock = socket_stream;
373 
374   socket_stream->qos->buffer = silc_realloc(socket_stream->qos->buffer,
375 					    read_limit_bytes);
376   if (!socket_stream->qos->buffer)
377     return FALSE;
378 
379   return TRUE;
380 }
381 
382 /* Return associated scheduler */
383 
silc_socket_stream_get_schedule(SilcStream stream)384 SilcSchedule silc_socket_stream_get_schedule(SilcStream stream)
385 {
386   SilcSocketStream socket_stream = stream;
387 
388   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
389       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
390     return NULL;
391 
392   return socket_stream->schedule;
393 }
394 
395 /* SILC Socket Stream ops.  Functions are implemented under the
396    platform specific subdirectories. */
397 const SilcStreamOps silc_socket_stream_ops =
398 {
399   silc_socket_stream_read,
400   silc_socket_stream_write,
401   silc_socket_stream_close,
402   silc_socket_stream_destroy,
403   silc_socket_stream_notifier,
404   silc_socket_stream_get_schedule,
405 };
406 const SilcStreamOps silc_socket_udp_stream_ops =
407 {
408   silc_socket_udp_stream_read,
409   silc_socket_udp_stream_write,
410   silc_socket_stream_close,
411   silc_socket_stream_destroy,
412   silc_socket_stream_notifier,
413   silc_socket_stream_get_schedule,
414 };
415