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