source: mainline/uspace/lib/socket/generic/socket_client.c@ c7a8442

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

Move net_modules.[ch] to the standard library. Note that this functionality is
not directly related to networking so the next step regarding these two files
would be to somehow merge its functionality with what we already have in lib c.

  • Property mode set to 100644
File size: 26.7 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup socket
30 * @{
31 */
32
33/** @file
34 * Socket application program interface (API) implementation.
35 * @see socket.h for more information.
36 * This is a part of the network application library.
37 */
38
39#include <assert.h>
40#include <async.h>
41#include <fibril_synch.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <errno.h>
45#include <err.h>
46
47#include <ipc/services.h>
48#include <ipc/socket.h>
49
50#include <net/modules.h>
51#include <net/in.h>
52#include <socket.h>
53#include <adt/dynamic_fifo.h>
54#include <adt/int_map.h>
55
56/** Initial received packet queue size.
57 */
58#define SOCKET_INITIAL_RECEIVED_SIZE 4
59
60/** Maximum received packet queue size.
61 */
62#define SOCKET_MAX_RECEIVED_SIZE 0
63
64/** Initial waiting sockets queue size.
65 */
66#define SOCKET_INITIAL_ACCEPTED_SIZE 1
67
68/** Maximum waiting sockets queue size.
69 */
70#define SOCKET_MAX_ACCEPTED_SIZE 0
71
72/** Default timeout for connections in microseconds.
73 */
74#define SOCKET_CONNECT_TIMEOUT (1 * 1000 * 1000)
75
76/** Maximum number of random attempts to find a new socket identifier before switching to the sequence.
77 */
78#define SOCKET_ID_TRIES 100
79
80/** Type definition of the socket specific data.
81 * @see socket
82 */
83typedef struct socket socket_t;
84
85/** Type definition of the socket specific data pointer.
86 * @see socket
87 */
88typedef socket_t * socket_ref;
89
90/** Socket specific data.
91 * Each socket lock locks only its structure part and any number of them may be locked simultaneously.
92 */
93struct socket{
94 /** Socket identifier.
95 */
96 int socket_id;
97 /** Parent module phone.
98 */
99 int phone;
100 /** Parent module service.
101 */
102 services_t service;
103 /** Underlying protocol header size.
104 * Sending and receiving optimalization.
105 */
106 size_t header_size;
107 /** Packet data fragment size.
108 * Sending optimalization.
109 */
110 size_t data_fragment_size;
111 /** Sending safety lock.
112 * Locks the header_size and data_fragment_size attributes.
113 */
114 fibril_rwlock_t sending_lock;
115 /** Received packets queue.
116 */
117 dyn_fifo_t received;
118 /** Received packets safety lock.
119 * Used for receiving and receive notifications.
120 * Locks the received attribute.
121 */
122 fibril_mutex_t receive_lock;
123 /** Received packets signaling.
124 * Signaled upon receive notification.
125 */
126 fibril_condvar_t receive_signal;
127 /** Waiting sockets queue.
128 */
129 dyn_fifo_t accepted;
130 /** Waiting sockets safety lock.
131 * Used for accepting and accept notifications.
132 * Locks the accepted attribute.
133 */
134 fibril_mutex_t accept_lock;
135 /** Waiting sockets signaling.
136 * Signaled upon accept notification.
137 */
138 fibril_condvar_t accept_signal;
139 /** The number of blocked functions called.
140 * Used while waiting for the received packets or accepted sockets.
141 */
142 int blocked;
143};
144
145/** Sockets map.
146 * Maps socket identifiers to the socket specific data.
147 * @see int_map.h
148 */
149INT_MAP_DECLARE(sockets, socket_t);
150
151/** Socket client library global data.
152 */
153static struct socket_client_globals {
154 /** TCP module phone.
155 */
156 int tcp_phone;
157 /** UDP module phone.
158 */
159 int udp_phone;
160// /** The last socket identifier.
161// */
162// int last_id;
163 /** Active sockets.
164 */
165 sockets_ref sockets;
166 /** Safety lock.
167 * Write lock is used only for adding or removing sockets.
168 * When locked for writing, no other socket locks need to be locked.
169 * When locked for reading, any other socket locks may be locked.
170 * No socket lock may be locked if this lock is unlocked.
171 */
172 fibril_rwlock_t lock;
173} socket_globals = {
174 .tcp_phone = -1,
175 .udp_phone = -1,
176// .last_id = 0,
177 .sockets = NULL,
178 .lock = {
179 .readers = 0,
180 .writers = 0,
181 .waiters = {
182 .prev = &socket_globals.lock.waiters,
183 .next = &socket_globals.lock.waiters
184 }
185 }
186};
187
188INT_MAP_IMPLEMENT(sockets, socket_t);
189
190/** Returns the active sockets.
191 * @returns The active sockets.
192 */
193static sockets_ref socket_get_sockets(void){
194 if(! socket_globals.sockets){
195 socket_globals.sockets = (sockets_ref) malloc(sizeof(sockets_t));
196 if(! socket_globals.sockets){
197 return NULL;
198 }
199 if(sockets_initialize(socket_globals.sockets) != EOK){
200 free(socket_globals.sockets);
201 socket_globals.sockets = NULL;
202 }
203 srand(task_get_id());
204 }
205 return socket_globals.sockets;
206}
207
208/** Default thread for new connections.
209 * @param[in] iid The initial message identifier.
210 * @param[in] icall The initial message call structure.
211 */
212static void socket_connection(ipc_callid_t iid, ipc_call_t * icall){
213 ERROR_DECLARE;
214
215 ipc_callid_t callid;
216 ipc_call_t call;
217 socket_ref socket;
218
219 while(true){
220
221 callid = async_get_call(&call);
222 switch(IPC_GET_METHOD(call)){
223 case NET_SOCKET_RECEIVED:
224 case NET_SOCKET_ACCEPTED:
225 case NET_SOCKET_DATA_FRAGMENT_SIZE:
226 fibril_rwlock_read_lock(&socket_globals.lock);
227 // find the socket
228 socket = sockets_find(socket_get_sockets(), SOCKET_GET_SOCKET_ID(call));
229 if(! socket){
230 ERROR_CODE = ENOTSOCK;
231 }else{
232 switch(IPC_GET_METHOD(call)){
233 case NET_SOCKET_RECEIVED:
234 fibril_mutex_lock(&socket->receive_lock);
235 // push the number of received packet fragments
236 if(! ERROR_OCCURRED(dyn_fifo_push(&socket->received, SOCKET_GET_DATA_FRAGMENTS(call), SOCKET_MAX_RECEIVED_SIZE))){
237 // signal the received packet
238 fibril_condvar_signal(&socket->receive_signal);
239 }
240 fibril_mutex_unlock(&socket->receive_lock);
241 break;
242 case NET_SOCKET_ACCEPTED:
243 // push the new socket identifier
244 fibril_mutex_lock(&socket->accept_lock);
245 if(! ERROR_OCCURRED(dyn_fifo_push(&socket->accepted, 1, SOCKET_MAX_ACCEPTED_SIZE))){
246 // signal the accepted socket
247 fibril_condvar_signal(&socket->accept_signal);
248 }
249 fibril_mutex_unlock(&socket->accept_lock);
250 break;
251 default:
252 ERROR_CODE = ENOTSUP;
253 }
254 if((SOCKET_GET_DATA_FRAGMENT_SIZE(call) > 0)
255 && (SOCKET_GET_DATA_FRAGMENT_SIZE(call) != socket->data_fragment_size)){
256 fibril_rwlock_write_lock(&socket->sending_lock);
257 // set the data fragment size
258 socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE(call);
259 fibril_rwlock_write_unlock(&socket->sending_lock);
260 }
261 }
262 fibril_rwlock_read_unlock(&socket_globals.lock);
263 break;
264 default:
265 ERROR_CODE = ENOTSUP;
266 }
267 ipc_answer_0(callid, (ipcarg_t) ERROR_CODE);
268 }
269}
270
271/** Returns the TCP module phone.
272 * Connects to the TCP module if necessary.
273 * @returns The TCP module phone.
274 * @returns Other error codes as defined for the bind_service_timeout() function.
275 */
276static int socket_get_tcp_phone(void){
277 if(socket_globals.tcp_phone < 0){
278 socket_globals.tcp_phone = bind_service_timeout(SERVICE_TCP, 0, 0, SERVICE_TCP, socket_connection, SOCKET_CONNECT_TIMEOUT);
279 }
280 return socket_globals.tcp_phone;
281}
282
283/** Returns the UDP module phone.
284 * Connects to the UDP module if necessary.
285 * @returns The UDP module phone.
286 * @returns Other error codes as defined for the bind_service_timeout() function.
287 */
288static int socket_get_udp_phone(void){
289 if(socket_globals.udp_phone < 0){
290 socket_globals.udp_phone = bind_service_timeout(SERVICE_UDP, 0, 0, SERVICE_UDP, socket_connection, SOCKET_CONNECT_TIMEOUT);
291 }
292 return socket_globals.udp_phone;
293}
294
295/** Tries to find a new free socket identifier.
296 * @returns The new socket identifier.
297 * @returns ELIMIT if there is no socket identifier available.
298 */
299static int socket_generate_new_id(void){
300 sockets_ref sockets;
301 int socket_id = 0;
302 int count;
303
304 sockets = socket_get_sockets();
305 count = 0;
306// socket_id = socket_globals.last_id;
307 do{
308 if(count < SOCKET_ID_TRIES){
309 socket_id = rand() % INT_MAX;
310 ++ count;
311 }else if(count == SOCKET_ID_TRIES){
312 socket_id = 1;
313 ++ count;
314 // only this branch for last_id
315 }else{
316 if(socket_id < INT_MAX){
317 ++ socket_id;
318/* }else if(socket_globals.last_id){
319* socket_globals.last_id = 0;
320* socket_id = 1;
321*/ }else{
322 return ELIMIT;
323 }
324 }
325 }while(sockets_find(sockets, socket_id));
326// last_id = socket_id
327 return socket_id;
328}
329
330/** Initializes a new socket specific data.
331 * @param[in,out] socket The socket to be initialized.
332 * @param[in] socket_id The new socket identifier.
333 * @param[in] phone The parent module phone.
334 * @param[in] service The parent module service.
335 */
336static void socket_initialize(socket_ref socket, int socket_id, int phone, services_t service){
337 socket->socket_id = socket_id;
338 socket->phone = phone;
339 socket->service = service;
340 dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE);
341 dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
342 fibril_mutex_initialize(&socket->receive_lock);
343 fibril_condvar_initialize(&socket->receive_signal);
344 fibril_mutex_initialize(&socket->accept_lock);
345 fibril_condvar_initialize(&socket->accept_signal);
346 fibril_rwlock_initialize(&socket->sending_lock);
347}
348
349int socket(int domain, int type, int protocol){
350 ERROR_DECLARE;
351
352 socket_ref socket;
353 int phone;
354 int socket_id;
355 services_t service;
356 ipcarg_t fragment_size;
357 ipcarg_t header_size;
358
359 // find the appropriate service
360 switch(domain){
361 case PF_INET:
362 switch(type){
363 case SOCK_STREAM:
364 if(! protocol){
365 protocol = IPPROTO_TCP;
366 }
367 switch(protocol){
368 case IPPROTO_TCP:
369 phone = socket_get_tcp_phone();
370 service = SERVICE_TCP;
371 break;
372 default:
373 return EPROTONOSUPPORT;
374 }
375 break;
376 case SOCK_DGRAM:
377 if(! protocol){
378 protocol = IPPROTO_UDP;
379 }
380 switch(protocol){
381 case IPPROTO_UDP:
382 phone = socket_get_udp_phone();
383 service = SERVICE_UDP;
384 break;
385 default:
386 return EPROTONOSUPPORT;
387 }
388 break;
389 case SOCK_RAW:
390 default:
391 return ESOCKTNOSUPPORT;
392 }
393 break;
394 // TODO IPv6
395 default:
396 return EPFNOSUPPORT;
397 }
398 if(phone < 0){
399 return phone;
400 }
401 // create a new socket structure
402 socket = (socket_ref) malloc(sizeof(socket_t));
403 if(! socket){
404 return ENOMEM;
405 }
406 bzero(socket, sizeof(*socket));
407 fibril_rwlock_write_lock(&socket_globals.lock);
408 // request a new socket
409 socket_id = socket_generate_new_id();
410 if(socket_id <= 0){
411 fibril_rwlock_write_unlock(&socket_globals.lock);
412 free(socket);
413 return socket_id;
414 }
415 if(ERROR_OCCURRED((int) async_req_3_3(phone, NET_SOCKET, socket_id, 0, service, NULL, &fragment_size, &header_size))){
416 fibril_rwlock_write_unlock(&socket_globals.lock);
417 free(socket);
418 return ERROR_CODE;
419 }
420 socket->data_fragment_size = (size_t) fragment_size;
421 socket->header_size = (size_t) header_size;
422 // finish the new socket initialization
423 socket_initialize(socket, socket_id, phone, service);
424 // store the new socket
425 ERROR_CODE = sockets_add(socket_get_sockets(), socket_id, socket);
426 fibril_rwlock_write_unlock(&socket_globals.lock);
427 if(ERROR_CODE < 0){
428 dyn_fifo_destroy(&socket->received);
429 dyn_fifo_destroy(&socket->accepted);
430 free(socket);
431 async_msg_3(phone, NET_SOCKET_CLOSE, (ipcarg_t) socket_id, 0, service);
432 return ERROR_CODE;
433 }
434
435 return socket_id;
436}
437
438/** Sends message to the socket parent module with specified data.
439 * @param[in] socket_id Socket identifier.
440 * @param[in] message The action message.
441 * @param[in] arg2 The second message parameter.
442 * @param[in] data The data to be sent.
443 * @param[in] datalength The data length.
444 * @returns EOK on success.
445 * @returns ENOTSOCK if the socket is not found.
446 * @returns EBADMEM if the data parameter is NULL.
447 * @returns NO_DATA if the datalength parameter is zero (0).
448 * @returns Other error codes as defined for the spcific message.
449 */
450static int socket_send_data(int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength){
451 socket_ref socket;
452 aid_t message_id;
453 ipcarg_t result;
454
455 if(! data){
456 return EBADMEM;
457 }
458 if(! datalength){
459 return NO_DATA;
460 }
461
462 fibril_rwlock_read_lock(&socket_globals.lock);
463 // find the socket
464 socket = sockets_find(socket_get_sockets(), socket_id);
465 if(! socket){
466 fibril_rwlock_read_unlock(&socket_globals.lock);
467 return ENOTSOCK;
468 }
469 // request the message
470 message_id = async_send_3(socket->phone, message, (ipcarg_t) socket->socket_id, arg2, socket->service, NULL);
471 // send the address
472 async_data_write_start(socket->phone, data, datalength);
473 fibril_rwlock_read_unlock(&socket_globals.lock);
474 async_wait_for(message_id, &result);
475 return (int) result;
476}
477
478int bind(int socket_id, const struct sockaddr * my_addr, socklen_t addrlen){
479 if(addrlen <= 0){
480 return EINVAL;
481 }
482 // send the address
483 return socket_send_data(socket_id, NET_SOCKET_BIND, 0, my_addr, (size_t) addrlen);
484}
485
486int listen(int socket_id, int backlog){
487 socket_ref socket;
488 int result;
489
490 if(backlog <= 0){
491 return EINVAL;
492 }
493 fibril_rwlock_read_lock(&socket_globals.lock);
494 // find the socket
495 socket = sockets_find(socket_get_sockets(), socket_id);
496 if(! socket){
497 fibril_rwlock_read_unlock(&socket_globals.lock);
498 return ENOTSOCK;
499 }
500 // request listen backlog change
501 result = (int) async_req_3_0(socket->phone, NET_SOCKET_LISTEN, (ipcarg_t) socket->socket_id, (ipcarg_t) backlog, socket->service);
502 fibril_rwlock_read_unlock(&socket_globals.lock);
503 return result;
504}
505
506int accept(int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen){
507 socket_ref socket;
508 socket_ref new_socket;
509 aid_t message_id;
510 ipcarg_t ipc_result;
511 int result;
512 ipc_call_t answer;
513
514 if((! cliaddr) || (! addrlen)){
515 return EBADMEM;
516 }
517
518 fibril_rwlock_write_lock(&socket_globals.lock);
519 // find the socket
520 socket = sockets_find(socket_get_sockets(), socket_id);
521 if(! socket){
522 fibril_rwlock_write_unlock(&socket_globals.lock);
523 return ENOTSOCK;
524 }
525 fibril_mutex_lock(&socket->accept_lock);
526 // wait for an accepted socket
527 ++ socket->blocked;
528 while(dyn_fifo_value(&socket->accepted) <= 0){
529 fibril_rwlock_write_unlock(&socket_globals.lock);
530 fibril_condvar_wait(&socket->accept_signal, &socket->accept_lock);
531 // drop the accept lock to avoid deadlock
532 fibril_mutex_unlock(&socket->accept_lock);
533 fibril_rwlock_write_lock(&socket_globals.lock);
534 fibril_mutex_lock(&socket->accept_lock);
535 }
536 -- socket->blocked;
537
538 // create a new scoket
539 new_socket = (socket_ref) malloc(sizeof(socket_t));
540 if(! new_socket){
541 fibril_mutex_unlock(&socket->accept_lock);
542 fibril_rwlock_write_unlock(&socket_globals.lock);
543 return ENOMEM;
544 }
545 bzero(new_socket, sizeof(*new_socket));
546 socket_id = socket_generate_new_id();
547 if(socket_id <= 0){
548 fibril_mutex_unlock(&socket->accept_lock);
549 fibril_rwlock_write_unlock(&socket_globals.lock);
550 free(new_socket);
551 return socket_id;
552 }
553 socket_initialize(new_socket, socket_id, socket->phone, socket->service);
554 result = sockets_add(socket_get_sockets(), new_socket->socket_id, new_socket);
555 if(result < 0){
556 fibril_mutex_unlock(&socket->accept_lock);
557 fibril_rwlock_write_unlock(&socket_globals.lock);
558 free(new_socket);
559 return result;
560 }
561
562 // request accept
563 message_id = async_send_5(socket->phone, NET_SOCKET_ACCEPT, (ipcarg_t) socket->socket_id, 0, socket->service, 0, new_socket->socket_id, &answer);
564 // read address
565 ipc_data_read_start(socket->phone, cliaddr, * addrlen);
566 fibril_rwlock_write_unlock(&socket_globals.lock);
567 async_wait_for(message_id, &ipc_result);
568 result = (int) ipc_result;
569 if(result > 0){
570 if(result != socket_id){
571 result = EINVAL;
572 }
573 // dequeue the accepted socket if successful
574 dyn_fifo_pop(&socket->accepted);
575 // set address length
576 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
577 new_socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
578 }else if(result == ENOTSOCK){
579 // empty the queue if no accepted sockets
580 while(dyn_fifo_pop(&socket->accepted) > 0);
581 }
582 fibril_mutex_unlock(&socket->accept_lock);
583 return result;
584}
585
586int connect(int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen){
587 if(! serv_addr){
588 return EDESTADDRREQ;
589 }
590 if(! addrlen){
591 return EDESTADDRREQ;
592 }
593 // send the address
594 return socket_send_data(socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen);
595}
596
597/** Clears and destroys the socket.
598 * @param[in] socket The socket to be destroyed.
599 */
600static void socket_destroy(socket_ref socket){
601 int accepted_id;
602
603 // destroy all accepted sockets
604 while((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0){
605 socket_destroy(sockets_find(socket_get_sockets(), accepted_id));
606 }
607 dyn_fifo_destroy(&socket->received);
608 dyn_fifo_destroy(&socket->accepted);
609 sockets_exclude(socket_get_sockets(), socket->socket_id);
610}
611
612int closesocket(int socket_id){
613 ERROR_DECLARE;
614
615 socket_ref socket;
616
617 fibril_rwlock_write_lock(&socket_globals.lock);
618 socket = sockets_find(socket_get_sockets(), socket_id);
619 if(! socket){
620 fibril_rwlock_write_unlock(&socket_globals.lock);
621 return ENOTSOCK;
622 }
623 if(socket->blocked){
624 fibril_rwlock_write_unlock(&socket_globals.lock);
625 return EINPROGRESS;
626 }
627 // request close
628 ERROR_PROPAGATE((int) async_req_3_0(socket->phone, NET_SOCKET_CLOSE, (ipcarg_t) socket->socket_id, 0, socket->service));
629 // free the socket structure
630 socket_destroy(socket);
631 fibril_rwlock_write_unlock(&socket_globals.lock);
632 return EOK;
633}
634
635/** Sends data via the socket to the remote address.
636 * Binds the socket to a free port if not already connected/bound.
637 * @param[in] message The action message.
638 * @param[in] socket_id Socket identifier.
639 * @param[in] data The data to be sent.
640 * @param[in] datalength The data length.
641 * @param[in] flags Various send flags.
642 * @param[in] toaddr The destination address. May be NULL for connected sockets.
643 * @param[in] addrlen The address length. Used only if toaddr is not NULL.
644 * @returns EOK on success.
645 * @returns ENOTSOCK if the socket is not found.
646 * @returns EBADMEM if the data or toaddr parameter is NULL.
647 * @returns NO_DATA if the datalength or the addrlen parameter is zero (0).
648 * @returns Other error codes as defined for the NET_SOCKET_SENDTO message.
649 */
650static int sendto_core(ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen){
651 socket_ref socket;
652 aid_t message_id;
653 ipcarg_t result;
654 size_t fragments;
655 ipc_call_t answer;
656
657 if(! data){
658 return EBADMEM;
659 }
660 if(! datalength){
661 return NO_DATA;
662 }
663 fibril_rwlock_read_lock(&socket_globals.lock);
664 // find socket
665 socket = sockets_find(socket_get_sockets(), socket_id);
666 if(! socket){
667 fibril_rwlock_read_unlock(&socket_globals.lock);
668 return ENOTSOCK;
669 }
670 fibril_rwlock_read_lock(&socket->sending_lock);
671 // compute data fragment count
672 if(socket->data_fragment_size > 0){
673 fragments = (datalength + socket->header_size) / socket->data_fragment_size;
674 if((datalength + socket->header_size) % socket->data_fragment_size) ++ fragments;
675 }else{
676 fragments = 1;
677 }
678 // request send
679 message_id = async_send_5(socket->phone, message, (ipcarg_t) socket->socket_id, (fragments == 1 ? datalength : socket->data_fragment_size), socket->service, (ipcarg_t) flags, fragments, &answer);
680 // send the address if given
681 if((! toaddr) || (async_data_write_start(socket->phone, toaddr, addrlen) == EOK)){
682 if(fragments == 1){
683 // send all if only one fragment
684 async_data_write_start(socket->phone, data, datalength);
685 }else{
686 // send the first fragment
687 async_data_write_start(socket->phone, data, socket->data_fragment_size - socket->header_size);
688 data = ((const uint8_t *) data) + socket->data_fragment_size - socket->header_size;
689 // send the middle fragments
690 while((-- fragments) > 1){
691 async_data_write_start(socket->phone, data, socket->data_fragment_size);
692 data = ((const uint8_t *) data) + socket->data_fragment_size;
693 }
694 // send the last fragment
695 async_data_write_start(socket->phone, data, (datalength + socket->header_size) % socket->data_fragment_size);
696 }
697 }
698 async_wait_for(message_id, &result);
699 if((SOCKET_GET_DATA_FRAGMENT_SIZE(answer) > 0)
700 && (SOCKET_GET_DATA_FRAGMENT_SIZE(answer) != socket->data_fragment_size)){
701 // set the data fragment size
702 socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
703 }
704 fibril_rwlock_read_unlock(&socket->sending_lock);
705 fibril_rwlock_read_unlock(&socket_globals.lock);
706 return (int) result;
707}
708
709int send(int socket_id, void * data, size_t datalength, int flags){
710 // without the address
711 return sendto_core(NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0);
712}
713
714int sendto(int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen){
715 if(! toaddr){
716 return EDESTADDRREQ;
717 }
718 if(! addrlen){
719 return EDESTADDRREQ;
720 }
721 // with the address
722 return sendto_core(NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen);
723}
724
725/** Receives data via the socket.
726 * @param[in] message The action message.
727 * @param[in] socket_id Socket identifier.
728 * @param[out] data The data buffer to be filled.
729 * @param[in] datalength The data length.
730 * @param[in] flags Various receive flags.
731 * @param[out] fromaddr The source address. May be NULL for connected sockets.
732 * @param[in,out] addrlen The address length. The maximum address length is read. The actual address length is set. Used only if fromaddr is not NULL.
733 * @returns EOK on success.
734 * @returns ENOTSOCK if the socket is not found.
735 * @returns EBADMEM if the data parameter is NULL.
736 * @returns NO_DATA if the datalength or addrlen parameter is zero (0).
737 * @returns Other error codes as defined for the spcific message.
738 */
739static int recvfrom_core(ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen){
740 socket_ref socket;
741 aid_t message_id;
742 ipcarg_t ipc_result;
743 int result;
744 size_t fragments;
745 size_t * lengths;
746 size_t index;
747 ipc_call_t answer;
748
749 if(! data){
750 return EBADMEM;
751 }
752 if(! datalength){
753 return NO_DATA;
754 }
755 if(fromaddr && (! addrlen)){
756 return EINVAL;
757 }
758 fibril_rwlock_read_lock(&socket_globals.lock);
759 // find the socket
760 socket = sockets_find(socket_get_sockets(), socket_id);
761 if(! socket){
762 fibril_rwlock_read_unlock(&socket_globals.lock);
763 return ENOTSOCK;
764 }
765 fibril_mutex_lock(&socket->receive_lock);
766 // wait for a received packet
767 ++ socket->blocked;
768 while((result = dyn_fifo_value(&socket->received)) <= 0){
769 fibril_rwlock_read_unlock(&socket_globals.lock);
770 fibril_condvar_wait(&socket->receive_signal, &socket->receive_lock);
771 // drop the receive lock to avoid deadlock
772 fibril_mutex_unlock(&socket->receive_lock);
773 fibril_rwlock_read_lock(&socket_globals.lock);
774 fibril_mutex_lock(&socket->receive_lock);
775 }
776 -- socket->blocked;
777 fragments = (size_t) result;
778 // prepare lengths if more fragments
779 if(fragments > 1){
780 lengths = (size_t *) malloc(sizeof(size_t) * fragments + sizeof(size_t));
781 if(! lengths){
782 fibril_mutex_unlock(&socket->receive_lock);
783 fibril_rwlock_read_unlock(&socket_globals.lock);
784 return ENOMEM;
785 }
786 // request packet data
787 message_id = async_send_4(socket->phone, message, (ipcarg_t) socket->socket_id, 0, socket->service, (ipcarg_t) flags, &answer);
788 // read the address if desired
789 if((! fromaddr) || (async_data_read_start(socket->phone, fromaddr, * addrlen) == EOK)){
790 // read the fragment lengths
791 if(async_data_read_start(socket->phone, lengths, sizeof(int) * (fragments + 1)) == EOK){
792 if(lengths[fragments] <= datalength){
793 // read all fragments if long enough
794 for(index = 0; index < fragments; ++ index){
795 async_data_read_start(socket->phone, data, lengths[index]);
796 data = ((uint8_t *) data) + lengths[index];
797 }
798 }
799 }
800 }
801 free(lengths);
802 }else{
803 // request packet data
804 message_id = async_send_4(socket->phone, message, (ipcarg_t) socket->socket_id, 0, socket->service, (ipcarg_t) flags, &answer);
805 // read the address if desired
806 if((! fromaddr) || (async_data_read_start(socket->phone, fromaddr, * addrlen) == EOK)){
807 // read all if only one fragment
808 async_data_read_start(socket->phone, data, datalength);
809 }
810 }
811 async_wait_for(message_id, &ipc_result);
812 result = (int) ipc_result;
813 // if successful
814 if(result == EOK){
815 // dequeue the received packet
816 dyn_fifo_pop(&socket->received);
817 // return read data length
818 result = SOCKET_GET_READ_DATA_LENGTH(answer);
819 // set address length
820 if(fromaddr && addrlen){
821 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
822 }
823 }
824 fibril_mutex_unlock(&socket->receive_lock);
825 fibril_rwlock_read_unlock(&socket_globals.lock);
826 return result;
827}
828
829int recv(int socket_id, void * data, size_t datalength, int flags){
830 // without the address
831 return recvfrom_core(NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL);
832}
833
834int recvfrom(int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen){
835 if(! fromaddr){
836 return EBADMEM;
837 }
838 if(! addrlen){
839 return NO_DATA;
840 }
841 // with the address
842 return recvfrom_core(NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen);
843}
844
845int getsockopt(int socket_id, int level, int optname, void * value, size_t * optlen){
846 socket_ref socket;
847 aid_t message_id;
848 ipcarg_t result;
849
850 if(!(value && optlen)){
851 return EBADMEM;
852 }
853 if(!(*optlen)){
854 return NO_DATA;
855 }
856 fibril_rwlock_read_lock(&socket_globals.lock);
857 // find the socket
858 socket = sockets_find(socket_get_sockets(), socket_id);
859 if(! socket){
860 fibril_rwlock_read_unlock(&socket_globals.lock);
861 return ENOTSOCK;
862 }
863 // request option value
864 message_id = async_send_3(socket->phone, NET_SOCKET_GETSOCKOPT, (ipcarg_t) socket->socket_id, (ipcarg_t) optname, socket->service, NULL);
865 // read the length
866 if(async_data_read_start(socket->phone, optlen, sizeof(*optlen)) == EOK){
867 // read the value
868 async_data_read_start(socket->phone, value, * optlen);
869 }
870 fibril_rwlock_read_unlock(&socket_globals.lock);
871 async_wait_for(message_id, &result);
872 return (int) result;
873}
874
875int setsockopt(int socket_id, int level, int optname, const void * value, size_t optlen){
876 // send the value
877 return socket_send_data(socket_id, NET_SOCKET_SETSOCKOPT, (ipcarg_t) optname, value, optlen);
878
879}
880
881/** @}
882 */
Note: See TracBrowser for help on using the repository browser.