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

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

Use the standard supported initializers for fibril mutexes and rwlocks.

  • 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#include <err.h>
46
47#include <ipc/services.h>
48#include <ipc/socket.h>
49
50#include <net/modules.h>
51#include <net/in.h>
52#include <net/socket.h>
53#include <adt/dynamic_fifo.h>
54#include <adt/int_map.h>
55
56/** Initial received packet queue size. */
57#define SOCKET_INITIAL_RECEIVED_SIZE 4
58
59/** Maximum received packet queue size. */
60#define SOCKET_MAX_RECEIVED_SIZE 0
61
62/** Initial waiting sockets queue size. */
63#define SOCKET_INITIAL_ACCEPTED_SIZE 1
64
65/** Maximum waiting sockets queue size. */
66#define SOCKET_MAX_ACCEPTED_SIZE 0
67
68/** Default timeout for connections in microseconds. */
69#define SOCKET_CONNECT_TIMEOUT (1 * 1000 * 1000)
70
71/**
72 * Maximum number of random attempts to find a new socket identifier before
73 * switching to the sequence.
74 */
75#define SOCKET_ID_TRIES 100
76
77/** Type definition of the socket specific data.
78 * @see socket
79 */
80typedef struct socket socket_t;
81
82/** Type definition of the socket specific data pointer.
83 * @see socket
84 */
85typedef socket_t *socket_ref;
86
87/** Socket specific data.
88 *
89 * Each socket lock locks only its structure part and any number of them may be
90 * locked simultaneously.
91 */
92struct socket {
93 /** Socket identifier. */
94 int socket_id;
95 /** Parent module phone. */
96 int phone;
97 /** Parent module service. */
98 services_t service;
99
100 /**
101 * Underlying protocol header size.
102 * Sending and receiving optimalization.
103 */
104 size_t header_size;
105
106 /** Packet data fragment size. Sending optimization. */
107 size_t data_fragment_size;
108
109 /**
110 * Sending safety lock.
111 * Locks the header_size and data_fragment_size attributes.
112 */
113 fibril_rwlock_t sending_lock;
114
115 /** Received packets queue. */
116 dyn_fifo_t received;
117
118 /**
119 * Received packets safety lock.
120 * Used for receiving and receive notifications.
121 * Locks the received attribute.
122 */
123 fibril_mutex_t receive_lock;
124
125 /** Received packets signaling. Signaled upon receive notification. */
126 fibril_condvar_t receive_signal;
127 /** Waiting sockets queue. */
128 dyn_fifo_t accepted;
129
130 /**
131 * Waiting sockets safety lock.
132 * Used for accepting and accept notifications.
133 * Locks the accepted attribute.
134 */
135 fibril_mutex_t accept_lock;
136
137 /** Waiting sockets signaling. Signaled upon accept notification. */
138 fibril_condvar_t accept_signal;
139
140 /**
141 * The number of blocked functions called.
142 * Used while waiting for the received packets or accepted sockets.
143 */
144 int blocked;
145};
146
147/** Sockets map.
148 * Maps socket identifiers to the socket specific data.
149 * @see int_map.h
150 */
151INT_MAP_DECLARE(sockets, socket_t);
152
153/** Socket client library global data. */
154static struct socket_client_globals {
155 /** TCP module phone. */
156 int tcp_phone;
157 /** UDP module phone. */
158 int udp_phone;
159
160// /** The last socket identifier.
161// */
162// int last_id;
163
164 /** Active sockets. */
165 sockets_ref sockets;
166
167 /** Safety lock.
168 * Write lock is used only for adding or removing sockets.
169 * When locked for writing, no other socket locks need to be locked.
170 * When locked for reading, any other socket locks may be locked.
171 * No socket lock may be locked if this lock is unlocked.
172 */
173 fibril_rwlock_t lock;
174} socket_globals = {
175 .tcp_phone = -1,
176 .udp_phone = -1,
177// .last_id = 0,
178 .sockets = NULL,
179 .lock = FIBRIL_RWLOCK_INITIALIZER(socket_globals.lock)
180};
181
182INT_MAP_IMPLEMENT(sockets, socket_t);
183
184/** Returns the active sockets.
185 *
186 * @returns The active sockets.
187 */
188static sockets_ref socket_get_sockets(void)
189{
190 if (!socket_globals.sockets) {
191 socket_globals.sockets =
192 (sockets_ref) malloc(sizeof(sockets_t));
193 if (!socket_globals.sockets)
194 return NULL;
195
196 if (sockets_initialize(socket_globals.sockets) != EOK) {
197 free(socket_globals.sockets);
198 socket_globals.sockets = NULL;
199 }
200
201 srand(task_get_id());
202 }
203
204 return socket_globals.sockets;
205}
206
207/** Default thread for new connections.
208 *
209 * @param[in] iid The initial message identifier.
210 * @param[in] icall The initial message call structure.
211 */
212static void socket_connection(ipc_callid_t iid, ipc_call_t * icall)
213{
214 ERROR_DECLARE;
215
216 ipc_callid_t callid;
217 ipc_call_t call;
218 socket_ref socket;
219
220loop:
221 callid = async_get_call(&call);
222
223 switch (IPC_GET_METHOD(call)) {
224 case NET_SOCKET_RECEIVED:
225 case NET_SOCKET_ACCEPTED:
226 case NET_SOCKET_DATA_FRAGMENT_SIZE:
227 fibril_rwlock_read_lock(&socket_globals.lock);
228
229 // find the socket
230 socket = sockets_find(socket_get_sockets(),
231 SOCKET_GET_SOCKET_ID(call));
232 if (!socket) {
233 ERROR_CODE = ENOTSOCK;
234 fibril_rwlock_read_unlock(&socket_globals.lock);
235 break;
236 }
237
238 switch (IPC_GET_METHOD(call)) {
239 case NET_SOCKET_RECEIVED:
240 fibril_mutex_lock(&socket->receive_lock);
241 // push the number of received packet fragments
242 if (!ERROR_OCCURRED(dyn_fifo_push(&socket->received,
243 SOCKET_GET_DATA_FRAGMENTS(call),
244 SOCKET_MAX_RECEIVED_SIZE))) {
245 // signal the received packet
246 fibril_condvar_signal(&socket->receive_signal);
247 }
248 fibril_mutex_unlock(&socket->receive_lock);
249 break;
250
251 case NET_SOCKET_ACCEPTED:
252 // push the new socket identifier
253 fibril_mutex_lock(&socket->accept_lock);
254 if (!ERROR_OCCURRED(dyn_fifo_push(&socket->accepted,
255 1, SOCKET_MAX_ACCEPTED_SIZE))) {
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 ERROR_CODE = 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 ERROR_CODE = ENOTSUP;
283 }
284
285 ipc_answer_0(callid, (ipcarg_t) ERROR_CODE);
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 ERROR_DECLARE;
408
409 socket_ref socket;
410 int phone;
411 int socket_id;
412 services_t service;
413 ipcarg_t fragment_size;
414 ipcarg_t header_size;
415
416 // find the appropriate service
417 switch (domain) {
418 case PF_INET:
419 switch (type) {
420 case SOCK_STREAM:
421 if (!protocol)
422 protocol = IPPROTO_TCP;
423
424 switch (protocol) {
425 case IPPROTO_TCP:
426 phone = socket_get_tcp_phone();
427 service = SERVICE_TCP;
428 break;
429 default:
430 return EPROTONOSUPPORT;
431 }
432
433 break;
434
435 case SOCK_DGRAM:
436 if (!protocol)
437 protocol = IPPROTO_UDP;
438
439 switch (protocol) {
440 case IPPROTO_UDP:
441 phone = socket_get_udp_phone();
442 service = SERVICE_UDP;
443 break;
444 default:
445 return EPROTONOSUPPORT;
446 }
447
448 break;
449
450 case SOCK_RAW:
451 default:
452 return ESOCKTNOSUPPORT;
453 }
454
455 break;
456
457 case PF_INET6:
458 default:
459 return EPFNOSUPPORT;
460 }
461
462 if (phone < 0)
463 return phone;
464
465 // create a new socket structure
466 socket = (socket_ref) malloc(sizeof(socket_t));
467 if (!socket)
468 return ENOMEM;
469
470 bzero(socket, sizeof(*socket));
471 fibril_rwlock_write_lock(&socket_globals.lock);
472
473 // request a new socket
474 socket_id = socket_generate_new_id();
475 if (socket_id <= 0) {
476 fibril_rwlock_write_unlock(&socket_globals.lock);
477 free(socket);
478 return socket_id;
479 }
480
481 if (ERROR_OCCURRED((int) async_req_3_3(phone, NET_SOCKET, socket_id, 0,
482 service, NULL, &fragment_size, &header_size))) {
483 fibril_rwlock_write_unlock(&socket_globals.lock);
484 free(socket);
485 return ERROR_CODE;
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 ERROR_CODE = sockets_add(socket_get_sockets(), socket_id, socket);
495
496 fibril_rwlock_write_unlock(&socket_globals.lock);
497 if (ERROR_CODE < 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 ERROR_CODE;
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 ERROR_DECLARE;
773
774 socket_ref socket;
775
776 fibril_rwlock_write_lock(&socket_globals.lock);
777
778 socket = sockets_find(socket_get_sockets(), socket_id);
779 if (!socket) {
780 fibril_rwlock_write_unlock(&socket_globals.lock);
781 return ENOTSOCK;
782 }
783 if (socket->blocked) {
784 fibril_rwlock_write_unlock(&socket_globals.lock);
785 return EINPROGRESS;
786 }
787
788 // request close
789 ERROR_PROPAGATE((int) async_req_3_0(socket->phone, NET_SOCKET_CLOSE,
790 (ipcarg_t) socket->socket_id, 0, socket->service));
791 // free the socket structure
792 socket_destroy(socket);
793
794 fibril_rwlock_write_unlock(&socket_globals.lock);
795 return EOK;
796}
797
798/** Sends data via the socket to the remote address.
799 *
800 * Binds the socket to a free port if not already connected/bound.
801 *
802 * @param[in] message The action message.
803 * @param[in] socket_id Socket identifier.
804 * @param[in] data The data to be sent.
805 * @param[in] datalength The data length.
806 * @param[in] flags Various send flags.
807 * @param[in] toaddr The destination address. May be NULL for connected
808 * sockets.
809 * @param[in] addrlen The address length. Used only if toaddr is not NULL.
810 * @returns EOK on success.
811 * @returns ENOTSOCK if the socket is not found.
812 * @returns EBADMEM if the data or toaddr parameter is NULL.
813 * @returns NO_DATA if the datalength or the addrlen parameter is
814 * zero (0).
815 * @returns Other error codes as defined for the NET_SOCKET_SENDTO
816 * message.
817 */
818static int
819sendto_core(ipcarg_t message, int socket_id, const void *data,
820 size_t datalength, int flags, const struct sockaddr *toaddr,
821 socklen_t addrlen)
822{
823 socket_ref socket;
824 aid_t message_id;
825 ipcarg_t result;
826 size_t fragments;
827 ipc_call_t answer;
828
829 if (!data)
830 return EBADMEM;
831
832 if (!datalength)
833 return NO_DATA;
834
835 fibril_rwlock_read_lock(&socket_globals.lock);
836
837 // find socket
838 socket = sockets_find(socket_get_sockets(), socket_id);
839 if (!socket) {
840 fibril_rwlock_read_unlock(&socket_globals.lock);
841 return ENOTSOCK;
842 }
843
844 fibril_rwlock_read_lock(&socket->sending_lock);
845
846 // compute data fragment count
847 if (socket->data_fragment_size > 0) {
848 fragments = (datalength + socket->header_size) /
849 socket->data_fragment_size;
850 if ((datalength + socket->header_size) %
851 socket->data_fragment_size)
852 ++fragments;
853 } else {
854 fragments = 1;
855 }
856
857 // request send
858 message_id = async_send_5(socket->phone, message,
859 (ipcarg_t) socket->socket_id,
860 (fragments == 1 ? datalength : socket->data_fragment_size),
861 socket->service, (ipcarg_t) flags, fragments, &answer);
862
863 // send the address if given
864 if (!toaddr ||
865 (async_data_write_start(socket->phone, toaddr, addrlen) == EOK)) {
866 if (fragments == 1) {
867 // send all if only one fragment
868 async_data_write_start(socket->phone, data, datalength);
869 } else {
870 // send the first fragment
871 async_data_write_start(socket->phone, data,
872 socket->data_fragment_size - socket->header_size);
873 data = ((const uint8_t *) data) +
874 socket->data_fragment_size - socket->header_size;
875
876 // send the middle fragments
877 while (--fragments > 1) {
878 async_data_write_start(socket->phone, data,
879 socket->data_fragment_size);
880 data = ((const uint8_t *) data) +
881 socket->data_fragment_size;
882 }
883
884 // send the last fragment
885 async_data_write_start(socket->phone, data,
886 (datalength + socket->header_size) %
887 socket->data_fragment_size);
888 }
889 }
890
891 async_wait_for(message_id, &result);
892
893 if ((SOCKET_GET_DATA_FRAGMENT_SIZE(answer) > 0) &&
894 (SOCKET_GET_DATA_FRAGMENT_SIZE(answer) !=
895 socket->data_fragment_size)) {
896 // set the data fragment size
897 socket->data_fragment_size =
898 SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
899 }
900
901 fibril_rwlock_read_unlock(&socket->sending_lock);
902 fibril_rwlock_read_unlock(&socket_globals.lock);
903 return (int) result;
904}
905
906/** Sends data via the socket.
907 *
908 * @param[in] socket_id Socket identifier.
909 * @param[in] data The data to be sent.
910 * @param[in] datalength The data length.
911 * @param[in] flags Various send flags.
912 * @returns EOK on success.
913 * @returns ENOTSOCK if the socket is not found.
914 * @returns EBADMEM if the data parameter is NULL.
915 * @returns NO_DATA if the datalength parameter is zero.
916 * @returns Other error codes as defined for the NET_SOCKET_SEND
917 * message.
918 */
919int send(int socket_id, void *data, size_t datalength, int flags)
920{
921 // without the address
922 return sendto_core(NET_SOCKET_SEND, socket_id, data, datalength, flags,
923 NULL, 0);
924}
925
926/** Sends data via the socket to the remote address.
927 *
928 * Binds the socket to a free port if not already connected/bound.
929 *
930 * @param[in] socket_id Socket identifier.
931 * @param[in] data The data to be sent.
932 * @param[in] datalength The data length.
933 * @param[in] flags Various send flags.
934 * @param[in] toaddr The destination address.
935 * @param[in] addrlen The address length.
936 * @returns EOK on success.
937 * @returns ENOTSOCK if the socket is not found.
938 * @returns EBADMEM if the data or toaddr parameter is NULL.
939 * @returns NO_DATA if the datalength or the addrlen parameter is
940 * zero.
941 * @returns Other error codes as defined for the NET_SOCKET_SENDTO
942 * message.
943 */
944int
945sendto(int socket_id, const void *data, size_t datalength, int flags,
946 const struct sockaddr *toaddr, socklen_t addrlen)
947{
948 if (!toaddr)
949 return EDESTADDRREQ;
950
951 if (!addrlen)
952 return EDESTADDRREQ;
953
954 // with the address
955 return sendto_core(NET_SOCKET_SENDTO, socket_id, data, datalength,
956 flags, toaddr, addrlen);
957}
958
959/** Receives data via the socket.
960 *
961 * @param[in] message The action message.
962 * @param[in] socket_id Socket identifier.
963 * @param[out] data The data buffer to be filled.
964 * @param[in] datalength The data length.
965 * @param[in] flags Various receive flags.
966 * @param[out] fromaddr The source address. May be NULL for connected sockets.
967 * @param[in,out] addrlen The address length. The maximum address length is
968 * read. The actual address length is set. Used only if
969 * fromaddr is not NULL.
970 * @returns EOK on success.
971 * @returns ENOTSOCK if the socket is not found.
972 * @returns EBADMEM if the data parameter is NULL.
973 * @returns NO_DATA if the datalength or addrlen parameter is zero.
974 * @returns Other error codes as defined for the spcific message.
975 */
976static int
977recvfrom_core(ipcarg_t message, int socket_id, void *data, size_t datalength,
978 int flags, struct sockaddr *fromaddr, socklen_t *addrlen)
979{
980 socket_ref socket;
981 aid_t message_id;
982 ipcarg_t ipc_result;
983 int result;
984 size_t fragments;
985 size_t *lengths;
986 size_t index;
987 ipc_call_t answer;
988
989 if (!data)
990 return EBADMEM;
991
992 if (!datalength)
993 return NO_DATA;
994
995 if (fromaddr && !addrlen)
996 return EINVAL;
997
998 fibril_rwlock_read_lock(&socket_globals.lock);
999
1000 // find the socket
1001 socket = sockets_find(socket_get_sockets(), socket_id);
1002 if (!socket) {
1003 fibril_rwlock_read_unlock(&socket_globals.lock);
1004 return ENOTSOCK;
1005 }
1006
1007 fibril_mutex_lock(&socket->receive_lock);
1008 // wait for a received packet
1009 ++socket->blocked;
1010 while ((result = dyn_fifo_value(&socket->received)) <= 0) {
1011 fibril_rwlock_read_unlock(&socket_globals.lock);
1012 fibril_condvar_wait(&socket->receive_signal,
1013 &socket->receive_lock);
1014
1015 // drop the receive lock to avoid deadlock
1016 fibril_mutex_unlock(&socket->receive_lock);
1017 fibril_rwlock_read_lock(&socket_globals.lock);
1018 fibril_mutex_lock(&socket->receive_lock);
1019 }
1020 --socket->blocked;
1021 fragments = (size_t) result;
1022
1023 // prepare lengths if more fragments
1024 if (fragments > 1) {
1025 lengths = (size_t *) malloc(sizeof(size_t) * fragments +
1026 sizeof(size_t));
1027 if (!lengths) {
1028 fibril_mutex_unlock(&socket->receive_lock);
1029 fibril_rwlock_read_unlock(&socket_globals.lock);
1030 return ENOMEM;
1031 }
1032
1033 // request packet data
1034 message_id = async_send_4(socket->phone, message,
1035 (ipcarg_t) socket->socket_id, 0, socket->service,
1036 (ipcarg_t) flags, &answer);
1037
1038 // read the address if desired
1039 if(!fromaddr ||
1040 (async_data_read_start(socket->phone, fromaddr,
1041 *addrlen) == EOK)) {
1042 // read the fragment lengths
1043 if (async_data_read_start(socket->phone, lengths,
1044 sizeof(int) * (fragments + 1)) == EOK) {
1045 if (lengths[fragments] <= datalength) {
1046
1047 // read all fragments if long enough
1048 for (index = 0; index < fragments;
1049 ++index) {
1050 async_data_read_start(
1051 socket->phone, data,
1052 lengths[index]);
1053 data = ((uint8_t *) data) +
1054 lengths[index];
1055 }
1056 }
1057 }
1058 }
1059
1060 free(lengths);
1061 } else {
1062 // request packet data
1063 message_id = async_send_4(socket->phone, message,
1064 (ipcarg_t) socket->socket_id, 0, socket->service,
1065 (ipcarg_t) flags, &answer);
1066
1067 // read the address if desired
1068 if (!fromaddr ||
1069 (async_data_read_start(socket->phone, fromaddr,
1070 *addrlen) == EOK)) {
1071 // read all if only one fragment
1072 async_data_read_start(socket->phone, data, datalength);
1073 }
1074 }
1075
1076 async_wait_for(message_id, &ipc_result);
1077 result = (int) ipc_result;
1078 if (result == EOK) {
1079 // dequeue the received packet
1080 dyn_fifo_pop(&socket->received);
1081 // return read data length
1082 result = SOCKET_GET_READ_DATA_LENGTH(answer);
1083 // set address length
1084 if (fromaddr && addrlen)
1085 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
1086 }
1087
1088 fibril_mutex_unlock(&socket->receive_lock);
1089 fibril_rwlock_read_unlock(&socket_globals.lock);
1090 return result;
1091}
1092
1093/** Receives data via the socket.
1094 *
1095 * @param[in] socket_id Socket identifier.
1096 * @param[out] data The data buffer to be filled.
1097 * @param[in] datalength The data length.
1098 * @param[in] flags Various receive flags.
1099 * @returns EOK on success.
1100 * @returns ENOTSOCK if the socket is not found.
1101 * @returns EBADMEM if the data parameter is NULL.
1102 * @returns NO_DATA if the datalength parameter is zero.
1103 * @returns Other error codes as defined for the NET_SOCKET_RECV
1104 * message.
1105 */
1106int recv(int socket_id, void *data, size_t datalength, int flags)
1107{
1108 // without the address
1109 return recvfrom_core(NET_SOCKET_RECV, socket_id, data, datalength,
1110 flags, NULL, NULL);
1111}
1112
1113/** Receives data via the socket.
1114 *
1115 * @param[in] socket_id Socket identifier.
1116 * @param[out] data The data buffer to be filled.
1117 * @param[in] datalength The data length.
1118 * @param[in] flags Various receive flags.
1119 * @param[out] fromaddr The source address.
1120 * @param[in,out] addrlen The address length. The maximum address length is
1121 * read. The actual address length is set.
1122 * @returns EOK on success.
1123 * @returns ENOTSOCK if the socket is not found.
1124 * @returns EBADMEM if the data or fromaddr parameter is NULL.
1125 * @returns NO_DATA if the datalength or addrlen parameter is zero.
1126 * @returns Other error codes as defined for the NET_SOCKET_RECVFROM
1127 * message.
1128 */
1129int
1130recvfrom(int socket_id, void *data, size_t datalength, int flags,
1131 struct sockaddr *fromaddr, socklen_t *addrlen)
1132{
1133 if (!fromaddr)
1134 return EBADMEM;
1135
1136 if (!addrlen)
1137 return NO_DATA;
1138
1139 // with the address
1140 return recvfrom_core(NET_SOCKET_RECVFROM, socket_id, data, datalength,
1141 flags, fromaddr, addrlen);
1142}
1143
1144/** Gets socket option.
1145 *
1146 * @param[in] socket_id Socket identifier.
1147 * @param[in] level The socket options level.
1148 * @param[in] optname The socket option to be get.
1149 * @param[out] value The value buffer to be filled.
1150 * @param[in,out] optlen The value buffer length. The maximum length is read.
1151 * The actual length is set.
1152 * @returns EOK on success.
1153 * @returns ENOTSOCK if the socket is not found.
1154 * @returns EBADMEM if the value or optlen parameter is NULL.
1155 * @returns NO_DATA if the optlen parameter is zero.
1156 * @returns Other error codes as defined for the
1157 * NET_SOCKET_GETSOCKOPT message.
1158 */
1159int
1160getsockopt(int socket_id, int level, int optname, void *value, size_t *optlen)
1161{
1162 socket_ref socket;
1163 aid_t message_id;
1164 ipcarg_t result;
1165
1166 if (!value || !optlen)
1167 return EBADMEM;
1168
1169 if (!*optlen)
1170 return NO_DATA;
1171
1172 fibril_rwlock_read_lock(&socket_globals.lock);
1173
1174 // find the socket
1175 socket = sockets_find(socket_get_sockets(), socket_id);
1176 if (!socket) {
1177 fibril_rwlock_read_unlock(&socket_globals.lock);
1178 return ENOTSOCK;
1179 }
1180
1181 // request option value
1182 message_id = async_send_3(socket->phone, NET_SOCKET_GETSOCKOPT,
1183 (ipcarg_t) socket->socket_id, (ipcarg_t) optname, socket->service,
1184 NULL);
1185
1186 // read the length
1187 if (async_data_read_start(socket->phone, optlen,
1188 sizeof(*optlen)) == EOK) {
1189 // read the value
1190 async_data_read_start(socket->phone, value, *optlen);
1191 }
1192
1193 fibril_rwlock_read_unlock(&socket_globals.lock);
1194 async_wait_for(message_id, &result);
1195 return (int) result;
1196}
1197
1198/** Sets socket option.
1199 *
1200 * @param[in] socket_id Socket identifier.
1201 * @param[in] level The socket options level.
1202 * @param[in] optname The socket option to be set.
1203 * @param[in] value The value to be set.
1204 * @param[in] optlen The value length.
1205 * @returns EOK on success.
1206 * @returns ENOTSOCK if the socket is not found.
1207 * @returns EBADMEM if the value parameter is NULL.
1208 * @returns NO_DATA if the optlen parameter is zero.
1209 * @returns Other error codes as defined for the
1210 * NET_SOCKET_SETSOCKOPT message.
1211 */
1212int
1213setsockopt(int socket_id, int level, int optname, const void *value,
1214 size_t optlen)
1215{
1216 // send the value
1217 return socket_send_data(socket_id, NET_SOCKET_SETSOCKOPT,
1218 (ipcarg_t) optname, value, optlen);
1219}
1220
1221/** @}
1222 */
Note: See TracBrowser for help on using the repository browser.