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

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

get rid of the weird timeout connection routines

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