2004-10-24 18:57:16 +04:00
/*
Unix SMB / CIFS implementation .
dcerpc over standard sockets transport
Copyright ( C ) Andrew Tridgell 2003
Copyright ( C ) Jelmer Vernooij 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"
2004-11-02 09:42:15 +03:00
# include "dlinklist.h"
2005-02-03 14:56:03 +03:00
# include "lib/events/events.h"
2004-11-01 13:30:34 +03:00
# include "librpc/gen_ndr/ndr_epmapper.h"
2005-02-10 09:59:29 +03:00
# include "lib/socket/socket.h"
2004-10-24 18:57:16 +04:00
# define MIN_HDR_SIZE 16
struct sock_blob {
struct sock_blob * next , * prev ;
DATA_BLOB data ;
} ;
/* transport private information used by general socket pipe transports */
struct sock_private {
struct event_context * event_ctx ;
struct fd_event * fde ;
2004-10-27 07:15:42 +04:00
struct socket_context * sock ;
2004-10-24 18:57:16 +04:00
char * server_name ;
uint32_t port ;
struct sock_blob * pending_send ;
struct {
size_t received ;
DATA_BLOB data ;
uint_t pending_count ;
} recv ;
} ;
/*
mark the socket dead
*/
2005-01-09 11:34:05 +03:00
static void sock_dead ( struct dcerpc_connection * p , NTSTATUS status )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock = p - > transport . private ;
2004-10-27 07:15:42 +04:00
if ( sock & & sock - > sock ! = NULL ) {
talloc_free ( sock - > sock ) ;
sock - > sock = NULL ;
2004-10-24 18:57:16 +04:00
}
/* wipe any pending sends */
while ( sock - > pending_send ) {
struct sock_blob * blob = sock - > pending_send ;
DLIST_REMOVE ( sock - > pending_send , blob ) ;
talloc_free ( blob ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
p - > transport . recv_data ( p , NULL , status ) ;
}
2004-11-24 14:24:33 +03:00
2005-02-03 05:35:52 +03:00
talloc_free ( sock - > fde ) ;
2004-10-24 18:57:16 +04:00
}
/*
process send requests
*/
2005-01-09 11:34:05 +03:00
static void sock_process_send ( struct dcerpc_connection * p )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock = p - > transport . private ;
while ( sock - > pending_send ) {
struct sock_blob * blob = sock - > pending_send ;
2004-10-27 07:15:42 +04:00
NTSTATUS status ;
size_t sent ;
2004-10-28 08:00:43 +04:00
status = socket_send ( sock - > sock , & blob - > data , & sent , 0 ) ;
2004-10-27 07:15:42 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
sock_dead ( p , NT_STATUS_NET_WRITE_FAULT ) ;
2004-10-24 18:57:16 +04:00
break ;
}
2004-10-27 07:15:42 +04:00
if ( sent = = 0 ) {
2004-10-24 18:57:16 +04:00
break ;
}
2004-10-27 07:15:42 +04:00
blob - > data . data + = sent ;
blob - > data . length - = sent ;
2004-10-24 18:57:16 +04:00
if ( blob - > data . length ! = 0 ) {
break ;
}
DLIST_REMOVE ( sock - > pending_send , blob ) ;
talloc_free ( blob ) ;
}
if ( sock - > pending_send = = NULL ) {
2005-02-03 05:35:52 +03:00
EVENT_FD_NOT_WRITEABLE ( sock - > fde ) ;
2004-10-24 18:57:16 +04:00
}
}
/*
process recv requests
*/
2005-01-09 11:34:05 +03:00
static void sock_process_recv ( struct dcerpc_connection * p )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock = p - > transport . private ;
2004-10-27 07:15:42 +04:00
NTSTATUS status ;
2004-10-28 08:00:43 +04:00
size_t nread ;
2004-10-24 18:57:16 +04:00
if ( sock - > recv . data . data = = NULL ) {
sock - > recv . data = data_blob_talloc ( sock , NULL , MIN_HDR_SIZE ) ;
}
/* read in the base header to get the fragment length */
if ( sock - > recv . received < MIN_HDR_SIZE ) {
uint32_t frag_length ;
2004-10-28 08:00:43 +04:00
status = socket_recv ( sock - > sock ,
sock - > recv . data . data + sock - > recv . received ,
MIN_HDR_SIZE - sock - > recv . received ,
& nread , 0 ) ;
2004-10-27 07:15:42 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
sock_dead ( p , NT_STATUS_NET_WRITE_FAULT ) ;
2004-10-24 18:57:16 +04:00
return ;
}
2004-10-28 08:00:43 +04:00
if ( nread = = 0 ) {
2004-10-24 18:57:16 +04:00
return ;
}
2004-10-27 07:15:42 +04:00
2004-10-28 08:00:43 +04:00
sock - > recv . received + = nread ;
2004-10-24 18:57:16 +04:00
if ( sock - > recv . received ! = MIN_HDR_SIZE ) {
return ;
}
frag_length = dcerpc_get_frag_length ( & sock - > recv . data ) ;
sock - > recv . data . data = talloc_realloc ( sock , sock - > recv . data . data ,
2005-01-07 07:39:16 +03:00
uint8_t , frag_length ) ;
2004-10-24 18:57:16 +04:00
if ( sock - > recv . data . data = = NULL ) {
sock_dead ( p , NT_STATUS_NO_MEMORY ) ;
return ;
}
sock - > recv . data . length = frag_length ;
}
/* read in the rest of the packet */
2004-10-28 08:00:43 +04:00
status = socket_recv ( sock - > sock ,
sock - > recv . data . data + sock - > recv . received ,
sock - > recv . data . length - sock - > recv . received ,
& nread , 0 ) ;
2004-10-27 07:15:42 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
sock_dead ( p , NT_STATUS_NET_WRITE_FAULT ) ;
2004-10-24 18:57:16 +04:00
return ;
}
2004-10-28 08:00:43 +04:00
if ( nread = = 0 ) {
2004-10-24 18:57:16 +04:00
return ;
}
2004-10-28 08:00:43 +04:00
sock - > recv . received + = nread ;
2004-10-24 18:57:16 +04:00
if ( sock - > recv . received ! = sock - > recv . data . length ) {
return ;
}
/* we have a full packet */
p - > transport . recv_data ( p , & sock - > recv . data , NT_STATUS_OK ) ;
talloc_free ( sock - > recv . data . data ) ;
sock - > recv . data = data_blob ( NULL , 0 ) ;
sock - > recv . received = 0 ;
sock - > recv . pending_count - - ;
if ( sock - > recv . pending_count = = 0 ) {
2005-02-03 05:35:52 +03:00
EVENT_FD_NOT_READABLE ( sock - > fde ) ;
2004-10-24 18:57:16 +04:00
}
}
/*
called when a IO is triggered by the events system
*/
static void sock_io_handler ( struct event_context * ev , struct fd_event * fde ,
2005-02-03 14:25:52 +03:00
uint16_t flags , void * private )
2004-10-24 18:57:16 +04:00
{
2005-02-03 05:35:52 +03:00
struct dcerpc_connection * p = talloc_get_type ( private , struct dcerpc_connection ) ;
2004-10-24 18:57:16 +04:00
struct sock_private * sock = p - > transport . private ;
if ( flags & EVENT_FD_WRITE ) {
sock_process_send ( p ) ;
2005-01-21 09:55:33 +03:00
return ;
2004-10-24 18:57:16 +04:00
}
2004-10-27 07:15:42 +04:00
if ( sock - > sock = = NULL ) {
2004-10-24 18:57:16 +04:00
return ;
}
if ( flags & EVENT_FD_READ ) {
sock_process_recv ( p ) ;
}
}
/*
initiate a read request
*/
2005-01-09 11:34:05 +03:00
static NTSTATUS sock_send_read ( struct dcerpc_connection * p )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock = p - > transport . private ;
sock - > recv . pending_count + + ;
if ( sock - > recv . pending_count = = 1 ) {
2005-02-03 05:35:52 +03:00
EVENT_FD_READABLE ( sock - > fde ) ;
2004-10-24 18:57:16 +04:00
}
return NT_STATUS_OK ;
}
/*
send an initial pdu in a multi - pdu sequence
*/
2005-01-09 11:34:05 +03:00
static NTSTATUS sock_send_request ( struct dcerpc_connection * p , DATA_BLOB * data , BOOL trigger_read )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock = p - > transport . private ;
struct sock_blob * blob ;
2004-11-24 14:24:33 +03:00
if ( sock - > sock = = NULL ) {
return NT_STATUS_CONNECTION_DISCONNECTED ;
}
2005-01-27 10:08:20 +03:00
blob = talloc ( sock , struct sock_blob ) ;
2004-10-24 18:57:16 +04:00
if ( blob = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
blob - > data = data_blob_talloc ( blob , data - > data , data - > length ) ;
if ( blob - > data . data = = NULL ) {
talloc_free ( blob ) ;
return NT_STATUS_NO_MEMORY ;
}
DLIST_ADD_END ( sock - > pending_send , blob , struct sock_blob * ) ;
2005-02-03 05:35:52 +03:00
EVENT_FD_WRITEABLE ( sock - > fde ) ;
2004-10-24 18:57:16 +04:00
if ( trigger_read ) {
sock_send_read ( p ) ;
}
return NT_STATUS_OK ;
}
/*
return the event context so the caller can process asynchronously
*/
2005-01-09 11:34:05 +03:00
static struct event_context * sock_event_context ( struct dcerpc_connection * p )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock = p - > transport . private ;
return sock - > event_ctx ;
}
/*
shutdown sock pipe connection
*/
2005-01-09 11:34:05 +03:00
static NTSTATUS sock_shutdown_pipe ( struct dcerpc_connection * p )
2004-10-24 18:57:16 +04:00
{
2005-03-23 02:20:41 +03:00
struct sock_private * sock = p - > transport . private ;
if ( sock & & sock - > sock ) {
sock_dead ( p , NT_STATUS_OK ) ;
}
2004-10-24 18:57:16 +04:00
return NT_STATUS_OK ;
}
/*
return sock server name
*/
2005-01-09 11:34:05 +03:00
static const char * sock_peer_name ( struct dcerpc_connection * p )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock = p - > transport . private ;
return sock - > server_name ;
}
/*
2004-10-27 07:15:42 +04:00
open a rpc connection using the generic socket library
2004-10-24 18:57:16 +04:00
*/
2005-01-09 11:34:05 +03:00
static NTSTATUS dcerpc_pipe_open_socket ( struct dcerpc_connection * c ,
2004-10-27 07:15:42 +04:00
const char * server ,
uint32_t port ,
const char * type ,
enum dcerpc_transport_t transport )
2004-10-24 18:57:16 +04:00
{
struct sock_private * sock ;
2004-10-27 07:15:42 +04:00
struct socket_context * socket_ctx ;
NTSTATUS status ;
2004-10-24 18:57:16 +04:00
2005-01-27 10:08:20 +03:00
sock = talloc ( c , struct sock_private ) ;
2004-10-27 07:15:42 +04:00
if ( ! sock ) {
return NT_STATUS_NO_MEMORY ;
2004-10-24 18:57:16 +04:00
}
2004-10-27 07:15:42 +04:00
status = socket_create ( type , SOCKET_TYPE_STREAM , & socket_ctx , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-01-09 11:34:05 +03:00
talloc_free ( sock ) ;
2004-10-27 07:15:42 +04:00
return status ;
2004-10-24 18:57:16 +04:00
}
2004-10-27 07:15:42 +04:00
talloc_steal ( sock , socket_ctx ) ;
2004-10-24 18:57:16 +04:00
2004-10-27 07:15:42 +04:00
status = socket_connect ( socket_ctx , NULL , 0 , server , port , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-01-09 11:34:05 +03:00
talloc_free ( sock ) ;
2004-10-27 07:15:42 +04:00
return status ;
2004-10-24 18:57:16 +04:00
}
2004-10-27 07:15:42 +04:00
2004-10-24 18:57:16 +04:00
/*
fill in the transport methods
*/
2005-01-09 11:34:05 +03:00
c - > transport . transport = transport ;
c - > transport . private = NULL ;
2004-10-24 18:57:16 +04:00
2005-01-09 11:34:05 +03:00
c - > transport . send_request = sock_send_request ;
c - > transport . send_read = sock_send_read ;
c - > transport . event_context = sock_event_context ;
c - > transport . recv_data = NULL ;
2004-10-24 18:57:16 +04:00
2005-01-09 11:34:05 +03:00
c - > transport . shutdown_pipe = sock_shutdown_pipe ;
c - > transport . peer_name = sock_peer_name ;
2004-10-24 18:57:16 +04:00
2004-10-27 07:15:42 +04:00
sock - > sock = socket_ctx ;
2005-02-14 08:09:53 +03:00
sock - > server_name = strupper_talloc ( sock , server ) ;
2004-10-24 18:57:16 +04:00
sock - > event_ctx = event_context_init ( sock ) ;
sock - > pending_send = NULL ;
sock - > recv . received = 0 ;
sock - > recv . data = data_blob ( NULL , 0 ) ;
sock - > recv . pending_count = 0 ;
2005-02-03 05:35:52 +03:00
sock - > fde = event_add_fd ( sock - > event_ctx , sock , socket_get_fd ( sock - > sock ) ,
0 , sock_io_handler , c ) ;
2004-10-24 18:57:16 +04:00
2005-01-09 11:34:05 +03:00
c - > transport . private = sock ;
2004-10-24 18:57:16 +04:00
/* ensure we don't get SIGPIPE */
BlockSignals ( True , SIGPIPE ) ;
2004-10-27 07:15:42 +04:00
return NT_STATUS_OK ;
2004-10-24 18:57:16 +04:00
}
/*
2004-10-27 07:15:42 +04:00
open a rpc connection using tcp
2004-10-24 18:57:16 +04:00
*/
2005-01-09 11:34:05 +03:00
NTSTATUS dcerpc_pipe_open_tcp ( struct dcerpc_connection * c , const char * server , uint32_t port )
2004-10-24 18:57:16 +04:00
{
2004-10-28 23:14:26 +04:00
NTSTATUS status ;
/* Try IPv6 first */
2005-01-09 11:34:05 +03:00
status = dcerpc_pipe_open_socket ( c , server , port , " ipv6 " , NCACN_IP_TCP ) ;
2004-10-28 23:14:26 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2005-01-09 11:34:05 +03:00
return dcerpc_pipe_open_socket ( c , server , port , " ipv4 " , NCACN_IP_TCP ) ;
2004-10-27 07:15:42 +04:00
}
2004-10-24 18:57:16 +04:00
2004-10-27 07:15:42 +04:00
/*
open a rpc connection to a unix socket
*/
2005-01-09 11:34:05 +03:00
NTSTATUS dcerpc_pipe_open_unix_stream ( struct dcerpc_connection * c , const char * path )
2004-10-27 07:15:42 +04:00
{
2005-01-09 11:34:05 +03:00
return dcerpc_pipe_open_socket ( c , path , 0 , " unix " , NCACN_UNIX_STREAM ) ;
2004-10-24 18:57:16 +04:00
}
/*
open a rpc connection to a named pipe
*/
2005-01-09 11:34:05 +03:00
NTSTATUS dcerpc_pipe_open_pipe ( struct dcerpc_connection * c , const char * identifier )
2004-10-24 18:57:16 +04:00
{
2004-10-27 07:15:42 +04:00
NTSTATUS status ;
2004-10-24 18:57:16 +04:00
char * canon , * full_path ;
2004-10-27 07:15:42 +04:00
canon = talloc_strdup ( NULL , identifier ) ;
2004-10-24 18:57:16 +04:00
string_replace ( canon , ' / ' , ' \\ ' ) ;
2004-10-27 07:15:42 +04:00
full_path = talloc_asprintf ( canon , " %s/%s " , lp_ncalrpc_dir ( ) , canon ) ;
2004-10-24 18:57:16 +04:00
2005-01-09 11:34:05 +03:00
status = dcerpc_pipe_open_socket ( c , full_path , 0 , " unix " , NCALRPC ) ;
2004-10-27 07:15:42 +04:00
talloc_free ( canon ) ;
2004-10-24 18:57:16 +04:00
2004-10-27 07:15:42 +04:00
return status ;
2004-10-24 18:57:16 +04:00
}