Subversion Repositories HelenOS

Rev

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