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

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

Release locks on exit.

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