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

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

Move the client socket code to the standard library.

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