source: mainline/uspace/lib/net/tl/socket_core.c@ 1a23f6e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1a23f6e was b1bd89ea, checked in by Martin Decky <martin@…>, 13 years ago

get rid of net/modules.{c|h}

  • Property mode set to 100644
File size: 16.2 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libnet
30 * @{
31 */
32
33/** @file
34 * Socket common core implementation.
35 */
36
37#include <socket_core.h>
38#include <net/socket_codes.h>
39#include <net/in.h>
40#include <net/inet.h>
41#include <stdint.h>
42#include <stdlib.h>
43#include <errno.h>
44#include <adt/dynamic_fifo.h>
45#include <adt/int_map.h>
46
47/**
48 * Maximum number of random attempts to find a new socket identifier before
49 * switching to the sequence.
50 */
51#define SOCKET_ID_TRIES 100
52
53/** Bound port sockets.*/
54struct socket_port {
55 /** The bound sockets map. */
56 socket_port_map_t map;
57 /** The bound sockets count. */
58 int count;
59};
60
61INT_MAP_IMPLEMENT(socket_cores, socket_core_t);
62
63GENERIC_CHAR_MAP_IMPLEMENT(socket_port_map, socket_core_t *);
64
65INT_MAP_IMPLEMENT(socket_ports, socket_port_t);
66
67/** Destroy the socket.
68 *
69 * If the socket is bound, the port is released.
70 * Release all buffered packets, call the release function and remove the
71 * socket from the local sockets.
72 *
73 * @param[in] sess Packet server session.
74 * @param[in] socket Socket to be destroyed.
75 * @param[in,out] local_sockets Local sockets to be updated.
76 * @param[in,out] global_sockets Global sockets to be updated.
77 * @param[in] socket_release Client release callback function.
78 *
79 */
80static void socket_destroy_core(async_sess_t *sess, socket_core_t *socket,
81 socket_cores_t *local_sockets, socket_ports_t *global_sockets,
82 void (* socket_release)(socket_core_t *socket))
83{
84 /* If bound */
85 if (socket->port) {
86 /* Release the port */
87 socket_port_release(global_sockets, socket);
88 }
89
90 dyn_fifo_destroy(&socket->accepted);
91
92 if (socket_release)
93 socket_release(socket);
94
95 socket_cores_exclude(local_sockets, socket->socket_id, free);
96}
97
98/** Destroy local sockets.
99 *
100 * Release all buffered packets and call the release function for each of the
101 * sockets.
102 *
103 * @param[in] sess Packet server session.
104 * @param[in] local_sockets Local sockets to be destroyed.
105 * @param[in,out] global_sockets Global sockets to be updated.
106 * @param[in] socket_release Client release callback function.
107 *
108 */
109void socket_cores_release(async_sess_t *sess, socket_cores_t *local_sockets,
110 socket_ports_t *global_sockets,
111 void (* socket_release)(socket_core_t *socket))
112{
113 if (!socket_cores_is_valid(local_sockets))
114 return;
115
116 local_sockets->magic = 0;
117
118 int index;
119 for (index = 0; index < local_sockets->next; ++index) {
120 if (socket_cores_item_is_valid(&local_sockets->items[index])) {
121 local_sockets->items[index].magic = 0;
122
123 if (local_sockets->items[index].value) {
124 socket_destroy_core(sess,
125 local_sockets->items[index].value,
126 local_sockets, global_sockets,
127 socket_release);
128 free(local_sockets->items[index].value);
129 local_sockets->items[index].value = NULL;
130 }
131 }
132 }
133
134 free(local_sockets->items);
135}
136
137/** Adds the socket to a socket port.
138 *
139 * @param[in,out] socket_port The socket port structure.
140 * @param[in] socket The socket to be added.
141 * @param[in] key The socket key identifier.
142 * @param[in] key_length The socket key length.
143 * @return EOK on success.
144 * @return ENOMEM if there is not enough memory left.
145 */
146static int
147socket_port_add_core(socket_port_t *socket_port, socket_core_t *socket,
148 const uint8_t *key, size_t key_length)
149{
150 socket_core_t **socket_ref;
151 int rc;
152
153 /* Create a wrapper */
154 socket_ref = malloc(sizeof(*socket_ref));
155 if (!socket_ref)
156 return ENOMEM;
157
158 *socket_ref = socket;
159 /* Add the wrapper */
160 rc = socket_port_map_add(&socket_port->map, key, key_length,
161 socket_ref);
162 if (rc != EOK) {
163 free(socket_ref);
164 return rc;
165 }
166
167 ++socket_port->count;
168 socket->key = key;
169 socket->key_length = key_length;
170
171 return EOK;
172}
173
174/** Binds the socket to the port.
175 *
176 * The SOCKET_MAP_KEY_LISTENING key identifier is used.
177 *
178 * @param[in] global_sockets The global sockets to be updated.
179 * @param[in] socket The socket to be added.
180 * @param[in] port The port number to be bound to.
181 * @return EOK on success.
182 * @return ENOMEM if there is not enough memory left.
183 * @return Other error codes as defined for the
184 * socket_ports_add() function.
185 */
186static int
187socket_bind_insert(socket_ports_t *global_sockets, socket_core_t *socket,
188 int port)
189{
190 socket_port_t *socket_port;
191 int rc;
192
193 /* Create a wrapper */
194 socket_port = malloc(sizeof(*socket_port));
195 if (!socket_port)
196 return ENOMEM;
197
198 socket_port->count = 0;
199 rc = socket_port_map_initialize(&socket_port->map);
200 if (rc != EOK)
201 goto fail;
202
203 rc = socket_port_add_core(socket_port, socket,
204 (const uint8_t *) SOCKET_MAP_KEY_LISTENING, 0);
205 if (rc != EOK)
206 goto fail;
207
208 /* Register the incoming port */
209 rc = socket_ports_add(global_sockets, port, socket_port);
210 if (rc < 0)
211 goto fail;
212
213 socket->port = port;
214 return EOK;
215
216fail:
217 socket_port_map_destroy(&socket_port->map, free);
218 free(socket_port);
219 return rc;
220
221}
222
223/** Binds the socket to the port.
224 *
225 * The address port is used if set, a free port is used if not.
226 *
227 * @param[in] local_sockets The local sockets to be searched.
228 * @param[in,out] global_sockets The global sockets to be updated.
229 * @param[in] socket_id The new socket identifier.
230 * @param[in] addr The address to be bound to.
231 * @param[in] addrlen The address length.
232 * @param[in] free_ports_start The minimum free port.
233 * @param[in] free_ports_end The maximum free port.
234 * @param[in] last_used_port The last used free port.
235 * @return EOK on success.
236 * @return ENOTSOCK if the socket was not found.
237 * @return EAFNOSUPPORT if the address family is not supported.
238 * @return EADDRINUSE if the port is already in use.
239 * @return Other error codes as defined for the
240 * socket_bind_free_port() function.
241 * @return Other error codes as defined for the
242 * socket_bind_insert() function.
243 */
244int
245socket_bind(socket_cores_t *local_sockets, socket_ports_t *global_sockets,
246 int socket_id, void *addr, size_t addrlen, int free_ports_start,
247 int free_ports_end, int last_used_port)
248{
249 socket_core_t *socket;
250 socket_port_t *socket_port;
251 struct sockaddr *address;
252 struct sockaddr_in *address_in;
253
254 if (addrlen < sizeof(struct sockaddr))
255 return EINVAL;
256
257 address = (struct sockaddr *) addr;
258 switch (address->sa_family) {
259 case AF_INET:
260 if (addrlen != sizeof(struct sockaddr_in))
261 return EINVAL;
262
263 address_in = (struct sockaddr_in *) addr;
264 /* Find the socket */
265 socket = socket_cores_find(local_sockets, socket_id);
266 if (!socket)
267 return ENOTSOCK;
268
269 /* Bind a free port? */
270 if (address_in->sin_port <= 0)
271 return socket_bind_free_port(global_sockets, socket,
272 free_ports_start, free_ports_end, last_used_port);
273
274 /* Try to find the port */
275 socket_port = socket_ports_find(global_sockets,
276 ntohs(address_in->sin_port));
277 if (socket_port) {
278 /* Already used */
279 return EADDRINUSE;
280 }
281
282 /* If bound */
283 if (socket->port) {
284 /* Release the port */
285 socket_port_release(global_sockets, socket);
286 }
287 socket->port = -1;
288
289 return socket_bind_insert(global_sockets, socket,
290 ntohs(address_in->sin_port));
291
292 case AF_INET6:
293 // TODO IPv6
294 break;
295 }
296
297 return EAFNOSUPPORT;
298}
299
300/** Binds the socket to a free port.
301 *
302 * The first free port is used.
303 *
304 * @param[in,out] global_sockets The global sockets to be updated.
305 * @param[in,out] socket The socket to be bound.
306 * @param[in] free_ports_start The minimum free port.
307 * @param[in] free_ports_end The maximum free port.
308 * @param[in] last_used_port The last used free port.
309 * @return EOK on success.
310 * @return ENOTCONN if no free port was found.
311 * @return Other error codes as defined for the
312 * socket_bind_insert() function.
313 */
314int
315socket_bind_free_port(socket_ports_t *global_sockets, socket_core_t *socket,
316 int free_ports_start, int free_ports_end, int last_used_port)
317{
318 int index;
319
320 /* From the last used one */
321 index = last_used_port;
322
323 do {
324 ++index;
325
326 /* Till the range end */
327 if (index >= free_ports_end) {
328 /* Start from the range beginning */
329 index = free_ports_start - 1;
330 do {
331 ++index;
332 /* Till the last used one */
333 if (index >= last_used_port) {
334 /* None found */
335 return ENOTCONN;
336 }
337 } while (socket_ports_find(global_sockets, index));
338
339 /* Found, break immediately */
340 break;
341 }
342
343 } while (socket_ports_find(global_sockets, index));
344
345 return socket_bind_insert(global_sockets, socket, index);
346}
347
348/** Tries to find a new free socket identifier.
349 *
350 * @param[in] local_sockets The local sockets to be searched.
351 * @param[in] positive A value indicating whether a positive identifier is
352 * requested. A negative identifier is requested if set to
353 * false.
354 * @return The new socket identifier.
355 * @return ELIMIT if there is no socket identifier available.
356 */
357static int socket_generate_new_id(socket_cores_t *local_sockets, int positive)
358{
359 int socket_id;
360 int count;
361
362 count = 0;
363#if 0
364 socket_id = socket_globals.last_id;
365#endif
366 do {
367 if (count < SOCKET_ID_TRIES) {
368 socket_id = rand() % INT_MAX;
369 ++count;
370 } else if (count == SOCKET_ID_TRIES) {
371 socket_id = 1;
372 ++count;
373 /* Only this branch for last_id */
374 } else {
375 if (socket_id < INT_MAX) {
376 ++ socket_id;
377#if 0
378 } else if(socket_globals.last_id) {
379 socket_globals.last_id = 0;
380 socket_id = 1;
381#endif
382 } else {
383 return ELIMIT;
384 }
385 }
386 } while (socket_cores_find(local_sockets,
387 ((positive ? 1 : -1) * socket_id)));
388
389// last_id = socket_id
390 return socket_id;
391}
392
393/** Create a new socket.
394 *
395 * @param[in,out] local_sockets Local sockets to be updated.
396 * @param[in] sess Application session.
397 * @param[in] specific_data Socket specific data.
398 * @param[in,out] socket_id New socket identifier. A new identifier
399 * is chosen if set to zero or negative.
400 * A negative identifier is chosen if set
401 * to negative.
402 *
403 * @return EOK on success.
404 * @return EINVAL if the socket_id parameter is NULL.
405 * @return ENOMEM if there is not enough memory left.
406 *
407 */
408int socket_create(socket_cores_t *local_sockets, async_sess_t* sess,
409 void *specific_data, int *socket_id)
410{
411 socket_core_t *socket;
412 int positive;
413 int rc;
414
415 if (!socket_id)
416 return EINVAL;
417
418 /* Store the socket */
419 if (*socket_id <= 0) {
420 positive = (*socket_id == 0);
421 *socket_id = socket_generate_new_id(local_sockets, positive);
422 if (*socket_id <= 0)
423 return *socket_id;
424 if (!positive)
425 *socket_id *= -1;
426 } else if(socket_cores_find(local_sockets, *socket_id)) {
427 return EEXIST;
428 }
429
430 socket = (socket_core_t *) malloc(sizeof(*socket));
431 if (!socket)
432 return ENOMEM;
433
434 /* Initialize */
435 socket->sess = sess;
436 socket->port = -1;
437 socket->key = NULL;
438 socket->key_length = 0;
439 socket->specific_data = specific_data;
440
441 rc = dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
442 if (rc != EOK) {
443 free(socket);
444 return rc;
445 }
446 socket->socket_id = *socket_id;
447 rc = socket_cores_add(local_sockets, socket->socket_id, socket);
448 if (rc < 0) {
449 dyn_fifo_destroy(&socket->accepted);
450 free(socket);
451 return rc;
452 }
453
454 return EOK;
455}
456
457/** Destroy the socket.
458 *
459 * If the socket is bound, the port is released.
460 * Release all buffered packets, call the release function and remove the
461 * socket from the local sockets.
462 *
463 * @param[in] sess Packet server session.
464 * @param[in] socket_id Socket identifier.
465 * @param[in,out] local_sockets Local sockets to be updated.
466 * @param[in,out] global_sockets Global sockets to be updated.
467 * @param[in] socket_release Client release callback function.
468 *
469 * @return EOK on success.
470 * @return ENOTSOCK if the socket is not found.
471 *
472 */
473int
474socket_destroy(async_sess_t *sess, int socket_id, socket_cores_t *local_sockets,
475 socket_ports_t *global_sockets,
476 void (*socket_release)(socket_core_t *socket))
477{
478 /* Find the socket */
479 socket_core_t *socket = socket_cores_find(local_sockets, socket_id);
480 if (!socket)
481 return ENOTSOCK;
482
483 /* Destroy all accepted sockets */
484 int accepted_id;
485 while ((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0)
486 socket_destroy(sess, accepted_id, local_sockets,
487 global_sockets, socket_release);
488
489 socket_destroy_core(sess, socket, local_sockets, global_sockets,
490 socket_release);
491
492 return EOK;
493}
494
495/** Finds the bound port socket.
496 *
497 * @param[in] global_sockets The global sockets to be searched.
498 * @param[in] port The port number.
499 * @param[in] key The socket key identifier.
500 * @param[in] key_length The socket key length.
501 * @return The found socket.
502 * @return NULL if no socket was found.
503 */
504socket_core_t *
505socket_port_find(socket_ports_t *global_sockets, int port, const uint8_t *key,
506 size_t key_length)
507{
508 socket_port_t *socket_port;
509 socket_core_t **socket_ref;
510
511 socket_port = socket_ports_find(global_sockets, port);
512 if (socket_port && (socket_port->count > 0)) {
513 socket_ref = socket_port_map_find(&socket_port->map, key,
514 key_length);
515 if (socket_ref)
516 return *socket_ref;
517 }
518
519 return NULL;
520}
521
522/** Releases the socket port.
523 *
524 * If the socket is bound the port entry is released.
525 * If there are no more port entries the port is release.
526 *
527 * @param[in] global_sockets The global sockets to be updated.
528 * @param[in] socket The socket to be unbound.
529 */
530void
531socket_port_release(socket_ports_t *global_sockets, socket_core_t *socket)
532{
533 socket_port_t *socket_port;
534 socket_core_t **socket_ref;
535
536 if (!socket->port)
537 return;
538
539 /* Find ports */
540 socket_port = socket_ports_find(global_sockets, socket->port);
541 if (socket_port) {
542 /* Find the socket */
543 socket_ref = socket_port_map_find(&socket_port->map,
544 socket->key, socket->key_length);
545
546 if (socket_ref) {
547 --socket_port->count;
548
549 /* Release if empty */
550 if (socket_port->count <= 0) {
551 /* Destroy the map */
552 socket_port_map_destroy(&socket_port->map, free);
553 /* Release the port */
554 socket_ports_exclude(global_sockets,
555 socket->port, free);
556 } else {
557 /* Remove */
558 socket_port_map_exclude(&socket_port->map,
559 socket->key, socket->key_length, free);
560 }
561 }
562 }
563
564 socket->port = 0;
565 socket->key = NULL;
566 socket->key_length = 0;
567}
568
569/** Adds the socket to an already bound port.
570 *
571 * @param[in] global_sockets The global sockets to be updated.
572 * @param[in] port The port number to be bound to.
573 * @param[in] socket The socket to be added.
574 * @param[in] key The socket key identifier.
575 * @param[in] key_length The socket key length.
576 * @return EOK on success.
577 * @return ENOENT if the port is not already used.
578 * @return Other error codes as defined for the
579 * socket_port_add_core() function.
580 */
581int
582socket_port_add(socket_ports_t *global_sockets, int port,
583 socket_core_t *socket, const uint8_t *key, size_t key_length)
584{
585 socket_port_t *socket_port;
586 int rc;
587
588 /* Find ports */
589 socket_port = socket_ports_find(global_sockets, port);
590 if (!socket_port)
591 return ENOENT;
592
593 /* Add the socket */
594 rc = socket_port_add_core(socket_port, socket, key, key_length);
595 if (rc != EOK)
596 return rc;
597
598 socket->port = port;
599 return EOK;
600}
601
602/** @}
603 */
Note: See TracBrowser for help on using the repository browser.