Subversion Repositories HelenOS

Rev

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