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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a07a454 was 9934f7d, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Add extra argument to async connection handlers that can be used for passing
information from async_connect_to_me() to the handler.

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