2005-03-28 05:00:39 +04:00
/*
Socket wrapper library . Passes all socket communication over
unix domain sockets if the environment variable SOCKET_WRAPPER_DIR
is set .
Copyright ( C ) Jelmer Vernooij 2005
2006-09-12 20:33:24 +04:00
Copyright ( C ) Stefan Metzmacher 2006
2005-03-28 05:00:39 +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
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 .
*/
2005-06-26 03:38:03 +04:00
# ifdef _SAMBA_BUILD_
2006-09-17 09:11:57 +04:00
# define SOCKET_WRAPPER_NOT_REPLACE
2005-03-28 05:00:39 +04:00
# include "includes.h"
# include "system/network.h"
2005-06-10 16:21:46 +04:00
# include "system/filesys.h"
2006-09-17 09:11:57 +04:00
# ifndef _DLINKLIST_H
# include "lib/util/dlinklist.h"
# endif
# ifdef malloc
# undef malloc
# endif
# ifdef calloc
# undef calloc
# endif
# ifdef strdup
# undef strdup
# endif
# else /* _SAMBA_BUILD_ */
2005-03-28 05:00:39 +04:00
# include <sys/types.h>
2005-06-10 16:21:46 +04:00
# include <sys/stat.h>
2005-03-28 05:00:39 +04:00
# include <sys/socket.h>
# include <errno.h>
# include <sys/un.h>
# include <netinet/in.h>
2005-03-31 03:28:15 +04:00
# include <netinet/tcp.h>
2005-03-28 05:00:39 +04:00
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <stdio.h>
2006-09-17 09:11:57 +04:00
# error "dlinklist.h missing"
2005-03-28 05:00:39 +04:00
# endif
/* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
* for now */
# define REWRITE_CALLS
# ifdef REWRITE_CALLS
# define real_accept accept
# define real_connect connect
# define real_bind bind
2006-09-27 14:13:45 +04:00
# define real_listen listen
2005-03-28 05:00:39 +04:00
# define real_getpeername getpeername
# define real_getsockname getsockname
# define real_getsockopt getsockopt
# define real_setsockopt setsockopt
# define real_recvfrom recvfrom
# define real_sendto sendto
2006-09-27 14:13:45 +04:00
# define real_ioctl ioctl
2006-09-12 10:19:11 +04:00
# define real_recv recv
# define real_send send
2005-03-28 05:00:39 +04:00
# define real_socket socket
# define real_close close
# endif
2005-07-21 12:42:17 +04:00
/* we need to use a very terse format here as IRIX 6.4 silently
truncates names to 16 chars , so if we use a longer name then we
can ' t tell which port a packet came from with recvfrom ( )
with this format we have 8 chars left for the directory name
*/
2005-11-07 18:36:51 +03:00
# define SOCKET_FORMAT "%c%02X%04X"
# define SOCKET_TYPE_CHAR_TCP 'T'
# define SOCKET_TYPE_CHAR_UDP 'U'
2005-07-21 12:42:17 +04:00
2006-09-26 15:31:14 +04:00
# define MAX_WRAPPED_INTERFACES 16
2005-03-31 03:28:15 +04:00
static struct sockaddr * sockaddr_dup ( const void * data , socklen_t len )
2005-03-28 05:00:39 +04:00
{
struct sockaddr * ret = ( struct sockaddr * ) malloc ( len ) ;
memcpy ( ret , data , len ) ;
return ret ;
}
struct socket_info
{
int fd ;
2006-09-12 18:59:08 +04:00
int family ;
2005-03-28 05:00:39 +04:00
int type ;
int protocol ;
2005-03-31 16:40:12 +04:00
int bound ;
2005-11-07 18:36:51 +03:00
int bcast ;
2006-09-29 13:31:20 +04:00
int is_server ;
2005-03-28 05:00:39 +04:00
char * path ;
2005-03-31 16:40:12 +04:00
char * tmp_path ;
2005-03-28 05:00:39 +04:00
struct sockaddr * myname ;
socklen_t myname_len ;
struct sockaddr * peername ;
socklen_t peername_len ;
struct socket_info * prev , * next ;
2005-03-31 16:40:12 +04:00
} ;
2006-09-12 20:33:24 +04:00
static struct socket_info * sockets ;
2005-03-28 05:00:39 +04:00
2005-07-21 17:45:07 +04:00
static const char * socket_wrapper_dir ( void )
{
const char * s = getenv ( " SOCKET_WRAPPER_DIR " ) ;
if ( s = = NULL ) {
return NULL ;
}
if ( strncmp ( s , " ./ " , 2 ) = = 0 ) {
s + = 2 ;
}
return s ;
}
2006-09-29 13:31:20 +04:00
static const char * socket_wrapper_pcap_file ( void )
2006-09-12 13:08:55 +04:00
{
2006-09-29 13:31:20 +04:00
const char * s = getenv ( " SOCKET_WRAPPER_PCAP_FILE " ) ;
2006-09-12 13:08:55 +04:00
if ( s = = NULL ) {
return NULL ;
}
if ( strncmp ( s , " ./ " , 2 ) = = 0 ) {
s + = 2 ;
}
return s ;
}
2005-11-07 18:36:51 +03:00
static unsigned int socket_wrapper_default_iface ( void )
{
const char * s = getenv ( " SOCKET_WRAPPER_DEFAULT_IFACE " ) ;
if ( s ) {
unsigned int iface ;
if ( sscanf ( s , " %u " , & iface ) = = 1 ) {
2006-09-26 15:31:14 +04:00
if ( iface > = 1 & & iface < = MAX_WRAPPED_INTERFACES ) {
2005-11-07 18:36:51 +03:00
return iface ;
}
}
}
return 1 ; /* 127.0.0.1 */
}
2005-03-28 05:00:39 +04:00
static int convert_un_in ( const struct sockaddr_un * un , struct sockaddr_in * in , socklen_t * len )
{
2005-11-07 18:36:51 +03:00
unsigned int iface ;
2005-03-28 05:00:39 +04:00
unsigned int prt ;
const char * p ;
2005-11-07 18:36:51 +03:00
char type ;
2005-03-28 05:00:39 +04:00
if ( ( * len ) < sizeof ( struct sockaddr_in ) ) {
return 0 ;
}
2005-06-10 16:21:46 +04:00
p = strrchr ( un - > sun_path , ' / ' ) ;
2005-03-28 05:00:39 +04:00
if ( p ) p + + ; else p = un - > sun_path ;
2005-11-07 18:36:51 +03:00
if ( sscanf ( p , SOCKET_FORMAT , & type , & iface , & prt ) ! = 3 ) {
errno = EINVAL ;
return - 1 ;
}
if ( type ! = SOCKET_TYPE_CHAR_TCP & & type ! = SOCKET_TYPE_CHAR_UDP ) {
errno = EINVAL ;
return - 1 ;
}
2006-09-26 15:31:14 +04:00
if ( iface = = 0 | | iface > MAX_WRAPPED_INTERFACES ) {
2005-11-07 18:36:51 +03:00
errno = EINVAL ;
return - 1 ;
}
if ( prt > 0xFFFF ) {
errno = EINVAL ;
return - 1 ;
2005-03-28 05:00:39 +04:00
}
2005-11-07 18:36:51 +03:00
2006-01-04 08:40:35 +03:00
in - > sin_family = AF_INET ;
2005-11-07 18:36:51 +03:00
in - > sin_addr . s_addr = htonl ( ( 127 < < 24 ) | iface ) ;
in - > sin_port = htons ( prt ) ;
2005-03-28 05:00:39 +04:00
* len = sizeof ( struct sockaddr_in ) ;
return 0 ;
}
2005-11-07 18:36:51 +03:00
static int convert_in_un_remote ( struct socket_info * si , const struct sockaddr_in * in , struct sockaddr_un * un ,
int * bcast )
2005-03-28 05:00:39 +04:00
{
2005-11-07 18:36:51 +03:00
char u_type = ' \0 ' ;
char b_type = ' \0 ' ;
char a_type = ' \0 ' ;
char type = ' \0 ' ;
unsigned int addr = ntohl ( in - > sin_addr . s_addr ) ;
unsigned int prt = ntohs ( in - > sin_port ) ;
unsigned int iface ;
int is_bcast = 0 ;
if ( bcast ) * bcast = 0 ;
if ( prt = = 0 ) {
errno = EINVAL ;
return - 1 ;
}
switch ( si - > type ) {
case SOCK_STREAM :
u_type = SOCKET_TYPE_CHAR_TCP ;
break ;
case SOCK_DGRAM :
u_type = SOCKET_TYPE_CHAR_UDP ;
a_type = SOCKET_TYPE_CHAR_UDP ;
b_type = SOCKET_TYPE_CHAR_UDP ;
break ;
}
if ( a_type & & addr = = 0xFFFFFFFF ) {
/* 255.255.255.255 only udp */
is_bcast = 2 ;
type = a_type ;
iface = socket_wrapper_default_iface ( ) ;
} else if ( b_type & & addr = = 0x7FFFFFFF ) {
/* 127.255.255.255 only udp */
is_bcast = 1 ;
type = b_type ;
iface = socket_wrapper_default_iface ( ) ;
} else if ( ( addr & 0xFFFFFF00 ) = = 0x7F000000 ) {
/* 127.0.0.X */
is_bcast = 0 ;
type = u_type ;
iface = ( addr & 0x000000FF ) ;
} else {
errno = ENETUNREACH ;
return - 1 ;
}
if ( bcast ) * bcast = is_bcast ;
if ( is_bcast ) {
snprintf ( un - > sun_path , sizeof ( un - > sun_path ) , " %s/EINVAL " ,
socket_wrapper_dir ( ) ) ;
/* the caller need to do more processing */
return 0 ;
}
snprintf ( un - > sun_path , sizeof ( un - > sun_path ) , " %s/ " SOCKET_FORMAT ,
socket_wrapper_dir ( ) , type , iface , prt ) ;
return 0 ;
}
static int convert_in_un_alloc ( struct socket_info * si , const struct sockaddr_in * in , struct sockaddr_un * un ,
int * bcast )
{
char u_type = ' \0 ' ;
char d_type = ' \0 ' ;
char b_type = ' \0 ' ;
char a_type = ' \0 ' ;
char type = ' \0 ' ;
unsigned int addr = ntohl ( in - > sin_addr . s_addr ) ;
unsigned int prt = ntohs ( in - > sin_port ) ;
unsigned int iface ;
struct stat st ;
int is_bcast = 0 ;
if ( bcast ) * bcast = 0 ;
switch ( si - > type ) {
case SOCK_STREAM :
u_type = SOCKET_TYPE_CHAR_TCP ;
d_type = SOCKET_TYPE_CHAR_TCP ;
break ;
case SOCK_DGRAM :
u_type = SOCKET_TYPE_CHAR_UDP ;
d_type = SOCKET_TYPE_CHAR_UDP ;
a_type = SOCKET_TYPE_CHAR_UDP ;
b_type = SOCKET_TYPE_CHAR_UDP ;
break ;
}
if ( addr = = 0 ) {
/* 0.0.0.0 */
is_bcast = 0 ;
type = d_type ;
iface = socket_wrapper_default_iface ( ) ;
} else if ( a_type & & addr = = 0xFFFFFFFF ) {
/* 255.255.255.255 only udp */
is_bcast = 2 ;
type = a_type ;
iface = socket_wrapper_default_iface ( ) ;
} else if ( b_type & & addr = = 0x7FFFFFFF ) {
/* 127.255.255.255 only udp */
is_bcast = 1 ;
type = b_type ;
iface = socket_wrapper_default_iface ( ) ;
} else if ( ( addr & 0xFFFFFF00 ) = = 0x7F000000 ) {
/* 127.0.0.X */
is_bcast = 0 ;
type = u_type ;
iface = ( addr & 0x000000FF ) ;
} else {
errno = EADDRNOTAVAIL ;
return - 1 ;
}
if ( bcast ) * bcast = is_bcast ;
2005-06-10 16:21:46 +04:00
if ( prt = = 0 ) {
/* handle auto-allocation of ephemeral ports */
2005-11-07 18:36:51 +03:00
for ( prt = 5001 ; prt < 10000 ; prt + + ) {
2005-07-21 12:42:17 +04:00
snprintf ( un - > sun_path , sizeof ( un - > sun_path ) , " %s/ " SOCKET_FORMAT ,
2005-11-07 18:36:51 +03:00
socket_wrapper_dir ( ) , type , iface , prt ) ;
if ( stat ( un - > sun_path , & st ) = = 0 ) continue ;
( ( struct sockaddr_in * ) si - > myname ) - > sin_port = htons ( prt ) ;
return 0 ;
}
errno = ENFILE ;
return - 1 ;
}
2005-07-21 12:42:17 +04:00
snprintf ( un - > sun_path , sizeof ( un - > sun_path ) , " %s/ " SOCKET_FORMAT ,
2005-11-07 18:36:51 +03:00
socket_wrapper_dir ( ) , type , iface , prt ) ;
2005-03-28 05:00:39 +04:00
return 0 ;
}
static struct socket_info * find_socket_info ( int fd )
{
struct socket_info * i ;
for ( i = sockets ; i ; i = i - > next ) {
if ( i - > fd = = fd )
return i ;
}
return NULL ;
}
2005-06-10 16:21:46 +04:00
static int sockaddr_convert_to_un ( struct socket_info * si , const struct sockaddr * in_addr , socklen_t in_len ,
2005-11-07 18:36:51 +03:00
struct sockaddr_un * out_addr , int alloc_sock , int * bcast )
2005-03-28 05:00:39 +04:00
{
if ( ! out_addr )
return 0 ;
2005-03-31 04:43:26 +04:00
out_addr - > sun_family = AF_UNIX ;
2005-03-31 03:28:15 +04:00
2005-03-28 05:00:39 +04:00
switch ( in_addr - > sa_family ) {
case AF_INET :
2005-11-07 18:36:51 +03:00
switch ( si - > type ) {
case SOCK_STREAM :
case SOCK_DGRAM :
break ;
default :
errno = ESOCKTNOSUPPORT ;
return - 1 ;
}
if ( alloc_sock ) {
return convert_in_un_alloc ( si , ( const struct sockaddr_in * ) in_addr , out_addr , bcast ) ;
} else {
return convert_in_un_remote ( si , ( const struct sockaddr_in * ) in_addr , out_addr , bcast ) ;
}
2005-03-28 05:00:39 +04:00
default :
break ;
}
errno = EAFNOSUPPORT ;
return - 1 ;
}
2005-03-31 03:28:15 +04:00
static int sockaddr_convert_from_un ( const struct socket_info * si ,
2005-03-31 16:40:12 +04:00
const struct sockaddr_un * in_addr ,
socklen_t un_addrlen ,
int family ,
struct sockaddr * out_addr ,
2005-12-05 18:22:38 +03:00
socklen_t * _out_addrlen )
2005-03-28 05:00:39 +04:00
{
2005-12-05 18:22:38 +03:00
socklen_t out_addrlen ;
if ( out_addr = = NULL | | _out_addrlen = = NULL )
2005-03-28 05:00:39 +04:00
return 0 ;
2005-03-31 16:40:12 +04:00
if ( un_addrlen = = 0 ) {
2005-12-05 18:22:38 +03:00
* _out_addrlen = 0 ;
2005-03-31 16:40:12 +04:00
return 0 ;
}
2005-12-05 18:22:38 +03:00
out_addrlen = * _out_addrlen ;
if ( out_addrlen > un_addrlen ) {
out_addrlen = un_addrlen ;
}
2005-03-28 05:00:39 +04:00
switch ( family ) {
case AF_INET :
2005-11-07 18:36:51 +03:00
switch ( si - > type ) {
case SOCK_STREAM :
case SOCK_DGRAM :
break ;
default :
errno = ESOCKTNOSUPPORT ;
return - 1 ;
}
2005-12-05 18:22:38 +03:00
return convert_un_in ( in_addr , ( struct sockaddr_in * ) out_addr , _out_addrlen ) ;
2005-03-28 05:00:39 +04:00
default :
break ;
}
errno = EAFNOSUPPORT ;
return - 1 ;
}
2006-09-12 10:19:11 +04:00
enum swrap_packet_type {
2006-09-29 13:31:20 +04:00
SWRAP_CONNECT_SEND ,
SWRAP_CONNECT_UNREACH ,
SWRAP_CONNECT_RECV ,
SWRAP_CONNECT_ACK ,
SWRAP_ACCEPT_SEND ,
SWRAP_ACCEPT_RECV ,
SWRAP_ACCEPT_ACK ,
2006-09-12 10:19:11 +04:00
SWRAP_RECVFROM ,
SWRAP_SENDTO ,
2006-09-29 13:31:20 +04:00
SWRAP_SENDTO_UNREACH ,
SWRAP_PENDING_RST ,
2006-09-12 10:19:11 +04:00
SWRAP_RECV ,
2006-09-29 13:31:20 +04:00
SWRAP_RECV_RST ,
2006-09-12 13:08:55 +04:00
SWRAP_SEND ,
2006-09-29 13:31:20 +04:00
SWRAP_SEND_RST ,
SWRAP_CLOSE_SEND ,
SWRAP_CLOSE_RECV ,
SWRAP_CLOSE_ACK
2006-09-12 10:19:11 +04:00
} ;
static void swrap_dump_packet ( struct socket_info * si , const struct sockaddr * addr ,
enum swrap_packet_type type ,
2006-09-29 13:31:20 +04:00
const void * buf , size_t len )
2006-09-12 10:19:11 +04:00
{
2006-09-29 13:31:20 +04:00
const char * file_name ;
file_name = socket_wrapper_pcap_file ( ) ;
if ( ! file_name ) {
2006-09-12 13:08:55 +04:00
return ;
}
2006-09-12 10:19:11 +04:00
2006-09-29 13:31:20 +04:00
if ( si - > family ! = AF_INET ) {
return ;
}
2006-09-12 10:19:11 +04:00
}
2006-09-12 18:59:08 +04:00
_PUBLIC_ int swrap_socket ( int family , int type , int protocol )
2005-03-28 05:00:39 +04:00
{
struct socket_info * si ;
int fd ;
2005-07-21 17:45:07 +04:00
if ( ! socket_wrapper_dir ( ) ) {
2006-09-12 18:59:08 +04:00
return real_socket ( family , type , protocol ) ;
2005-03-28 05:00:39 +04:00
}
2006-09-12 18:44:44 +04:00
2006-09-12 18:59:08 +04:00
switch ( family ) {
2006-09-12 18:44:44 +04:00
case AF_INET :
break ;
case AF_UNIX :
2006-09-12 18:59:08 +04:00
return real_socket ( family , type , protocol ) ;
2006-09-12 18:44:44 +04:00
default :
errno = EAFNOSUPPORT ;
return - 1 ;
}
2006-09-26 17:15:31 +04:00
switch ( type ) {
case SOCK_STREAM :
break ;
case SOCK_DGRAM :
break ;
default :
errno = EPROTONOSUPPORT ;
return - 1 ;
}
switch ( protocol ) {
case 0 :
break ;
default :
errno = EPROTONOSUPPORT ;
return - 1 ;
}
2005-03-31 04:43:26 +04:00
fd = real_socket ( AF_UNIX , type , 0 ) ;
2005-03-28 05:00:39 +04:00
2005-03-31 16:40:12 +04:00
if ( fd = = - 1 ) return - 1 ;
2005-03-28 05:00:39 +04:00
2006-09-19 04:55:40 +04:00
si = ( struct socket_info * ) calloc ( 1 , sizeof ( struct socket_info ) ) ;
2005-03-28 05:00:39 +04:00
2006-09-12 18:59:08 +04:00
si - > family = family ;
2005-03-28 05:00:39 +04:00
si - > type = type ;
si - > protocol = protocol ;
si - > fd = fd ;
DLIST_ADD ( sockets , si ) ;
return si - > fd ;
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_accept ( int s , struct sockaddr * addr , socklen_t * addrlen )
2005-03-28 05:00:39 +04:00
{
struct socket_info * parent_si , * child_si ;
int fd ;
struct sockaddr_un un_addr ;
2005-12-05 18:26:49 +03:00
socklen_t un_addrlen = sizeof ( un_addr ) ;
struct sockaddr_un un_my_addr ;
socklen_t un_my_addrlen = sizeof ( un_my_addr ) ;
struct sockaddr my_addr ;
socklen_t my_addrlen = sizeof ( my_addr ) ;
2005-03-28 05:00:39 +04:00
int ret ;
parent_si = find_socket_info ( s ) ;
if ( ! parent_si ) {
return real_accept ( s , addr , addrlen ) ;
}
2005-07-21 12:42:17 +04:00
memset ( & un_addr , 0 , sizeof ( un_addr ) ) ;
2005-12-05 18:26:49 +03:00
memset ( & un_my_addr , 0 , sizeof ( un_my_addr ) ) ;
memset ( & my_addr , 0 , sizeof ( my_addr ) ) ;
2005-07-21 12:42:17 +04:00
2005-03-28 05:00:39 +04:00
ret = real_accept ( s , ( struct sockaddr * ) & un_addr , & un_addrlen ) ;
2005-03-31 16:40:12 +04:00
if ( ret = = - 1 ) return ret ;
2005-03-28 05:00:39 +04:00
fd = ret ;
2005-03-31 16:40:12 +04:00
ret = sockaddr_convert_from_un ( parent_si , & un_addr , un_addrlen ,
2006-09-12 18:59:08 +04:00
parent_si - > family , addr , addrlen ) ;
2006-09-16 21:57:50 +04:00
if ( ret = = - 1 ) {
close ( fd ) ;
return ret ;
}
2005-03-28 05:00:39 +04:00
2006-09-19 04:55:40 +04:00
child_si = ( struct socket_info * ) malloc ( sizeof ( struct socket_info ) ) ;
2005-03-28 05:00:39 +04:00
memset ( child_si , 0 , sizeof ( * child_si ) ) ;
child_si - > fd = fd ;
2006-09-12 18:59:08 +04:00
child_si - > family = parent_si - > family ;
2005-12-05 18:26:49 +03:00
child_si - > type = parent_si - > type ;
child_si - > protocol = parent_si - > protocol ;
2005-06-19 16:34:59 +04:00
child_si - > bound = 1 ;
2006-09-29 13:31:20 +04:00
child_si - > is_server = 1 ;
2005-03-28 05:00:39 +04:00
2006-04-30 17:54:03 +04:00
ret = real_getsockname ( fd , ( struct sockaddr * ) & un_my_addr , & un_my_addrlen ) ;
2006-09-16 21:57:50 +04:00
if ( ret = = - 1 ) {
free ( child_si ) ;
close ( fd ) ;
return ret ;
}
2005-12-05 18:26:49 +03:00
ret = sockaddr_convert_from_un ( child_si , & un_my_addr , un_my_addrlen ,
2006-09-12 18:59:08 +04:00
child_si - > family , & my_addr , & my_addrlen ) ;
2006-09-16 21:57:50 +04:00
if ( ret = = - 1 ) {
free ( child_si ) ;
close ( fd ) ;
return ret ;
}
2005-12-05 18:26:49 +03:00
child_si - > myname_len = my_addrlen ;
child_si - > myname = sockaddr_dup ( & my_addr , my_addrlen ) ;
2005-03-28 05:00:39 +04:00
2005-06-10 16:21:46 +04:00
child_si - > peername_len = * addrlen ;
child_si - > peername = sockaddr_dup ( addr , * addrlen ) ;
DLIST_ADD ( sockets , child_si ) ;
2006-09-29 13:31:20 +04:00
swrap_dump_packet ( child_si , addr , SWRAP_ACCEPT_SEND , NULL , 0 ) ;
swrap_dump_packet ( child_si , addr , SWRAP_ACCEPT_RECV , NULL , 0 ) ;
swrap_dump_packet ( child_si , addr , SWRAP_ACCEPT_ACK , NULL , 0 ) ;
2006-09-12 13:08:55 +04:00
2005-03-28 05:00:39 +04:00
return fd ;
}
2006-09-26 17:15:31 +04:00
static int autobind_start_init ;
static int autobind_start ;
2005-06-10 16:21:46 +04:00
/* using sendto() or connect() on an unbound socket would give the
recipient no way to reply , as unlike UDP and TCP , a unix domain
socket can ' t auto - assign emphemeral port numbers , so we need to
assign it here */
static int swrap_auto_bind ( struct socket_info * si )
{
struct sockaddr_un un_addr ;
struct sockaddr_in in ;
int i ;
2005-11-07 18:36:51 +03:00
char type ;
int ret ;
2006-09-12 20:25:50 +04:00
int port ;
2005-11-07 18:36:51 +03:00
struct stat st ;
2006-09-26 17:15:31 +04:00
if ( autobind_start_init ! = 1 ) {
autobind_start_init = 1 ;
autobind_start = getpid ( ) ;
autobind_start % = 50000 ;
autobind_start + = 10000 ;
}
2005-06-10 16:21:46 +04:00
un_addr . sun_family = AF_UNIX ;
2005-11-07 18:36:51 +03:00
switch ( si - > type ) {
case SOCK_STREAM :
type = SOCKET_TYPE_CHAR_TCP ;
break ;
case SOCK_DGRAM :
type = SOCKET_TYPE_CHAR_UDP ;
break ;
default :
errno = ESOCKTNOSUPPORT ;
return - 1 ;
}
2006-09-26 17:15:31 +04:00
if ( autobind_start > 60000 ) {
autobind_start = 10000 ;
}
2005-06-10 16:21:46 +04:00
for ( i = 0 ; i < 1000 ; i + + ) {
2006-09-26 17:15:31 +04:00
port = autobind_start + i ;
2005-06-10 16:21:46 +04:00
snprintf ( un_addr . sun_path , sizeof ( un_addr . sun_path ) ,
2005-07-21 17:45:07 +04:00
" %s/ " SOCKET_FORMAT , socket_wrapper_dir ( ) ,
2006-09-12 20:25:50 +04:00
type , socket_wrapper_default_iface ( ) , port ) ;
2005-11-07 18:36:51 +03:00
if ( stat ( un_addr . sun_path , & st ) = = 0 ) continue ;
ret = real_bind ( si - > fd , ( struct sockaddr * ) & un_addr , sizeof ( un_addr ) ) ;
if ( ret = = - 1 ) return ret ;
si - > tmp_path = strdup ( un_addr . sun_path ) ;
si - > bound = 1 ;
2006-09-26 17:15:31 +04:00
autobind_start = port + 1 ;
2005-11-07 18:36:51 +03:00
break ;
2005-06-10 16:21:46 +04:00
}
if ( i = = 1000 ) {
2005-11-07 18:36:51 +03:00
errno = ENFILE ;
2005-06-10 16:21:46 +04:00
return - 1 ;
}
2006-09-26 17:15:31 +04:00
2005-06-10 16:21:46 +04:00
memset ( & in , 0 , sizeof ( in ) ) ;
in . sin_family = AF_INET ;
2006-09-12 20:25:50 +04:00
in . sin_port = htons ( port ) ;
2005-11-07 18:36:51 +03:00
in . sin_addr . s_addr = htonl ( 127 < < 24 | socket_wrapper_default_iface ( ) ) ;
2005-06-10 16:21:46 +04:00
si - > myname_len = sizeof ( in ) ;
si - > myname = sockaddr_dup ( & in , si - > myname_len ) ;
return 0 ;
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_connect ( int s , const struct sockaddr * serv_addr , socklen_t addrlen )
2005-03-28 05:00:39 +04:00
{
int ret ;
struct sockaddr_un un_addr ;
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_connect ( s , serv_addr , addrlen ) ;
}
2006-09-12 18:44:44 +04:00
if ( si - > bound = = 0 ) {
2005-06-10 16:21:46 +04:00
ret = swrap_auto_bind ( si ) ;
if ( ret = = - 1 ) return - 1 ;
}
2005-11-07 18:36:51 +03:00
ret = sockaddr_convert_to_un ( si , ( const struct sockaddr * ) serv_addr , addrlen , & un_addr , 0 , NULL ) ;
2005-03-31 16:40:12 +04:00
if ( ret = = - 1 ) return - 1 ;
2005-03-28 05:00:39 +04:00
2006-09-29 13:31:20 +04:00
swrap_dump_packet ( si , serv_addr , SWRAP_CONNECT_SEND , NULL , 0 ) ;
2005-03-31 16:40:12 +04:00
ret = real_connect ( s , ( struct sockaddr * ) & un_addr ,
sizeof ( struct sockaddr_un ) ) ;
2005-03-28 05:00:39 +04:00
2005-11-07 18:36:51 +03:00
/* to give better errors */
2006-09-12 18:44:44 +04:00
if ( ret = = - 1 & & errno = = ENOENT ) {
errno = EHOSTUNREACH ;
2005-11-07 18:36:51 +03:00
}
2005-03-31 16:40:12 +04:00
if ( ret = = 0 ) {
2005-03-28 05:00:39 +04:00
si - > peername_len = addrlen ;
2005-03-31 03:28:15 +04:00
si - > peername = sockaddr_dup ( serv_addr , addrlen ) ;
2005-03-28 05:00:39 +04:00
2006-09-29 13:31:20 +04:00
swrap_dump_packet ( si , serv_addr , SWRAP_CONNECT_RECV , NULL , 0 ) ;
swrap_dump_packet ( si , serv_addr , SWRAP_CONNECT_ACK , NULL , 0 ) ;
} else {
swrap_dump_packet ( si , serv_addr , SWRAP_CONNECT_UNREACH , NULL , 0 ) ;
}
2006-09-12 13:08:55 +04:00
2005-03-28 05:00:39 +04:00
return ret ;
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_bind ( int s , const struct sockaddr * myaddr , socklen_t addrlen )
2005-03-28 05:00:39 +04:00
{
int ret ;
struct sockaddr_un un_addr ;
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_bind ( s , myaddr , addrlen ) ;
}
2005-06-10 16:21:46 +04:00
si - > myname_len = addrlen ;
si - > myname = sockaddr_dup ( myaddr , addrlen ) ;
2005-11-07 18:36:51 +03:00
ret = sockaddr_convert_to_un ( si , ( const struct sockaddr * ) myaddr , addrlen , & un_addr , 1 , & si - > bcast ) ;
2005-03-31 16:40:12 +04:00
if ( ret = = - 1 ) return - 1 ;
2005-03-28 05:00:39 +04:00
unlink ( un_addr . sun_path ) ;
2005-03-31 16:40:12 +04:00
ret = real_bind ( s , ( struct sockaddr * ) & un_addr ,
sizeof ( struct sockaddr_un ) ) ;
2005-03-28 05:00:39 +04:00
2005-03-31 16:40:12 +04:00
if ( ret = = 0 ) {
si - > bound = 1 ;
2005-03-28 05:00:39 +04:00
}
return ret ;
}
2006-09-27 14:13:45 +04:00
_PUBLIC_ int swrap_listen ( int s , int backlog )
{
int ret ;
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_listen ( s , backlog ) ;
}
ret = real_listen ( s , backlog ) ;
return ret ;
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_getpeername ( int s , struct sockaddr * name , socklen_t * addrlen )
2005-03-28 05:00:39 +04:00
{
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_getpeername ( s , name , addrlen ) ;
}
2006-09-29 13:31:20 +04:00
if ( ! si - > peername )
2005-03-28 05:00:39 +04:00
{
errno = ENOTCONN ;
return - 1 ;
}
memcpy ( name , si - > peername , si - > peername_len ) ;
* addrlen = si - > peername_len ;
return 0 ;
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_getsockname ( int s , struct sockaddr * name , socklen_t * addrlen )
2005-03-28 05:00:39 +04:00
{
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
2005-04-06 14:06:08 +04:00
return real_getsockname ( s , name , addrlen ) ;
2005-03-28 05:00:39 +04:00
}
memcpy ( name , si - > myname , si - > myname_len ) ;
* addrlen = si - > myname_len ;
return 0 ;
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_getsockopt ( int s , int level , int optname , void * optval , socklen_t * optlen )
2005-03-28 05:00:39 +04:00
{
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_getsockopt ( s , level , optname , optval , optlen ) ;
}
2005-03-31 03:28:15 +04:00
if ( level = = SOL_SOCKET ) {
return real_getsockopt ( s , level , optname , optval , optlen ) ;
}
2006-09-12 18:59:08 +04:00
errno = ENOPROTOOPT ;
return - 1 ;
2005-03-28 05:00:39 +04:00
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_setsockopt ( int s , int level , int optname , const void * optval , socklen_t optlen )
2005-03-28 05:00:39 +04:00
{
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_setsockopt ( s , level , optname , optval , optlen ) ;
}
2005-03-31 03:28:15 +04:00
if ( level = = SOL_SOCKET ) {
return real_setsockopt ( s , level , optname , optval , optlen ) ;
}
2006-09-12 18:59:08 +04:00
switch ( si - > family ) {
2005-03-31 03:28:15 +04:00
case AF_INET :
2006-04-08 06:44:37 +04:00
return 0 ;
2005-03-31 03:28:15 +04:00
default :
2005-03-28 05:00:39 +04:00
errno = ENOPROTOOPT ;
return - 1 ;
}
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ ssize_t swrap_recvfrom ( int s , void * buf , size_t len , int flags , struct sockaddr * from , socklen_t * fromlen )
2005-03-28 05:00:39 +04:00
{
struct sockaddr_un un_addr ;
2005-03-31 16:40:12 +04:00
socklen_t un_addrlen = sizeof ( un_addr ) ;
2005-03-28 05:00:39 +04:00
int ret ;
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_recvfrom ( s , buf , len , flags , from , fromlen ) ;
}
2005-07-21 12:42:17 +04:00
/* irix 6.4 forgets to null terminate the sun_path string :-( */
memset ( & un_addr , 0 , sizeof ( un_addr ) ) ;
2005-03-28 05:00:39 +04:00
ret = real_recvfrom ( s , buf , len , flags , ( struct sockaddr * ) & un_addr , & un_addrlen ) ;
2005-03-31 16:40:12 +04:00
if ( ret = = - 1 )
2005-03-28 05:00:39 +04:00
return ret ;
2005-03-31 16:40:12 +04:00
if ( sockaddr_convert_from_un ( si , & un_addr , un_addrlen ,
2006-09-12 18:59:08 +04:00
si - > family , from , fromlen ) = = - 1 ) {
2005-03-31 16:40:12 +04:00
return - 1 ;
}
2006-09-12 10:19:11 +04:00
2006-09-29 13:31:20 +04:00
swrap_dump_packet ( si , from , SWRAP_RECVFROM , buf , ret ) ;
2006-09-12 10:19:11 +04:00
2005-03-28 05:00:39 +04:00
return ret ;
}
2005-06-10 16:21:46 +04:00
2006-09-12 10:19:11 +04:00
_PUBLIC_ ssize_t swrap_sendto ( int s , const void * buf , size_t len , int flags , const struct sockaddr * to , socklen_t tolen )
2005-03-28 05:00:39 +04:00
{
struct sockaddr_un un_addr ;
int ret ;
struct socket_info * si = find_socket_info ( s ) ;
2005-11-07 18:36:51 +03:00
int bcast = 0 ;
2005-03-28 05:00:39 +04:00
if ( ! si ) {
return real_sendto ( s , buf , len , flags , to , tolen ) ;
}
2006-09-12 18:44:44 +04:00
if ( si - > bound = = 0 ) {
2005-06-10 16:21:46 +04:00
ret = swrap_auto_bind ( si ) ;
if ( ret = = - 1 ) return - 1 ;
2005-03-31 16:40:12 +04:00
}
2005-11-07 18:36:51 +03:00
ret = sockaddr_convert_to_un ( si , to , tolen , & un_addr , 0 , & bcast ) ;
2005-03-31 16:40:12 +04:00
if ( ret = = - 1 ) return - 1 ;
2005-03-28 05:00:39 +04:00
2005-11-07 18:36:51 +03:00
if ( bcast ) {
struct stat st ;
unsigned int iface ;
unsigned int prt = ntohs ( ( ( const struct sockaddr_in * ) to ) - > sin_port ) ;
char type ;
type = SOCKET_TYPE_CHAR_UDP ;
2006-09-26 15:31:14 +04:00
for ( iface = 0 ; iface < = MAX_WRAPPED_INTERFACES ; iface + + ) {
2005-11-07 18:36:51 +03:00
snprintf ( un_addr . sun_path , sizeof ( un_addr . sun_path ) , " %s/ " SOCKET_FORMAT ,
socket_wrapper_dir ( ) , type , iface , prt ) ;
if ( stat ( un_addr . sun_path , & st ) ! = 0 ) continue ;
/* ignore the any errors in broadcast sends */
real_sendto ( s , buf , len , flags , ( struct sockaddr * ) & un_addr , sizeof ( un_addr ) ) ;
}
2006-09-12 10:19:11 +04:00
2006-09-29 13:31:20 +04:00
swrap_dump_packet ( si , to , SWRAP_SENDTO , buf , len ) ;
2006-09-12 10:19:11 +04:00
2005-11-07 18:36:51 +03:00
return len ;
}
2005-03-28 05:00:39 +04:00
ret = real_sendto ( s , buf , len , flags , ( struct sockaddr * ) & un_addr , sizeof ( un_addr ) ) ;
2005-03-31 16:40:12 +04:00
2005-11-07 18:36:51 +03:00
/* to give better errors */
2006-09-12 18:44:44 +04:00
if ( ret = = - 1 & & errno = = ENOENT ) {
errno = EHOSTUNREACH ;
2005-11-07 18:36:51 +03:00
}
2006-09-29 13:31:20 +04:00
if ( ret = = - 1 ) {
swrap_dump_packet ( si , to , SWRAP_SENDTO , buf , len ) ;
swrap_dump_packet ( si , to , SWRAP_SENDTO_UNREACH , buf , len ) ;
} else {
swrap_dump_packet ( si , to , SWRAP_SENDTO , buf , ret ) ;
}
2006-09-12 10:19:11 +04:00
2006-09-27 14:13:45 +04:00
return ret ;
}
_PUBLIC_ int swrap_ioctl ( int s , int r , void * p )
{
int ret ;
2006-09-29 13:31:20 +04:00
struct socket_info * si = find_socket_info ( s ) ;
int value ;
2006-09-27 14:13:45 +04:00
if ( ! si ) {
return real_ioctl ( s , r , p ) ;
}
ret = real_ioctl ( s , r , p ) ;
2006-09-29 13:31:20 +04:00
switch ( r ) {
case FIONREAD :
value = * ( ( int * ) p ) ;
if ( ret = = - 1 & & errno ! = EAGAIN & & errno ! = ENOBUFS ) {
swrap_dump_packet ( si , NULL , SWRAP_PENDING_RST , NULL , 0 ) ;
} else if ( value = = 0 ) { /* END OF FILE */
swrap_dump_packet ( si , NULL , SWRAP_PENDING_RST , NULL , 0 ) ;
}
break ;
}
2006-09-12 10:19:11 +04:00
return ret ;
}
_PUBLIC_ ssize_t swrap_recv ( int s , void * buf , size_t len , int flags )
{
int ret ;
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_recv ( s , buf , len , flags ) ;
}
ret = real_recv ( s , buf , len , flags ) ;
2006-09-29 13:31:20 +04:00
if ( ret = = - 1 & & errno ! = EAGAIN & & errno ! = ENOBUFS ) {
swrap_dump_packet ( si , NULL , SWRAP_RECV_RST , NULL , 0 ) ;
} else if ( ret = = 0 ) { /* END OF FILE */
swrap_dump_packet ( si , NULL , SWRAP_RECV_RST , NULL , 0 ) ;
} else {
swrap_dump_packet ( si , NULL , SWRAP_RECV , buf , ret ) ;
}
2006-09-12 10:19:11 +04:00
return ret ;
}
_PUBLIC_ ssize_t swrap_send ( int s , const void * buf , size_t len , int flags )
{
int ret ;
struct socket_info * si = find_socket_info ( s ) ;
if ( ! si ) {
return real_send ( s , buf , len , flags ) ;
}
ret = real_send ( s , buf , len , flags ) ;
2006-09-29 13:31:20 +04:00
if ( ret = = - 1 ) {
swrap_dump_packet ( si , NULL , SWRAP_SEND , buf , len ) ;
swrap_dump_packet ( si , NULL , SWRAP_SEND_RST , NULL , 0 ) ;
} else {
swrap_dump_packet ( si , NULL , SWRAP_SEND , buf , ret ) ;
}
2006-09-12 10:19:11 +04:00
2005-03-28 05:00:39 +04:00
return ret ;
}
2006-03-05 20:15:19 +03:00
_PUBLIC_ int swrap_close ( int fd )
2005-03-28 05:00:39 +04:00
{
struct socket_info * si = find_socket_info ( fd ) ;
2006-09-26 17:15:31 +04:00
int ret ;
if ( ! si ) {
return real_close ( fd ) ;
}
2005-03-28 05:00:39 +04:00
2006-09-26 17:15:31 +04:00
DLIST_REMOVE ( sockets , si ) ;
2005-03-28 05:00:39 +04:00
2006-09-29 13:31:20 +04:00
if ( si - > myname & & si - > peername ) {
swrap_dump_packet ( si , NULL , SWRAP_CLOSE_SEND , NULL , 0 ) ;
}
2006-09-26 17:15:31 +04:00
ret = real_close ( fd ) ;
2006-09-12 13:08:55 +04:00
2006-09-29 13:31:20 +04:00
if ( si - > myname & & si - > peername ) {
swrap_dump_packet ( si , NULL , SWRAP_CLOSE_RECV , NULL , 0 ) ;
swrap_dump_packet ( si , NULL , SWRAP_CLOSE_ACK , NULL , 0 ) ;
}
if ( si - > path ) free ( si - > path ) ;
if ( si - > myname ) free ( si - > myname ) ;
if ( si - > peername ) free ( si - > peername ) ;
2006-09-26 17:15:31 +04:00
if ( si - > tmp_path ) {
unlink ( si - > tmp_path ) ;
free ( si - > tmp_path ) ;
2005-03-28 05:00:39 +04:00
}
2006-09-26 17:15:31 +04:00
free ( si ) ;
2005-03-28 05:00:39 +04:00
2006-09-26 17:15:31 +04:00
return ret ;
2005-03-28 05:00:39 +04:00
}