Subversion Repositories HelenOS

Rev

Rev 4603 | Rev 4726 | 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.     int                 header_size;
  99.     /** Packet data fragment size.
  100.      *  Sending and receiving optimalization.
  101.      */
  102.     int                 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();
  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_tcp_phone();
  156.  
  157. /** Returns the active sockets.
  158.  *  @returns The active sockets.
  159.  */
  160. static sockets_ref  socket_get_sockets();
  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, int 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( int 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( int 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(){
  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(){
  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(){
  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, 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( 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, socket_id, 0, service );
  413.         return ERROR_CODE;
  414.     }
  415.  
  416.     return socket_id;
  417. }
  418.  
  419. int socket_send_data( int socket_id, int 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, 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.     // send the address
  439.     return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, addrlen );
  440. }
  441.  
  442. int listen( int socket_id, int backlog ){
  443.     socket_ref      socket;
  444.  
  445.     if( backlog <= 0 ) return EINVAL;
  446.     // find the socket
  447.     socket = sockets_find( socket_get_sockets(), socket_id );
  448.     if( ! socket ) return ENOTSOCK;
  449.     // request listen backlog change
  450.     return async_req_3_0( socket->phone, NET_SOCKET_LISTEN, socket->socket_id, backlog, socket->service );
  451. }
  452.  
  453. int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){
  454.     socket_ref      socket;
  455.     aid_t           message_id;
  456.     int             result;
  457.  
  458.     if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM;
  459.     // find the socket
  460.     socket = sockets_find( socket_get_sockets(), socket_id );
  461.     if( ! socket ) return ENOTSOCK;
  462.     fibril_mutex_lock( & socket->accept_lock );
  463.     // wait for an accepted socket
  464.     while( dyn_fifo_value( & socket->accepted ) <= 0 ){
  465.         fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock );
  466.     }
  467.     // request accept
  468.     message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, socket->socket_id, dyn_fifo_value( & socket->accepted ), socket->service, NULL );
  469.     // read address
  470.     ipc_data_read_start( socket->phone, cliaddr, * addrlen );
  471.     async_wait_for( message_id, ( ipcarg_t * ) & result );
  472.     if( result > 0 ){
  473.         // dequeue the accepted apcket if successful
  474.         dyn_fifo_pop( & socket->accepted );
  475.     }
  476.     fibril_mutex_unlock( & socket->accept_lock );
  477.     return result;
  478. }
  479.  
  480. int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){
  481.     // send the address
  482.     return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen );
  483. }
  484.  
  485. int closesocket( int socket_id ){
  486.     ERROR_DECLARE;
  487.  
  488.     socket_ref      socket;
  489.  
  490.     socket = sockets_find( socket_get_sockets(), socket_id );
  491.     if( ! socket ) return ENOTSOCK;
  492.     // request close
  493.     ERROR_PROPAGATE( async_req_3_0( socket->phone, NET_SOCKET_CLOSE, socket->socket_id, 0, socket->service ));
  494.     // free the socket structure
  495.     socket_destroy( socket );
  496.     return EOK;
  497. }
  498.  
  499. void socket_destroy( socket_ref socket ){
  500.     int accepted_id;
  501.  
  502.     // destroy all accepted sockets
  503.     while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){
  504.         socket_destroy( sockets_find( socket_get_sockets(), accepted_id ));
  505.     }
  506.     dyn_fifo_destroy( & socket->received );
  507.     dyn_fifo_destroy( & socket->accepted );
  508.     sockets_exclude( socket_get_sockets(), socket->socket_id );
  509. }
  510.  
  511. int send( int socket_id, void * data, size_t datalength, int flags ){
  512.     // without the address
  513.     return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 );
  514. }
  515.  
  516. int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
  517.     if( ! toaddr ) return EBADMEM;
  518.     if( ! addrlen ) return NO_DATA;
  519.     // with the address
  520.     return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen );
  521. }
  522.  
  523. int sendto_core( int message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
  524.     socket_ref      socket;
  525.     aid_t           message_id;
  526.     ipcarg_t        result;
  527.     int             fragments;
  528.  
  529.     if( ! data ) return EBADMEM;
  530.     if( ! datalength ) return NO_DATA;
  531.     // find socket
  532.     socket = sockets_find( socket_get_sockets(), socket_id );
  533.     if( ! socket ) return ENOTSOCK;
  534.     // compute data fragment count
  535.     fragments = ( datalength + socket->header_size ) / socket->data_fragment_size;
  536.     if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments;
  537.     // request send
  538.     message_id = async_send_4( socket->phone, message, socket->socket_id, fragments, socket->service, flags, NULL );
  539.     // send the address if given
  540.     if(( ! toaddr ) || ( ipc_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){
  541.         if( fragments == 1 ){
  542.             // send all if only one fragment
  543.             ipc_data_write_start( socket->phone, data, datalength );
  544.         }else{
  545.             // send the first fragment
  546.             ipc_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size );
  547.             data += socket->data_fragment_size - socket->header_size;
  548.             // send the middle fragments
  549.             while(( -- fragments ) > 1 ){
  550.                 ipc_data_write_start( socket->phone, data, socket->data_fragment_size );
  551.                 data += socket->data_fragment_size;
  552.             }
  553.             // send the last fragment
  554.             ipc_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size );
  555.         }
  556.     }
  557.     async_wait_for( message_id, & result );
  558.     return ( int ) result;
  559. }
  560.  
  561. int recv( int socket_id, void * data, size_t datalength, int flags ){
  562.     // without the address
  563.     return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL );
  564. }
  565.  
  566. int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
  567.     if( ! fromaddr ) return EBADMEM;
  568.     if( ! addrlen ) return NO_DATA;
  569.     // with the address
  570.     return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen );
  571. }
  572.  
  573. int recvfrom_core( int message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
  574.     socket_ref      socket;
  575.     aid_t           message_id;
  576.     int             result;
  577.     int             fragments;
  578.     int *           lengths;
  579.     int             index;
  580.     ipc_call_t      answer;
  581.  
  582.     if( ! data ) return EBADMEM;
  583.     if( ! datalength ) return NO_DATA;
  584.     if( fromaddr && ( ! addrlen )) return EINVAL;
  585.     // find the socket
  586.     socket = sockets_find( socket_get_sockets(), socket_id );
  587.     if( ! socket ) return ENOTSOCK;
  588.     fibril_mutex_lock( & socket->receive_lock );
  589.     // wait for a received packet
  590.     while(( fragments = dyn_fifo_value( & socket->received )) <= 0 ){
  591.         fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock );
  592.     }
  593.     // prepare lengths if more fragments
  594.     if( fragments > 1 ){
  595.         lengths = ( int * ) malloc( sizeof( int ) * ( fragments + 1 ));
  596.         if( ! lengths ){
  597.             fibril_mutex_unlock( & socket->receive_lock );
  598.             return ENOMEM;
  599.         }
  600.     }
  601.     // request packet data
  602.     message_id = async_send_4( socket->phone, message, socket->socket_id, 0, socket->service, flags, & answer );
  603.     // read the address if desired
  604.     if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
  605.         if( fragments == 1 ){
  606.             // read all if only one fragment
  607.             ipc_data_read_start( socket->phone, data, datalength );
  608.         }else{
  609.         // read the fragment lengths
  610.             if( ipc_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){
  611.                 if( lengths[ fragments ] <= datalength ){
  612.                 // read all fragments if long enough
  613.                     for( index = 0; index < fragments; ++ index ){
  614.                         ipc_data_read_start( socket->phone, data, lengths[ index ] );
  615.                         data += lengths[ index ];
  616.                     }
  617.                 }
  618.             }
  619.             free( lengths );
  620.         }
  621.     }else if( fragments > 1 ){
  622.         free( lengths );
  623.     }
  624.     async_wait_for( message_id, ( ipcarg_t * ) & result );
  625.     // if successful
  626.     if( result == EOK ){
  627.         // dequeue the received packet
  628.         dyn_fifo_pop( & socket->received );
  629.         // return read data length
  630.         result = SOCKET_GET_READ_DATA_LENGTH( answer );
  631.         // set address length
  632.         if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
  633.     }
  634.     fibril_mutex_unlock( & socket->receive_lock );
  635.     return result;
  636. }
  637.  
  638. int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){
  639.     socket_ref      socket;
  640.     aid_t           message_id;
  641.     ipcarg_t        result;
  642.  
  643.     if( !( value && optlen )) return EBADMEM;
  644.     if( !( * optlen )) return NO_DATA;
  645.     // find the socket
  646.     socket = sockets_find( socket_get_sockets(), socket_id );
  647.     if( ! socket ) return ENOTSOCK;
  648.     // request option value
  649.     message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, socket->socket_id, optname, socket->service, NULL );
  650.     // read the length
  651.     if( ipc_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){
  652.         // read the value
  653.         ipc_data_read_start( socket->phone, value, * optlen );
  654.     }
  655.     async_wait_for( message_id, & result );
  656.     return ( int ) result;
  657. }
  658.  
  659. int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){
  660.     // send the value
  661.     return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, optname, value, optlen );
  662.  
  663. }
  664.  
  665. /** @}
  666.  */
  667.