0,0 → 1,399 |
/* |
* Copyright (c) 2009 Lukas Mejdrech |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
|
/** @addtogroup socket |
* @{ |
*/ |
|
/** @file |
* Socket application program interface (API) implementation. |
* @see socket.h for more information. |
* This is a part of the network application library. |
*/ |
|
#include <assert.h> |
#include <async.h> |
//#include <fibril_sync.h> |
|
#include <ipc/services.h> |
|
#include "../err.h" |
#include "../modules.h" |
|
#include "../include/in.h" |
#include "../include/socket.h" |
#include "../include/socket_errno.h" |
|
#include "../structures/int_map.h" |
|
#include "socket_messages.h" |
|
typedef struct socket socket_t; |
typedef socket_t * socket_ref; |
|
struct socket{ |
int socket_id; |
int phone; |
services_t service; |
int max_content; |
int received; |
// fibril_mutex_t receive_lock; |
// fibril_condvar_t receive_signal; |
int accepted; |
// fibril_mutex_t accept_lock; |
// fibril_condvar_t accept_signal; |
}; |
|
INT_MAP_DECLARE( sockets, socket_t ); |
|
static struct{ |
int tcp_phone; |
int udp_phone; |
sockets_ref sockets; |
} socket_globals = { -1, -1, NULL }; |
|
INT_MAP_IMPLEMENT( sockets, socket_t ); |
|
static int socket_get_tcp_phone(); |
static int socket_get_tcp_phone(); |
static sockets_ref socket_get_sockets(); |
void socket_connection( ipc_callid_t iid, ipc_call_t * icall ); |
int socket_send_data( int socket_id, int message, ipcarg_t arg2, const void * data, size_t datalength ); |
void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ); |
|
static int socket_get_tcp_phone(){ |
if( socket_globals.tcp_phone < 0 ){ |
socket_globals.tcp_phone = bind_service( SERVICE_TCP, 0, 0, SERVICE_TCP, socket_connection ); |
} |
return socket_globals.tcp_phone; |
} |
|
static int socket_get_udp_phone(){ |
if( socket_globals.udp_phone < 0 ){ |
socket_globals.udp_phone = bind_service( SERVICE_UDP, 0, 0, SERVICE_UDP, socket_connection ); |
} |
return socket_globals.udp_phone; |
} |
|
static sockets_ref socket_get_sockets(){ |
if( ! socket_globals.sockets ){ |
socket_globals.sockets = ( sockets_ref ) malloc( sizeof( sockets_t )); |
if( ! socket_globals.sockets ) return NULL; |
if( sockets_initialize( socket_globals.sockets ) != EOK ){ |
free( socket_globals.sockets ); |
socket_globals.sockets = NULL; |
} |
} |
return socket_globals.sockets; |
} |
|
void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ){ |
socket->socket_id = socket_id; |
socket->phone = phone; |
socket->service = service; |
socket->received = 0; |
socket->accepted = 0; |
// fibril_mutex_initialize( & socket->receive_lock ); |
// fibril_condvar_initialize( & socket->receive_signal ); |
// fibril_mutex_initialize( & socket->accept_lock ); |
// fibril_condvar_initialize( & socket->accept_signal ); |
} |
|
void socket_connection( ipc_callid_t iid, ipc_call_t * icall ){ |
ERROR_DECLARE; |
|
ipc_callid_t callid; |
ipc_call_t call; |
socket_ref socket; |
socket_ref new_socket; |
|
while( true ){ |
|
callid = async_get_call( & call ); |
switch( IPC_GET_METHOD( call )){ |
case NET_SOCKET_RECEIVED: |
socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( & call )); |
if( ! socket ){ |
ERROR_CODE = ENOTSOCK; |
break; |
} |
// fibril_mutex_lock( & socket->receive_lock ); |
++ socket->received; |
// fibril_condvar_signal( & socket->receive_signal ); |
// fibril_mutex_unlock( & socket->receive_lock ); |
ERROR_CODE = EOK; |
break; |
case NET_SOCKET_ACCEPTED: |
socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( & call )); |
if( ! socket ){ |
ERROR_CODE = ENOTSOCK; |
break; |
} |
// fibril_mutex_lock( & socket->accept_lock ); |
new_socket = ( socket_ref ) malloc( sizeof( socket_t )); |
if( ! new_socket ){ |
ERROR_CODE = ENOMEM; |
break; |
} |
socket_initialize( new_socket, SOCKET_GET_NEW_SOCKET_ID( & call ), socket->phone, socket->service ); |
if( ERROR_OCCURRED( sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket ))){ |
break; |
} |
++ socket->accepted; |
// fibril_condvar_signal( & socket->accept_signal ); |
// fibril_mutex_unlock( & socket->accept_lock ); |
break; |
/* case NET_SOCKET_MTU: |
socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( & call )); |
if( ! socket ){ |
ERROR_CODE = ENOTSOCK; |
break; |
} |
socket->mtu = |
ERROR_CODE = EOK; |
break; |
*/ default: |
ERROR_CODE = ENOTSUP; |
} |
ipc_answer_0( callid, ERROR_CODE ); |
} |
} |
|
int socket( int domain, int type, int protocol ){ |
socket_ref socket; |
int phone; |
int socket_id; |
services_t service; |
|
switch( domain ){ |
case PF_INET: |
switch( type ){ |
case SOCK_STREAM: |
if( ! protocol ) protocol = IPPROTO_TCP; |
switch( protocol ){ |
case IPPROTO_TCP: |
phone = socket_get_tcp_phone(); |
service = SERVICE_TCP; |
break; |
default: |
return EPROTONOSUPPORT; |
} |
break; |
case SOCK_DGRAM: |
if( ! protocol ) protocol = IPPROTO_UDP; |
switch( protocol ){ |
case IPPROTO_UDP: |
phone = socket_get_udp_phone(); |
service = SERVICE_UDP; |
break; |
default: |
return EPROTONOSUPPORT; |
} |
break; |
case SOCK_RAW: |
default: |
return ESOCKTNOSUPPORT; |
} |
break; |
// TODO IPv6 |
default: |
return EPFNOSUPPORT; |
} |
assert( phone ); |
socket = ( socket_ref ) malloc( sizeof( socket_t )); |
if( ! socket ) return ENOMEM; |
socket_id = async_req_3_0( phone, NET_SOCKET, 0, 0, service ); |
if( socket_id > 0 ){ |
socket_initialize( socket, socket_id, phone, service ); |
if( sockets_add( socket_get_sockets(), socket_id, socket ) != EOK ){ |
free( socket ); |
async_msg_3( phone, NET_SOCKET_CLOSE, socket_id, 0, service ); |
} |
}else{ |
free( socket ); |
} |
return socket_id; |
} |
|
int socket_send_data( int socket_id, int message, ipcarg_t arg2, const void * data, size_t datalength ){ |
socket_ref socket; |
aid_t message_id; |
ipcarg_t result; |
|
if( ! data ) return EBADMEM; |
if( ! datalength ) return NO_DATA; |
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
message_id = async_send_3( socket->phone, message, socket->socket_id, arg2, socket->service, NULL ); |
ipc_data_write_start( socket->phone, data, datalength ); |
async_wait_for( message_id, & result ); |
return ( int ) result; |
} |
|
int bind( int socket_id, const struct sockaddr * my_addr, socklen_t addrlen ){ |
return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, addrlen ); |
} |
|
int listen( int socket_id, int backlog ){ |
socket_ref socket; |
|
if( backlog <= 0 ) return EINVAL; |
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
return async_req_3_0( socket->phone, NET_SOCKET_LISTEN, socket->socket_id, backlog, socket->service ); |
} |
|
int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){ |
socket_ref socket; |
aid_t message_id; |
ipcarg_t result; |
|
if( ! cliaddr ) return EBADMEM; |
if( ! addrlen ) return NO_DATA; |
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
// fibril_mutex_lock( & socket->accept_lock ); |
while( socket->accepted <= 0 ){ |
// fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock ); |
} |
message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, socket->socket_id, 0, socket->service, NULL ); |
ipc_data_read_start( socket->phone, cliaddr, * addrlen ); |
async_wait_for( message_id, & result ); |
if( result > 0 ){ |
-- socket->accepted; |
} |
// fibril_mutex_unlock( & socket->accept_lock ); |
return ( int ) result; |
} |
|
int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){ |
return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen ); |
} |
|
int closesocket( int socket_id ){ |
socket_ref socket; |
|
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
return async_req_3_0( socket->phone, NET_SOCKET_CLOSE, socket->socket_id, 0, socket->service ); |
} |
|
int send( int socket_id, void * data, size_t datalength, int flags ){ |
return socket_send_data( socket_id, NET_SOCKET_SEND, flags, data, datalength ); |
} |
|
int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){ |
socket_ref socket; |
aid_t message_id; |
ipcarg_t result; |
|
if( ! toaddr ) return EBADMEM; |
if( ! addrlen ) return NO_DATA; |
if( ! data ) return EBADMEM; |
if( ! datalength ) return NO_DATA; |
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
message_id = async_send_3( socket->phone, NET_SOCKET_SENDTO, socket->socket_id, flags, socket->service, NULL ); |
if( ipc_data_write_start( socket->phone, toaddr, addrlen ) == EOK ){ |
ipc_data_write_start( socket->phone, data, datalength ); |
} |
async_wait_for( message_id, & result ); |
return ( int ) result; |
} |
|
int recv( int socket_id, void * data, size_t datalength, int flags ){ |
socket_ref socket; |
aid_t message_id; |
ipcarg_t result; |
|
if( ! data ) return EBADMEM; |
if( ! datalength ) return NO_DATA; |
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
// fibril_mutex_lock( & socket->receive_lock ); |
while( socket->received <= 0 ){ |
// fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock ); |
} |
message_id = async_send_3( socket->phone, NET_SOCKET_RECV, socket->socket_id, flags, socket->service, NULL ); |
ipc_data_read_start( socket->phone, data, datalength ); |
async_wait_for( message_id, & result ); |
if( result > 0 ){ |
-- socket->received; |
} |
// fibril_mutex_unlock( & socket->receive_lock ); |
return ( int ) result; |
} |
|
int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){ |
socket_ref socket; |
aid_t message_id; |
ipcarg_t result; |
|
if( ! fromaddr ) return EBADMEM; |
if( ! addrlen ) return NO_DATA; |
if( ! data ) return EBADMEM; |
if( ! datalength ) return NO_DATA; |
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
// fibril_mutex_lock( & socket->receive_lock ); |
while( socket->received <= 0 ){ |
// fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock ); |
} |
message_id = async_send_3( socket->phone, NET_SOCKET_RECVFROM, socket->socket_id, flags, socket->service, NULL ); |
if( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK ){ |
ipc_data_read_start( socket->phone, data, datalength ); |
} |
async_wait_for( message_id, & result ); |
if( result > 0 ){ |
-- socket->received; |
} |
// fibril_mutex_unlock( & socket->receive_lock ); |
return ( int ) result; |
} |
|
int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){ |
socket_ref socket; |
aid_t message_id; |
ipcarg_t result; |
|
if( ! value ) return EBADMEM; |
if( ! optlen ) return NO_DATA; |
socket = sockets_find( socket_get_sockets(), socket_id ); |
if( ! socket ) return ENOTSOCK; |
message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, socket->socket_id, optname, socket->service, NULL ); |
ipc_data_read_start( socket->phone, value, * optlen ); |
async_wait_for( message_id, & result ); |
return ( int ) result; |
} |
|
int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){ |
return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, optname, value, optlen ); |
} |
|
/** @} |
*/ |
Property changes: |
Added: svn:mergeinfo |