2004-09-13 18:17:41 +04:00
/*
Unix SMB / CIFS implementation .
Socket functions
Copyright ( C ) Stefan Metzmacher 2004
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2005-02-10 09:59:29 +03:00
# include "lib/socket/socket.h"
2005-02-10 08:09:35 +03:00
# include "system/filesys.h"
2004-09-13 18:17:41 +04:00
2004-09-26 07:05:04 +04:00
/*
auto - close sockets on free
*/
static int socket_destructor ( void * ptr )
{
struct socket_context * sock = ptr ;
2004-11-02 05:01:04 +03:00
if ( sock - > ops - > fn_close ) {
sock - > ops - > fn_close ( sock ) ;
2004-09-26 07:05:04 +04:00
}
return 0 ;
}
2005-01-19 06:20:20 +03:00
static NTSTATUS socket_create_with_ops ( TALLOC_CTX * mem_ctx , const struct socket_ops * ops ,
struct socket_context * * new_sock ,
enum socket_type type , uint32_t flags )
2004-09-13 18:17:41 +04:00
{
NTSTATUS status ;
2005-01-11 18:19:32 +03:00
( * new_sock ) = talloc ( mem_ctx , struct socket_context ) ;
2004-09-13 18:17:41 +04:00
if ( ! ( * new_sock ) ) {
return NT_STATUS_NO_MEMORY ;
}
2005-01-19 06:20:20 +03:00
( * new_sock ) - > type = type ;
2004-09-13 18:17:41 +04:00
( * new_sock ) - > state = SOCKET_STATE_UNDEFINED ;
( * new_sock ) - > flags = flags ;
( * new_sock ) - > fd = - 1 ;
( * new_sock ) - > private_data = NULL ;
2005-01-11 18:19:32 +03:00
( * new_sock ) - > ops = ops ;
2004-09-13 18:17:41 +04:00
2004-11-02 05:01:04 +03:00
status = ( * new_sock ) - > ops - > fn_init ( ( * new_sock ) ) ;
2004-09-13 18:17:41 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2004-09-26 07:05:04 +04:00
talloc_free ( * new_sock ) ;
2004-09-13 18:17:41 +04:00
return status ;
}
2004-10-28 11:55:33 +04:00
/* by enabling "testnonblock" mode, all socket receive and
send calls on non - blocking sockets will randomly recv / send
less data than requested */
if ( ! ( flags & SOCKET_FLAG_BLOCK ) & &
2005-01-19 06:20:20 +03:00
type = = SOCKET_TYPE_STREAM & &
2004-10-28 11:55:33 +04:00
lp_parm_bool ( - 1 , " socket " , " testnonblock " , False ) ) {
( * new_sock ) - > flags | = SOCKET_FLAG_TESTNONBLOCK ;
}
2004-09-26 07:05:04 +04:00
talloc_set_destructor ( * new_sock , socket_destructor ) ;
2004-09-13 18:17:41 +04:00
return NT_STATUS_OK ;
}
2005-01-19 06:20:20 +03:00
NTSTATUS socket_create ( const char * name , enum socket_type type ,
struct socket_context * * new_sock , uint32_t flags )
2005-01-11 18:19:32 +03:00
{
const struct socket_ops * ops ;
ops = socket_getops_byname ( name , type ) ;
if ( ! ops ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2005-01-19 06:20:20 +03:00
return socket_create_with_ops ( NULL , ops , new_sock , type , flags ) ;
2005-01-11 18:19:32 +03:00
}
2004-09-13 18:17:41 +04:00
void socket_destroy ( struct socket_context * sock )
{
2004-09-26 07:05:04 +04:00
/* the close is handled by the destructor */
2004-09-13 18:17:41 +04:00
talloc_free ( sock ) ;
}
NTSTATUS socket_connect ( struct socket_context * sock ,
const char * my_address , int my_port ,
const char * server_address , int server_port ,
uint32_t flags )
{
if ( sock - > state ! = SOCKET_STATE_UNDEFINED ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_connect ) {
2004-09-13 18:17:41 +04:00
return NT_STATUS_NOT_IMPLEMENTED ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_connect ( sock , my_address , my_port , server_address , server_port , flags ) ;
2004-09-13 18:17:41 +04:00
}
2005-01-15 13:28:08 +03:00
NTSTATUS socket_connect_complete ( struct socket_context * sock , uint32_t flags )
{
if ( ! sock - > ops - > fn_connect_complete ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
return sock - > ops - > fn_connect_complete ( sock , flags ) ;
}
2004-09-13 18:17:41 +04:00
NTSTATUS socket_listen ( struct socket_context * sock , const char * my_address , int port , int queue_size , uint32_t flags )
{
if ( sock - > state ! = SOCKET_STATE_UNDEFINED ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_listen ) {
2004-09-13 18:17:41 +04:00
return NT_STATUS_NOT_IMPLEMENTED ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_listen ( sock , my_address , port , queue_size , flags ) ;
2004-09-13 18:17:41 +04:00
}
2004-10-28 11:55:33 +04:00
NTSTATUS socket_accept ( struct socket_context * sock , struct socket_context * * new_sock )
2004-09-13 18:17:41 +04:00
{
2004-09-26 07:05:04 +04:00
NTSTATUS status ;
2004-09-13 18:17:41 +04:00
if ( sock - > type ! = SOCKET_TYPE_STREAM ) {
return NT_STATUS_INVALID_PARAMETER ;
}
if ( sock - > state ! = SOCKET_STATE_SERVER_LISTEN ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_accept ) {
2004-09-13 18:17:41 +04:00
return NT_STATUS_NOT_IMPLEMENTED ;
}
2004-11-02 05:01:04 +03:00
status = sock - > ops - > fn_accept ( sock , new_sock ) ;
2004-09-26 07:05:04 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
talloc_set_destructor ( * new_sock , socket_destructor ) ;
}
return status ;
2004-09-13 18:17:41 +04:00
}
2004-10-28 08:00:43 +04:00
NTSTATUS socket_recv ( struct socket_context * sock , void * buf ,
size_t wantlen , size_t * nread , uint32_t flags )
2004-09-13 18:17:41 +04:00
{
2004-09-20 13:13:17 +04:00
if ( sock - > state ! = SOCKET_STATE_CLIENT_CONNECTED & &
2004-09-13 18:17:41 +04:00
sock - > state ! = SOCKET_STATE_SERVER_CONNECTED ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_recv ) {
2004-09-13 18:17:41 +04:00
return NT_STATUS_NOT_IMPLEMENTED ;
}
2004-10-28 11:55:33 +04:00
if ( ( sock - > flags & SOCKET_FLAG_TESTNONBLOCK ) & & wantlen > 1 ) {
2004-10-28 15:57:20 +04:00
if ( random ( ) % 10 = = 0 ) {
* nread = 0 ;
return STATUS_MORE_ENTRIES ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_recv ( sock , buf , 1 + ( random ( ) % wantlen ) , nread , flags ) ;
2004-10-28 11:55:33 +04:00
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_recv ( sock , buf , wantlen , nread , flags ) ;
2004-09-13 18:17:41 +04:00
}
2005-01-19 06:20:20 +03:00
NTSTATUS socket_recvfrom ( struct socket_context * sock , void * buf ,
size_t wantlen , size_t * nread , uint32_t flags ,
const char * * src_addr , int * src_port )
2004-09-13 18:17:41 +04:00
{
2005-01-19 06:20:20 +03:00
if ( sock - > type ! = SOCKET_TYPE_DGRAM ) {
2004-09-13 18:17:41 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
2005-01-19 06:20:20 +03:00
if ( ! sock - > ops - > fn_recvfrom ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
return sock - > ops - > fn_recvfrom ( sock , buf , wantlen , nread , flags ,
src_addr , src_port ) ;
}
NTSTATUS socket_send ( struct socket_context * sock ,
const DATA_BLOB * blob , size_t * sendlen , uint32_t flags )
{
2004-09-20 13:13:17 +04:00
if ( sock - > state ! = SOCKET_STATE_CLIENT_CONNECTED & &
2004-09-13 18:17:41 +04:00
sock - > state ! = SOCKET_STATE_SERVER_CONNECTED ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_send ) {
2004-09-13 18:17:41 +04:00
return NT_STATUS_NOT_IMPLEMENTED ;
}
2004-10-28 11:55:33 +04:00
if ( ( sock - > flags & SOCKET_FLAG_TESTNONBLOCK ) & & blob - > length > 1 ) {
DATA_BLOB blob2 = * blob ;
2004-10-28 15:57:20 +04:00
if ( random ( ) % 10 = = 0 ) {
* sendlen = 0 ;
return STATUS_MORE_ENTRIES ;
}
blob2 . length = 1 + ( random ( ) % blob2 . length ) ;
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_send ( sock , & blob2 , sendlen , flags ) ;
2004-10-28 11:55:33 +04:00
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_send ( sock , blob , sendlen , flags ) ;
2004-09-13 18:17:41 +04:00
}
2005-01-19 06:20:20 +03:00
NTSTATUS socket_sendto ( struct socket_context * sock ,
const DATA_BLOB * blob , size_t * sendlen , uint32_t flags ,
const char * dest_addr , int dest_port )
{
if ( sock - > type ! = SOCKET_TYPE_DGRAM ) {
return NT_STATUS_INVALID_PARAMETER ;
}
if ( sock - > state = = SOCKET_STATE_CLIENT_CONNECTED | |
sock - > state = = SOCKET_STATE_SERVER_CONNECTED ) {
return NT_STATUS_INVALID_PARAMETER ;
}
if ( ! sock - > ops - > fn_sendto ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
return sock - > ops - > fn_sendto ( sock , blob , sendlen , flags , dest_addr , dest_port ) ;
}
2004-09-13 18:17:41 +04:00
NTSTATUS socket_set_option ( struct socket_context * sock , const char * option , const char * val )
{
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_set_option ) {
2004-09-13 18:17:41 +04:00
return NT_STATUS_NOT_IMPLEMENTED ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_set_option ( sock , option , val ) ;
2004-09-13 18:17:41 +04:00
}
2004-09-24 07:34:55 +04:00
char * socket_get_peer_name ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
{
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_get_peer_name ) {
2004-09-24 07:34:55 +04:00
return NULL ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_get_peer_name ( sock , mem_ctx ) ;
2004-09-24 07:34:55 +04:00
}
2004-09-15 16:14:47 +04:00
char * socket_get_peer_addr ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
2004-09-13 18:17:41 +04:00
{
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_get_peer_addr ) {
2004-09-13 18:17:41 +04:00
return NULL ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_get_peer_addr ( sock , mem_ctx ) ;
2004-09-13 18:17:41 +04:00
}
2004-09-20 13:13:17 +04:00
int socket_get_peer_port ( struct socket_context * sock )
2004-09-13 18:17:41 +04:00
{
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_get_peer_port ) {
2004-09-13 18:17:41 +04:00
return - 1 ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_get_peer_port ( sock ) ;
2004-09-13 18:17:41 +04:00
}
2004-09-15 16:14:47 +04:00
char * socket_get_my_addr ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
2004-09-13 18:17:41 +04:00
{
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_get_my_addr ) {
2004-09-13 18:17:41 +04:00
return NULL ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_get_my_addr ( sock , mem_ctx ) ;
2004-09-13 18:17:41 +04:00
}
2004-09-20 13:13:17 +04:00
int socket_get_my_port ( struct socket_context * sock )
2004-09-13 18:17:41 +04:00
{
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_get_my_port ) {
2004-09-13 18:17:41 +04:00
return - 1 ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_get_my_port ( sock ) ;
2004-09-13 18:17:41 +04:00
}
2004-09-20 13:13:17 +04:00
int socket_get_fd ( struct socket_context * sock )
2004-09-13 18:17:41 +04:00
{
2004-11-02 05:01:04 +03:00
if ( ! sock - > ops - > fn_get_fd ) {
2004-09-13 18:17:41 +04:00
return - 1 ;
}
2004-11-02 05:01:04 +03:00
return sock - > ops - > fn_get_fd ( sock ) ;
2004-09-13 18:17:41 +04:00
}
2004-10-29 11:00:14 +04:00
/*
call dup ( ) on a socket , and close the old fd . This is used to change
the fd to the lowest available number , to make select ( ) more
efficient ( select speed depends on the maxiumum fd number passed to
it )
*/
NTSTATUS socket_dup ( struct socket_context * sock )
{
int fd ;
if ( sock - > fd = = - 1 ) {
return NT_STATUS_INVALID_HANDLE ;
}
fd = dup ( sock - > fd ) ;
if ( fd = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
close ( sock - > fd ) ;
sock - > fd = fd ;
return NT_STATUS_OK ;
}
2004-09-13 18:17:41 +04:00
const struct socket_ops * socket_getops_byname ( const char * name , enum socket_type type )
{
2005-02-10 09:59:29 +03:00
extern const struct socket_ops * socket_ipv4_ops ( enum socket_type ) ;
extern const struct socket_ops * socket_ipv6_ops ( enum socket_type ) ;
extern const struct socket_ops * socket_unixdom_ops ( enum socket_type ) ;
2004-10-17 09:29:03 +04:00
if ( strcmp ( " ip " , name ) = = 0 | |
strcmp ( " ipv4 " , name ) = = 0 ) {
2005-01-19 06:20:20 +03:00
return socket_ipv4_ops ( type ) ;
2004-09-13 18:17:41 +04:00
}
2004-10-29 01:36:27 +04:00
# if HAVE_SOCKET_IPV6
2004-10-28 22:57:48 +04:00
if ( strcmp ( " ipv6 " , name ) = = 0 ) {
2004-10-29 01:41:21 +04:00
if ( lp_parm_bool ( - 1 , " socket " , " noipv6 " , False ) ) {
DEBUG ( 3 , ( " IPv6 support was disabled in smb.conf " ) ) ;
return NULL ;
}
2005-01-19 06:20:20 +03:00
return socket_ipv6_ops ( type ) ;
2004-10-28 22:57:48 +04:00
}
2004-10-29 01:36:27 +04:00
# endif
2004-10-28 22:57:48 +04:00
2004-10-17 09:29:03 +04:00
if ( strcmp ( " unix " , name ) = = 0 ) {
2005-01-19 06:20:20 +03:00
return socket_unixdom_ops ( type ) ;
2004-10-17 09:07:07 +04:00
}
2004-09-13 18:17:41 +04:00
return NULL ;
}