source: mainline/uspace/lib/c/generic/net/socket_client.c@ 77ad86c

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

cstyle (no change in functionality)

  • Property mode set to 100644
File size: 33.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 libc
30 * @{
31 */
32
33/** @file
34 * Socket application program interface (API) implementation.
35 * @see socket.h for more information.
36 * This is a part of the network application library.
37 */
38
39#include <assert.h>
40#include <async.h>
41#include <fibril_synch.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <errno.h>
45#include <task.h>
46#include <ns.h>
47#include <ipc/services.h>
48#include <ipc/socket.h>
49#include <net/in.h>
50#include <net/socket.h>
51#include <adt/dynamic_fifo.h>
52#include <adt/int_map.h>
53
54/** Initial received packet queue size. */
55#define SOCKET_INITIAL_RECEIVED_SIZE 4
56
57/** Maximum received packet queue size. */
58#define SOCKET_MAX_RECEIVED_SIZE 0
59
60/** Initial waiting sockets queue size. */
61#define SOCKET_INITIAL_ACCEPTED_SIZE 1
62
63/** Maximum waiting sockets queue size. */
64#define SOCKET_MAX_ACCEPTED_SIZE 0
65
66/**
67 * Maximum number of random attempts to find a new socket identifier before
68 * switching to the sequence.
69 */
70#define SOCKET_ID_TRIES 100
71
72/** Type definition of the socket specific data.
73 * @see socket
74 */
75typedef struct socket socket_t;
76
77/** Socket specific data.
78 *
79 * Each socket lock locks only its structure part and any number of them may be
80 * locked simultaneously.
81 */
82struct socket {
83 /** Socket identifier. */
84 int socket_id;
85 /** Parent module session. */
86 async_sess_t *sess;
87 /** Parent module service. */
88 services_t service;
89
90 /**
91 * Underlying protocol header size.
92 * Sending and receiving optimalization.
93 */
94 size_t header_size;
95
96 /** Packet data fragment size. Sending optimization. */
97 size_t data_fragment_size;
98
99 /**
100 * Sending safety lock.
101 * Locks the header_size and data_fragment_size attributes.
102 */
103 fibril_rwlock_t sending_lock;
104
105 /** Received packets queue. */
106 dyn_fifo_t received;
107
108 /**
109 * Received packets safety lock.
110 * Used for receiving and receive notifications.
111 * Locks the received attribute.
112 */
113 fibril_mutex_t receive_lock;
114
115 /** Received packets signaling. Signaled upon receive notification. */
116 fibril_condvar_t receive_signal;
117 /** Waiting sockets queue. */
118 dyn_fifo_t accepted;
119
120 /**
121 * Waiting sockets safety lock.
122 * Used for accepting and accept notifications.
123 * Locks the accepted attribute.
124 */
125 fibril_mutex_t accept_lock;
126
127 /** Waiting sockets signaling. Signaled upon accept notification. */
128 fibril_condvar_t accept_signal;
129
130 /**
131 * The number of blocked functions called.
132 * Used while waiting for the received packets or accepted sockets.
133 */
134 int blocked;
135};
136
137/** Sockets map.
138 * Maps socket identifiers to the socket specific data.
139 * @see int_map.h
140 */
141INT_MAP_DECLARE(sockets, socket_t);
142
143/** Socket client library global data. */
144static struct socket_client_globals {
145 /** TCP module session. */
146 async_sess_t *tcp_sess;
147 /** UDP module session. */
148 async_sess_t *udp_sess;
149
150 /** Active sockets. */
151 sockets_t *sockets;
152
153 /** Safety lock.
154 * Write lock is used only for adding or removing sockets.
155 * When locked for writing, no other socket locks need to be locked.
156 * When locked for reading, any other socket locks may be locked.
157 * No socket lock may be locked if this lock is unlocked.
158 */
159 fibril_rwlock_t lock;
160} socket_globals = {
161 .tcp_sess = NULL,
162 .udp_sess = NULL,
163 .sockets = NULL,
164 .lock = FIBRIL_RWLOCK_INITIALIZER(socket_globals.lock)
165};
166
167INT_MAP_IMPLEMENT(sockets, socket_t);
168
169/** Returns the active sockets.
170 *
171 * @return The active sockets.
172 */
173static sockets_t *socket_get_sockets(void)
174{
175 if (!socket_globals.sockets) {
176 socket_globals.sockets =
177 (sockets_t *) malloc(sizeof(sockets_t));
178 if (!socket_globals.sockets)
179 return NULL;
180
181 if (sockets_initialize(socket_globals.sockets) != EOK) {
182 free(socket_globals.sockets);
183 socket_globals.sockets = NULL;
184 }
185
186 srand(task_get_id());
187 }
188
189 return socket_globals.sockets;
190}
191
192/** Default thread for new connections.
193 *
194 * @param[in] iid The initial message identifier.
195 * @param[in] icall The initial message call structure.
196 * @param[in] arg Local argument.
197 */
198static void socket_connection(ipc_callid_t iid, ipc_call_t * icall, void *arg)
199{
200 ipc_callid_t callid;
201 ipc_call_t call;
202 socket_t *socket;
203 int rc;
204
205loop:
206 callid = async_get_call(&call);
207
208 switch (IPC_GET_IMETHOD(call)) {
209 case NET_SOCKET_RECEIVED:
210 case NET_SOCKET_ACCEPTED:
211 case NET_SOCKET_DATA_FRAGMENT_SIZE:
212 fibril_rwlock_read_lock(&socket_globals.lock);
213
214 /* Find the socket */
215 socket = sockets_find(socket_get_sockets(),
216 SOCKET_GET_SOCKET_ID(call));
217 if (!socket) {
218 rc = ENOTSOCK;
219 fibril_rwlock_read_unlock(&socket_globals.lock);
220 break;
221 }
222
223 switch (IPC_GET_IMETHOD(call)) {
224 case NET_SOCKET_RECEIVED:
225 fibril_mutex_lock(&socket->receive_lock);
226 /* Push the number of received packet fragments */
227 rc = dyn_fifo_push(&socket->received,
228 SOCKET_GET_DATA_FRAGMENTS(call),
229 SOCKET_MAX_RECEIVED_SIZE);
230 if (rc == EOK) {
231 /* Signal the received packet */
232 fibril_condvar_signal(&socket->receive_signal);
233 }
234 fibril_mutex_unlock(&socket->receive_lock);
235 break;
236
237 case NET_SOCKET_ACCEPTED:
238 /* Push the new socket identifier */
239 fibril_mutex_lock(&socket->accept_lock);
240 rc = dyn_fifo_push(&socket->accepted, 1,
241 SOCKET_MAX_ACCEPTED_SIZE);
242 if (rc == EOK) {
243 /* Signal the accepted socket */
244 fibril_condvar_signal(&socket->accept_signal);
245 }
246 fibril_mutex_unlock(&socket->accept_lock);
247 break;
248
249 default:
250 rc = ENOTSUP;
251 }
252
253 if ((SOCKET_GET_DATA_FRAGMENT_SIZE(call) > 0) &&
254 (SOCKET_GET_DATA_FRAGMENT_SIZE(call) !=
255 socket->data_fragment_size)) {
256 fibril_rwlock_write_lock(&socket->sending_lock);
257
258 /* Set the data fragment size */
259 socket->data_fragment_size =
260 SOCKET_GET_DATA_FRAGMENT_SIZE(call);
261
262 fibril_rwlock_write_unlock(&socket->sending_lock);
263 }
264
265 fibril_rwlock_read_unlock(&socket_globals.lock);
266 break;
267
268 default:
269 rc = ENOTSUP;
270 }
271
272 async_answer_0(callid, (sysarg_t) rc);
273 goto loop;
274}
275
276/** Return the TCP module session.
277 *
278 * Connect to the TCP module if necessary.
279 *
280 * @return The TCP module session.
281 *
282 */
283static async_sess_t *socket_get_tcp_sess(void)
284{
285 if (socket_globals.tcp_sess == NULL)
286 socket_globals.tcp_sess = service_bind(SERVICE_TCP,
287 0, 0, SERVICE_TCP, socket_connection);
288
289 return socket_globals.tcp_sess;
290}
291
292/** Return the UDP module session.
293 *
294 * Connect to the UDP module if necessary.
295 *
296 * @return The UDP module session.
297 *
298 */
299static async_sess_t *socket_get_udp_sess(void)
300{
301 if (socket_globals.udp_sess == NULL)
302 socket_globals.udp_sess = service_bind(SERVICE_UDP,
303 0, 0, SERVICE_UDP, socket_connection);
304
305 return socket_globals.udp_sess;
306}
307
308/** Tries to find a new free socket identifier.
309 *
310 * @return The new socket identifier.
311 * @return ELIMIT if there is no socket identifier available.
312 */
313static int socket_generate_new_id(void)
314{
315 sockets_t *sockets;
316 int socket_id = 0;
317 int count;
318
319 sockets = socket_get_sockets();
320 count = 0;
321
322 do {
323 if (count < SOCKET_ID_TRIES) {
324 socket_id = rand() % INT_MAX;
325 ++count;
326 } else if (count == SOCKET_ID_TRIES) {
327 socket_id = 1;
328 ++count;
329 /* Only this branch for last_id */
330 } else {
331 if (socket_id < INT_MAX) {
332 ++socket_id;
333 } else {
334 return ELIMIT;
335 }
336 }
337 } while (sockets_find(sockets, socket_id));
338
339 return socket_id;
340}
341
342/** Initializes a new socket specific data.
343 *
344 * @param[in,out] socket The socket to be initialized.
345 * @param[in] socket_id The new socket identifier.
346 * @param[in] sess The parent module session.
347 * @param[in] service The parent module service.
348 */
349static void socket_initialize(socket_t *socket, int socket_id,
350 async_sess_t *sess, services_t service)
351{
352 socket->socket_id = socket_id;
353 socket->sess = sess;
354 socket->service = service;
355 dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE);
356 dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
357 fibril_mutex_initialize(&socket->receive_lock);
358 fibril_condvar_initialize(&socket->receive_signal);
359 fibril_mutex_initialize(&socket->accept_lock);
360 fibril_condvar_initialize(&socket->accept_signal);
361 fibril_rwlock_initialize(&socket->sending_lock);
362}
363
364/** Creates a new socket.
365 *
366 * @param[in] domain The socket protocol family.
367 * @param[in] type Socket type.
368 * @param[in] protocol Socket protocol.
369 * @return The socket identifier on success.
370 * @return EPFNOTSUPPORT if the protocol family is not supported.
371 * @return ESOCKNOTSUPPORT if the socket type is not supported.
372 * @return EPROTONOSUPPORT if the protocol is not supported.
373 * @return ENOMEM if there is not enough memory left.
374 * @return ELIMIT if there was not a free socket identifier found
375 * this time.
376 * @return Other error codes as defined for the NET_SOCKET message.
377 * @return Other error codes as defined for the
378 * service_bind() function.
379 */
380int socket(int domain, int type, int protocol)
381{
382 socket_t *socket;
383 async_sess_t *sess;
384 int socket_id;
385 services_t service;
386 sysarg_t fragment_size;
387 sysarg_t header_size;
388 int rc;
389
390 /* Find the appropriate service */
391 switch (domain) {
392 case PF_INET:
393 switch (type) {
394 case SOCK_STREAM:
395 if (!protocol)
396 protocol = IPPROTO_TCP;
397
398 switch (protocol) {
399 case IPPROTO_TCP:
400 sess = socket_get_tcp_sess();
401 service = SERVICE_TCP;
402 break;
403 default:
404 return EPROTONOSUPPORT;
405 }
406
407 break;
408
409 case SOCK_DGRAM:
410 if (!protocol)
411 protocol = IPPROTO_UDP;
412
413 switch (protocol) {
414 case IPPROTO_UDP:
415 sess = socket_get_udp_sess();
416 service = SERVICE_UDP;
417 break;
418 default:
419 return EPROTONOSUPPORT;
420 }
421
422 break;
423
424 case SOCK_RAW:
425 default:
426 return ESOCKTNOSUPPORT;
427 }
428
429 break;
430
431 case PF_INET6:
432 default:
433 return EPFNOSUPPORT;
434 }
435
436 if (sess == NULL)
437 return ENOENT;
438
439 /* Create a new socket structure */
440 socket = (socket_t *) malloc(sizeof(socket_t));
441 if (!socket)
442 return ENOMEM;
443
444 bzero(socket, sizeof(*socket));
445 fibril_rwlock_write_lock(&socket_globals.lock);
446
447 /* Request a new socket */
448 socket_id = socket_generate_new_id();
449 if (socket_id <= 0) {
450 fibril_rwlock_write_unlock(&socket_globals.lock);
451 free(socket);
452 return socket_id;
453 }
454
455 async_exch_t *exch = async_exchange_begin(sess);
456 rc = (int) async_req_3_3(exch, NET_SOCKET, socket_id, 0, service, NULL,
457 &fragment_size, &header_size);
458 async_exchange_end(exch);
459
460 if (rc != EOK) {
461 fibril_rwlock_write_unlock(&socket_globals.lock);
462 free(socket);
463 return rc;
464 }
465
466 socket->data_fragment_size = (size_t) fragment_size;
467 socket->header_size = (size_t) header_size;
468
469 /* Finish the new socket initialization */
470 socket_initialize(socket, socket_id, sess, service);
471 /* Store the new socket */
472 rc = sockets_add(socket_get_sockets(), socket_id, socket);
473
474 fibril_rwlock_write_unlock(&socket_globals.lock);
475 if (rc < 0) {
476 dyn_fifo_destroy(&socket->received);
477 dyn_fifo_destroy(&socket->accepted);
478 free(socket);
479
480 exch = async_exchange_begin(sess);
481 async_msg_3(exch, NET_SOCKET_CLOSE, (sysarg_t) socket_id, 0,
482 service);
483 async_exchange_end(exch);
484
485 return rc;
486 }
487
488 return socket_id;
489}
490
491/** Sends message to the socket parent module with specified data.
492 *
493 * @param[in] socket_id Socket identifier.
494 * @param[in] message The action message.
495 * @param[in] arg2 The second message parameter.
496 * @param[in] data The data to be sent.
497 * @param[in] datalength The data length.
498 * @return EOK on success.
499 * @return ENOTSOCK if the socket is not found.
500 * @return EBADMEM if the data parameter is NULL.
501 * @return NO_DATA if the datalength parameter is zero (0).
502 * @return Other error codes as defined for the spcific message.
503 */
504static int
505socket_send_data(int socket_id, sysarg_t message, sysarg_t arg2,
506 const void *data, size_t datalength)
507{
508 socket_t *socket;
509 aid_t message_id;
510 sysarg_t result;
511
512 if (!data)
513 return EBADMEM;
514
515 if (!datalength)
516 return NO_DATA;
517
518 fibril_rwlock_read_lock(&socket_globals.lock);
519
520 /* Find the socket */
521 socket = sockets_find(socket_get_sockets(), socket_id);
522 if (!socket) {
523 fibril_rwlock_read_unlock(&socket_globals.lock);
524 return ENOTSOCK;
525 }
526
527 /* Request the message */
528 async_exch_t *exch = async_exchange_begin(socket->sess);
529 message_id = async_send_3(exch, message,
530 (sysarg_t) socket->socket_id, arg2, socket->service, NULL);
531 /* Send the address */
532 async_data_write_start(exch, data, datalength);
533 async_exchange_end(exch);
534
535 fibril_rwlock_read_unlock(&socket_globals.lock);
536 async_wait_for(message_id, &result);
537 return (int) result;
538}
539
540/** Binds the socket to a port address.
541 *
542 * @param[in] socket_id Socket identifier.
543 * @param[in] my_addr The port address.
544 * @param[in] addrlen The address length.
545 * @return EOK on success.
546 * @return ENOTSOCK if the socket is not found.
547 * @return EBADMEM if the my_addr parameter is NULL.
548 * @return NO_DATA if the addlen parameter is zero.
549 * @return Other error codes as defined for the NET_SOCKET_BIND
550 * message.
551 */
552int bind(int socket_id, const struct sockaddr * my_addr, socklen_t addrlen)
553{
554 if (addrlen <= 0)
555 return EINVAL;
556
557 /* Send the address */
558 return socket_send_data(socket_id, NET_SOCKET_BIND, 0, my_addr,
559 (size_t) addrlen);
560}
561
562/** Sets the number of connections waiting to be accepted.
563 *
564 * @param[in] socket_id Socket identifier.
565 * @param[in] backlog The maximum number of waiting sockets to be accepted.
566 * @return EOK on success.
567 * @return EINVAL if the backlog parameter is not positive (<=0).
568 * @return ENOTSOCK if the socket is not found.
569 * @return Other error codes as defined for the NET_SOCKET_LISTEN
570 * message.
571 */
572int listen(int socket_id, int backlog)
573{
574 socket_t *socket;
575 int result;
576
577 if (backlog <= 0)
578 return EINVAL;
579
580 fibril_rwlock_read_lock(&socket_globals.lock);
581
582 /* Find the socket */
583 socket = sockets_find(socket_get_sockets(), socket_id);
584 if (!socket) {
585 fibril_rwlock_read_unlock(&socket_globals.lock);
586 return ENOTSOCK;
587 }
588
589 /* Request listen backlog change */
590 async_exch_t *exch = async_exchange_begin(socket->sess);
591 result = (int) async_req_3_0(exch, NET_SOCKET_LISTEN,
592 (sysarg_t) socket->socket_id, (sysarg_t) backlog, socket->service);
593 async_exchange_end(exch);
594
595 fibril_rwlock_read_unlock(&socket_globals.lock);
596 return result;
597}
598
599/** Accepts waiting socket.
600 *
601 * Blocks until such a socket exists.
602 *
603 * @param[in] socket_id Socket identifier.
604 * @param[out] cliaddr The remote client address.
605 * @param[in] addrlen The address length.
606 * @return EOK on success.
607 * @return EBADMEM if the cliaddr or addrlen parameter is NULL.
608 * @return EINVAL if the backlog parameter is not positive (<=0).
609 * @return ENOTSOCK if the socket is not found.
610 * @return Other error codes as defined for the NET_SOCKET_ACCEPT
611 * message.
612 */
613int accept(int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen)
614{
615 socket_t *socket;
616 socket_t *new_socket;
617 aid_t message_id;
618 sysarg_t ipc_result;
619 int result;
620 ipc_call_t answer;
621
622 if (!cliaddr || !addrlen)
623 return EBADMEM;
624
625 fibril_rwlock_write_lock(&socket_globals.lock);
626
627 /* Find the socket */
628 socket = sockets_find(socket_get_sockets(), socket_id);
629 if (!socket) {
630 fibril_rwlock_write_unlock(&socket_globals.lock);
631 return ENOTSOCK;
632 }
633
634 fibril_mutex_lock(&socket->accept_lock);
635
636 /* Wait for an accepted socket */
637 ++ socket->blocked;
638 while (dyn_fifo_value(&socket->accepted) <= 0) {
639 fibril_rwlock_write_unlock(&socket_globals.lock);
640 fibril_condvar_wait(&socket->accept_signal, &socket->accept_lock);
641 /* Drop the accept lock to avoid deadlock */
642 fibril_mutex_unlock(&socket->accept_lock);
643 fibril_rwlock_write_lock(&socket_globals.lock);
644 fibril_mutex_lock(&socket->accept_lock);
645 }
646 -- socket->blocked;
647
648 /* Create a new socket */
649 new_socket = (socket_t *) malloc(sizeof(socket_t));
650 if (!new_socket) {
651 fibril_mutex_unlock(&socket->accept_lock);
652 fibril_rwlock_write_unlock(&socket_globals.lock);
653 return ENOMEM;
654 }
655 bzero(new_socket, sizeof(*new_socket));
656 socket_id = socket_generate_new_id();
657 if (socket_id <= 0) {
658 fibril_mutex_unlock(&socket->accept_lock);
659 fibril_rwlock_write_unlock(&socket_globals.lock);
660 free(new_socket);
661 return socket_id;
662 }
663 socket_initialize(new_socket, socket_id, socket->sess,
664 socket->service);
665 result = sockets_add(socket_get_sockets(), new_socket->socket_id,
666 new_socket);
667 if (result < 0) {
668 fibril_mutex_unlock(&socket->accept_lock);
669 fibril_rwlock_write_unlock(&socket_globals.lock);
670 free(new_socket);
671 return result;
672 }
673
674 /* Request accept */
675 async_exch_t *exch = async_exchange_begin(socket->sess);
676 message_id = async_send_5(exch, NET_SOCKET_ACCEPT,
677 (sysarg_t) socket->socket_id, 0, socket->service, 0,
678 new_socket->socket_id, &answer);
679
680 /* Read address */
681 async_data_read_start(exch, cliaddr, *addrlen);
682 async_exchange_end(exch);
683
684 fibril_rwlock_write_unlock(&socket_globals.lock);
685 async_wait_for(message_id, &ipc_result);
686 result = (int) ipc_result;
687 if (result > 0) {
688 if (result != socket_id)
689 result = EINVAL;
690
691 /* Dequeue the accepted socket if successful */
692 dyn_fifo_pop(&socket->accepted);
693 /* Set address length */
694 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
695 new_socket->data_fragment_size =
696 SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
697 } else if (result == ENOTSOCK) {
698 /* Empty the queue if no accepted sockets */
699 while (dyn_fifo_pop(&socket->accepted) > 0)
700 ;
701 }
702
703 fibril_mutex_unlock(&socket->accept_lock);
704 return result;
705}
706
707/** Connects socket to the remote server.
708 *
709 * @param[in] socket_id Socket identifier.
710 * @param[in] serv_addr The remote server address.
711 * @param[in] addrlen The address length.
712 * @return EOK on success.
713 * @return EBADMEM if the serv_addr parameter is NULL.
714 * @return NO_DATA if the addlen parameter is zero.
715 * @return ENOTSOCK if the socket is not found.
716 * @return Other error codes as defined for the NET_SOCKET_CONNECT
717 * message.
718 */
719int connect(int socket_id, const struct sockaddr *serv_addr, socklen_t addrlen)
720{
721 if (!serv_addr)
722 return EDESTADDRREQ;
723
724 if (!addrlen)
725 return EDESTADDRREQ;
726
727 /* Send the address */
728 return socket_send_data(socket_id, NET_SOCKET_CONNECT, 0, serv_addr,
729 addrlen);
730}
731
732/** Clears and destroys the socket.
733 *
734 * @param[in] socket The socket to be destroyed.
735 */
736static void socket_destroy(socket_t *socket)
737{
738 int accepted_id;
739
740 /* Destroy all accepted sockets */
741 while ((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0)
742 socket_destroy(sockets_find(socket_get_sockets(), accepted_id));
743
744 dyn_fifo_destroy(&socket->received);
745 dyn_fifo_destroy(&socket->accepted);
746 sockets_exclude(socket_get_sockets(), socket->socket_id, free);
747}
748
749/** Closes the socket.
750 *
751 * @param[in] socket_id Socket identifier.
752 * @return EOK on success.
753 * @return ENOTSOCK if the socket is not found.
754 * @return EINPROGRESS if there is another blocking function in
755 * progress.
756 * @return Other error codes as defined for the NET_SOCKET_CLOSE
757 * message.
758 */
759int closesocket(int socket_id)
760{
761 socket_t *socket;
762 int rc;
763
764 fibril_rwlock_write_lock(&socket_globals.lock);
765
766 socket = sockets_find(socket_get_sockets(), socket_id);
767 if (!socket) {
768 fibril_rwlock_write_unlock(&socket_globals.lock);
769 return ENOTSOCK;
770 }
771 if (socket->blocked) {
772 fibril_rwlock_write_unlock(&socket_globals.lock);
773 return EINPROGRESS;
774 }
775
776 /* Request close */
777 async_exch_t *exch = async_exchange_begin(socket->sess);
778 rc = (int) async_req_3_0(exch, NET_SOCKET_CLOSE,
779 (sysarg_t) socket->socket_id, 0, socket->service);
780 async_exchange_end(exch);
781
782 if (rc != EOK) {
783 fibril_rwlock_write_unlock(&socket_globals.lock);
784 return rc;
785 }
786 /* Free the socket structure */
787 socket_destroy(socket);
788
789 fibril_rwlock_write_unlock(&socket_globals.lock);
790 return EOK;
791}
792
793/** Sends data via the socket to the remote address.
794 *
795 * Binds the socket to a free port if not already connected/bound.
796 *
797 * @param[in] message The action message.
798 * @param[in] socket_id Socket identifier.
799 * @param[in] data The data to be sent.
800 * @param[in] datalength The data length.
801 * @param[in] flags Various send flags.
802 * @param[in] toaddr The destination address. May be NULL for connected
803 * sockets.
804 * @param[in] addrlen The address length. Used only if toaddr is not NULL.
805 * @return EOK on success.
806 * @return ENOTSOCK if the socket is not found.
807 * @return EBADMEM if the data or toaddr parameter is NULL.
808 * @return NO_DATA if the datalength or the addrlen parameter is
809 * zero (0).
810 * @return Other error codes as defined for the NET_SOCKET_SENDTO
811 * message.
812 */
813static int
814sendto_core(sysarg_t message, int socket_id, const void *data,
815 size_t datalength, int flags, const struct sockaddr *toaddr,
816 socklen_t addrlen)
817{
818 socket_t *socket;
819 aid_t message_id;
820 sysarg_t result;
821 size_t fragments;
822 ipc_call_t answer;
823
824 if (!data)
825 return EBADMEM;
826
827 if (!datalength)
828 return NO_DATA;
829
830 fibril_rwlock_read_lock(&socket_globals.lock);
831
832 /* Find socket */
833 socket = sockets_find(socket_get_sockets(), socket_id);
834 if (!socket) {
835 fibril_rwlock_read_unlock(&socket_globals.lock);
836 return ENOTSOCK;
837 }
838
839 fibril_rwlock_read_lock(&socket->sending_lock);
840
841 /* Compute data fragment count */
842 if (socket->data_fragment_size > 0) {
843 fragments = (datalength + socket->header_size) /
844 socket->data_fragment_size;
845 if ((datalength + socket->header_size) %
846 socket->data_fragment_size)
847 ++fragments;
848 } else {
849 fragments = 1;
850 }
851
852 /* Request send */
853 async_exch_t *exch = async_exchange_begin(socket->sess);
854
855 message_id = async_send_5(exch, message,
856 (sysarg_t) socket->socket_id,
857 (fragments == 1 ? datalength : socket->data_fragment_size),
858 socket->service, (sysarg_t) flags, fragments, &answer);
859
860 /* Send the address if given */
861 if (!toaddr ||
862 (async_data_write_start(exch, toaddr, addrlen) == EOK)) {
863 if (fragments == 1) {
864 /* Send all if only one fragment */
865 async_data_write_start(exch, data, datalength);
866 } else {
867 /* Send the first fragment */
868 async_data_write_start(exch, data,
869 socket->data_fragment_size - socket->header_size);
870 data = ((const uint8_t *) data) +
871 socket->data_fragment_size - socket->header_size;
872
873 /* Send the middle fragments */
874 while (--fragments > 1) {
875 async_data_write_start(exch, data,
876 socket->data_fragment_size);
877 data = ((const uint8_t *) data) +
878 socket->data_fragment_size;
879 }
880
881 /* Send the last fragment */
882 async_data_write_start(exch, data,
883 (datalength + socket->header_size) %
884 socket->data_fragment_size);
885 }
886 }
887
888 async_exchange_end(exch);
889
890 async_wait_for(message_id, &result);
891
892 if ((SOCKET_GET_DATA_FRAGMENT_SIZE(answer) > 0) &&
893 (SOCKET_GET_DATA_FRAGMENT_SIZE(answer) !=
894 socket->data_fragment_size)) {
895 /* Set the data fragment size */
896 socket->data_fragment_size =
897 SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
898 }
899
900 fibril_rwlock_read_unlock(&socket->sending_lock);
901 fibril_rwlock_read_unlock(&socket_globals.lock);
902 return (int) result;
903}
904
905/** Sends data via the socket.
906 *
907 * @param[in] socket_id Socket identifier.
908 * @param[in] data The data to be sent.
909 * @param[in] datalength The data length.
910 * @param[in] flags Various send flags.
911 * @return EOK on success.
912 * @return ENOTSOCK if the socket is not found.
913 * @return EBADMEM if the data parameter is NULL.
914 * @return NO_DATA if the datalength parameter is zero.
915 * @return Other error codes as defined for the NET_SOCKET_SEND
916 * message.
917 */
918int send(int socket_id, void *data, size_t datalength, int flags)
919{
920 /* Without the address */
921 return sendto_core(NET_SOCKET_SEND, socket_id, data, datalength, flags,
922 NULL, 0);
923}
924
925/** Sends data via the socket to the remote address.
926 *
927 * Binds the socket to a free port if not already connected/bound.
928 *
929 * @param[in] socket_id Socket identifier.
930 * @param[in] data The data to be sent.
931 * @param[in] datalength The data length.
932 * @param[in] flags Various send flags.
933 * @param[in] toaddr The destination address.
934 * @param[in] addrlen The address length.
935 * @return EOK on success.
936 * @return ENOTSOCK if the socket is not found.
937 * @return EBADMEM if the data or toaddr parameter is NULL.
938 * @return NO_DATA if the datalength or the addrlen parameter is
939 * zero.
940 * @return Other error codes as defined for the NET_SOCKET_SENDTO
941 * message.
942 */
943int
944sendto(int socket_id, const void *data, size_t datalength, int flags,
945 const struct sockaddr *toaddr, socklen_t addrlen)
946{
947 if (!toaddr)
948 return EDESTADDRREQ;
949
950 if (!addrlen)
951 return EDESTADDRREQ;
952
953 /* With the address */
954 return sendto_core(NET_SOCKET_SENDTO, socket_id, data, datalength,
955 flags, toaddr, addrlen);
956}
957
958/** Receives data via the socket.
959 *
960 * @param[in] message The action message.
961 * @param[in] socket_id Socket identifier.
962 * @param[out] data The data buffer to be filled.
963 * @param[in] datalength The data length.
964 * @param[in] flags Various receive flags.
965 * @param[out] fromaddr The source address. May be NULL for connected sockets.
966 * @param[in,out] addrlen The address length. The maximum address length is
967 * read. The actual address length is set. Used only if
968 * fromaddr is not NULL.
969 * @return Positive received message size in bytes on success.
970 * @return Zero if no more data (other side closed the connection).
971 * @return ENOTSOCK if the socket is not found.
972 * @return EBADMEM if the data parameter is NULL.
973 * @return NO_DATA if the datalength or addrlen parameter is zero.
974 * @return Other error codes as defined for the spcific message.
975 */
976static ssize_t
977recvfrom_core(sysarg_t message, int socket_id, void *data, size_t datalength,
978 int flags, struct sockaddr *fromaddr, socklen_t *addrlen)
979{
980 socket_t *socket;
981 aid_t message_id;
982 sysarg_t ipc_result;
983 int result;
984 size_t fragments;
985 size_t *lengths;
986 size_t index;
987 ipc_call_t answer;
988 ssize_t retval;
989
990 if (!data)
991 return EBADMEM;
992
993 if (!datalength)
994 return NO_DATA;
995
996 if (fromaddr && !addrlen)
997 return EINVAL;
998
999 fibril_rwlock_read_lock(&socket_globals.lock);
1000
1001 /* Find the socket */
1002 socket = sockets_find(socket_get_sockets(), socket_id);
1003 if (!socket) {
1004 fibril_rwlock_read_unlock(&socket_globals.lock);
1005 return ENOTSOCK;
1006 }
1007
1008 fibril_mutex_lock(&socket->receive_lock);
1009 /* Wait for a received packet */
1010 ++socket->blocked;
1011 while ((result = dyn_fifo_value(&socket->received)) < 0) {
1012 fibril_rwlock_read_unlock(&socket_globals.lock);
1013 fibril_condvar_wait(&socket->receive_signal,
1014 &socket->receive_lock);
1015
1016 /* Drop the receive lock to avoid deadlock */
1017 fibril_mutex_unlock(&socket->receive_lock);
1018 fibril_rwlock_read_lock(&socket_globals.lock);
1019 fibril_mutex_lock(&socket->receive_lock);
1020 }
1021 --socket->blocked;
1022 fragments = (size_t) result;
1023
1024 if (fragments == 0) {
1025 /* No more data, other side has closed the connection. */
1026 fibril_mutex_unlock(&socket->receive_lock);
1027 fibril_rwlock_read_unlock(&socket_globals.lock);
1028 return 0;
1029 }
1030
1031 async_exch_t *exch = async_exchange_begin(socket->sess);
1032
1033 /* Prepare lengths if more fragments */
1034 if (fragments > 1) {
1035 lengths = (size_t *) malloc(sizeof(size_t) * fragments +
1036 sizeof(size_t));
1037 if (!lengths) {
1038 fibril_mutex_unlock(&socket->receive_lock);
1039 fibril_rwlock_read_unlock(&socket_globals.lock);
1040 return ENOMEM;
1041 }
1042
1043 /* Request packet data */
1044 message_id = async_send_4(exch, message,
1045 (sysarg_t) socket->socket_id, 0, socket->service,
1046 (sysarg_t) flags, &answer);
1047
1048 /* Read the address if desired */
1049 if(!fromaddr ||
1050 (async_data_read_start(exch, fromaddr,
1051 *addrlen) == EOK)) {
1052 /* Read the fragment lengths */
1053 if (async_data_read_start(exch, lengths,
1054 sizeof(int) * (fragments + 1)) == EOK) {
1055 if (lengths[fragments] <= datalength) {
1056
1057 /* Read all fragments if long enough */
1058 for (index = 0; index < fragments;
1059 ++index) {
1060 async_data_read_start(exch, data,
1061 lengths[index]);
1062 data = ((uint8_t *) data) +
1063 lengths[index];
1064 }
1065 }
1066 }
1067 }
1068
1069 free(lengths);
1070 } else { /* fragments == 1 */
1071 /* Request packet data */
1072 message_id = async_send_4(exch, message,
1073 (sysarg_t) socket->socket_id, 0, socket->service,
1074 (sysarg_t) flags, &answer);
1075
1076 /* Read the address if desired */
1077 if (!fromaddr ||
1078 (async_data_read_start(exch, fromaddr, *addrlen) == EOK)) {
1079 /* Read all if only one fragment */
1080 async_data_read_start(exch, data, datalength);
1081 }
1082 }
1083
1084 async_exchange_end(exch);
1085
1086 async_wait_for(message_id, &ipc_result);
1087 result = (int) ipc_result;
1088 if (result == EOK) {
1089 /* Dequeue the received packet */
1090 dyn_fifo_pop(&socket->received);
1091 /* Return read data length */
1092 retval = SOCKET_GET_READ_DATA_LENGTH(answer);
1093 /* Set address length */
1094 if (fromaddr && addrlen)
1095 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
1096 } else {
1097 retval = (ssize_t) result;
1098 }
1099
1100 fibril_mutex_unlock(&socket->receive_lock);
1101 fibril_rwlock_read_unlock(&socket_globals.lock);
1102 return retval;
1103}
1104
1105/** Receives data via the socket.
1106 *
1107 * @param[in] socket_id Socket identifier.
1108 * @param[out] data The data buffer to be filled.
1109 * @param[in] datalength The data length.
1110 * @param[in] flags Various receive flags.
1111 * @return Positive received message size in bytes on success.
1112 * @return Zero if no more data (other side closed the connection).
1113 * @return ENOTSOCK if the socket is not found.
1114 * @return EBADMEM if the data parameter is NULL.
1115 * @return NO_DATA if the datalength parameter is zero.
1116 * @return Other error codes as defined for the NET_SOCKET_RECV
1117 * message.
1118 */
1119ssize_t recv(int socket_id, void *data, size_t datalength, int flags)
1120{
1121 /* Without the address */
1122 return recvfrom_core(NET_SOCKET_RECV, socket_id, data, datalength,
1123 flags, NULL, NULL);
1124}
1125
1126/** Receives data via the socket.
1127 *
1128 * @param[in] socket_id Socket identifier.
1129 * @param[out] data The data buffer to be filled.
1130 * @param[in] datalength The data length.
1131 * @param[in] flags Various receive flags.
1132 * @param[out] fromaddr The source address.
1133 * @param[in,out] addrlen The address length. The maximum address length is
1134 * read. The actual address length is set.
1135 * @return Positive received message size in bytes on success.
1136 * @return Zero if no more data (other side closed the connection).
1137 * @return ENOTSOCK if the socket is not found.
1138 * @return EBADMEM if the data or fromaddr parameter is NULL.
1139 * @return NO_DATA if the datalength or addrlen parameter is zero.
1140 * @return Other error codes as defined for the NET_SOCKET_RECVFROM
1141 * message.
1142 */
1143ssize_t
1144recvfrom(int socket_id, void *data, size_t datalength, int flags,
1145 struct sockaddr *fromaddr, socklen_t *addrlen)
1146{
1147 if (!fromaddr)
1148 return EBADMEM;
1149
1150 if (!addrlen)
1151 return NO_DATA;
1152
1153 /* With the address */
1154 return recvfrom_core(NET_SOCKET_RECVFROM, socket_id, data, datalength,
1155 flags, fromaddr, addrlen);
1156}
1157
1158/** Gets socket option.
1159 *
1160 * @param[in] socket_id Socket identifier.
1161 * @param[in] level The socket options level.
1162 * @param[in] optname The socket option to be get.
1163 * @param[out] value The value buffer to be filled.
1164 * @param[in,out] optlen The value buffer length. The maximum length is read.
1165 * The actual length is set.
1166 * @return EOK on success.
1167 * @return ENOTSOCK if the socket is not found.
1168 * @return EBADMEM if the value or optlen parameter is NULL.
1169 * @return NO_DATA if the optlen parameter is zero.
1170 * @return Other error codes as defined for the
1171 * NET_SOCKET_GETSOCKOPT message.
1172 */
1173int
1174getsockopt(int socket_id, int level, int optname, void *value, size_t *optlen)
1175{
1176 socket_t *socket;
1177 aid_t message_id;
1178 sysarg_t result;
1179
1180 if (!value || !optlen)
1181 return EBADMEM;
1182
1183 if (!*optlen)
1184 return NO_DATA;
1185
1186 fibril_rwlock_read_lock(&socket_globals.lock);
1187
1188 /* Find the socket */
1189 socket = sockets_find(socket_get_sockets(), socket_id);
1190 if (!socket) {
1191 fibril_rwlock_read_unlock(&socket_globals.lock);
1192 return ENOTSOCK;
1193 }
1194
1195 /* Request option value */
1196 async_exch_t *exch = async_exchange_begin(socket->sess);
1197
1198 message_id = async_send_3(exch, NET_SOCKET_GETSOCKOPT,
1199 (sysarg_t) socket->socket_id, (sysarg_t) optname, socket->service,
1200 NULL);
1201
1202 /* Read the length */
1203 if (async_data_read_start(exch, optlen,
1204 sizeof(*optlen)) == EOK) {
1205 /* Read the value */
1206 async_data_read_start(exch, value, *optlen);
1207 }
1208
1209 async_exchange_end(exch);
1210
1211 fibril_rwlock_read_unlock(&socket_globals.lock);
1212 async_wait_for(message_id, &result);
1213 return (int) result;
1214}
1215
1216/** Sets socket option.
1217 *
1218 * @param[in] socket_id Socket identifier.
1219 * @param[in] level The socket options level.
1220 * @param[in] optname The socket option to be set.
1221 * @param[in] value The value to be set.
1222 * @param[in] optlen The value length.
1223 * @return EOK on success.
1224 * @return ENOTSOCK if the socket is not found.
1225 * @return EBADMEM if the value parameter is NULL.
1226 * @return NO_DATA if the optlen parameter is zero.
1227 * @return Other error codes as defined for the
1228 * NET_SOCKET_SETSOCKOPT message.
1229 */
1230int
1231setsockopt(int socket_id, int level, int optname, const void *value,
1232 size_t optlen)
1233{
1234 /* Send the value */
1235 return socket_send_data(socket_id, NET_SOCKET_SETSOCKOPT,
1236 (sysarg_t) optname, value, optlen);
1237}
1238
1239/** @}
1240 */
Note: See TracBrowser for help on using the repository browser.