source: mainline/uspace/lib/net/generic/socket_core.c@ b01f878

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b01f878 was 137f8aa0, checked in by Jakub Jermar <jakub@…>, 15 years ago

Cleanup socket_core.[ch].

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