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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 64d2b10 was 64d2b10, checked in by Martin Decky <martin@…>, 14 years ago

libc: do not intermix low-level IPC methods with async framework methods

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