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

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

Make data parameter of send constant

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