2004-09-13 18:17:41 +04:00
/*
Unix SMB / CIFS implementation .
2005-01-15 13:28:08 +03:00
2007-09-04 02:39:16 +04:00
Socket IPv4 / IPv6 functions
2005-01-15 13:28:08 +03:00
2004-09-13 18:17:41 +04:00
Copyright ( C ) Stefan Metzmacher 2004
2005-01-15 13:28:08 +03:00
Copyright ( C ) Andrew Tridgell 2004 - 2005
2007-09-04 02:39:16 +04:00
Copyright ( C ) Jelmer Vernooij 2004
2004-09-13 18:17:41 +04:00
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-09-13 18:17:41 +04:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-09-13 18:17:41 +04:00
*/
# include "includes.h"
2005-02-10 08:09:35 +03:00
# include "system/filesys.h"
2005-02-10 09:59:29 +03:00
# include "lib/socket/socket.h"
2006-08-14 02:57:05 +04:00
# include "system/network.h"
2004-09-13 18:17:41 +04:00
2005-01-19 06:20:20 +03:00
static NTSTATUS ipv4_init ( struct socket_context * sock )
2004-09-13 18:17:41 +04:00
{
2005-01-19 06:20:20 +03:00
int type ;
switch ( sock - > type ) {
case SOCKET_TYPE_STREAM :
type = SOCK_STREAM ;
break ;
case SOCKET_TYPE_DGRAM :
type = SOCK_DGRAM ;
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
sock - > fd = socket ( PF_INET , type , 0 ) ;
2004-09-13 18:17:41 +04:00
if ( sock - > fd = = - 1 ) {
2004-10-27 07:15:42 +04:00
return map_nt_error_from_unix ( errno ) ;
2004-09-13 18:17:41 +04:00
}
2005-08-28 06:37:38 +04:00
sock - > backend_name = " ipv4 " ;
2004-09-13 18:17:41 +04:00
return NT_STATUS_OK ;
}
2007-09-04 02:39:16 +04:00
static void ip_close ( struct socket_context * sock )
2004-09-13 18:17:41 +04:00
{
close ( sock - > fd ) ;
}
2007-09-04 02:39:16 +04:00
static NTSTATUS ip_connect_complete ( struct socket_context * sock , uint32_t flags )
2005-01-15 13:28:08 +03:00
{
int error = 0 , ret ;
socklen_t len = sizeof ( error ) ;
/* check for any errors that may have occurred - this is needed
for non - blocking connect */
ret = getsockopt ( sock - > fd , SOL_SOCKET , SO_ERROR , & error , & len ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
if ( error ! = 0 ) {
return map_nt_error_from_unix ( error ) ;
}
if ( ! ( flags & SOCKET_FLAG_BLOCK ) ) {
ret = set_blocking ( sock - > fd , False ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
}
sock - > state = SOCKET_STATE_CLIENT_CONNECTED ;
return NT_STATUS_OK ;
}
2005-01-19 06:20:20 +03:00
static NTSTATUS ipv4_connect ( struct socket_context * sock ,
2006-01-10 01:12:53 +03:00
const struct socket_address * my_address ,
const struct socket_address * srv_address ,
uint32_t flags )
2004-09-13 18:17:41 +04:00
{
struct sockaddr_in srv_addr ;
2004-11-02 01:48:25 +03:00
struct ipv4_addr my_ip ;
struct ipv4_addr srv_ip ;
2004-09-13 18:17:41 +04:00
int ret ;
2006-01-10 01:12:53 +03:00
if ( my_address & & my_address - > sockaddr ) {
ret = bind ( sock - > fd , my_address - > sockaddr , my_address - > sockaddrlen ) ;
2004-10-27 07:15:42 +04:00
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
2006-01-10 01:12:53 +03:00
} else if ( my_address ) {
my_ip = interpret_addr2 ( my_address - > addr ) ;
if ( my_ip . addr ! = 0 | | my_address - > port ! = 0 ) {
struct sockaddr_in my_addr ;
ZERO_STRUCT ( my_addr ) ;
# ifdef HAVE_SOCK_SIN_LEN
my_addr . sin_len = sizeof ( my_addr ) ;
# endif
my_addr . sin_addr . s_addr = my_ip . addr ;
my_addr . sin_port = htons ( my_address - > port ) ;
my_addr . sin_family = PF_INET ;
ret = bind ( sock - > fd , ( struct sockaddr * ) & my_addr , sizeof ( my_addr ) ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
}
2004-09-13 18:17:41 +04:00
}
2006-01-10 01:12:53 +03:00
if ( srv_address - > sockaddr ) {
ret = connect ( sock - > fd , srv_address - > sockaddr , srv_address - > sockaddrlen ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
} else {
srv_ip = interpret_addr2 ( srv_address - > addr ) ;
if ( ! srv_ip . addr ) {
return NT_STATUS_BAD_NETWORK_NAME ;
}
ZERO_STRUCT ( srv_addr ) ;
2004-09-13 18:17:41 +04:00
# ifdef HAVE_SOCK_SIN_LEN
2006-01-10 01:12:53 +03:00
srv_addr . sin_len = sizeof ( srv_addr ) ;
2004-09-13 18:17:41 +04:00
# endif
2006-01-10 01:12:53 +03:00
srv_addr . sin_addr . s_addr = srv_ip . addr ;
srv_addr . sin_port = htons ( srv_address - > port ) ;
srv_addr . sin_family = PF_INET ;
2004-09-13 18:17:41 +04:00
2006-01-10 01:12:53 +03:00
ret = connect ( sock - > fd , ( const struct sockaddr * ) & srv_addr , sizeof ( srv_addr ) ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
2004-10-27 07:15:42 +04:00
}
2007-09-04 02:39:16 +04:00
return ip_connect_complete ( sock , flags ) ;
2004-09-13 18:17:41 +04:00
}
2005-01-15 13:28:08 +03:00
2005-01-19 06:20:20 +03:00
/*
note that for simplicity of the API , socket_listen ( ) is also
use for DGRAM sockets , but in reality only a bind ( ) is done
*/
static NTSTATUS ipv4_listen ( struct socket_context * sock ,
2006-01-10 01:12:53 +03:00
const struct socket_address * my_address ,
2005-01-19 06:20:20 +03:00
int queue_size , uint32_t flags )
2004-09-13 18:17:41 +04:00
{
struct sockaddr_in my_addr ;
2004-11-02 01:48:25 +03:00
struct ipv4_addr ip_addr ;
2004-09-13 18:17:41 +04:00
int ret ;
2005-01-30 13:24:36 +03:00
socket_set_option ( sock , " SO_REUSEADDR=1 " , NULL ) ;
2006-01-10 01:12:53 +03:00
if ( my_address - > sockaddr ) {
ret = bind ( sock - > fd , my_address - > sockaddr , my_address - > sockaddrlen ) ;
} else {
ip_addr = interpret_addr2 ( my_address - > addr ) ;
ZERO_STRUCT ( my_addr ) ;
2004-09-13 18:17:41 +04:00
# ifdef HAVE_SOCK_SIN_LEN
2006-01-10 01:12:53 +03:00
my_addr . sin_len = sizeof ( my_addr ) ;
2004-09-13 18:17:41 +04:00
# endif
2006-01-10 01:12:53 +03:00
my_addr . sin_addr . s_addr = ip_addr . addr ;
my_addr . sin_port = htons ( my_address - > port ) ;
my_addr . sin_family = PF_INET ;
ret = bind ( sock - > fd , ( struct sockaddr * ) & my_addr , sizeof ( my_addr ) ) ;
}
2004-09-13 18:17:41 +04:00
if ( ret = = - 1 ) {
2004-10-27 07:15:42 +04:00
return map_nt_error_from_unix ( errno ) ;
2004-09-13 18:17:41 +04:00
}
2005-01-19 06:20:20 +03:00
if ( sock - > type = = SOCKET_TYPE_STREAM ) {
ret = listen ( sock - > fd , queue_size ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
2004-09-13 18:17:41 +04:00
}
2004-09-15 16:14:47 +04:00
if ( ! ( flags & SOCKET_FLAG_BLOCK ) ) {
ret = set_blocking ( sock - > fd , False ) ;
if ( ret = = - 1 ) {
2004-10-27 07:15:42 +04:00
return map_nt_error_from_unix ( errno ) ;
2004-09-15 16:14:47 +04:00
}
}
2004-09-20 13:13:17 +04:00
sock - > state = SOCKET_STATE_SERVER_LISTEN ;
return NT_STATUS_OK ;
2004-09-13 18:17:41 +04:00
}
2005-01-19 06:20:20 +03:00
static NTSTATUS ipv4_accept ( struct socket_context * sock , struct socket_context * * new_sock )
2004-09-13 18:17:41 +04:00
{
struct sockaddr_in cli_addr ;
2004-10-17 09:07:07 +04:00
socklen_t cli_addr_len = sizeof ( cli_addr ) ;
2004-09-13 18:17:41 +04:00
int new_fd ;
2005-01-19 06:20:20 +03:00
if ( sock - > type ! = SOCKET_TYPE_STREAM ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2004-09-25 15:48:30 +04:00
new_fd = accept ( sock - > fd , ( struct sockaddr * ) & cli_addr , & cli_addr_len ) ;
2004-09-13 18:17:41 +04:00
if ( new_fd = = - 1 ) {
2004-10-27 07:15:42 +04:00
return map_nt_error_from_unix ( errno ) ;
2004-09-13 18:17:41 +04:00
}
2004-10-28 11:55:33 +04:00
if ( ! ( sock - > flags & SOCKET_FLAG_BLOCK ) ) {
2004-10-28 11:34:11 +04:00
int ret = set_blocking ( new_fd , False ) ;
if ( ret = = - 1 ) {
close ( new_fd ) ;
return map_nt_error_from_unix ( errno ) ;
}
}
2004-09-13 18:17:41 +04:00
/* TODO: we could add a 'accept_check' hook here
* which get the black / white lists via socket_set_accept_filter ( )
* or something like that
* - - metze
*/
2005-01-27 10:08:20 +03:00
( * new_sock ) = talloc ( NULL , struct socket_context ) ;
2004-09-13 18:17:41 +04:00
if ( ! ( * new_sock ) ) {
2004-10-17 09:07:07 +04:00
close ( new_fd ) ;
2004-09-13 18:17:41 +04:00
return NT_STATUS_NO_MEMORY ;
}
/* copy the socket_context */
( * new_sock ) - > type = sock - > type ;
( * new_sock ) - > state = SOCKET_STATE_SERVER_CONNECTED ;
2004-10-28 11:55:33 +04:00
( * new_sock ) - > flags = sock - > flags ;
2004-09-13 18:17:41 +04:00
( * new_sock ) - > fd = new_fd ;
( * new_sock ) - > private_data = NULL ;
( * new_sock ) - > ops = sock - > ops ;
2005-08-28 06:37:49 +04:00
( * new_sock ) - > backend_name = sock - > backend_name ;
2004-09-13 18:17:41 +04:00
2004-09-15 16:14:47 +04:00
return NT_STATUS_OK ;
2004-09-13 18:17:41 +04:00
}
2007-09-04 02:39:16 +04:00
static NTSTATUS ip_recv ( struct socket_context * sock , void * buf ,
2006-04-30 09:58:31 +04:00
size_t wantlen , size_t * nread )
2004-09-13 18:17:41 +04:00
{
ssize_t gotlen ;
2004-09-25 15:16:30 +04:00
2004-10-28 08:00:43 +04:00
* nread = 0 ;
2006-04-30 09:58:31 +04:00
gotlen = recv ( sock - > fd , buf , wantlen , 0 ) ;
2004-09-15 16:14:47 +04:00
if ( gotlen = = 0 ) {
return NT_STATUS_END_OF_FILE ;
} else if ( gotlen = = - 1 ) {
2004-10-28 08:00:43 +04:00
return map_nt_error_from_unix ( errno ) ;
2004-09-13 18:17:41 +04:00
}
2004-10-28 08:00:43 +04:00
* nread = gotlen ;
2004-09-13 18:17:41 +04:00
return NT_STATUS_OK ;
}
2005-01-19 06:20:20 +03:00
static NTSTATUS ipv4_recvfrom ( struct socket_context * sock , void * buf ,
2006-04-30 09:58:31 +04:00
size_t wantlen , size_t * nread ,
2006-01-10 01:12:53 +03:00
TALLOC_CTX * addr_ctx , struct socket_address * * _src )
2005-01-19 06:20:20 +03:00
{
ssize_t gotlen ;
2006-01-10 01:12:53 +03:00
struct sockaddr_in * from_addr ;
socklen_t from_len = sizeof ( * from_addr ) ;
struct socket_address * src ;
2005-01-19 06:20:20 +03:00
const char * addr ;
2006-01-10 01:12:53 +03:00
src = talloc ( addr_ctx , struct socket_address ) ;
if ( ! src ) {
return NT_STATUS_NO_MEMORY ;
}
src - > family = sock - > backend_name ;
2005-01-19 06:20:20 +03:00
2006-01-10 01:12:53 +03:00
from_addr = talloc ( src , struct sockaddr_in ) ;
if ( ! from_addr ) {
talloc_free ( src ) ;
return NT_STATUS_NO_MEMORY ;
}
src - > sockaddr = ( struct sockaddr * ) from_addr ;
2005-01-19 06:20:20 +03:00
* nread = 0 ;
2006-04-30 09:58:31 +04:00
gotlen = recvfrom ( sock - > fd , buf , wantlen , 0 ,
2006-01-10 01:12:53 +03:00
src - > sockaddr , & from_len ) ;
2005-01-19 06:20:20 +03:00
if ( gotlen = = 0 ) {
2006-01-10 01:12:53 +03:00
talloc_free ( src ) ;
2005-01-19 06:20:20 +03:00
return NT_STATUS_END_OF_FILE ;
} else if ( gotlen = = - 1 ) {
2006-01-10 01:12:53 +03:00
talloc_free ( src ) ;
2005-01-19 06:20:20 +03:00
return map_nt_error_from_unix ( errno ) ;
}
2006-01-10 01:12:53 +03:00
src - > sockaddrlen = from_len ;
addr = inet_ntoa ( from_addr - > sin_addr ) ;
2005-01-19 06:20:20 +03:00
if ( addr = = NULL ) {
2006-01-10 01:12:53 +03:00
talloc_free ( src ) ;
2005-01-19 06:20:20 +03:00
return NT_STATUS_INTERNAL_ERROR ;
}
2006-01-10 01:12:53 +03:00
src - > addr = talloc_strdup ( src , addr ) ;
if ( src - > addr = = NULL ) {
talloc_free ( src ) ;
return NT_STATUS_NO_MEMORY ;
}
src - > port = ntohs ( from_addr - > sin_port ) ;
2005-01-19 06:20:20 +03:00
2006-01-10 01:12:53 +03:00
* nread = gotlen ;
* _src = src ;
2005-01-19 06:20:20 +03:00
return NT_STATUS_OK ;
}
2007-09-04 02:39:16 +04:00
static NTSTATUS ip_send ( struct socket_context * sock ,
2006-04-30 09:58:31 +04:00
const DATA_BLOB * blob , size_t * sendlen )
2004-09-13 18:17:41 +04:00
{
ssize_t len ;
2004-10-28 01:29:55 +04:00
* sendlen = 0 ;
2006-04-30 09:58:31 +04:00
len = send ( sock - > fd , blob - > data , blob - > length , 0 ) ;
2004-09-13 18:17:41 +04:00
if ( len = = - 1 ) {
2004-10-27 07:15:42 +04:00
return map_nt_error_from_unix ( errno ) ;
2004-09-13 18:17:41 +04:00
}
* sendlen = len ;
return NT_STATUS_OK ;
}
2005-01-19 06:20:20 +03:00
static NTSTATUS ipv4_sendto ( struct socket_context * sock ,
2006-04-30 09:58:31 +04:00
const DATA_BLOB * blob , size_t * sendlen ,
2006-01-10 01:12:53 +03:00
const struct socket_address * dest_addr )
2005-01-19 06:20:20 +03:00
{
ssize_t len ;
2006-01-10 01:12:53 +03:00
if ( dest_addr - > sockaddr ) {
2006-04-30 09:58:31 +04:00
len = sendto ( sock - > fd , blob - > data , blob - > length , 0 ,
2006-01-10 01:12:53 +03:00
dest_addr - > sockaddr , dest_addr - > sockaddrlen ) ;
} else {
struct sockaddr_in srv_addr ;
struct ipv4_addr addr ;
ZERO_STRUCT ( srv_addr ) ;
2005-01-19 06:20:20 +03:00
# ifdef HAVE_SOCK_SIN_LEN
2006-01-10 01:12:53 +03:00
srv_addr . sin_len = sizeof ( srv_addr ) ;
2005-01-19 06:20:20 +03:00
# endif
2006-01-10 01:12:53 +03:00
addr = interpret_addr2 ( dest_addr - > addr ) ;
2007-04-05 11:36:46 +04:00
if ( addr . addr = = 0 ) {
return NT_STATUS_HOST_UNREACHABLE ;
}
2006-01-10 01:12:53 +03:00
srv_addr . sin_addr . s_addr = addr . addr ;
srv_addr . sin_port = htons ( dest_addr - > port ) ;
srv_addr . sin_family = PF_INET ;
* sendlen = 0 ;
2006-04-30 09:58:31 +04:00
len = sendto ( sock - > fd , blob - > data , blob - > length , 0 ,
2006-01-10 01:12:53 +03:00
( struct sockaddr * ) & srv_addr , sizeof ( srv_addr ) ) ;
}
2005-01-19 06:20:20 +03:00
if ( len = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
* sendlen = len ;
return NT_STATUS_OK ;
}
static NTSTATUS ipv4_set_option ( struct socket_context * sock , const char * option , const char * val )
2004-09-13 18:17:41 +04:00
{
2004-09-15 16:14:47 +04:00
set_socket_options ( sock - > fd , option ) ;
return NT_STATUS_OK ;
2004-09-13 18:17:41 +04:00
}
2005-01-19 06:20:20 +03:00
static char * ipv4_get_peer_name ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
2004-09-24 07:34:55 +04:00
{
struct sockaddr_in peer_addr ;
socklen_t len = sizeof ( peer_addr ) ;
struct hostent * he ;
int ret ;
ret = getpeername ( sock - > fd , ( struct sockaddr * ) & peer_addr , & len ) ;
if ( ret = = - 1 ) {
return NULL ;
}
he = gethostbyaddr ( ( char * ) & peer_addr . sin_addr , sizeof ( peer_addr . sin_addr ) , AF_INET ) ;
if ( he = = NULL ) {
return NULL ;
}
return talloc_strdup ( mem_ctx , he - > h_name ) ;
}
2006-01-10 01:12:53 +03:00
static struct socket_address * ipv4_get_peer_addr ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
2004-09-13 18:17:41 +04:00
{
2006-01-10 01:12:53 +03:00
struct sockaddr_in * peer_addr ;
socklen_t len = sizeof ( * peer_addr ) ;
const char * addr ;
struct socket_address * peer ;
2004-09-20 16:03:49 +04:00
int ret ;
2006-01-10 01:12:53 +03:00
peer = talloc ( mem_ctx , struct socket_address ) ;
if ( ! peer ) {
return NULL ;
}
peer - > family = sock - > backend_name ;
peer_addr = talloc ( peer , struct sockaddr_in ) ;
if ( ! peer_addr ) {
talloc_free ( peer ) ;
return NULL ;
}
2004-09-20 16:03:49 +04:00
2006-01-10 01:12:53 +03:00
peer - > sockaddr = ( struct sockaddr * ) peer_addr ;
ret = getpeername ( sock - > fd , peer - > sockaddr , & len ) ;
2004-09-20 16:03:49 +04:00
if ( ret = = - 1 ) {
2006-01-10 01:12:53 +03:00
talloc_free ( peer ) ;
2004-09-20 16:03:49 +04:00
return NULL ;
}
2006-01-10 01:12:53 +03:00
peer - > sockaddrlen = len ;
2004-09-13 18:17:41 +04:00
2006-01-10 01:12:53 +03:00
addr = inet_ntoa ( peer_addr - > sin_addr ) ;
if ( addr = = NULL ) {
talloc_free ( peer ) ;
return NULL ;
}
peer - > addr = talloc_strdup ( peer , addr ) ;
if ( ! peer - > addr ) {
talloc_free ( peer ) ;
return NULL ;
2004-09-20 16:03:49 +04:00
}
2006-01-10 01:12:53 +03:00
peer - > port = ntohs ( peer_addr - > sin_port ) ;
2004-09-20 16:03:49 +04:00
2006-01-10 01:12:53 +03:00
return peer ;
2004-09-13 18:17:41 +04:00
}
2006-01-10 01:12:53 +03:00
static struct socket_address * ipv4_get_my_addr ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
2004-09-13 18:17:41 +04:00
{
2006-01-10 01:12:53 +03:00
struct sockaddr_in * local_addr ;
socklen_t len = sizeof ( * local_addr ) ;
const char * addr ;
struct socket_address * local ;
2004-09-20 16:03:49 +04:00
int ret ;
2006-01-10 01:12:53 +03:00
local = talloc ( mem_ctx , struct socket_address ) ;
if ( ! local ) {
return NULL ;
}
local - > family = sock - > backend_name ;
local_addr = talloc ( local , struct sockaddr_in ) ;
if ( ! local_addr ) {
talloc_free ( local ) ;
return NULL ;
}
2004-09-20 16:03:49 +04:00
2006-01-10 01:12:53 +03:00
local - > sockaddr = ( struct sockaddr * ) local_addr ;
ret = getsockname ( sock - > fd , local - > sockaddr , & len ) ;
2004-09-20 16:03:49 +04:00
if ( ret = = - 1 ) {
2006-01-10 01:12:53 +03:00
talloc_free ( local ) ;
2004-09-20 16:03:49 +04:00
return NULL ;
}
2006-01-10 01:12:53 +03:00
local - > sockaddrlen = len ;
2004-09-20 16:03:49 +04:00
2006-01-10 01:12:53 +03:00
addr = inet_ntoa ( local_addr - > sin_addr ) ;
if ( addr = = NULL ) {
talloc_free ( local ) ;
return NULL ;
}
local - > addr = talloc_strdup ( local , addr ) ;
if ( ! local - > addr ) {
talloc_free ( local ) ;
return NULL ;
2004-09-20 16:03:49 +04:00
}
2006-01-10 01:12:53 +03:00
local - > port = ntohs ( local_addr - > sin_port ) ;
2004-09-20 16:03:49 +04:00
2006-01-10 01:12:53 +03:00
return local ;
2004-09-13 18:17:41 +04:00
}
2007-09-04 02:39:16 +04:00
static int ip_get_fd ( struct socket_context * sock )
2004-09-13 18:17:41 +04:00
{
return sock - > fd ;
}
2007-09-04 02:39:16 +04:00
static NTSTATUS ip_pending ( struct socket_context * sock , size_t * npending )
2005-06-03 17:20:08 +04:00
{
int value = 0 ;
if ( ioctl ( sock - > fd , FIONREAD , & value ) = = 0 ) {
* npending = value ;
return NT_STATUS_OK ;
}
return map_nt_error_from_unix ( errno ) ;
}
2005-01-19 06:20:20 +03:00
static const struct socket_ops ipv4_ops = {
2004-11-02 05:01:04 +03:00
. name = " ipv4 " ,
2005-01-19 06:20:20 +03:00
. fn_init = ipv4_init ,
. fn_connect = ipv4_connect ,
2007-09-04 02:39:16 +04:00
. fn_connect_complete = ip_connect_complete ,
2005-01-19 06:20:20 +03:00
. fn_listen = ipv4_listen ,
. fn_accept = ipv4_accept ,
2007-09-04 02:39:16 +04:00
. fn_recv = ip_recv ,
2005-01-19 06:20:20 +03:00
. fn_recvfrom = ipv4_recvfrom ,
2007-09-04 02:39:16 +04:00
. fn_send = ip_send ,
2005-01-19 06:20:20 +03:00
. fn_sendto = ipv4_sendto ,
2007-09-04 02:39:16 +04:00
. fn_pending = ip_pending ,
. fn_close = ip_close ,
2005-01-19 06:20:20 +03:00
. fn_set_option = ipv4_set_option ,
. fn_get_peer_name = ipv4_get_peer_name ,
. fn_get_peer_addr = ipv4_get_peer_addr ,
. fn_get_my_addr = ipv4_get_my_addr ,
2007-09-28 01:17:44 +04:00
2007-09-04 02:39:16 +04:00
. fn_get_fd = ip_get_fd
2004-09-13 18:17:41 +04:00
} ;
2005-01-19 06:20:20 +03:00
const struct socket_ops * socket_ipv4_ops ( enum socket_type type )
2004-09-13 18:17:41 +04:00
{
2005-01-19 06:20:20 +03:00
return & ipv4_ops ;
2004-09-13 18:17:41 +04:00
}
2007-09-04 02:39:16 +04:00
# if HAVE_IPV6
static struct in6_addr interpret_addr6 ( const char * name )
{
struct hostent * he ;
if ( name = = NULL ) return in6addr_any ;
if ( strcasecmp ( name , " localhost " ) = = 0 ) {
name = " ::1 " ;
}
he = gethostbyname2 ( name , PF_INET6 ) ;
if ( he = = NULL ) return in6addr_any ;
return * ( ( struct in6_addr * ) he - > h_addr ) ;
}
2007-09-28 01:17:44 +04:00
static NTSTATUS ipv6_init ( struct socket_context * sock )
2007-09-04 02:39:16 +04:00
{
2007-09-28 01:17:44 +04:00
int type ;
switch ( sock - > type ) {
case SOCKET_TYPE_STREAM :
type = SOCK_STREAM ;
break ;
case SOCKET_TYPE_DGRAM :
type = SOCK_DGRAM ;
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
sock - > fd = socket ( PF_INET6 , type , 0 ) ;
2007-09-04 02:39:16 +04:00
if ( sock - > fd = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
sock - > backend_name = " ipv6 " ;
return NT_STATUS_OK ;
}
static NTSTATUS ipv6_tcp_connect ( struct socket_context * sock ,
const struct socket_address * my_address ,
const struct socket_address * srv_address ,
uint32_t flags )
{
int ret ;
if ( my_address & & my_address - > sockaddr ) {
ret = bind ( sock - > fd , my_address - > sockaddr , my_address - > sockaddrlen ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
} else if ( my_address ) {
struct in6_addr my_ip ;
my_ip = interpret_addr6 ( my_address - > addr ) ;
if ( memcmp ( & my_ip , & in6addr_any , sizeof ( my_ip ) ) | | my_address - > port ! = 0 ) {
struct sockaddr_in6 my_addr ;
ZERO_STRUCT ( my_addr ) ;
my_addr . sin6_addr = my_ip ;
my_addr . sin6_port = htons ( my_address - > port ) ;
my_addr . sin6_family = PF_INET6 ;
ret = bind ( sock - > fd , ( struct sockaddr * ) & my_addr , sizeof ( my_addr ) ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
}
}
if ( srv_address - > sockaddr ) {
ret = connect ( sock - > fd , srv_address - > sockaddr , srv_address - > sockaddrlen ) ;
} else {
struct in6_addr srv_ip ;
struct sockaddr_in6 srv_addr ;
srv_ip = interpret_addr6 ( srv_address - > addr ) ;
if ( memcmp ( & srv_ip , & in6addr_any , sizeof ( srv_ip ) ) = = 0 ) {
return NT_STATUS_BAD_NETWORK_NAME ;
}
ZERO_STRUCT ( srv_addr ) ;
srv_addr . sin6_addr = srv_ip ;
srv_addr . sin6_port = htons ( srv_address - > port ) ;
srv_addr . sin6_family = PF_INET6 ;
ret = connect ( sock - > fd , ( const struct sockaddr * ) & srv_addr , sizeof ( srv_addr ) ) ;
}
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
return ip_connect_complete ( sock , flags ) ;
}
2007-09-28 01:17:44 +04:00
static NTSTATUS ipv6_listen ( struct socket_context * sock ,
2007-09-04 02:39:16 +04:00
const struct socket_address * my_address ,
int queue_size , uint32_t flags )
{
struct sockaddr_in6 my_addr ;
struct in6_addr ip_addr ;
int ret ;
socket_set_option ( sock , " SO_REUSEADDR=1 " , NULL ) ;
if ( my_address - > sockaddr ) {
ret = bind ( sock - > fd , my_address - > sockaddr , my_address - > sockaddrlen ) ;
} else {
ip_addr = interpret_addr6 ( my_address - > addr ) ;
ZERO_STRUCT ( my_addr ) ;
my_addr . sin6_addr = ip_addr ;
my_addr . sin6_port = htons ( my_address - > port ) ;
my_addr . sin6_family = PF_INET6 ;
ret = bind ( sock - > fd , ( struct sockaddr * ) & my_addr , sizeof ( my_addr ) ) ;
}
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
if ( sock - > type = = SOCKET_TYPE_STREAM ) {
ret = listen ( sock - > fd , queue_size ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
}
if ( ! ( flags & SOCKET_FLAG_BLOCK ) ) {
ret = set_blocking ( sock - > fd , False ) ;
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
}
sock - > state = SOCKET_STATE_SERVER_LISTEN ;
return NT_STATUS_OK ;
}
static NTSTATUS ipv6_tcp_accept ( struct socket_context * sock , struct socket_context * * new_sock )
{
struct sockaddr_in cli_addr ;
socklen_t cli_addr_len = sizeof ( cli_addr ) ;
int new_fd ;
if ( sock - > type ! = SOCKET_TYPE_STREAM ) {
return NT_STATUS_INVALID_PARAMETER ;
}
new_fd = accept ( sock - > fd , ( struct sockaddr * ) & cli_addr , & cli_addr_len ) ;
if ( new_fd = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
if ( ! ( sock - > flags & SOCKET_FLAG_BLOCK ) ) {
int ret = set_blocking ( new_fd , False ) ;
if ( ret = = - 1 ) {
close ( new_fd ) ;
return map_nt_error_from_unix ( errno ) ;
}
}
/* TODO: we could add a 'accept_check' hook here
* which get the black / white lists via socket_set_accept_filter ( )
* or something like that
* - - metze
*/
( * new_sock ) = talloc ( NULL , struct socket_context ) ;
if ( ! ( * new_sock ) ) {
close ( new_fd ) ;
return NT_STATUS_NO_MEMORY ;
}
/* copy the socket_context */
( * new_sock ) - > type = sock - > type ;
( * new_sock ) - > state = SOCKET_STATE_SERVER_CONNECTED ;
( * new_sock ) - > flags = sock - > flags ;
( * new_sock ) - > fd = new_fd ;
( * new_sock ) - > private_data = NULL ;
( * new_sock ) - > ops = sock - > ops ;
( * new_sock ) - > backend_name = sock - > backend_name ;
return NT_STATUS_OK ;
}
static NTSTATUS ipv6_recvfrom ( struct socket_context * sock , void * buf ,
size_t wantlen , size_t * nread ,
TALLOC_CTX * addr_ctx , struct socket_address * * _src )
{
ssize_t gotlen ;
struct sockaddr_in6 * from_addr ;
socklen_t from_len = sizeof ( * from_addr ) ;
struct socket_address * src ;
struct hostent * he ;
src = talloc ( addr_ctx , struct socket_address ) ;
if ( ! src ) {
return NT_STATUS_NO_MEMORY ;
}
src - > family = sock - > backend_name ;
from_addr = talloc ( src , struct sockaddr_in6 ) ;
if ( ! from_addr ) {
talloc_free ( src ) ;
return NT_STATUS_NO_MEMORY ;
}
src - > sockaddr = ( struct sockaddr * ) from_addr ;
* nread = 0 ;
gotlen = recvfrom ( sock - > fd , buf , wantlen , 0 ,
src - > sockaddr , & from_len ) ;
if ( gotlen = = 0 ) {
talloc_free ( src ) ;
return NT_STATUS_END_OF_FILE ;
} else if ( gotlen = = - 1 ) {
talloc_free ( src ) ;
return map_nt_error_from_unix ( errno ) ;
}
src - > sockaddrlen = from_len ;
he = gethostbyaddr ( ( void * ) & from_addr - > sin6_addr , sizeof ( from_addr - > sin6_addr ) , AF_INET6 ) ;
if ( he = = NULL ) {
talloc_free ( src ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
src - > addr = talloc_strdup ( src , he - > h_name ) ;
if ( src - > addr = = NULL ) {
talloc_free ( src ) ;
return NT_STATUS_NO_MEMORY ;
}
src - > port = ntohs ( from_addr - > sin6_port ) ;
* nread = gotlen ;
* _src = src ;
return NT_STATUS_OK ;
}
static NTSTATUS ipv6_sendto ( struct socket_context * sock ,
const DATA_BLOB * blob , size_t * sendlen ,
const struct socket_address * dest_addr )
{
ssize_t len ;
if ( dest_addr - > sockaddr ) {
len = sendto ( sock - > fd , blob - > data , blob - > length , 0 ,
dest_addr - > sockaddr , dest_addr - > sockaddrlen ) ;
} else {
struct sockaddr_in6 srv_addr ;
struct in6_addr addr ;
ZERO_STRUCT ( srv_addr ) ;
addr = interpret_addr6 ( dest_addr - > addr ) ;
if ( addr . s6_addr = = 0 ) {
return NT_STATUS_HOST_UNREACHABLE ;
}
srv_addr . sin6_addr = addr ;
srv_addr . sin6_port = htons ( dest_addr - > port ) ;
srv_addr . sin6_family = PF_INET6 ;
* sendlen = 0 ;
len = sendto ( sock - > fd , blob - > data , blob - > length , 0 ,
( struct sockaddr * ) & srv_addr , sizeof ( srv_addr ) ) ;
}
if ( len = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
* sendlen = len ;
return NT_STATUS_OK ;
}
2007-09-28 01:17:44 +04:00
static NTSTATUS ipv6_set_option ( struct socket_context * sock , const char * option , const char * val )
2007-09-04 02:39:16 +04:00
{
set_socket_options ( sock - > fd , option ) ;
return NT_STATUS_OK ;
}
static char * ipv6_tcp_get_peer_name ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
{
struct sockaddr_in6 peer_addr ;
socklen_t len = sizeof ( peer_addr ) ;
struct hostent * he ;
int ret ;
ret = getpeername ( sock - > fd , ( struct sockaddr * ) & peer_addr , & len ) ;
if ( ret = = - 1 ) {
return NULL ;
}
he = gethostbyaddr ( ( char * ) & peer_addr . sin6_addr , sizeof ( peer_addr . sin6_addr ) , AF_INET6 ) ;
if ( he = = NULL ) {
return NULL ;
}
return talloc_strdup ( mem_ctx , he - > h_name ) ;
}
static struct socket_address * ipv6_tcp_get_peer_addr ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
{
struct sockaddr_in6 * peer_addr ;
socklen_t len = sizeof ( * peer_addr ) ;
struct socket_address * peer ;
int ret ;
char addr [ 128 ] ;
const char * addr_ret ;
peer = talloc ( mem_ctx , struct socket_address ) ;
if ( ! peer ) {
return NULL ;
}
peer - > family = sock - > backend_name ;
peer_addr = talloc ( peer , struct sockaddr_in6 ) ;
if ( ! peer_addr ) {
talloc_free ( peer ) ;
return NULL ;
}
peer - > sockaddr = ( struct sockaddr * ) peer_addr ;
ret = getpeername ( sock - > fd , peer - > sockaddr , & len ) ;
if ( ret = = - 1 ) {
talloc_free ( peer ) ;
return NULL ;
}
peer - > sockaddrlen = len ;
addr_ret = inet_ntop ( AF_INET6 , & peer_addr - > sin6_addr , addr , sizeof ( addr ) ) ;
if ( addr_ret = = NULL ) {
talloc_free ( peer ) ;
return NULL ;
}
peer - > addr = talloc_strdup ( peer , addr_ret ) ;
if ( peer - > addr = = NULL ) {
talloc_free ( peer ) ;
return NULL ;
}
peer - > port = ntohs ( peer_addr - > sin6_port ) ;
return peer ;
}
static struct socket_address * ipv6_tcp_get_my_addr ( struct socket_context * sock , TALLOC_CTX * mem_ctx )
{
struct sockaddr_in6 * local_addr ;
socklen_t len = sizeof ( * local_addr ) ;
struct socket_address * local ;
int ret ;
struct hostent * he ;
local = talloc ( mem_ctx , struct socket_address ) ;
if ( ! local ) {
return NULL ;
}
local - > family = sock - > backend_name ;
local_addr = talloc ( local , struct sockaddr_in6 ) ;
if ( ! local_addr ) {
talloc_free ( local ) ;
return NULL ;
}
local - > sockaddr = ( struct sockaddr * ) local_addr ;
ret = getsockname ( sock - > fd , local - > sockaddr , & len ) ;
if ( ret = = - 1 ) {
talloc_free ( local ) ;
return NULL ;
}
local - > sockaddrlen = len ;
he = gethostbyaddr ( ( char * ) & local_addr - > sin6_addr , len , AF_INET6 ) ;
if ( ! he | | ! he - > h_name ) {
talloc_free ( local ) ;
return NULL ;
}
local - > addr = talloc_strdup ( mem_ctx , he - > h_name ) ;
if ( ! local - > addr ) {
talloc_free ( local ) ;
return NULL ;
}
local - > port = ntohs ( local_addr - > sin6_port ) ;
return local ;
}
static const struct socket_ops ipv6_tcp_ops = {
. name = " ipv6 " ,
2007-09-28 01:17:44 +04:00
. fn_init = ipv6_init ,
2007-09-04 02:39:16 +04:00
. fn_connect = ipv6_tcp_connect ,
. fn_connect_complete = ip_connect_complete ,
2007-09-28 01:17:44 +04:00
. fn_listen = ipv6_listen ,
2007-09-04 02:39:16 +04:00
. fn_accept = ipv6_tcp_accept ,
. fn_recv = ip_recv ,
. fn_recvfrom = ipv6_recvfrom ,
. fn_send = ip_send ,
2007-09-28 01:17:44 +04:00
. fn_sendto = ipv6_sendto ,
2007-09-04 02:39:16 +04:00
. fn_pending = ip_pending ,
2007-09-28 01:17:44 +04:00
. fn_close = ip_close ,
2007-09-04 02:39:16 +04:00
2007-09-28 01:17:44 +04:00
. fn_set_option = ipv6_set_option ,
2007-09-04 02:39:16 +04:00
. fn_get_peer_name = ipv6_tcp_get_peer_name ,
. fn_get_peer_addr = ipv6_tcp_get_peer_addr ,
. fn_get_my_addr = ipv6_tcp_get_my_addr ,
. fn_get_fd = ip_get_fd
} ;
const struct socket_ops * socket_ipv6_ops ( enum socket_type type )
{
return & ipv6_tcp_ops ;
}
# endif