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

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

improve comments, use C++ style comments for TODOs and FIXMEs

  • Property mode set to 100644
File size: 18.7 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 <packet_client.h>
39#include <packet_remote.h>
40
41#include <net/socket_codes.h>
42#include <net/in.h>
43#include <net/inet.h>
44#include <net/packet.h>
45#include <net/modules.h>
46
47#include <stdint.h>
48#include <stdlib.h>
49#include <errno.h>
50
51#include <adt/dynamic_fifo.h>
52#include <adt/int_map.h>
53
54/**
55 * Maximum number of random attempts to find a new socket identifier before
56 * switching to the sequence.
57 */
58#define SOCKET_ID_TRIES 100
59
60/** Bound port sockets.*/
61struct socket_port {
62 /** The bound sockets map. */
63 socket_port_map_t map;
64 /** The bound sockets count. */
65 int count;
66};
67
68INT_MAP_IMPLEMENT(socket_cores, socket_core_t);
69
70GENERIC_CHAR_MAP_IMPLEMENT(socket_port_map, socket_core_t *);
71
72INT_MAP_IMPLEMENT(socket_ports, socket_port_t);
73
74/** Destroys the socket.
75 *
76 * If the socket is bound, the port is released.
77 * Releases all buffered packets, calls the release function and removes the
78 * socket from the local sockets.
79 *
80 * @param[in] packet_phone The packet server phone to release buffered packets.
81 * @param[in] socket The socket to be destroyed.
82 * @param[in,out] local_sockets The local sockets to be updated.
83 * @param[in,out] global_sockets The global sockets to be updated.
84 * @param[in] socket_release The client release callback function.
85 */
86static void
87socket_destroy_core(int packet_phone, socket_core_t *socket,
88 socket_cores_t *local_sockets, socket_ports_t *global_sockets,
89 void (* socket_release)(socket_core_t *socket))
90{
91 int packet_id;
92
93 /* If bound */
94 if (socket->port) {
95 /* Release the port */
96 socket_port_release(global_sockets, socket);
97 }
98
99 /* Release all received packets */
100 while ((packet_id = dyn_fifo_pop(&socket->received)) >= 0)
101 pq_release_remote(packet_phone, packet_id);
102
103 dyn_fifo_destroy(&socket->received);
104 dyn_fifo_destroy(&socket->accepted);
105
106 if (socket_release)
107 socket_release(socket);
108
109 socket_cores_exclude(local_sockets, socket->socket_id, free);
110}
111
112/** Destroys local sockets.
113 *
114 * Releases all buffered packets and calls the release function for each of the
115 * sockets.
116 *
117 * @param[in] packet_phone The packet server phone to release buffered packets.
118 * @param[in] local_sockets The local sockets to be destroyed.
119 * @param[in,out] global_sockets The global sockets to be updated.
120 * @param[in] socket_release The client release callback function.
121 */
122void
123socket_cores_release(int packet_phone, socket_cores_t *local_sockets,
124 socket_ports_t *global_sockets,
125 void (* socket_release)(socket_core_t *socket))
126{
127 int index;
128
129 if (!socket_cores_is_valid(local_sockets))
130 return;
131
132 local_sockets->magic = 0;
133
134 for (index = 0; index < local_sockets->next; ++index) {
135 if (socket_cores_item_is_valid(&local_sockets->items[index])) {
136 local_sockets->items[index].magic = 0;
137
138 if (local_sockets->items[index].value) {
139 socket_destroy_core(packet_phone,
140 local_sockets->items[index].value,
141 local_sockets, global_sockets,
142 socket_release);
143 free(local_sockets->items[index].value);
144 local_sockets->items[index].value = NULL;
145 }
146 }
147 }
148
149 free(local_sockets->items);
150}
151
152/** Adds the socket to a socket port.
153 *
154 * @param[in,out] socket_port The socket port structure.
155 * @param[in] socket The socket to be added.
156 * @param[in] key The socket key identifier.
157 * @param[in] key_length The socket key length.
158 * @return EOK on success.
159 * @return ENOMEM if there is not enough memory left.
160 */
161static int
162socket_port_add_core(socket_port_t *socket_port, socket_core_t *socket,
163 const uint8_t *key, size_t key_length)
164{
165 socket_core_t **socket_ref;
166 int rc;
167
168 /* Create a wrapper */
169 socket_ref = malloc(sizeof(*socket_ref));
170 if (!socket_ref)
171 return ENOMEM;
172
173 *socket_ref = socket;
174 /* Add the wrapper */
175 rc = socket_port_map_add(&socket_port->map, key, key_length,
176 socket_ref);
177 if (rc != EOK) {
178 free(socket_ref);
179 return rc;
180 }
181
182 ++socket_port->count;
183 socket->key = key;
184 socket->key_length = key_length;
185
186 return EOK;
187}
188
189/** Binds the socket to the port.
190 *
191 * The SOCKET_MAP_KEY_LISTENING key identifier is used.
192 *
193 * @param[in] global_sockets The global sockets to be updated.
194 * @param[in] socket The socket to be added.
195 * @param[in] port The port number to be bound to.
196 * @return EOK on success.
197 * @return ENOMEM if there is not enough memory left.
198 * @return Other error codes as defined for the
199 * socket_ports_add() function.
200 */
201static int
202socket_bind_insert(socket_ports_t *global_sockets, socket_core_t *socket,
203 int port)
204{
205 socket_port_t *socket_port;
206 int rc;
207
208 /* Create a wrapper */
209 socket_port = malloc(sizeof(*socket_port));
210 if (!socket_port)
211 return ENOMEM;
212
213 socket_port->count = 0;
214 rc = socket_port_map_initialize(&socket_port->map);
215 if (rc != EOK)
216 goto fail;
217
218 rc = socket_port_add_core(socket_port, socket,
219 (const uint8_t *) SOCKET_MAP_KEY_LISTENING, 0);
220 if (rc != EOK)
221 goto fail;
222
223 /* Register the incoming port */
224 rc = socket_ports_add(global_sockets, port, socket_port);
225 if (rc < 0)
226 goto fail;
227
228 socket->port = port;
229 return EOK;
230
231fail:
232 socket_port_map_destroy(&socket_port->map, free);
233 free(socket_port);
234 return rc;
235
236}
237
238/** Binds the socket to the port.
239 *
240 * The address port is used if set, a free port is used if not.
241 *
242 * @param[in] local_sockets The local sockets to be searched.
243 * @param[in,out] global_sockets The global sockets to be updated.
244 * @param[in] socket_id The new socket identifier.
245 * @param[in] addr The address to be bound to.
246 * @param[in] addrlen The address length.
247 * @param[in] free_ports_start The minimum free port.
248 * @param[in] free_ports_end The maximum free port.
249 * @param[in] last_used_port The last used free port.
250 * @return EOK on success.
251 * @return ENOTSOCK if the socket was not found.
252 * @return EAFNOSUPPORT if the address family is not supported.
253 * @return EADDRINUSE if the port is already in use.
254 * @return Other error codes as defined for the
255 * socket_bind_free_port() function.
256 * @return Other error codes as defined for the
257 * socket_bind_insert() function.
258 */
259int
260socket_bind(socket_cores_t *local_sockets, socket_ports_t *global_sockets,
261 int socket_id, void *addr, size_t addrlen, int free_ports_start,
262 int free_ports_end, int last_used_port)
263{
264 socket_core_t *socket;
265 socket_port_t *socket_port;
266 struct sockaddr *address;
267 struct sockaddr_in *address_in;
268
269 if (addrlen < sizeof(struct sockaddr))
270 return EINVAL;
271
272 address = (struct sockaddr *) addr;
273 switch (address->sa_family) {
274 case AF_INET:
275 if (addrlen != sizeof(struct sockaddr_in))
276 return EINVAL;
277
278 address_in = (struct sockaddr_in *) addr;
279 /* Find the socket */
280 socket = socket_cores_find(local_sockets, socket_id);
281 if (!socket)
282 return ENOTSOCK;
283
284 /* Bind a free port? */
285 if (address_in->sin_port <= 0)
286 return socket_bind_free_port(global_sockets, socket,
287 free_ports_start, free_ports_end, last_used_port);
288
289 /* Try to find the port */
290 socket_port = socket_ports_find(global_sockets,
291 ntohs(address_in->sin_port));
292 if (socket_port) {
293 /* Already used */
294 return EADDRINUSE;
295 }
296
297 /* If bound */
298 if (socket->port) {
299 /* Release the port */
300 socket_port_release(global_sockets, socket);
301 }
302 socket->port = -1;
303
304 return socket_bind_insert(global_sockets, socket,
305 ntohs(address_in->sin_port));
306
307 case AF_INET6:
308 // TODO IPv6
309 break;
310 }
311
312 return EAFNOSUPPORT;
313}
314
315/** Binds the socket to a free port.
316 *
317 * The first free port is used.
318 *
319 * @param[in,out] global_sockets The global sockets to be updated.
320 * @param[in,out] socket The socket to be bound.
321 * @param[in] free_ports_start The minimum free port.
322 * @param[in] free_ports_end The maximum free port.
323 * @param[in] last_used_port The last used free port.
324 * @return EOK on success.
325 * @return ENOTCONN if no free port was found.
326 * @return Other error codes as defined for the
327 * socket_bind_insert() function.
328 */
329int
330socket_bind_free_port(socket_ports_t *global_sockets, socket_core_t *socket,
331 int free_ports_start, int free_ports_end, int last_used_port)
332{
333 int index;
334
335 /* From the last used one */
336 index = last_used_port;
337
338 do {
339 ++index;
340
341 /* Till the range end */
342 if (index >= free_ports_end) {
343 /* Start from the range beginning */
344 index = free_ports_start - 1;
345 do {
346 ++index;
347 /* Till the last used one */
348 if (index >= last_used_port) {
349 /* None found */
350 return ENOTCONN;
351 }
352 } while (socket_ports_find(global_sockets, index));
353
354 /* Found, break immediately */
355 break;
356 }
357
358 } while (socket_ports_find(global_sockets, index));
359
360 return socket_bind_insert(global_sockets, socket, index);
361}
362
363/** Tries to find a new free socket identifier.
364 *
365 * @param[in] local_sockets The local sockets to be searched.
366 * @param[in] positive A value indicating whether a positive identifier is
367 * requested. A negative identifier is requested if set to
368 * false.
369 * @return The new socket identifier.
370 * @return ELIMIT if there is no socket identifier available.
371 */
372static int socket_generate_new_id(socket_cores_t *local_sockets, int positive)
373{
374 int socket_id;
375 int count;
376
377 count = 0;
378#if 0
379 socket_id = socket_globals.last_id;
380#endif
381 do {
382 if (count < SOCKET_ID_TRIES) {
383 socket_id = rand() % INT_MAX;
384 ++count;
385 } else if (count == SOCKET_ID_TRIES) {
386 socket_id = 1;
387 ++count;
388 /* Only this branch for last_id */
389 } else {
390 if (socket_id < INT_MAX) {
391 ++ socket_id;
392#if 0
393 } else if(socket_globals.last_id) {
394 socket_globals.last_id = 0;
395 socket_id = 1;
396#endif
397 } else {
398 return ELIMIT;
399 }
400 }
401 } while (socket_cores_find(local_sockets,
402 ((positive ? 1 : -1) * socket_id)));
403
404// last_id = socket_id
405 return socket_id;
406}
407
408/** Creates a new socket.
409 *
410 * @param[in,out] local_sockets The local sockets to be updated.
411 * @param[in] app_phone The application phone.
412 * @param[in] specific_data The socket specific data.
413 * @param[in,out] socket_id The new socket identifier. A new identifier is
414 * chosen if set to zero or negative. A negative identifier
415 * is chosen if set to negative.
416 * @return EOK on success.
417 * @return EINVAL if the socket_id parameter is NULL.
418 * @return ENOMEM if there is not enough memory left.
419 */
420int
421socket_create(socket_cores_t *local_sockets, int app_phone,
422 void *specific_data, int *socket_id)
423{
424 socket_core_t *socket;
425 int positive;
426 int rc;
427
428 if (!socket_id)
429 return EINVAL;
430
431 /* Store the socket */
432 if (*socket_id <= 0) {
433 positive = (*socket_id == 0);
434 *socket_id = socket_generate_new_id(local_sockets, positive);
435 if (*socket_id <= 0)
436 return *socket_id;
437 if (!positive)
438 *socket_id *= -1;
439 } else if(socket_cores_find(local_sockets, *socket_id)) {
440 return EEXIST;
441 }
442
443 socket = (socket_core_t *) malloc(sizeof(*socket));
444 if (!socket)
445 return ENOMEM;
446
447 /* Initialize */
448 socket->phone = app_phone;
449 socket->port = -1;
450 socket->key = NULL;
451 socket->key_length = 0;
452 socket->specific_data = specific_data;
453 rc = dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE);
454 if (rc != EOK) {
455 free(socket);
456 return rc;
457 }
458
459 rc = dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
460 if (rc != EOK) {
461 dyn_fifo_destroy(&socket->received);
462 free(socket);
463 return rc;
464 }
465 socket->socket_id = *socket_id;
466 rc = socket_cores_add(local_sockets, socket->socket_id, socket);
467 if (rc < 0) {
468 dyn_fifo_destroy(&socket->received);
469 dyn_fifo_destroy(&socket->accepted);
470 free(socket);
471 return rc;
472 }
473
474 return EOK;
475}
476
477/** Destroys the socket.
478 *
479 * If the socket is bound, the port is released.
480 * Releases all buffered packets, calls the release function and removes the
481 * socket from the local sockets.
482 *
483 * @param[in] packet_phone The packet server phone to release buffered packets.
484 * @param[in] socket_id The socket identifier.
485 * @param[in,out] local_sockets The local sockets to be updated.
486 * @param[in,out] global_sockets The global sockets to be updated.
487 * @param[in] socket_release The client release callback function.
488 * @return EOK on success.
489 * @return ENOTSOCK if the socket is not found.
490 */
491int
492socket_destroy(int packet_phone, int socket_id, socket_cores_t *local_sockets,
493 socket_ports_t *global_sockets,
494 void (*socket_release)(socket_core_t *socket))
495{
496 socket_core_t *socket;
497 int accepted_id;
498
499 /* Find the socket */
500 socket = socket_cores_find(local_sockets, socket_id);
501 if (!socket)
502 return ENOTSOCK;
503
504 /* Destroy all accepted sockets */
505 while ((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0)
506 socket_destroy(packet_phone, accepted_id, local_sockets,
507 global_sockets, socket_release);
508
509 socket_destroy_core(packet_phone, socket, local_sockets, global_sockets,
510 socket_release);
511
512 return EOK;
513}
514
515/** Replies the packet or the packet queue data to the application via the
516 * socket.
517 *
518 * Uses the current message processing fibril.
519 *
520 * @param[in] packet The packet to be transfered.
521 * @param[out] length The total data length.
522 * @return EOK on success.
523 * @return EBADMEM if the length parameter is NULL.
524 * @return ENOMEM if there is not enough memory left.
525 * @return Other error codes as defined for the data_reply()
526 * function.
527 */
528int socket_reply_packets(packet_t *packet, size_t *length)
529{
530 packet_t *next_packet;
531 size_t fragments;
532 size_t *lengths;
533 size_t index;
534 int rc;
535
536 if (!length)
537 return EBADMEM;
538
539 next_packet = pq_next(packet);
540 if (!next_packet) {
541 /* Write all if only one fragment */
542 rc = data_reply(packet_get_data(packet),
543 packet_get_data_length(packet));
544 if (rc != EOK)
545 return rc;
546 /* Store the total length */
547 *length = packet_get_data_length(packet);
548 } else {
549 /* Count the packet fragments */
550 fragments = 1;
551 next_packet = pq_next(packet);
552 while ((next_packet = pq_next(next_packet)))
553 ++fragments;
554
555 /* Compute and store the fragment lengths */
556 lengths = (size_t *) malloc(sizeof(size_t) * fragments +
557 sizeof(size_t));
558 if (!lengths)
559 return ENOMEM;
560
561 lengths[0] = packet_get_data_length(packet);
562 lengths[fragments] = lengths[0];
563 next_packet = pq_next(packet);
564
565 for (index = 1; index < fragments; ++index) {
566 lengths[index] = packet_get_data_length(next_packet);
567 lengths[fragments] += lengths[index];
568 next_packet = pq_next(packet);
569 }
570
571 /* Write the fragment lengths */
572 rc = data_reply(lengths, sizeof(int) * (fragments + 1));
573 if (rc != EOK) {
574 free(lengths);
575 return rc;
576 }
577 next_packet = packet;
578
579 /* Write the fragments */
580 for (index = 0; index < fragments; ++index) {
581 rc = data_reply(packet_get_data(next_packet),
582 lengths[index]);
583 if (rc != EOK) {
584 free(lengths);
585 return rc;
586 }
587 next_packet = pq_next(next_packet);
588 }
589
590 /* Store the total length */
591 *length = lengths[fragments];
592 free(lengths);
593 }
594
595 return EOK;
596}
597
598/** Finds the bound port socket.
599 *
600 * @param[in] global_sockets The global sockets to be searched.
601 * @param[in] port The port number.
602 * @param[in] key The socket key identifier.
603 * @param[in] key_length The socket key length.
604 * @return The found socket.
605 * @return NULL if no socket was found.
606 */
607socket_core_t *
608socket_port_find(socket_ports_t *global_sockets, int port, const uint8_t *key,
609 size_t key_length)
610{
611 socket_port_t *socket_port;
612 socket_core_t **socket_ref;
613
614 socket_port = socket_ports_find(global_sockets, port);
615 if (socket_port && (socket_port->count > 0)) {
616 socket_ref = socket_port_map_find(&socket_port->map, key,
617 key_length);
618 if (socket_ref)
619 return *socket_ref;
620 }
621
622 return NULL;
623}
624
625/** Releases the socket port.
626 *
627 * If the socket is bound the port entry is released.
628 * If there are no more port entries the port is release.
629 *
630 * @param[in] global_sockets The global sockets to be updated.
631 * @param[in] socket The socket to be unbound.
632 */
633void
634socket_port_release(socket_ports_t *global_sockets, socket_core_t *socket)
635{
636 socket_port_t *socket_port;
637 socket_core_t **socket_ref;
638
639 if (!socket->port)
640 return;
641
642 /* Find ports */
643 socket_port = socket_ports_find(global_sockets, socket->port);
644 if (socket_port) {
645 /* Find the socket */
646 socket_ref = socket_port_map_find(&socket_port->map,
647 socket->key, socket->key_length);
648
649 if (socket_ref) {
650 --socket_port->count;
651
652 /* Release if empty */
653 if (socket_port->count <= 0) {
654 /* Destroy the map */
655 socket_port_map_destroy(&socket_port->map, free);
656 /* Release the port */
657 socket_ports_exclude(global_sockets,
658 socket->port, free);
659 } else {
660 /* Remove */
661 socket_port_map_exclude(&socket_port->map,
662 socket->key, socket->key_length, free);
663 }
664 }
665 }
666
667 socket->port = 0;
668 socket->key = NULL;
669 socket->key_length = 0;
670}
671
672/** Adds the socket to an already bound port.
673 *
674 * @param[in] global_sockets The global sockets to be updated.
675 * @param[in] port The port number to be bound to.
676 * @param[in] socket The socket to be added.
677 * @param[in] key The socket key identifier.
678 * @param[in] key_length The socket key length.
679 * @return EOK on success.
680 * @return ENOENT if the port is not already used.
681 * @return Other error codes as defined for the
682 * socket_port_add_core() function.
683 */
684int
685socket_port_add(socket_ports_t *global_sockets, int port,
686 socket_core_t *socket, const uint8_t *key, size_t key_length)
687{
688 socket_port_t *socket_port;
689 int rc;
690
691 /* Find ports */
692 socket_port = socket_ports_find(global_sockets, port);
693 if (!socket_port)
694 return ENOENT;
695
696 /* Add the socket */
697 rc = socket_port_add_core(socket_port, socket, key, key_length);
698 if (rc != EOK)
699 return rc;
700
701 socket->port = port;
702 return EOK;
703}
704
705/** @}
706 */
Note: See TracBrowser for help on using the repository browser.