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

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

libc: do not intermix low-level IPC methods with async framework methods

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