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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 014dd57b was 61bfc370, checked in by Martin Decky <martin@…>, 14 years ago
  • make measured_string and other network-related data types binary-safe
  • fix several network-related routines binary-safe (replace clearly suspicious use of str_lcmp() with bcmp())
  • rename spawn() to net_spawn()
  • Property mode set to 100644
File size: 18.6 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);
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 incomming 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);
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 // til 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 // til 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// socket_id = socket_globals.last_id;
379 do {
380 if (count < SOCKET_ID_TRIES) {
381 socket_id = rand() % INT_MAX;
382 ++count;
383 } else if (count == SOCKET_ID_TRIES) {
384 socket_id = 1;
385 ++count;
386 // only this branch for last_id
387 } else {
388 if (socket_id < INT_MAX) {
389 ++ socket_id;
390/* } else if(socket_globals.last_id) {
391* socket_globals.last_id = 0;
392* socket_id = 1;
393*/ } else {
394 return ELIMIT;
395 }
396 }
397 } while (socket_cores_find(local_sockets,
398 ((positive ? 1 : -1) * socket_id)));
399
400// last_id = socket_id
401 return socket_id;
402}
403
404/** Creates a new socket.
405 *
406 * @param[in,out] local_sockets The local sockets to be updated.
407 * @param[in] app_phone The application phone.
408 * @param[in] specific_data The socket specific data.
409 * @param[in,out] socket_id The new socket identifier. A new identifier is
410 * chosen if set to zero or negative. A negative identifier
411 * is chosen if set to negative.
412 * @return EOK on success.
413 * @return EINVAL if the socket_id parameter is NULL.
414 * @return ENOMEM if there is not enough memory left.
415 */
416int
417socket_create(socket_cores_t *local_sockets, int app_phone,
418 void *specific_data, int *socket_id)
419{
420 socket_core_t *socket;
421 int positive;
422 int rc;
423
424 if (!socket_id)
425 return EINVAL;
426
427 // store the socket
428 if (*socket_id <= 0) {
429 positive = (*socket_id == 0);
430 *socket_id = socket_generate_new_id(local_sockets, positive);
431 if (*socket_id <= 0)
432 return *socket_id;
433 if (!positive)
434 *socket_id *= -1;
435 } else if(socket_cores_find(local_sockets, *socket_id)) {
436 return EEXIST;
437 }
438
439 socket = (socket_core_t *) malloc(sizeof(*socket));
440 if (!socket)
441 return ENOMEM;
442
443 // initialize
444 socket->phone = app_phone;
445 socket->port = -1;
446 socket->key = NULL;
447 socket->key_length = 0;
448 socket->specific_data = specific_data;
449 rc = dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE);
450 if (rc != EOK) {
451 free(socket);
452 return rc;
453 }
454
455 rc = dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
456 if (rc != EOK) {
457 dyn_fifo_destroy(&socket->received);
458 free(socket);
459 return rc;
460 }
461 socket->socket_id = *socket_id;
462 rc = socket_cores_add(local_sockets, socket->socket_id, socket);
463 if (rc < 0) {
464 dyn_fifo_destroy(&socket->received);
465 dyn_fifo_destroy(&socket->accepted);
466 free(socket);
467 return rc;
468 }
469
470 return EOK;
471}
472
473/** Destroys the socket.
474 *
475 * If the socket is bound, the port is released.
476 * Releases all buffered packets, calls the release function and removes the
477 * socket from the local sockets.
478 *
479 * @param[in] packet_phone The packet server phone to release buffered packets.
480 * @param[in] socket_id The socket identifier.
481 * @param[in,out] local_sockets The local sockets to be updated.
482 * @param[in,out] global_sockets The global sockets to be updated.
483 * @param[in] socket_release The client release callback function.
484 * @return EOK on success.
485 * @return ENOTSOCK if the socket is not found.
486 */
487int
488socket_destroy(int packet_phone, int socket_id, socket_cores_t *local_sockets,
489 socket_ports_t *global_sockets,
490 void (*socket_release)(socket_core_t *socket))
491{
492 socket_core_t *socket;
493 int accepted_id;
494
495 // find the socket
496 socket = socket_cores_find(local_sockets, socket_id);
497 if (!socket)
498 return ENOTSOCK;
499
500 // destroy all accepted sockets
501 while ((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0)
502 socket_destroy(packet_phone, accepted_id, local_sockets,
503 global_sockets, socket_release);
504
505 socket_destroy_core(packet_phone, socket, local_sockets, global_sockets,
506 socket_release);
507
508 return EOK;
509}
510
511/** Replies the packet or the packet queue data to the application via the
512 * socket.
513 *
514 * Uses the current message processing fibril.
515 *
516 * @param[in] packet The packet to be transfered.
517 * @param[out] length The total data length.
518 * @return EOK on success.
519 * @return EBADMEM if the length parameter is NULL.
520 * @return ENOMEM if there is not enough memory left.
521 * @return Other error codes as defined for the data_reply()
522 * function.
523 */
524int socket_reply_packets(packet_t *packet, size_t *length)
525{
526 packet_t *next_packet;
527 size_t fragments;
528 size_t *lengths;
529 size_t index;
530 int rc;
531
532 if (!length)
533 return EBADMEM;
534
535 next_packet = pq_next(packet);
536 if (!next_packet) {
537 // write all if only one fragment
538 rc = data_reply(packet_get_data(packet),
539 packet_get_data_length(packet));
540 if (rc != EOK)
541 return rc;
542 // store the total length
543 *length = packet_get_data_length(packet);
544 } else {
545 // count the packet fragments
546 fragments = 1;
547 next_packet = pq_next(packet);
548 while ((next_packet = pq_next(next_packet)))
549 ++fragments;
550
551 // compute and store the fragment lengths
552 lengths = (size_t *) malloc(sizeof(size_t) * fragments +
553 sizeof(size_t));
554 if (!lengths)
555 return ENOMEM;
556
557 lengths[0] = packet_get_data_length(packet);
558 lengths[fragments] = lengths[0];
559 next_packet = pq_next(packet);
560
561 for (index = 1; index < fragments; ++index) {
562 lengths[index] = packet_get_data_length(next_packet);
563 lengths[fragments] += lengths[index];
564 next_packet = pq_next(packet);
565 }
566
567 // write the fragment lengths
568 rc = data_reply(lengths, sizeof(int) * (fragments + 1));
569 if (rc != EOK) {
570 free(lengths);
571 return rc;
572 }
573 next_packet = packet;
574
575 // write the fragments
576 for (index = 0; index < fragments; ++index) {
577 rc = data_reply(packet_get_data(next_packet),
578 lengths[index]);
579 if (rc != EOK) {
580 free(lengths);
581 return rc;
582 }
583 next_packet = pq_next(next_packet);
584 }
585
586 // store the total length
587 *length = lengths[fragments];
588 free(lengths);
589 }
590
591 return EOK;
592}
593
594/** Finds the bound port socket.
595 *
596 * @param[in] global_sockets The global sockets to be searched.
597 * @param[in] port The port number.
598 * @param[in] key The socket key identifier.
599 * @param[in] key_length The socket key length.
600 * @return The found socket.
601 * @return NULL if no socket was found.
602 */
603socket_core_t *
604socket_port_find(socket_ports_t *global_sockets, int port, const uint8_t *key,
605 size_t key_length)
606{
607 socket_port_t *socket_port;
608 socket_core_t **socket_ref;
609
610 socket_port = socket_ports_find(global_sockets, port);
611 if (socket_port && (socket_port->count > 0)) {
612 socket_ref = socket_port_map_find(&socket_port->map, key,
613 key_length);
614 if (socket_ref)
615 return *socket_ref;
616 }
617
618 return NULL;
619}
620
621/** Releases the socket port.
622 *
623 * If the socket is bound the port entry is released.
624 * If there are no more port entries the port is release.
625 *
626 * @param[in] global_sockets The global sockets to be updated.
627 * @param[in] socket The socket to be unbound.
628 */
629void
630socket_port_release(socket_ports_t *global_sockets, socket_core_t *socket)
631{
632 socket_port_t *socket_port;
633 socket_core_t **socket_ref;
634
635 if (!socket->port)
636 return;
637
638 // find ports
639 socket_port = socket_ports_find(global_sockets, socket->port);
640 if (socket_port) {
641 // find the socket
642 socket_ref = socket_port_map_find(&socket_port->map,
643 socket->key, socket->key_length);
644
645 if (socket_ref) {
646 --socket_port->count;
647
648 // release if empty
649 if (socket_port->count <= 0) {
650 // destroy the map
651 socket_port_map_destroy(&socket_port->map);
652 // release the port
653 socket_ports_exclude(global_sockets,
654 socket->port);
655 } else {
656 // remove
657 socket_port_map_exclude(&socket_port->map,
658 socket->key, socket->key_length);
659 }
660 }
661 }
662
663 socket->port = 0;
664 socket->key = NULL;
665 socket->key_length = 0;
666}
667
668/** Adds the socket to an already bound port.
669 *
670 * @param[in] global_sockets The global sockets to be updated.
671 * @param[in] port The port number to be bound to.
672 * @param[in] socket The socket to be added.
673 * @param[in] key The socket key identifier.
674 * @param[in] key_length The socket key length.
675 * @return EOK on success.
676 * @return ENOENT if the port is not already used.
677 * @return Other error codes as defined for the
678 * socket_port_add_core() function.
679 */
680int
681socket_port_add(socket_ports_t *global_sockets, int port,
682 socket_core_t *socket, const uint8_t *key, size_t key_length)
683{
684 socket_port_t *socket_port;
685 int rc;
686
687 // find ports
688 socket_port = socket_ports_find(global_sockets, port);
689 if (!socket_port)
690 return ENOENT;
691
692 // add the socket
693 rc = socket_port_add_core(socket_port, socket, key, key_length);
694 if (rc != EOK)
695 return rc;
696
697 socket->port = port;
698 return EOK;
699}
700
701/** @}
702 */
Note: See TracBrowser for help on using the repository browser.