source: mainline/uspace/srv/net/socket/socket_client.c@ 1a0fb3f8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1a0fb3f8 was 1a0fb3f8, checked in by Lukas Mejdrech <lukasmejdrech@…>, 15 years ago

+ icmp and libsocket timeouting connecting

  • Property mode set to 100644
File size: 25.9 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
43#include <ipc/services.h>
44
45#include "../err.h"
46#include "../modules.h"
47
48#include "../include/in.h"
49#include "../include/socket.h"
50#include "../include/socket_errno.h"
51
52#include "../structures/dynamic_fifo.h"
53#include "../structures/int_map.h"
54
55#include "socket_messages.h"
56
57/** Initial received packet queue size.
58 */
59#define SOCKET_INITIAL_RECEIVED_SIZE 4
60
61/** Maximum received packet queue size.
62 */
63#define SOCKET_MAX_RECEIVED_SIZE 0
64
65/** Initial waiting sockets queue size.
66 */
67#define SOCKET_INITIAL_ACCEPTED_SIZE 1
68
69/** Maximum waiting sockets queue size.
70 */
71#define SOCKET_MAX_ACCEPTED_SIZE 0
72
73/** Default timeout for connections in microseconds.
74 */
75#define SOCKET_CONNECT_TIMEOUT ( 1 * 1000 * 1000 )
76
77/** Type definition of the socket specific data.
78 * @see socket
79 */
80typedef struct socket socket_t;
81
82/** Type definition of the socket specific data pointer.
83 * @see socket
84 */
85typedef socket_t * socket_ref;
86
87/** Socket specific data.
88 * Each socket lock locks only its structure part and any number of them may be locked simultaneously.
89 */
90struct socket{
91 /** Socket identifier.
92 */
93 int socket_id;
94 /** Parent module phone.
95 */
96 int phone;
97 /** Parent module service.
98 */
99 services_t service;
100 /** Underlying protocol header size.
101 * Sending and receiving optimalization.
102 */
103 size_t header_size;
104 /** Packet data fragment size.
105 * Sending optimalization.
106 */
107 size_t data_fragment_size;
108 /** Sending safety lock.
109 * Locks the header_size and data_fragment_size attributes.
110 */
111 fibril_rwlock_t sending_lock;
112 /** Received packets queue.
113 */
114 dyn_fifo_t received;
115 /** Received packets safety lock.
116 * Used for receiving and receive notifications.
117 * Locks the received attribute.
118 */
119 fibril_mutex_t receive_lock;
120 /** Received packets signaling.
121 * Signaled upon receive notification.
122 */
123 fibril_condvar_t receive_signal;
124 /** Waiting sockets queue.
125 */
126 dyn_fifo_t accepted;
127 /** Waiting sockets safety lock.
128 * Used for accepting and accept notifications.
129 * Locks the accepted attribute.
130 */
131 fibril_mutex_t accept_lock;
132 /** Waiting sockets signaling.
133 * Signaled upon accept notification.
134 */
135 fibril_condvar_t accept_signal;
136 /** The number of blocked functions called.
137 * Used while waiting for the received packets or accepted sockets.
138 */
139 int blocked;
140};
141
142/** Sockets map.
143 * Maps socket identifiers to the socket specific data.
144 * @see int_map.h
145 */
146INT_MAP_DECLARE( sockets, socket_t );
147
148/** Socket client library global data.
149 */
150static struct socket_client_globals {
151 /** TCP module phone.
152 */
153 int tcp_phone;
154 /** UDP module phone.
155 */
156 int udp_phone;
157 /** Active sockets.
158 */
159 sockets_ref sockets;
160 /** Safety lock.
161 * Write lock is used only for adding or removing sockets.
162 * When locked for writing, no other socket locks need to be locked.
163 * When locked for reading, any other socket locks may be locked.
164 * No socket lock may be locked if this lock is unlocked.
165 */
166 fibril_rwlock_t lock;
167} socket_globals = {
168 .tcp_phone = -1,
169 .udp_phone = -1,
170 .sockets = NULL,
171 .lock = {
172 .readers = 0,
173 .writers = 0,
174 .waiters = {
175 .prev = & socket_globals.lock.waiters,
176 .next = & socket_globals.lock.waiters
177 }
178 }
179};
180
181INT_MAP_IMPLEMENT( sockets, socket_t );
182
183/** Returns the TCP module phone.
184 * Connects to the TCP module if necessary.
185 * @returns The TCP module phone.
186 * @returns Other error codes as defined for the bind_service_timeout() function.
187 */
188static int socket_get_tcp_phone( void );
189
190/** Returns the UDP module phone.
191 * Connects to the UDP module if necessary.
192 * @returns The UDP module phone.
193 * @returns Other error codes as defined for the bind_service_timeout() function.
194 */
195static int socket_get_udp_phone( void );
196
197/** Returns the active sockets.
198 * @returns The active sockets.
199 */
200static sockets_ref socket_get_sockets( void );
201
202/** Default thread for new connections.
203 * @param[in] iid The initial message identifier.
204 * @param[in] icall The initial message call structure.
205 */
206void socket_connection( ipc_callid_t iid, ipc_call_t * icall );
207
208/** Sends message to the socket parent module with specified data.
209 * @param[in] socket_id Socket identifier.
210 * @param[in] message The action message.
211 * @param[in] arg2 The second message parameter.
212 * @param[in] data The data to be sent.
213 * @param[in] datalength The data length.
214 * @returns EOK on success.
215 * @returns ENOTSOCK if the socket is not found.
216 * @returns EBADMEM if the data parameter is NULL.
217 * @returns NO_DATA if the datalength parameter is zero (0).
218 * @returns Other error codes as defined for the spcific message.
219 */
220int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength );
221
222/** Initializes a new socket specific data.
223 * @param[in,out] socket The socket to be initialized.
224 * @param[in] socket_id The new socket identifier.
225 * @param[in] phone The parent module phone.
226 * @param[in] service The parent module service.
227 */
228void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service );
229
230/** Clears and destroys the socket.
231 * @param[in] socket The socket to be destroyed.
232 */
233void socket_destroy( socket_ref socket );
234
235/** Receives data via the socket.
236 * @param[in] message The action message.
237 * @param[in] socket_id Socket identifier.
238 * @param[out] data The data buffer to be filled.
239 * @param[in] datalength The data length.
240 * @param[in] flags Various receive flags.
241 * @param[out] fromaddr The source address. May be NULL for connected sockets.
242 * @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.
243 * @returns EOK on success.
244 * @returns ENOTSOCK if the socket is not found.
245 * @returns EBADMEM if the data parameter is NULL.
246 * @returns NO_DATA if the datalength or addrlen parameter is zero (0).
247 * @returns Other error codes as defined for the spcific message.
248 */
249int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen );
250
251/** Sends data via the socket to the remote address.
252 * Binds the socket to a free port if not already connected/bound.
253 * @param[in] message The action message.
254 * @param[in] socket_id Socket identifier.
255 * @param[in] data The data to be sent.
256 * @param[in] datalength The data length.
257 * @param[in] flags Various send flags.
258 * @param[in] toaddr The destination address. May be NULL for connected sockets.
259 * @param[in] addrlen The address length. Used only if toaddr is not NULL.
260 * @returns EOK on success.
261 * @returns ENOTSOCK if the socket is not found.
262 * @returns EBADMEM if the data or toaddr parameter is NULL.
263 * @returns NO_DATA if the datalength or the addrlen parameter is zero (0).
264 * @returns Other error codes as defined for the NET_SOCKET_SENDTO message.
265 */
266int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen );
267
268static int socket_get_tcp_phone( void ){
269 if( socket_globals.tcp_phone < 0 ){
270 socket_globals.tcp_phone = bind_service_timeout( SERVICE_TCP, 0, 0, SERVICE_TCP, socket_connection, SOCKET_CONNECT_TIMEOUT );
271 }
272 return socket_globals.tcp_phone;
273}
274
275static int socket_get_udp_phone( void ){
276 if( socket_globals.udp_phone < 0 ){
277 socket_globals.udp_phone = bind_service_timeout( SERVICE_UDP, 0, 0, SERVICE_UDP, socket_connection, SOCKET_CONNECT_TIMEOUT );
278 }
279 return socket_globals.udp_phone;
280}
281
282static sockets_ref socket_get_sockets( void ){
283 if( ! socket_globals.sockets ){
284 socket_globals.sockets = ( sockets_ref ) malloc( sizeof( sockets_t ));
285 if( ! socket_globals.sockets ) return NULL;
286 if( sockets_initialize( socket_globals.sockets ) != EOK ){
287 free( socket_globals.sockets );
288 socket_globals.sockets = NULL;
289 }
290 }
291 return socket_globals.sockets;
292}
293
294void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ){
295 socket->socket_id = socket_id;
296 socket->phone = phone;
297 socket->service = service;
298 dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE );
299 dyn_fifo_initialize( & socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE );
300 fibril_mutex_initialize( & socket->receive_lock );
301 fibril_condvar_initialize( & socket->receive_signal );
302 fibril_mutex_initialize( & socket->accept_lock );
303 fibril_condvar_initialize( & socket->accept_signal );
304 fibril_rwlock_initialize( & socket->sending_lock );
305}
306
307void socket_connection( ipc_callid_t iid, ipc_call_t * icall ){
308 ERROR_DECLARE;
309
310 ipc_callid_t callid;
311 ipc_call_t call;
312 socket_ref socket;
313 socket_ref new_socket;
314
315 while( true ){
316
317 callid = async_get_call( & call );
318 switch( IPC_GET_METHOD( call )){
319 case NET_SOCKET_RECEIVED:
320 fibril_rwlock_read_lock( & socket_globals.lock );
321 // find the socket
322 socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
323 if( ! socket ){
324 ERROR_CODE = ENOTSOCK;
325 }else{
326 fibril_mutex_lock( & socket->receive_lock );
327 // push the number of received packet fragments
328 if( ! ERROR_OCCURRED( dyn_fifo_push( & socket->received, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_MAX_RECEIVED_SIZE ))){
329 // signal the received packet
330 fibril_condvar_signal( & socket->receive_signal );
331 }
332 fibril_mutex_unlock( & socket->receive_lock );
333 }
334 fibril_rwlock_read_unlock( & socket_globals.lock );
335 break;
336 case NET_SOCKET_ACCEPTED:
337 fibril_rwlock_read_lock( & socket_globals.lock );
338 // find the socket
339 socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
340 if( ! socket ){
341 ERROR_CODE = ENOTSOCK;
342 }else{
343 // create a new scoket
344 new_socket = ( socket_ref ) malloc( sizeof( socket_t ));
345 if( ! new_socket ){
346 ERROR_CODE = ENOMEM;
347 }else{
348 bzero( new_socket, sizeof( * new_socket ));
349 socket_initialize( new_socket, SOCKET_GET_NEW_SOCKET_ID( call ), socket->phone, socket->service );
350 ERROR_CODE = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket );
351 if( ERROR_CODE < 0 ){
352 free( new_socket );
353 }else{
354 // push the new socket identifier
355 fibril_mutex_lock( & socket->accept_lock );
356 if( ERROR_OCCURRED( dyn_fifo_push( & socket->accepted, new_socket->socket_id, SOCKET_MAX_ACCEPTED_SIZE ))){
357 sockets_exclude( socket_get_sockets(), new_socket->socket_id );
358 }else{
359 // signal the accepted socket
360 fibril_condvar_signal( & socket->accept_signal );
361 }
362 fibril_mutex_unlock( & socket->accept_lock );
363 ERROR_CODE = EOK;
364 }
365 }
366 }
367 fibril_rwlock_read_unlock( & socket_globals.lock );
368 break;
369 case NET_SOCKET_DATA_FRAGMENT_SIZE:
370 fibril_rwlock_read_lock( & socket_globals.lock );
371 // find the socket
372 socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
373 if( ! socket ){
374 ERROR_CODE = ENOTSOCK;
375 }else{
376 fibril_rwlock_write_lock( & socket->sending_lock );
377 // set the data fragment size
378 socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call );
379 fibril_rwlock_write_unlock( & socket->sending_lock );
380 ERROR_CODE = EOK;
381 }
382 fibril_rwlock_read_unlock( & socket_globals.lock );
383 break;
384 default:
385 ERROR_CODE = ENOTSUP;
386 }
387 ipc_answer_0( callid, ( ipcarg_t ) ERROR_CODE );
388 }
389}
390
391int socket( int domain, int type, int protocol ){
392 ERROR_DECLARE;
393
394 socket_ref socket;
395 int phone;
396 int socket_id;
397 services_t service;
398
399 // find the appropriate service
400 switch( domain ){
401 case PF_INET:
402 switch( type ){
403 case SOCK_STREAM:
404 if( ! protocol ) protocol = IPPROTO_TCP;
405 switch( protocol ){
406 case IPPROTO_TCP:
407 phone = socket_get_tcp_phone();
408 service = SERVICE_TCP;
409 break;
410 default:
411 return EPROTONOSUPPORT;
412 }
413 break;
414 case SOCK_DGRAM:
415 if( ! protocol ) protocol = IPPROTO_UDP;
416 switch( protocol ){
417 case IPPROTO_UDP:
418 phone = socket_get_udp_phone();
419 service = SERVICE_UDP;
420 break;
421 default:
422 return EPROTONOSUPPORT;
423 }
424 break;
425 case SOCK_RAW:
426 default:
427 return ESOCKTNOSUPPORT;
428 }
429 break;
430 // TODO IPv6
431 default:
432 return EPFNOSUPPORT;
433 }
434 if( phone < 0 ) return phone;
435 // create a new socket structure
436 socket = ( socket_ref ) malloc( sizeof( socket_t ));
437 if( ! socket ) return ENOMEM;
438 bzero( socket, sizeof( * socket ));
439 // request a new socket
440 if( ERROR_OCCURRED(( int ) async_req_3_3( phone, NET_SOCKET, 0, 0, service, ( ipcarg_t * ) & socket_id, ( ipcarg_t * ) & socket->data_fragment_size, ( ipcarg_t * ) & socket->header_size ))){
441 free( socket );
442 return ERROR_CODE;
443 }
444 // finish the new socket initialization
445 socket_initialize( socket, socket_id, phone, service );
446 // store the new socket
447 fibril_rwlock_write_lock( & socket_globals.lock );
448 ERROR_CODE = sockets_add( socket_get_sockets(), socket_id, socket );
449 fibril_rwlock_write_unlock( & socket_globals.lock );
450 if( ERROR_CODE < 0 ){
451 dyn_fifo_destroy( & socket->received );
452 dyn_fifo_destroy( & socket->accepted );
453 free( socket );
454 async_msg_3( phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket_id, 0, service );
455 return ERROR_CODE;
456 }
457
458 return socket_id;
459}
460
461int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength ){
462 socket_ref socket;
463 aid_t message_id;
464 ipcarg_t result;
465
466 if( ! data ) return EBADMEM;
467 if( ! datalength ) return NO_DATA;
468
469 fibril_rwlock_read_lock( & socket_globals.lock );
470 // find the socket
471 socket = sockets_find( socket_get_sockets(), socket_id );
472 if( ! socket ){
473 fibril_rwlock_read_unlock( & socket_globals.lock );
474 return ENOTSOCK;
475 }
476 // request the message
477 message_id = async_send_3( socket->phone, message, ( ipcarg_t ) socket->socket_id, arg2, socket->service, NULL );
478 // send the address
479 async_data_write_start( socket->phone, data, datalength );
480 fibril_rwlock_read_unlock( & socket_globals.lock );
481 async_wait_for( message_id, & result );
482 return ( int ) result;
483}
484
485int bind( int socket_id, const struct sockaddr * my_addr, socklen_t addrlen ){
486 if( addrlen <= 0 ) return EINVAL;
487 // send the address
488 return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, ( size_t ) addrlen );
489}
490
491int listen( int socket_id, int backlog ){
492 socket_ref socket;
493 int result;
494
495 if( backlog <= 0 ) return EINVAL;
496 fibril_rwlock_read_lock( & socket_globals.lock );
497 // find the socket
498 socket = sockets_find( socket_get_sockets(), socket_id );
499 if( ! socket ){
500 fibril_rwlock_read_unlock( & socket_globals.lock );
501 return ENOTSOCK;
502 }
503 // request listen backlog change
504 result = ( int ) async_req_3_0( socket->phone, NET_SOCKET_LISTEN, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) backlog, socket->service );
505 fibril_rwlock_read_unlock( & socket_globals.lock );
506 return result;
507}
508
509int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){
510 socket_ref socket;
511 aid_t message_id;
512 int result;
513 ipc_call_t answer;
514
515 if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM;
516
517 fibril_rwlock_read_lock( & socket_globals.lock );
518 // find the socket
519 socket = sockets_find( socket_get_sockets(), socket_id );
520 if( ! socket ){
521 fibril_rwlock_read_unlock( & socket_globals.lock );
522 return ENOTSOCK;
523 }
524 fibril_mutex_lock( & socket->accept_lock );
525 // wait for an accepted socket
526 ++ socket->blocked;
527 while( dyn_fifo_value( & socket->accepted ) <= 0 ){
528 fibril_rwlock_read_unlock( & socket_globals.lock );
529 fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock );
530 fibril_rwlock_read_lock( & socket_globals.lock );
531 }
532 -- socket->blocked;
533 // request accept
534 message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, ( ipcarg_t ) socket->socket_id, 0, socket->service, & answer );
535 // read address
536 async_data_read_start( socket->phone, cliaddr, * addrlen );
537 fibril_rwlock_read_unlock( & socket_globals.lock );
538 async_wait_for( message_id, ( ipcarg_t * ) & result );
539 if( result > 0 ){
540 // dequeue the accepted socket if successful
541 dyn_fifo_pop( & socket->accepted );
542 // set address length
543 * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
544 }else if( result == ENOTSOCK ){
545 // empty the queue if no accepted sockets
546 while( dyn_fifo_pop( & socket->accepted ) > 0 );
547 }
548 fibril_mutex_unlock( & socket->accept_lock );
549 return result;
550}
551
552int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){
553 if( ! serv_addr ) return EDESTADDRREQ;
554 if( ! addrlen ) return EDESTADDRREQ;
555 // send the address
556 return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen );
557}
558
559int closesocket( int socket_id ){
560 ERROR_DECLARE;
561
562 socket_ref socket;
563
564 fibril_rwlock_write_lock( & socket_globals.lock );
565 socket = sockets_find( socket_get_sockets(), socket_id );
566 if( ! socket ){
567 fibril_rwlock_write_unlock( & socket_globals.lock );
568 return ENOTSOCK;
569 }
570 if( socket->blocked ){
571 fibril_rwlock_write_unlock( & socket_globals.lock );
572 return EINPROGRESS;
573 }
574 // request close
575 ERROR_PROPAGATE(( int ) async_req_3_0( socket->phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket->socket_id, 0, socket->service ));
576 // free the socket structure
577 socket_destroy( socket );
578 fibril_rwlock_write_unlock( & socket_globals.lock );
579 return EOK;
580}
581
582void socket_destroy( socket_ref socket ){
583 int accepted_id;
584
585 // destroy all accepted sockets
586 while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){
587 socket_destroy( sockets_find( socket_get_sockets(), accepted_id ));
588 }
589 dyn_fifo_destroy( & socket->received );
590 dyn_fifo_destroy( & socket->accepted );
591 sockets_exclude( socket_get_sockets(), socket->socket_id );
592}
593
594int send( int socket_id, void * data, size_t datalength, int flags ){
595 // without the address
596 return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 );
597}
598
599int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
600 if( ! toaddr ) return EDESTADDRREQ;
601 if( ! addrlen ) return EDESTADDRREQ;
602 // with the address
603 return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen );
604}
605
606int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
607 socket_ref socket;
608 aid_t message_id;
609 ipcarg_t result;
610 size_t fragments;
611
612 if( ! data ) return EBADMEM;
613 if( ! datalength ) return NO_DATA;
614 fibril_rwlock_read_lock( & socket_globals.lock );
615 // find socket
616 socket = sockets_find( socket_get_sockets(), socket_id );
617 if( ! socket ){
618 fibril_rwlock_read_unlock( & socket_globals.lock );
619 return ENOTSOCK;
620 }
621 fibril_rwlock_read_lock( & socket->sending_lock );
622 // compute data fragment count
623 fragments = ( datalength + socket->header_size ) / socket->data_fragment_size;
624 if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments;
625 // request send
626 message_id = async_send_5( socket->phone, message, ( ipcarg_t ) socket->socket_id, socket->data_fragment_size, socket->service, ( ipcarg_t ) flags, fragments, NULL );
627 // send the address if given
628 if(( ! toaddr ) || ( async_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){
629 if( fragments == 1 ){
630 // send all if only one fragment
631 async_data_write_start( socket->phone, data, datalength );
632 }else{
633 // send the first fragment
634 async_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size );
635 data = (( const uint8_t * ) data ) + socket->data_fragment_size - socket->header_size;
636 // send the middle fragments
637 while(( -- fragments ) > 1 ){
638 async_data_write_start( socket->phone, data, socket->data_fragment_size );
639 data = (( const uint8_t * ) data ) + socket->data_fragment_size;
640 }
641 // send the last fragment
642 async_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size );
643 }
644 }
645 fibril_rwlock_read_unlock( & socket->sending_lock );
646 fibril_rwlock_read_unlock( & socket_globals.lock );
647 async_wait_for( message_id, & result );
648 return ( int ) result;
649}
650
651int recv( int socket_id, void * data, size_t datalength, int flags ){
652 // without the address
653 return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL );
654}
655
656int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
657 if( ! fromaddr ) return EBADMEM;
658 if( ! addrlen ) return NO_DATA;
659 // with the address
660 return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen );
661}
662
663int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
664 socket_ref socket;
665 aid_t message_id;
666 int result;
667 size_t fragments;
668 size_t * lengths;
669 size_t index;
670 ipc_call_t answer;
671
672 if( ! data ) return EBADMEM;
673 if( ! datalength ) return NO_DATA;
674 if( fromaddr && ( ! addrlen )) return EINVAL;
675 fibril_rwlock_read_lock( & socket_globals.lock );
676 // find the socket
677 socket = sockets_find( socket_get_sockets(), socket_id );
678 if( ! socket ){
679 fibril_rwlock_read_unlock( & socket_globals.lock );
680 return ENOTSOCK;
681 }
682 fibril_mutex_lock( & socket->receive_lock );
683 // wait for a received packet
684 ++ socket->blocked;
685 while(( result = dyn_fifo_value( & socket->received )) <= 0 ){
686 fibril_rwlock_read_unlock( & socket_globals.lock );
687 fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock );
688 fibril_rwlock_read_lock( & socket_globals.lock );
689 }
690 -- socket->blocked;
691 fragments = ( size_t ) result;
692 // prepare lengths if more fragments
693 if( fragments > 1 ){
694 lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t ));
695 if( ! lengths ){
696 fibril_mutex_unlock( & socket->receive_lock );
697 fibril_rwlock_read_unlock( & socket_globals.lock );
698 return ENOMEM;
699 }
700 // request packet data
701 message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
702 // read the address if desired
703 if(( ! fromaddr ) || ( async_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
704 // read the fragment lengths
705 if( async_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){
706 if( lengths[ fragments ] <= datalength ){
707 // read all fragments if long enough
708 for( index = 0; index < fragments; ++ index ){
709 async_data_read_start( socket->phone, data, lengths[ index ] );
710 data = (( uint8_t * ) data ) + lengths[ index ];
711 }
712 }
713 }
714 }
715 free( lengths );
716 }else{
717 // request packet data
718 message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
719 // read the address if desired
720 if(( ! fromaddr ) || ( async_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
721 // read all if only one fragment
722 async_data_read_start( socket->phone, data, datalength );
723 }
724 }
725 async_wait_for( message_id, ( ipcarg_t * ) & result );
726 // if successful
727 if( result == EOK ){
728 // dequeue the received packet
729 dyn_fifo_pop( & socket->received );
730 // return read data length
731 result = SOCKET_GET_READ_DATA_LENGTH( answer );
732 // set address length
733 if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
734 }
735 fibril_mutex_unlock( & socket->receive_lock );
736 fibril_rwlock_read_unlock( & socket_globals.lock );
737 return result;
738}
739
740int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){
741 socket_ref socket;
742 aid_t message_id;
743 ipcarg_t result;
744
745 if( !( value && optlen )) return EBADMEM;
746 if( !( * optlen )) return NO_DATA;
747 fibril_rwlock_read_lock( & socket_globals.lock );
748 // find the socket
749 socket = sockets_find( socket_get_sockets(), socket_id );
750 if( ! socket ){
751 fibril_rwlock_read_unlock( & socket_globals.lock );
752 return ENOTSOCK;
753 }
754 // request option value
755 message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) optname, socket->service, NULL );
756 // read the length
757 if( async_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){
758 // read the value
759 async_data_read_start( socket->phone, value, * optlen );
760 }
761 fibril_rwlock_read_unlock( & socket_globals.lock );
762 async_wait_for( message_id, & result );
763 return ( int ) result;
764}
765
766int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){
767 // send the value
768 return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, ( ipcarg_t ) optname, value, optlen );
769
770}
771
772/** @}
773 */
Note: See TracBrowser for help on using the repository browser.