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

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

Replace usage of bzero() with C standard memset().

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