source: mainline/uspace/srv/net/socket/socket_client.c@ 01a9ef5

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