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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 16ac756 was 16ac756, checked in by Jakub Jermar <jakub@…>, 15 years ago

Get rid of the ERROR_CODE madness in libc/net.

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