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

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

get rid of the weird timeout connection routines

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