source: mainline/uspace/lib/c/generic/net/socket_client.c@ 32fef47

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

networking stack: convert to the new async framework

  • 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 <ipc/services.h>
47#include <ipc/socket.h>
48#include <net/modules.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 = bind_service(SERVICE_TCP,
287 0, 0, SERVICE_TCP, socket_connection);
288 }
289
290 return socket_globals.tcp_sess;
291}
292
293/** Return the UDP module session.
294 *
295 * Connect to the UDP module if necessary.
296 *
297 * @return The UDP module session.
298 *
299 */
300static async_sess_t *socket_get_udp_sess(void)
301{
302 if (socket_globals.udp_sess == NULL) {
303 socket_globals.udp_sess = bind_service(SERVICE_UDP,
304 0, 0, SERVICE_UDP, socket_connection);
305 }
306
307 return socket_globals.udp_sess;
308}
309
310/** Tries to find a new free socket identifier.
311 *
312 * @return The new socket identifier.
313 * @return ELIMIT if there is no socket identifier available.
314 */
315static int socket_generate_new_id(void)
316{
317 sockets_t *sockets;
318 int socket_id = 0;
319 int count;
320
321 sockets = socket_get_sockets();
322 count = 0;
323
324 do {
325 if (count < SOCKET_ID_TRIES) {
326 socket_id = rand() % INT_MAX;
327 ++count;
328 } else if (count == SOCKET_ID_TRIES) {
329 socket_id = 1;
330 ++count;
331 /* Only this branch for last_id */
332 } else {
333 if (socket_id < INT_MAX) {
334 ++socket_id;
335 } else {
336 return ELIMIT;
337 }
338 }
339 } while (sockets_find(sockets, socket_id));
340
341 return socket_id;
342}
343
344/** Initializes a new socket specific data.
345 *
346 * @param[in,out] socket The socket to be initialized.
347 * @param[in] socket_id The new socket identifier.
348 * @param[in] sess The parent module session.
349 * @param[in] service The parent module service.
350 */
351static void socket_initialize(socket_t *socket, int socket_id,
352 async_sess_t *sess, services_t service)
353{
354 socket->socket_id = socket_id;
355 socket->sess = sess;
356 socket->service = service;
357 dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE);
358 dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
359 fibril_mutex_initialize(&socket->receive_lock);
360 fibril_condvar_initialize(&socket->receive_signal);
361 fibril_mutex_initialize(&socket->accept_lock);
362 fibril_condvar_initialize(&socket->accept_signal);
363 fibril_rwlock_initialize(&socket->sending_lock);
364}
365
366/** Creates a new socket.
367 *
368 * @param[in] domain The socket protocol family.
369 * @param[in] type Socket type.
370 * @param[in] protocol Socket protocol.
371 * @return The socket identifier on success.
372 * @return EPFNOTSUPPORT if the protocol family is not supported.
373 * @return ESOCKNOTSUPPORT if the socket type is not supported.
374 * @return EPROTONOSUPPORT if the protocol is not supported.
375 * @return ENOMEM if there is not enough memory left.
376 * @return ELIMIT if there was not a free socket identifier found
377 * this time.
378 * @return Other error codes as defined for the NET_SOCKET message.
379 * @return Other error codes as defined for the
380 * bind_service() function.
381 */
382int socket(int domain, int type, int protocol)
383{
384 socket_t *socket;
385 async_sess_t *sess;
386 int socket_id;
387 services_t service;
388 sysarg_t fragment_size;
389 sysarg_t header_size;
390 int rc;
391
392 /* Find the appropriate service */
393 switch (domain) {
394 case PF_INET:
395 switch (type) {
396 case SOCK_STREAM:
397 if (!protocol)
398 protocol = IPPROTO_TCP;
399
400 switch (protocol) {
401 case IPPROTO_TCP:
402 sess = socket_get_tcp_sess();
403 service = SERVICE_TCP;
404 break;
405 default:
406 return EPROTONOSUPPORT;
407 }
408
409 break;
410
411 case SOCK_DGRAM:
412 if (!protocol)
413 protocol = IPPROTO_UDP;
414
415 switch (protocol) {
416 case IPPROTO_UDP:
417 sess = socket_get_udp_sess();
418 service = SERVICE_UDP;
419 break;
420 default:
421 return EPROTONOSUPPORT;
422 }
423
424 break;
425
426 case SOCK_RAW:
427 default:
428 return ESOCKTNOSUPPORT;
429 }
430
431 break;
432
433 case PF_INET6:
434 default:
435 return EPFNOSUPPORT;
436 }
437
438 if (sess == NULL)
439 return ENOENT;
440
441 /* Create a new socket structure */
442 socket = (socket_t *) malloc(sizeof(socket_t));
443 if (!socket)
444 return ENOMEM;
445
446 bzero(socket, sizeof(*socket));
447 fibril_rwlock_write_lock(&socket_globals.lock);
448
449 /* Request a new socket */
450 socket_id = socket_generate_new_id();
451 if (socket_id <= 0) {
452 fibril_rwlock_write_unlock(&socket_globals.lock);
453 free(socket);
454 return socket_id;
455 }
456
457 async_exch_t *exch = async_exchange_begin(sess);
458 rc = (int) async_req_3_3(exch, NET_SOCKET, socket_id, 0, service, NULL,
459 &fragment_size, &header_size);
460 async_exchange_end(exch);
461
462 if (rc != EOK) {
463 fibril_rwlock_write_unlock(&socket_globals.lock);
464 free(socket);
465 return rc;
466 }
467
468 socket->data_fragment_size = (size_t) fragment_size;
469 socket->header_size = (size_t) header_size;
470
471 /* Finish the new socket initialization */
472 socket_initialize(socket, socket_id, sess, service);
473 /* Store the new socket */
474 rc = sockets_add(socket_get_sockets(), socket_id, socket);
475
476 fibril_rwlock_write_unlock(&socket_globals.lock);
477 if (rc < 0) {
478 dyn_fifo_destroy(&socket->received);
479 dyn_fifo_destroy(&socket->accepted);
480 free(socket);
481
482 exch = async_exchange_begin(sess);
483 async_msg_3(exch, NET_SOCKET_CLOSE, (sysarg_t) socket_id, 0,
484 service);
485 async_exchange_end(exch);
486
487 return rc;
488 }
489
490 return socket_id;
491}
492
493/** Sends message to the socket parent module with specified data.
494 *
495 * @param[in] socket_id Socket identifier.
496 * @param[in] message The action message.
497 * @param[in] arg2 The second message parameter.
498 * @param[in] data The data to be sent.
499 * @param[in] datalength The data length.
500 * @return EOK on success.
501 * @return ENOTSOCK if the socket is not found.
502 * @return EBADMEM if the data parameter is NULL.
503 * @return NO_DATA if the datalength parameter is zero (0).
504 * @return Other error codes as defined for the spcific message.
505 */
506static int
507socket_send_data(int socket_id, sysarg_t message, sysarg_t arg2,
508 const void *data, size_t datalength)
509{
510 socket_t *socket;
511 aid_t message_id;
512 sysarg_t result;
513
514 if (!data)
515 return EBADMEM;
516
517 if (!datalength)
518 return NO_DATA;
519
520 fibril_rwlock_read_lock(&socket_globals.lock);
521
522 /* Find the socket */
523 socket = sockets_find(socket_get_sockets(), socket_id);
524 if (!socket) {
525 fibril_rwlock_read_unlock(&socket_globals.lock);
526 return ENOTSOCK;
527 }
528
529 /* Request the message */
530 async_exch_t *exch = async_exchange_begin(socket->sess);
531 message_id = async_send_3(exch, message,
532 (sysarg_t) socket->socket_id, arg2, socket->service, NULL);
533 /* Send the address */
534 async_data_write_start(exch, data, datalength);
535 async_exchange_end(exch);
536
537 fibril_rwlock_read_unlock(&socket_globals.lock);
538 async_wait_for(message_id, &result);
539 return (int) result;
540}
541
542/** Binds the socket to a port address.
543 *
544 * @param[in] socket_id Socket identifier.
545 * @param[in] my_addr The port address.
546 * @param[in] addrlen The address length.
547 * @return EOK on success.
548 * @return ENOTSOCK if the socket is not found.
549 * @return EBADMEM if the my_addr parameter is NULL.
550 * @return NO_DATA if the addlen parameter is zero.
551 * @return Other error codes as defined for the NET_SOCKET_BIND
552 * message.
553 */
554int bind(int socket_id, const struct sockaddr * my_addr, socklen_t addrlen)
555{
556 if (addrlen <= 0)
557 return EINVAL;
558
559 /* Send the address */
560 return socket_send_data(socket_id, NET_SOCKET_BIND, 0, my_addr,
561 (size_t) addrlen);
562}
563
564/** Sets the number of connections waiting to be accepted.
565 *
566 * @param[in] socket_id Socket identifier.
567 * @param[in] backlog The maximum number of waiting sockets to be accepted.
568 * @return EOK on success.
569 * @return EINVAL if the backlog parameter is not positive (<=0).
570 * @return ENOTSOCK if the socket is not found.
571 * @return Other error codes as defined for the NET_SOCKET_LISTEN
572 * message.
573 */
574int listen(int socket_id, int backlog)
575{
576 socket_t *socket;
577 int result;
578
579 if (backlog <= 0)
580 return EINVAL;
581
582 fibril_rwlock_read_lock(&socket_globals.lock);
583
584 /* Find the socket */
585 socket = sockets_find(socket_get_sockets(), socket_id);
586 if (!socket) {
587 fibril_rwlock_read_unlock(&socket_globals.lock);
588 return ENOTSOCK;
589 }
590
591 /* Request listen backlog change */
592 async_exch_t *exch = async_exchange_begin(socket->sess);
593 result = (int) async_req_3_0(exch, NET_SOCKET_LISTEN,
594 (sysarg_t) socket->socket_id, (sysarg_t) backlog, socket->service);
595 async_exchange_end(exch);
596
597 fibril_rwlock_read_unlock(&socket_globals.lock);
598 return result;
599}
600
601/** Accepts waiting socket.
602 *
603 * Blocks until such a socket exists.
604 *
605 * @param[in] socket_id Socket identifier.
606 * @param[out] cliaddr The remote client address.
607 * @param[in] addrlen The address length.
608 * @return EOK on success.
609 * @return EBADMEM if the cliaddr or addrlen parameter is NULL.
610 * @return EINVAL if the backlog parameter is not positive (<=0).
611 * @return ENOTSOCK if the socket is not found.
612 * @return Other error codes as defined for the NET_SOCKET_ACCEPT
613 * message.
614 */
615int accept(int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen)
616{
617 socket_t *socket;
618 socket_t *new_socket;
619 aid_t message_id;
620 sysarg_t ipc_result;
621 int result;
622 ipc_call_t answer;
623
624 if (!cliaddr || !addrlen)
625 return EBADMEM;
626
627 fibril_rwlock_write_lock(&socket_globals.lock);
628
629 /* Find the socket */
630 socket = sockets_find(socket_get_sockets(), socket_id);
631 if (!socket) {
632 fibril_rwlock_write_unlock(&socket_globals.lock);
633 return ENOTSOCK;
634 }
635
636 fibril_mutex_lock(&socket->accept_lock);
637
638 /* Wait for an accepted socket */
639 ++ socket->blocked;
640 while (dyn_fifo_value(&socket->accepted) <= 0) {
641 fibril_rwlock_write_unlock(&socket_globals.lock);
642 fibril_condvar_wait(&socket->accept_signal, &socket->accept_lock);
643 /* Drop the accept lock to avoid deadlock */
644 fibril_mutex_unlock(&socket->accept_lock);
645 fibril_rwlock_write_lock(&socket_globals.lock);
646 fibril_mutex_lock(&socket->accept_lock);
647 }
648 -- socket->blocked;
649
650 /* Create a new socket */
651 new_socket = (socket_t *) malloc(sizeof(socket_t));
652 if (!new_socket) {
653 fibril_mutex_unlock(&socket->accept_lock);
654 fibril_rwlock_write_unlock(&socket_globals.lock);
655 return ENOMEM;
656 }
657 bzero(new_socket, sizeof(*new_socket));
658 socket_id = socket_generate_new_id();
659 if (socket_id <= 0) {
660 fibril_mutex_unlock(&socket->accept_lock);
661 fibril_rwlock_write_unlock(&socket_globals.lock);
662 free(new_socket);
663 return socket_id;
664 }
665 socket_initialize(new_socket, socket_id, socket->sess,
666 socket->service);
667 result = sockets_add(socket_get_sockets(), new_socket->socket_id,
668 new_socket);
669 if (result < 0) {
670 fibril_mutex_unlock(&socket->accept_lock);
671 fibril_rwlock_write_unlock(&socket_globals.lock);
672 free(new_socket);
673 return result;
674 }
675
676 /* Request accept */
677 async_exch_t *exch = async_exchange_begin(socket->sess);
678 message_id = async_send_5(exch, NET_SOCKET_ACCEPT,
679 (sysarg_t) socket->socket_id, 0, socket->service, 0,
680 new_socket->socket_id, &answer);
681
682 /* Read address */
683 async_data_read_start(exch, cliaddr, *addrlen);
684 async_exchange_end(exch);
685
686 fibril_rwlock_write_unlock(&socket_globals.lock);
687 async_wait_for(message_id, &ipc_result);
688 result = (int) ipc_result;
689 if (result > 0) {
690 if (result != socket_id)
691 result = EINVAL;
692
693 /* Dequeue the accepted socket if successful */
694 dyn_fifo_pop(&socket->accepted);
695 /* Set address length */
696 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
697 new_socket->data_fragment_size =
698 SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
699 } else if (result == ENOTSOCK) {
700 /* Empty the queue if no accepted sockets */
701 while (dyn_fifo_pop(&socket->accepted) > 0)
702 ;
703 }
704
705 fibril_mutex_unlock(&socket->accept_lock);
706 return result;
707}
708
709/** Connects socket to the remote server.
710 *
711 * @param[in] socket_id Socket identifier.
712 * @param[in] serv_addr The remote server address.
713 * @param[in] addrlen The address length.
714 * @return EOK on success.
715 * @return EBADMEM if the serv_addr parameter is NULL.
716 * @return NO_DATA if the addlen parameter is zero.
717 * @return ENOTSOCK if the socket is not found.
718 * @return Other error codes as defined for the NET_SOCKET_CONNECT
719 * message.
720 */
721int connect(int socket_id, const struct sockaddr *serv_addr, socklen_t addrlen)
722{
723 if (!serv_addr)
724 return EDESTADDRREQ;
725
726 if (!addrlen)
727 return EDESTADDRREQ;
728
729 /* Send the address */
730 return socket_send_data(socket_id, NET_SOCKET_CONNECT, 0, serv_addr,
731 addrlen);
732}
733
734/** Clears and destroys the socket.
735 *
736 * @param[in] socket The socket to be destroyed.
737 */
738static void socket_destroy(socket_t *socket)
739{
740 int accepted_id;
741
742 /* Destroy all accepted sockets */
743 while ((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0)
744 socket_destroy(sockets_find(socket_get_sockets(), accepted_id));
745
746 dyn_fifo_destroy(&socket->received);
747 dyn_fifo_destroy(&socket->accepted);
748 sockets_exclude(socket_get_sockets(), socket->socket_id, free);
749}
750
751/** Closes the socket.
752 *
753 * @param[in] socket_id Socket identifier.
754 * @return EOK on success.
755 * @return ENOTSOCK if the socket is not found.
756 * @return EINPROGRESS if there is another blocking function in
757 * progress.
758 * @return Other error codes as defined for the NET_SOCKET_CLOSE
759 * message.
760 */
761int closesocket(int socket_id)
762{
763 socket_t *socket;
764 int rc;
765
766 fibril_rwlock_write_lock(&socket_globals.lock);
767
768 socket = sockets_find(socket_get_sockets(), socket_id);
769 if (!socket) {
770 fibril_rwlock_write_unlock(&socket_globals.lock);
771 return ENOTSOCK;
772 }
773 if (socket->blocked) {
774 fibril_rwlock_write_unlock(&socket_globals.lock);
775 return EINPROGRESS;
776 }
777
778 /* Request close */
779 async_exch_t *exch = async_exchange_begin(socket->sess);
780 rc = (int) async_req_3_0(exch, NET_SOCKET_CLOSE,
781 (sysarg_t) socket->socket_id, 0, socket->service);
782 async_exchange_end(exch);
783
784 if (rc != EOK) {
785 fibril_rwlock_write_unlock(&socket_globals.lock);
786 return rc;
787 }
788 /* Free the socket structure */
789 socket_destroy(socket);
790
791 fibril_rwlock_write_unlock(&socket_globals.lock);
792 return EOK;
793}
794
795/** Sends data via the socket to the remote address.
796 *
797 * Binds the socket to a free port if not already connected/bound.
798 *
799 * @param[in] message The action message.
800 * @param[in] socket_id Socket identifier.
801 * @param[in] data The data to be sent.
802 * @param[in] datalength The data length.
803 * @param[in] flags Various send flags.
804 * @param[in] toaddr The destination address. May be NULL for connected
805 * sockets.
806 * @param[in] addrlen The address length. Used only if toaddr is not NULL.
807 * @return EOK on success.
808 * @return ENOTSOCK if the socket is not found.
809 * @return EBADMEM if the data or toaddr parameter is NULL.
810 * @return NO_DATA if the datalength or the addrlen parameter is
811 * zero (0).
812 * @return Other error codes as defined for the NET_SOCKET_SENDTO
813 * message.
814 */
815static int
816sendto_core(sysarg_t message, int socket_id, const void *data,
817 size_t datalength, int flags, const struct sockaddr *toaddr,
818 socklen_t addrlen)
819{
820 socket_t *socket;
821 aid_t message_id;
822 sysarg_t result;
823 size_t fragments;
824 ipc_call_t answer;
825
826 if (!data)
827 return EBADMEM;
828
829 if (!datalength)
830 return NO_DATA;
831
832 fibril_rwlock_read_lock(&socket_globals.lock);
833
834 /* Find socket */
835 socket = sockets_find(socket_get_sockets(), socket_id);
836 if (!socket) {
837 fibril_rwlock_read_unlock(&socket_globals.lock);
838 return ENOTSOCK;
839 }
840
841 fibril_rwlock_read_lock(&socket->sending_lock);
842
843 /* Compute data fragment count */
844 if (socket->data_fragment_size > 0) {
845 fragments = (datalength + socket->header_size) /
846 socket->data_fragment_size;
847 if ((datalength + socket->header_size) %
848 socket->data_fragment_size)
849 ++fragments;
850 } else {
851 fragments = 1;
852 }
853
854 /* Request send */
855 async_exch_t *exch = async_exchange_begin(socket->sess);
856
857 message_id = async_send_5(exch, message,
858 (sysarg_t) socket->socket_id,
859 (fragments == 1 ? datalength : socket->data_fragment_size),
860 socket->service, (sysarg_t) flags, fragments, &answer);
861
862 /* Send the address if given */
863 if (!toaddr ||
864 (async_data_write_start(exch, toaddr, addrlen) == EOK)) {
865 if (fragments == 1) {
866 /* Send all if only one fragment */
867 async_data_write_start(exch, data, datalength);
868 } else {
869 /* Send the first fragment */
870 async_data_write_start(exch, data,
871 socket->data_fragment_size - socket->header_size);
872 data = ((const uint8_t *) data) +
873 socket->data_fragment_size - socket->header_size;
874
875 /* Send the middle fragments */
876 while (--fragments > 1) {
877 async_data_write_start(exch, data,
878 socket->data_fragment_size);
879 data = ((const uint8_t *) data) +
880 socket->data_fragment_size;
881 }
882
883 /* Send the last fragment */
884 async_data_write_start(exch, data,
885 (datalength + socket->header_size) %
886 socket->data_fragment_size);
887 }
888 }
889
890 async_exchange_end(exch);
891
892 async_wait_for(message_id, &result);
893
894 if ((SOCKET_GET_DATA_FRAGMENT_SIZE(answer) > 0) &&
895 (SOCKET_GET_DATA_FRAGMENT_SIZE(answer) !=
896 socket->data_fragment_size)) {
897 /* Set the data fragment size */
898 socket->data_fragment_size =
899 SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
900 }
901
902 fibril_rwlock_read_unlock(&socket->sending_lock);
903 fibril_rwlock_read_unlock(&socket_globals.lock);
904 return (int) result;
905}
906
907/** Sends data via the socket.
908 *
909 * @param[in] socket_id Socket identifier.
910 * @param[in] data The data to be sent.
911 * @param[in] datalength The data length.
912 * @param[in] flags Various send flags.
913 * @return EOK on success.
914 * @return ENOTSOCK if the socket is not found.
915 * @return EBADMEM if the data parameter is NULL.
916 * @return NO_DATA if the datalength parameter is zero.
917 * @return Other error codes as defined for the NET_SOCKET_SEND
918 * message.
919 */
920int send(int socket_id, void *data, size_t datalength, int flags)
921{
922 /* Without the address */
923 return sendto_core(NET_SOCKET_SEND, socket_id, data, datalength, flags,
924 NULL, 0);
925}
926
927/** Sends data via the socket to the remote address.
928 *
929 * Binds the socket to a free port if not already connected/bound.
930 *
931 * @param[in] socket_id Socket identifier.
932 * @param[in] data The data to be sent.
933 * @param[in] datalength The data length.
934 * @param[in] flags Various send flags.
935 * @param[in] toaddr The destination address.
936 * @param[in] addrlen The address length.
937 * @return EOK on success.
938 * @return ENOTSOCK if the socket is not found.
939 * @return EBADMEM if the data or toaddr parameter is NULL.
940 * @return NO_DATA if the datalength or the addrlen parameter is
941 * zero.
942 * @return Other error codes as defined for the NET_SOCKET_SENDTO
943 * message.
944 */
945int
946sendto(int socket_id, const void *data, size_t datalength, int flags,
947 const struct sockaddr *toaddr, socklen_t addrlen)
948{
949 if (!toaddr)
950 return EDESTADDRREQ;
951
952 if (!addrlen)
953 return EDESTADDRREQ;
954
955 /* With the address */
956 return sendto_core(NET_SOCKET_SENDTO, socket_id, data, datalength,
957 flags, toaddr, addrlen);
958}
959
960/** Receives data via the socket.
961 *
962 * @param[in] message The action message.
963 * @param[in] socket_id Socket identifier.
964 * @param[out] data The data buffer to be filled.
965 * @param[in] datalength The data length.
966 * @param[in] flags Various receive flags.
967 * @param[out] fromaddr The source address. May be NULL for connected sockets.
968 * @param[in,out] addrlen The address length. The maximum address length is
969 * read. The actual address length is set. Used only if
970 * fromaddr is not NULL.
971 * @return Positive received message size in bytes on success.
972 * @return Zero if no more data (other side closed the connection).
973 * @return ENOTSOCK if the socket is not found.
974 * @return EBADMEM if the data parameter is NULL.
975 * @return NO_DATA if the datalength or addrlen parameter is zero.
976 * @return Other error codes as defined for the spcific message.
977 */
978static ssize_t
979recvfrom_core(sysarg_t message, int socket_id, void *data, size_t datalength,
980 int flags, struct sockaddr *fromaddr, socklen_t *addrlen)
981{
982 socket_t *socket;
983 aid_t message_id;
984 sysarg_t ipc_result;
985 int result;
986 size_t fragments;
987 size_t *lengths;
988 size_t index;
989 ipc_call_t answer;
990 ssize_t retval;
991
992 if (!data)
993 return EBADMEM;
994
995 if (!datalength)
996 return NO_DATA;
997
998 if (fromaddr && !addrlen)
999 return EINVAL;
1000
1001 fibril_rwlock_read_lock(&socket_globals.lock);
1002
1003 /* Find the socket */
1004 socket = sockets_find(socket_get_sockets(), socket_id);
1005 if (!socket) {
1006 fibril_rwlock_read_unlock(&socket_globals.lock);
1007 return ENOTSOCK;
1008 }
1009
1010 fibril_mutex_lock(&socket->receive_lock);
1011 /* Wait for a received packet */
1012 ++socket->blocked;
1013 while ((result = dyn_fifo_value(&socket->received)) < 0) {
1014 fibril_rwlock_read_unlock(&socket_globals.lock);
1015 fibril_condvar_wait(&socket->receive_signal,
1016 &socket->receive_lock);
1017
1018 /* Drop the receive lock to avoid deadlock */
1019 fibril_mutex_unlock(&socket->receive_lock);
1020 fibril_rwlock_read_lock(&socket_globals.lock);
1021 fibril_mutex_lock(&socket->receive_lock);
1022 }
1023 --socket->blocked;
1024 fragments = (size_t) result;
1025
1026 if (fragments == 0) {
1027 /* No more data, other side has closed the connection. */
1028 fibril_mutex_unlock(&socket->receive_lock);
1029 fibril_rwlock_read_unlock(&socket_globals.lock);
1030 return 0;
1031 }
1032
1033 async_exch_t *exch = async_exchange_begin(socket->sess);
1034
1035 /* Prepare lengths if more fragments */
1036 if (fragments > 1) {
1037 lengths = (size_t *) malloc(sizeof(size_t) * fragments +
1038 sizeof(size_t));
1039 if (!lengths) {
1040 fibril_mutex_unlock(&socket->receive_lock);
1041 fibril_rwlock_read_unlock(&socket_globals.lock);
1042 return ENOMEM;
1043 }
1044
1045 /* Request packet data */
1046 message_id = async_send_4(exch, message,
1047 (sysarg_t) socket->socket_id, 0, socket->service,
1048 (sysarg_t) flags, &answer);
1049
1050 /* Read the address if desired */
1051 if(!fromaddr ||
1052 (async_data_read_start(exch, fromaddr,
1053 *addrlen) == EOK)) {
1054 /* Read the fragment lengths */
1055 if (async_data_read_start(exch, lengths,
1056 sizeof(int) * (fragments + 1)) == EOK) {
1057 if (lengths[fragments] <= datalength) {
1058
1059 /* Read all fragments if long enough */
1060 for (index = 0; index < fragments;
1061 ++index) {
1062 async_data_read_start(exch, data,
1063 lengths[index]);
1064 data = ((uint8_t *) data) +
1065 lengths[index];
1066 }
1067 }
1068 }
1069 }
1070
1071 free(lengths);
1072 } else { /* fragments == 1 */
1073 /* Request packet data */
1074 message_id = async_send_4(exch, message,
1075 (sysarg_t) socket->socket_id, 0, socket->service,
1076 (sysarg_t) flags, &answer);
1077
1078 /* Read the address if desired */
1079 if (!fromaddr ||
1080 (async_data_read_start(exch, fromaddr, *addrlen) == EOK)) {
1081 /* Read all if only one fragment */
1082 async_data_read_start(exch, data, datalength);
1083 }
1084 }
1085
1086 async_exchange_end(exch);
1087
1088 async_wait_for(message_id, &ipc_result);
1089 result = (int) ipc_result;
1090 if (result == EOK) {
1091 /* Dequeue the received packet */
1092 dyn_fifo_pop(&socket->received);
1093 /* Return read data length */
1094 retval = SOCKET_GET_READ_DATA_LENGTH(answer);
1095 /* Set address length */
1096 if (fromaddr && addrlen)
1097 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
1098 } else {
1099 retval = (ssize_t) result;
1100 }
1101
1102 fibril_mutex_unlock(&socket->receive_lock);
1103 fibril_rwlock_read_unlock(&socket_globals.lock);
1104 return retval;
1105}
1106
1107/** Receives data via the socket.
1108 *
1109 * @param[in] socket_id Socket identifier.
1110 * @param[out] data The data buffer to be filled.
1111 * @param[in] datalength The data length.
1112 * @param[in] flags Various receive flags.
1113 * @return Positive received message size in bytes on success.
1114 * @return Zero if no more data (other side closed the connection).
1115 * @return ENOTSOCK if the socket is not found.
1116 * @return EBADMEM if the data parameter is NULL.
1117 * @return NO_DATA if the datalength parameter is zero.
1118 * @return Other error codes as defined for the NET_SOCKET_RECV
1119 * message.
1120 */
1121ssize_t recv(int socket_id, void *data, size_t datalength, int flags)
1122{
1123 /* Without the address */
1124 return recvfrom_core(NET_SOCKET_RECV, socket_id, data, datalength,
1125 flags, NULL, NULL);
1126}
1127
1128/** Receives data via the socket.
1129 *
1130 * @param[in] socket_id Socket identifier.
1131 * @param[out] data The data buffer to be filled.
1132 * @param[in] datalength The data length.
1133 * @param[in] flags Various receive flags.
1134 * @param[out] fromaddr The source address.
1135 * @param[in,out] addrlen The address length. The maximum address length is
1136 * read. The actual address length is set.
1137 * @return Positive received message size in bytes on success.
1138 * @return Zero if no more data (other side closed the connection).
1139 * @return ENOTSOCK if the socket is not found.
1140 * @return EBADMEM if the data or fromaddr parameter is NULL.
1141 * @return NO_DATA if the datalength or addrlen parameter is zero.
1142 * @return Other error codes as defined for the NET_SOCKET_RECVFROM
1143 * message.
1144 */
1145ssize_t
1146recvfrom(int socket_id, void *data, size_t datalength, int flags,
1147 struct sockaddr *fromaddr, socklen_t *addrlen)
1148{
1149 if (!fromaddr)
1150 return EBADMEM;
1151
1152 if (!addrlen)
1153 return NO_DATA;
1154
1155 /* With the address */
1156 return recvfrom_core(NET_SOCKET_RECVFROM, socket_id, data, datalength,
1157 flags, fromaddr, addrlen);
1158}
1159
1160/** Gets socket option.
1161 *
1162 * @param[in] socket_id Socket identifier.
1163 * @param[in] level The socket options level.
1164 * @param[in] optname The socket option to be get.
1165 * @param[out] value The value buffer to be filled.
1166 * @param[in,out] optlen The value buffer length. The maximum length is read.
1167 * The actual length is set.
1168 * @return EOK on success.
1169 * @return ENOTSOCK if the socket is not found.
1170 * @return EBADMEM if the value or optlen parameter is NULL.
1171 * @return NO_DATA if the optlen parameter is zero.
1172 * @return Other error codes as defined for the
1173 * NET_SOCKET_GETSOCKOPT message.
1174 */
1175int
1176getsockopt(int socket_id, int level, int optname, void *value, size_t *optlen)
1177{
1178 socket_t *socket;
1179 aid_t message_id;
1180 sysarg_t result;
1181
1182 if (!value || !optlen)
1183 return EBADMEM;
1184
1185 if (!*optlen)
1186 return NO_DATA;
1187
1188 fibril_rwlock_read_lock(&socket_globals.lock);
1189
1190 /* Find the socket */
1191 socket = sockets_find(socket_get_sockets(), socket_id);
1192 if (!socket) {
1193 fibril_rwlock_read_unlock(&socket_globals.lock);
1194 return ENOTSOCK;
1195 }
1196
1197 /* Request option value */
1198 async_exch_t *exch = async_exchange_begin(socket->sess);
1199
1200 message_id = async_send_3(exch, NET_SOCKET_GETSOCKOPT,
1201 (sysarg_t) socket->socket_id, (sysarg_t) optname, socket->service,
1202 NULL);
1203
1204 /* Read the length */
1205 if (async_data_read_start(exch, optlen,
1206 sizeof(*optlen)) == EOK) {
1207 /* Read the value */
1208 async_data_read_start(exch, value, *optlen);
1209 }
1210
1211 async_exchange_end(exch);
1212
1213 fibril_rwlock_read_unlock(&socket_globals.lock);
1214 async_wait_for(message_id, &result);
1215 return (int) result;
1216}
1217
1218/** Sets socket option.
1219 *
1220 * @param[in] socket_id Socket identifier.
1221 * @param[in] level The socket options level.
1222 * @param[in] optname The socket option to be set.
1223 * @param[in] value The value to be set.
1224 * @param[in] optlen The value length.
1225 * @return EOK on success.
1226 * @return ENOTSOCK if the socket is not found.
1227 * @return EBADMEM if the value parameter is NULL.
1228 * @return NO_DATA if the optlen parameter is zero.
1229 * @return Other error codes as defined for the
1230 * NET_SOCKET_SETSOCKOPT message.
1231 */
1232int
1233setsockopt(int socket_id, int level, int optname, const void *value,
1234 size_t optlen)
1235{
1236 /* Send the value */
1237 return socket_send_data(socket_id, NET_SOCKET_SETSOCKOPT,
1238 (sysarg_t) optname, value, optlen);
1239}
1240
1241/** @}
1242 */
Note: See TracBrowser for help on using the repository browser.