Subversion Repositories HelenOS

Rev

Rev 4729 | Rev 4747 | 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.                         bzero( socket, sizeof( * socket ));
  344.                         socket_initialize( new_socket, SOCKET_GET_SOCKET_ID( call ), socket->phone, socket->service );
  345.                         ERROR_CODE = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket );
  346.                         if( ERROR_CODE < 0 ){
  347.                             free( new_socket );
  348.                         }else{
  349.                             // push the new socket identifier
  350.                             fibril_mutex_lock( & socket->accept_lock );
  351.                             if( ERROR_OCCURRED( dyn_fifo_push( & socket->accepted, new_socket->socket_id, SOCKET_MAX_ACCEPTED_SIZE ))){
  352.                                 sockets_exclude( socket_get_sockets(), new_socket->socket_id );
  353.                                 free( new_socket );
  354.                             }else{
  355.                                 // signal the accepted socket
  356.                                 fibril_condvar_signal( & socket->accept_signal );
  357.                             }
  358.                             fibril_mutex_unlock( & socket->accept_lock );
  359.                             ERROR_CODE = EOK;
  360.                         }
  361.                     }
  362.                 }
  363.                 fibril_rwlock_read_unlock( & socket_globals.lock );
  364.                 break;
  365.             // TODO obsolete?
  366.             case NET_SOCKET_DATA_FRAGMENT_SIZE:
  367.                 fibril_rwlock_read_lock( & socket_globals.lock );
  368.                 // find the socket
  369.                 socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
  370.                 if( ! socket ){
  371.                     ERROR_CODE = ENOTSOCK;
  372.                 }else{
  373.                     fibril_rwlock_write_lock( & socket->sending_lock );
  374.                     // set the data fragment size
  375.                     socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call );
  376.                     fibril_rwlock_write_unlock( & socket->sending_lock );
  377.                     ERROR_CODE = EOK;
  378.                 }
  379.                 fibril_rwlock_read_unlock( & socket_globals.lock );
  380.                 break;
  381.             default:
  382.                 ERROR_CODE = ENOTSUP;
  383.         }
  384.         ipc_answer_0( callid, ( ipcarg_t ) ERROR_CODE );
  385.     }
  386. }
  387.  
  388. int socket( int domain, int type, int protocol ){
  389.     ERROR_DECLARE;
  390.  
  391.     socket_ref  socket;
  392.     int         phone;
  393.     int         socket_id;
  394.     services_t  service;
  395.  
  396.     // find the appropriate service
  397.     switch( domain ){
  398.         case PF_INET:
  399.             switch( type ){
  400.                 case SOCK_STREAM:
  401.                     if( ! protocol ) protocol = IPPROTO_TCP;
  402.                     switch( protocol ){
  403.                         case IPPROTO_TCP:
  404.                             phone = socket_get_tcp_phone();
  405.                             service = SERVICE_TCP;
  406.                             break;
  407.                         default:
  408.                             return EPROTONOSUPPORT;
  409.                     }
  410.                     break;
  411.                 case SOCK_DGRAM:
  412.                     if( ! protocol ) protocol = IPPROTO_UDP;
  413.                     switch( protocol ){
  414.                         case IPPROTO_UDP:
  415.                             phone = socket_get_udp_phone();
  416.                             service = SERVICE_UDP;
  417.                             break;
  418.                         default:
  419.                             return EPROTONOSUPPORT;
  420.                     }
  421.                     break;
  422.                 case SOCK_RAW:
  423.                 default:
  424.                     return ESOCKTNOSUPPORT;
  425.             }
  426.             break;
  427.         // TODO IPv6
  428.         default:
  429.             return EPFNOSUPPORT;
  430.     }
  431.     // create a new socket structure
  432.     socket = ( socket_ref ) malloc( sizeof( socket_t ));
  433.     if( ! socket ) return ENOMEM;
  434.     bzero( socket, sizeof( * socket ));
  435.     // request a new socket
  436.     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 ))){
  437.         free( socket );
  438.         return ERROR_CODE;
  439.     }
  440.     // finish the new socket initialization
  441.     socket_initialize( socket, socket_id, phone, service );
  442.     // store the new socket
  443.     fibril_rwlock_write_lock( & socket_globals.lock );
  444.     ERROR_CODE = sockets_add( socket_get_sockets(), socket_id, socket );
  445.     fibril_rwlock_write_unlock( & socket_globals.lock );
  446.     if( ERROR_CODE < 0 ){
  447.         dyn_fifo_destroy( & socket->received );
  448.         dyn_fifo_destroy( & socket->accepted );
  449.         free( socket );
  450.         async_msg_3( phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket_id, 0, service );
  451.         return ERROR_CODE;
  452.     }
  453.  
  454.     return socket_id;
  455. }
  456.  
  457. int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength ){
  458.     socket_ref      socket;
  459.     aid_t           message_id;
  460.     ipcarg_t        result;
  461.  
  462.     if( ! data ) return EBADMEM;
  463.     if( ! datalength ) return NO_DATA;
  464.  
  465.     fibril_rwlock_read_lock( & socket_globals.lock );
  466.     // find the socket
  467.     socket = sockets_find( socket_get_sockets(), socket_id );
  468.     if( ! socket ){
  469.         fibril_rwlock_read_unlock( & socket_globals.lock );
  470.         return ENOTSOCK;
  471.     }
  472.     // request the message
  473.     message_id = async_send_3( socket->phone, message, ( ipcarg_t ) socket->socket_id, arg2, socket->service, NULL );
  474.     // send the address
  475.     ipc_data_write_start( socket->phone, data, datalength );
  476.     fibril_rwlock_read_unlock( & socket_globals.lock );
  477.     async_wait_for( message_id, & result );
  478.     return ( int ) result;
  479. }
  480.  
  481. int bind( int socket_id, const struct sockaddr * my_addr, socklen_t addrlen ){
  482.     if( addrlen <= 0 ) return EINVAL;
  483.     // send the address
  484.     return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, ( size_t ) addrlen );
  485. }
  486.  
  487. int listen( int socket_id, int backlog ){
  488.     socket_ref  socket;
  489.     int         result;
  490.  
  491.     if( backlog <= 0 ) return EINVAL;
  492.     fibril_rwlock_read_lock( & socket_globals.lock );
  493.     // find the socket
  494.     socket = sockets_find( socket_get_sockets(), socket_id );
  495.     if( ! socket ){
  496.         fibril_rwlock_read_unlock( & socket_globals.lock );
  497.         return ENOTSOCK;
  498.     }
  499.     // request listen backlog change
  500.     result = ( int ) async_req_3_0( socket->phone, NET_SOCKET_LISTEN, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) backlog, socket->service );
  501.     fibril_rwlock_read_unlock( & socket_globals.lock );
  502.     return result;
  503. }
  504.  
  505. int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){
  506.     socket_ref      socket;
  507.     aid_t           message_id;
  508.     int             result;
  509.  
  510.     if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM;
  511.  
  512.     fibril_rwlock_read_lock( & socket_globals.lock );
  513.     // find the socket
  514.     socket = sockets_find( socket_get_sockets(), socket_id );
  515.     if( ! socket ){
  516.         fibril_rwlock_read_unlock( & socket_globals.lock );
  517.         return ENOTSOCK;
  518.     }
  519.     fibril_mutex_lock( & socket->accept_lock );
  520.     // wait for an accepted socket
  521.     ++ socket->blocked;
  522.     while( dyn_fifo_value( & socket->accepted ) <= 0 ){
  523.         fibril_rwlock_read_unlock( & socket_globals.lock );
  524.         fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock );
  525.         fibril_rwlock_read_lock( & socket_globals.lock );
  526.     }
  527.     -- socket->blocked;
  528.     // request accept
  529.     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 );
  530.     // read address
  531.     ipc_data_read_start( socket->phone, cliaddr, * addrlen );
  532.     fibril_rwlock_read_unlock( & socket_globals.lock );
  533.     async_wait_for( message_id, ( ipcarg_t * ) & result );
  534.     if( result > 0 ){
  535.         // dequeue the accepted apcket if successful
  536.         dyn_fifo_pop( & socket->accepted );
  537.     }
  538.     fibril_mutex_unlock( & socket->accept_lock );
  539.     return result;
  540. }
  541.  
  542. int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){
  543.     if( ! serv_addr ) return EDESTADDRREQ;
  544.     if( ! addrlen ) return EDESTADDRREQ;
  545.     // send the address
  546.     return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen );
  547. }
  548.  
  549. int closesocket( int socket_id ){
  550.     ERROR_DECLARE;
  551.  
  552.     socket_ref      socket;
  553.  
  554.     fibril_rwlock_write_lock( & socket_globals.lock );
  555.     socket = sockets_find( socket_get_sockets(), socket_id );
  556.     if( ! socket ){
  557.         fibril_rwlock_write_unlock( & socket_globals.lock );
  558.         return ENOTSOCK;
  559.     }
  560.     if( socket->blocked ){
  561.         fibril_rwlock_write_unlock( & socket_globals.lock );
  562.         return EINPROGRESS;
  563.     }
  564.     // request close
  565.     ERROR_PROPAGATE(( int ) async_req_3_0( socket->phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket->socket_id, 0, socket->service ));
  566.     // free the socket structure
  567.     socket_destroy( socket );
  568.     fibril_rwlock_write_unlock( & socket_globals.lock );
  569.     return EOK;
  570. }
  571.  
  572. void socket_destroy( socket_ref socket ){
  573.     int accepted_id;
  574.  
  575.     // destroy all accepted sockets
  576.     while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){
  577.         socket_destroy( sockets_find( socket_get_sockets(), accepted_id ));
  578.     }
  579.     dyn_fifo_destroy( & socket->received );
  580.     dyn_fifo_destroy( & socket->accepted );
  581.     sockets_exclude( socket_get_sockets(), socket->socket_id );
  582. }
  583.  
  584. int send( int socket_id, void * data, size_t datalength, int flags ){
  585.     // without the address
  586.     return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 );
  587. }
  588.  
  589. int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
  590.     if( ! toaddr ) return EDESTADDRREQ;
  591.     if( ! addrlen ) return EDESTADDRREQ;
  592.     // with the address
  593.     return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen );
  594. }
  595.  
  596. int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
  597.     socket_ref      socket;
  598.     aid_t           message_id;
  599.     ipcarg_t        result;
  600.     size_t          fragments;
  601.  
  602.     if( ! data ) return EBADMEM;
  603.     if( ! datalength ) return NO_DATA;
  604.     fibril_rwlock_read_lock( & socket_globals.lock );
  605.     // find socket
  606.     socket = sockets_find( socket_get_sockets(), socket_id );
  607.     if( ! socket ){
  608.         fibril_rwlock_read_unlock( & socket_globals.lock );
  609.         return ENOTSOCK;
  610.     }
  611.     fibril_rwlock_read_lock( & socket->sending_lock );
  612.     // compute data fragment count
  613.     fragments = ( datalength + socket->header_size ) / socket->data_fragment_size;
  614.     if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments;
  615.     // request send
  616.     message_id = async_send_5( socket->phone, message, ( ipcarg_t ) socket->socket_id, socket->data_fragment_size, socket->service, ( ipcarg_t ) flags, fragments, NULL );
  617.     // send the address if given
  618.     if(( ! toaddr ) || ( ipc_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){
  619.         if( fragments == 1 ){
  620.             // send all if only one fragment
  621.             ipc_data_write_start( socket->phone, data, datalength );
  622.         }else{
  623.             // send the first fragment
  624.             ipc_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size );
  625.             data = (( const uint8_t * ) data ) + socket->data_fragment_size - socket->header_size;
  626.             // send the middle fragments
  627.             while(( -- fragments ) > 1 ){
  628.                 ipc_data_write_start( socket->phone, data, socket->data_fragment_size );
  629.                 data = (( const uint8_t * ) data ) + socket->data_fragment_size;
  630.             }
  631.             // send the last fragment
  632.             ipc_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size );
  633.         }
  634.     }
  635.     fibril_rwlock_read_unlock( & socket->sending_lock );
  636.     fibril_rwlock_read_unlock( & socket_globals.lock );
  637.     async_wait_for( message_id, & result );
  638.     return ( int ) result;
  639. }
  640.  
  641. int recv( int socket_id, void * data, size_t datalength, int flags ){
  642.     // without the address
  643.     return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL );
  644. }
  645.  
  646. int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
  647.     if( ! fromaddr ) return EBADMEM;
  648.     if( ! addrlen ) return NO_DATA;
  649.     // with the address
  650.     return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen );
  651. }
  652.  
  653. int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
  654.     socket_ref      socket;
  655.     aid_t           message_id;
  656.     int             result;
  657.     size_t          fragments;
  658.     size_t *        lengths;
  659.     size_t          index;
  660.     ipc_call_t      answer;
  661.  
  662.     if( ! data ) return EBADMEM;
  663.     if( ! datalength ) return NO_DATA;
  664.     if( fromaddr && ( ! addrlen )) return EINVAL;
  665.     fibril_rwlock_read_lock( & socket_globals.lock );
  666.     // find the socket
  667.     socket = sockets_find( socket_get_sockets(), socket_id );
  668.     if( ! socket ){
  669.         fibril_rwlock_read_unlock( & socket_globals.lock );
  670.         return ENOTSOCK;
  671.     }
  672.     fibril_mutex_lock( & socket->receive_lock );
  673.     // wait for a received packet
  674.     ++ socket->blocked;
  675.     while(( result = dyn_fifo_value( & socket->received )) <= 0 ){
  676.         fibril_rwlock_read_unlock( & socket_globals.lock );
  677.         fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock );
  678.         fibril_rwlock_read_lock( & socket_globals.lock );
  679.     }
  680.     -- socket->blocked;
  681.     fragments = ( size_t ) result;
  682.     // prepare lengths if more fragments
  683.     if( fragments > 1 ){
  684.         lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t ));
  685.         if( ! lengths ){
  686.             fibril_mutex_unlock( & socket->receive_lock );
  687.             fibril_rwlock_read_unlock( & socket_globals.lock );
  688.             return ENOMEM;
  689.         }
  690.         // request packet data
  691.         message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
  692.         // read the address if desired
  693.         if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
  694.         // read the fragment lengths
  695.             if( ipc_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){
  696.                 if( lengths[ fragments ] <= datalength ){
  697.                     // read all fragments if long enough
  698.                     for( index = 0; index < fragments; ++ index ){
  699.                         ipc_data_read_start( socket->phone, data, lengths[ index ] );
  700.                         data = (( uint8_t * ) data ) + lengths[ index ];
  701.                     }
  702.                 }
  703.             }
  704.         }
  705.         free( lengths );
  706.     }else{
  707.         // request packet data
  708.         message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
  709.         // read the address if desired
  710.         if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
  711.             // read all if only one fragment
  712.             ipc_data_read_start( socket->phone, data, datalength );
  713.         }
  714.     }
  715.     async_wait_for( message_id, ( ipcarg_t * ) & result );
  716.     // if successful
  717.     if( result == EOK ){
  718.         // dequeue the received packet
  719.         dyn_fifo_pop( & socket->received );
  720.         // return read data length
  721.         result = SOCKET_GET_READ_DATA_LENGTH( answer );
  722.         // set address length
  723.         if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
  724.     }
  725.     fibril_mutex_unlock( & socket->receive_lock );
  726.     fibril_rwlock_read_unlock( & socket_globals.lock );
  727.     return result;
  728. }
  729.  
  730. int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){
  731.     socket_ref      socket;
  732.     aid_t           message_id;
  733.     ipcarg_t        result;
  734.  
  735.     if( !( value && optlen )) return EBADMEM;
  736.     if( !( * optlen )) return NO_DATA;
  737.     fibril_rwlock_read_lock( & socket_globals.lock );
  738.     // find the socket
  739.     socket = sockets_find( socket_get_sockets(), socket_id );
  740.     if( ! socket ){
  741.         fibril_rwlock_read_unlock( & socket_globals.lock );
  742.         return ENOTSOCK;
  743.     }
  744.     // request option value
  745.     message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) optname, socket->service, NULL );
  746.     // read the length
  747.     if( ipc_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){
  748.         // read the value
  749.         ipc_data_read_start( socket->phone, value, * optlen );
  750.     }
  751.     fibril_rwlock_read_unlock( & socket_globals.lock );
  752.     async_wait_for( message_id, & result );
  753.     return ( int ) result;
  754. }
  755.  
  756. int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){
  757.     // send the value
  758.     return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, ( ipcarg_t ) optname, value, optlen );
  759.  
  760. }
  761.  
  762. /** @}
  763.  */
  764.