source: mainline/uspace/srv/net/socket/socket_client.c@ 91001e2

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

Fix a couple of stack corruptions in networking code.

  • 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 ipcarg_t ipc_result;
544 int result;
545 ipc_call_t answer;
546
547 if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM;
548
549 fibril_rwlock_write_lock( & socket_globals.lock );
550 // find the socket
551 socket = sockets_find( socket_get_sockets(), socket_id );
552 if( ! socket ){
553 fibril_rwlock_write_unlock( & socket_globals.lock );
554 return ENOTSOCK;
555 }
556 fibril_mutex_lock( & socket->accept_lock );
557 // wait for an accepted socket
558 ++ socket->blocked;
559 while( dyn_fifo_value( & socket->accepted ) <= 0 ){
560 fibril_rwlock_write_unlock( & socket_globals.lock );
561 fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock );
562 fibril_rwlock_write_lock( & socket_globals.lock );
563 }
564 -- socket->blocked;
565
566 // create a new scoket
567 new_socket = ( socket_ref ) malloc( sizeof( socket_t ));
568 if( ! new_socket ){
569 fibril_mutex_unlock( & socket->accept_lock );
570 fibril_rwlock_write_unlock( & socket_globals.lock );
571 return ENOMEM;
572 }
573 bzero( new_socket, sizeof( * new_socket ));
574 socket_id = socket_generate_new_id();
575 if( socket_id <= 0 ){
576 fibril_mutex_unlock( & socket->accept_lock );
577 fibril_rwlock_write_unlock( & socket_globals.lock );
578 free( new_socket );
579 return socket_id;
580 }
581 socket_initialize( new_socket, socket_id, socket->phone, socket->service );
582 result = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket );
583 if( result < 0 ){
584 fibril_mutex_unlock( & socket->accept_lock );
585 fibril_rwlock_write_unlock( & socket_globals.lock );
586 free( new_socket );
587 return result;
588 }
589
590 // request accept
591 message_id = async_send_5( socket->phone, NET_SOCKET_ACCEPT, ( ipcarg_t ) socket->socket_id, 0, socket->service, 0, new_socket->socket_id, & answer );
592 // read address
593 ipc_data_read_start( socket->phone, cliaddr, * addrlen );
594 fibril_rwlock_write_unlock( & socket_globals.lock );
595 async_wait_for( message_id, & ipc_result );
596 result = (int) ipc_result;
597 if( result > 0 ){
598 if( result != socket_id ){
599 result = EINVAL;
600 }
601 // dequeue the accepted socket if successful
602 dyn_fifo_pop( & socket->accepted );
603 // set address length
604 * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
605 new_socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( answer );
606 }else if( result == ENOTSOCK ){
607 // empty the queue if no accepted sockets
608 while( dyn_fifo_pop( & socket->accepted ) > 0 );
609 }
610 fibril_mutex_unlock( & socket->accept_lock );
611 return result;
612}
613
614int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){
615 if( ! serv_addr ) return EDESTADDRREQ;
616 if( ! addrlen ) return EDESTADDRREQ;
617 // send the address
618 return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen );
619}
620
621int closesocket( int socket_id ){
622 ERROR_DECLARE;
623
624 socket_ref socket;
625
626 fibril_rwlock_write_lock( & socket_globals.lock );
627 socket = sockets_find( socket_get_sockets(), socket_id );
628 if( ! socket ){
629 fibril_rwlock_write_unlock( & socket_globals.lock );
630 return ENOTSOCK;
631 }
632 if( socket->blocked ){
633 fibril_rwlock_write_unlock( & socket_globals.lock );
634 return EINPROGRESS;
635 }
636 // request close
637 ERROR_PROPAGATE(( int ) async_req_3_0( socket->phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket->socket_id, 0, socket->service ));
638 // free the socket structure
639 socket_destroy( socket );
640 fibril_rwlock_write_unlock( & socket_globals.lock );
641 return EOK;
642}
643
644void socket_destroy( socket_ref socket ){
645 int accepted_id;
646
647 // destroy all accepted sockets
648 while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){
649 socket_destroy( sockets_find( socket_get_sockets(), accepted_id ));
650 }
651 dyn_fifo_destroy( & socket->received );
652 dyn_fifo_destroy( & socket->accepted );
653 sockets_exclude( socket_get_sockets(), socket->socket_id );
654}
655
656int send( int socket_id, void * data, size_t datalength, int flags ){
657 // without the address
658 return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 );
659}
660
661int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
662 if( ! toaddr ) return EDESTADDRREQ;
663 if( ! addrlen ) return EDESTADDRREQ;
664 // with the address
665 return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen );
666}
667
668int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
669 socket_ref socket;
670 aid_t message_id;
671 ipcarg_t result;
672 size_t fragments;
673 ipc_call_t answer;
674
675 if( ! data ) return EBADMEM;
676 if( ! datalength ) return NO_DATA;
677 fibril_rwlock_read_lock( & socket_globals.lock );
678 // find socket
679 socket = sockets_find( socket_get_sockets(), socket_id );
680 if( ! socket ){
681 fibril_rwlock_read_unlock( & socket_globals.lock );
682 return ENOTSOCK;
683 }
684 fibril_rwlock_read_lock( & socket->sending_lock );
685 // compute data fragment count
686 if( socket->data_fragment_size > 0 ){
687 fragments = ( datalength + socket->header_size ) / socket->data_fragment_size;
688 if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments;
689 }else{
690 fragments = 1;
691 }
692 // request send
693 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 );
694 // send the address if given
695 if(( ! toaddr ) || ( async_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){
696 if( fragments == 1 ){
697 // send all if only one fragment
698 async_data_write_start( socket->phone, data, datalength );
699 }else{
700 // send the first fragment
701 async_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size );
702 data = (( const uint8_t * ) data ) + socket->data_fragment_size - socket->header_size;
703 // send the middle fragments
704 while(( -- fragments ) > 1 ){
705 async_data_write_start( socket->phone, data, socket->data_fragment_size );
706 data = (( const uint8_t * ) data ) + socket->data_fragment_size;
707 }
708 // send the last fragment
709 async_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size );
710 }
711 }
712 async_wait_for( message_id, & result );
713 if(( SOCKET_GET_DATA_FRAGMENT_SIZE( answer ) > 0 )
714 && ( SOCKET_GET_DATA_FRAGMENT_SIZE( answer ) != socket->data_fragment_size )){
715 // set the data fragment size
716 socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( answer );
717 }
718 fibril_rwlock_read_unlock( & socket->sending_lock );
719 fibril_rwlock_read_unlock( & socket_globals.lock );
720 return ( int ) result;
721}
722
723int recv( int socket_id, void * data, size_t datalength, int flags ){
724 // without the address
725 return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL );
726}
727
728int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
729 if( ! fromaddr ) return EBADMEM;
730 if( ! addrlen ) return NO_DATA;
731 // with the address
732 return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen );
733}
734
735int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
736 socket_ref socket;
737 aid_t message_id;
738 ipcarg_t ipc_result;
739 int result;
740 size_t fragments;
741 size_t * lengths;
742 size_t index;
743 ipc_call_t answer;
744
745 if( ! data ) return EBADMEM;
746 if( ! datalength ) return NO_DATA;
747 if( fromaddr && ( ! addrlen )) return EINVAL;
748 fibril_rwlock_read_lock( & socket_globals.lock );
749 // find the socket
750 socket = sockets_find( socket_get_sockets(), socket_id );
751 if( ! socket ){
752 fibril_rwlock_read_unlock( & socket_globals.lock );
753 return ENOTSOCK;
754 }
755 fibril_mutex_lock( & socket->receive_lock );
756 // wait for a received packet
757 ++ socket->blocked;
758 while(( result = dyn_fifo_value( & socket->received )) <= 0 ){
759 fibril_rwlock_read_unlock( & socket_globals.lock );
760 fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock );
761 fibril_rwlock_read_lock( & socket_globals.lock );
762 }
763 -- socket->blocked;
764 fragments = ( size_t ) result;
765 // prepare lengths if more fragments
766 if( fragments > 1 ){
767 lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t ));
768 if( ! lengths ){
769 fibril_mutex_unlock( & socket->receive_lock );
770 fibril_rwlock_read_unlock( & socket_globals.lock );
771 return ENOMEM;
772 }
773 // request packet data
774 message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
775 // read the address if desired
776 if(( ! fromaddr ) || ( async_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
777 // read the fragment lengths
778 if( async_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){
779 if( lengths[ fragments ] <= datalength ){
780 // read all fragments if long enough
781 for( index = 0; index < fragments; ++ index ){
782 async_data_read_start( socket->phone, data, lengths[ index ] );
783 data = (( uint8_t * ) data ) + lengths[ index ];
784 }
785 }
786 }
787 }
788 free( lengths );
789 }else{
790 // request packet data
791 message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
792 // read the address if desired
793 if(( ! fromaddr ) || ( async_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
794 // read all if only one fragment
795 async_data_read_start( socket->phone, data, datalength );
796 }
797 }
798 async_wait_for( message_id, & ipc_result );
799 result = (int) ipc_result;
800 // if successful
801 if( result == EOK ){
802 // dequeue the received packet
803 dyn_fifo_pop( & socket->received );
804 // return read data length
805 result = SOCKET_GET_READ_DATA_LENGTH( answer );
806 // set address length
807 if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
808 }
809 fibril_mutex_unlock( & socket->receive_lock );
810 fibril_rwlock_read_unlock( & socket_globals.lock );
811 return result;
812}
813
814int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){
815 socket_ref socket;
816 aid_t message_id;
817 ipcarg_t result;
818
819 if( !( value && optlen )) return EBADMEM;
820 if( !( * optlen )) return NO_DATA;
821 fibril_rwlock_read_lock( & socket_globals.lock );
822 // find the socket
823 socket = sockets_find( socket_get_sockets(), socket_id );
824 if( ! socket ){
825 fibril_rwlock_read_unlock( & socket_globals.lock );
826 return ENOTSOCK;
827 }
828 // request option value
829 message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) optname, socket->service, NULL );
830 // read the length
831 if( async_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){
832 // read the value
833 async_data_read_start( socket->phone, value, * optlen );
834 }
835 fibril_rwlock_read_unlock( & socket_globals.lock );
836 async_wait_for( message_id, & result );
837 return ( int ) result;
838}
839
840int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){
841 // send the value
842 return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, ( ipcarg_t ) optname, value, optlen );
843
844}
845
846/** @}
847 */
Note: See TracBrowser for help on using the repository browser.