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