Subversion Repositories HelenOS

Rev

Rev 4701 | Rev 4729 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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