2010-06-23 02:01:45 +04:00
/*
* Unix SMB / CIFS implementation .
* RPC client transport over tstream
* Copyright ( C ) Simo Sorce 2010
*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-04-28 19:38:09 +04:00
# include "../lib/util/tevent_ntstatus.h"
2011-03-31 02:34:28 +04:00
# include "rpc_client/rpc_transport.h"
2010-06-23 02:01:45 +04:00
# include "lib/tsocket/tsocket.h"
2010-12-14 20:20:25 +03:00
# include "libsmb/cli_np_tstream.h"
2011-02-28 12:19:44 +03:00
# include "cli_pipe.h"
2010-06-23 02:01:45 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_RPC_CLI
struct rpc_tstream_state {
struct tstream_context * stream ;
struct tevent_queue * read_queue ;
struct tevent_queue * write_queue ;
2010-12-14 20:19:14 +03:00
unsigned int timeout ;
2010-06-23 02:01:45 +04:00
} ;
static void rpc_tstream_disconnect ( struct rpc_tstream_state * s )
{
TALLOC_FREE ( s - > stream ) ;
}
static bool rpc_tstream_is_connected ( void * priv )
{
struct rpc_tstream_state * transp =
talloc_get_type_abort ( priv , struct rpc_tstream_state ) ;
2010-12-14 20:20:25 +03:00
ssize_t ret ;
2010-06-23 02:01:45 +04:00
if ( ! transp - > stream ) {
return false ;
}
2010-12-14 20:20:25 +03:00
if ( ! tstream_is_cli_np ( transp - > stream ) ) {
return true ;
}
ret = tstream_pending_bytes ( transp - > stream ) ;
if ( ret = = - 1 ) {
return false ;
}
2010-06-23 02:01:45 +04:00
return true ;
}
static unsigned int rpc_tstream_set_timeout ( void * priv , unsigned int timeout )
{
struct rpc_tstream_state * transp =
talloc_get_type_abort ( priv , struct rpc_tstream_state ) ;
int orig_timeout ;
bool ok ;
ok = rpc_tstream_is_connected ( transp ) ;
if ( ! ok ) {
return 0 ;
}
2010-12-14 20:20:25 +03:00
if ( tstream_is_cli_np ( transp - > stream ) ) {
transp - > timeout = timeout ;
return tstream_cli_np_set_timeout ( transp - > stream , timeout ) ;
}
2010-06-23 02:01:45 +04:00
orig_timeout = transp - > timeout ;
transp - > timeout = timeout ;
return orig_timeout ;
}
struct rpc_tstream_next_vector_state {
uint8_t * buf ;
size_t len ;
off_t ofs ;
size_t remaining ;
} ;
static void rpc_tstream_next_vector_init (
struct rpc_tstream_next_vector_state * s ,
uint8_t * buf , size_t len )
{
ZERO_STRUCTP ( s ) ;
s - > buf = buf ;
s - > len = MIN ( len , UINT16_MAX ) ;
}
static int rpc_tstream_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * count )
{
struct rpc_tstream_next_vector_state * state =
( struct rpc_tstream_next_vector_state * ) private_data ;
struct iovec * vector ;
ssize_t pending ;
size_t wanted ;
if ( state - > ofs = = state - > len ) {
* _vector = NULL ;
* count = 0 ;
return 0 ;
}
pending = tstream_pending_bytes ( stream ) ;
if ( pending = = - 1 ) {
return - 1 ;
}
if ( pending = = 0 & & state - > ofs ! = 0 ) {
/* return a short read */
* _vector = NULL ;
* count = 0 ;
return 0 ;
}
if ( pending = = 0 ) {
/* we want at least one byte and recheck again */
wanted = 1 ;
} else {
size_t missing = state - > len - state - > ofs ;
if ( pending > missing ) {
/* there's more available */
state - > remaining = pending - missing ;
wanted = missing ;
} else {
/* read what we can get and recheck in the next cycle */
wanted = pending ;
}
}
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( ! vector ) {
return - 1 ;
}
vector [ 0 ] . iov_base = state - > buf + state - > ofs ;
vector [ 0 ] . iov_len = wanted ;
state - > ofs + = wanted ;
* _vector = vector ;
* count = 1 ;
return 0 ;
}
struct rpc_tstream_read_state {
struct rpc_tstream_state * transp ;
struct rpc_tstream_next_vector_state next_vector ;
ssize_t nread ;
} ;
static void rpc_tstream_read_done ( struct tevent_req * subreq ) ;
static struct tevent_req * rpc_tstream_read_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
uint8_t * data , size_t size ,
void * priv )
{
struct rpc_tstream_state * transp =
talloc_get_type_abort ( priv , struct rpc_tstream_state ) ;
struct tevent_req * req , * subreq ;
struct rpc_tstream_read_state * state ;
struct timeval endtime ;
req = tevent_req_create ( mem_ctx , & state , struct rpc_tstream_read_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( ! rpc_tstream_is_connected ( transp ) ) {
2012-07-03 15:01:47 +04:00
NTSTATUS status = NT_STATUS_CONNECTION_DISCONNECTED ;
if ( tstream_is_cli_np ( transp - > stream ) ) {
status = NT_STATUS_PIPE_DISCONNECTED ;
}
tevent_req_nterror ( req , status ) ;
2010-06-23 02:01:45 +04:00
return tevent_req_post ( req , ev ) ;
}
state - > transp = transp ;
rpc_tstream_next_vector_init ( & state - > next_vector , data , size ) ;
subreq = tstream_readv_pdu_queue_send ( state , ev ,
transp - > stream ,
transp - > read_queue ,
rpc_tstream_next_vector ,
& state - > next_vector ) ;
if ( subreq = = NULL ) {
tevent_req_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return tevent_req_post ( req , ev ) ;
}
2011-06-01 05:51:15 +04:00
endtime = timeval_current_ofs_msec ( transp - > timeout ) ;
2010-06-23 02:01:45 +04:00
if ( ! tevent_req_set_endtime ( subreq , ev , endtime ) ) {
goto fail ;
}
tevent_req_set_callback ( subreq , rpc_tstream_read_done , req ) ;
return req ;
fail :
TALLOC_FREE ( req ) ;
return NULL ;
}
static void rpc_tstream_read_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq , struct tevent_req ) ;
struct rpc_tstream_read_state * state =
tevent_req_data ( req , struct rpc_tstream_read_state ) ;
int err ;
state - > nread = tstream_readv_pdu_queue_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( state - > nread < 0 ) {
rpc_tstream_disconnect ( state - > transp ) ;
tevent_req_nterror ( req , map_nt_error_from_unix ( err ) ) ;
return ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS rpc_tstream_read_recv ( struct tevent_req * req , ssize_t * size )
{
struct rpc_tstream_read_state * state = tevent_req_data (
req , struct rpc_tstream_read_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* size = state - > nread ;
return NT_STATUS_OK ;
}
struct rpc_tstream_write_state {
struct event_context * ev ;
struct rpc_tstream_state * transp ;
struct iovec iov ;
ssize_t nwritten ;
} ;
static void rpc_tstream_write_done ( struct tevent_req * subreq ) ;
static struct tevent_req * rpc_tstream_write_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
const uint8_t * data , size_t size ,
void * priv )
{
struct rpc_tstream_state * transp =
talloc_get_type_abort ( priv , struct rpc_tstream_state ) ;
struct tevent_req * req , * subreq ;
struct rpc_tstream_write_state * state ;
struct timeval endtime ;
req = tevent_req_create ( mem_ctx , & state , struct rpc_tstream_write_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( ! rpc_tstream_is_connected ( transp ) ) {
2012-07-03 15:01:47 +04:00
NTSTATUS status = NT_STATUS_CONNECTION_DISCONNECTED ;
if ( tstream_is_cli_np ( transp - > stream ) ) {
status = NT_STATUS_PIPE_DISCONNECTED ;
}
tevent_req_nterror ( req , status ) ;
2010-06-23 02:01:45 +04:00
return tevent_req_post ( req , ev ) ;
}
state - > ev = ev ;
state - > transp = transp ;
state - > iov . iov_base = discard_const_p ( void * , data ) ;
state - > iov . iov_len = size ;
subreq = tstream_writev_queue_send ( state , ev ,
transp - > stream ,
transp - > write_queue ,
& state - > iov , 1 ) ;
if ( subreq = = NULL ) {
goto fail ;
}
2011-06-01 05:51:15 +04:00
endtime = timeval_current_ofs_msec ( transp - > timeout ) ;
2010-06-23 02:01:45 +04:00
if ( ! tevent_req_set_endtime ( subreq , ev , endtime ) ) {
goto fail ;
}
tevent_req_set_callback ( subreq , rpc_tstream_write_done , req ) ;
return req ;
fail :
TALLOC_FREE ( req ) ;
return NULL ;
}
static void rpc_tstream_write_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq , struct tevent_req ) ;
struct rpc_tstream_write_state * state =
tevent_req_data ( req , struct rpc_tstream_write_state ) ;
int err ;
state - > nwritten = tstream_writev_queue_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( state - > nwritten < 0 ) {
rpc_tstream_disconnect ( state - > transp ) ;
tevent_req_nterror ( req , map_nt_error_from_unix ( err ) ) ;
return ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS rpc_tstream_write_recv ( struct tevent_req * req , ssize_t * sent )
{
struct rpc_tstream_write_state * state =
tevent_req_data ( req , struct rpc_tstream_write_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* sent = state - > nwritten ;
return NT_STATUS_OK ;
}
2010-12-14 20:20:25 +03:00
struct rpc_tstream_trans_state {
struct tevent_context * ev ;
struct rpc_tstream_state * transp ;
struct iovec req ;
uint32_t max_rdata_len ;
struct iovec rep ;
} ;
static void rpc_tstream_trans_writev ( struct tevent_req * subreq ) ;
static void rpc_tstream_trans_readv_pdu ( struct tevent_req * subreq ) ;
static int rpc_tstream_trans_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * count ) ;
static struct tevent_req * rpc_tstream_trans_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
uint8_t * data , size_t data_len ,
uint32_t max_rdata_len ,
void * priv )
{
struct rpc_tstream_state * transp =
talloc_get_type_abort ( priv , struct rpc_tstream_state ) ;
struct tevent_req * req , * subreq ;
struct rpc_tstream_trans_state * state ;
struct timeval endtime ;
2011-07-28 14:54:31 +04:00
bool use_trans = false ;
2010-12-14 20:20:25 +03:00
req = tevent_req_create ( mem_ctx , & state ,
struct rpc_tstream_trans_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( ! rpc_tstream_is_connected ( transp ) ) {
2012-07-03 15:01:47 +04:00
NTSTATUS status = NT_STATUS_CONNECTION_DISCONNECTED ;
if ( tstream_is_cli_np ( transp - > stream ) ) {
status = NT_STATUS_PIPE_DISCONNECTED ;
}
tevent_req_nterror ( req , status ) ;
2010-12-14 20:20:25 +03:00
return tevent_req_post ( req , ev ) ;
}
state - > ev = ev ;
state - > transp = transp ;
state - > req . iov_len = data_len ;
state - > req . iov_base = discard_const_p ( void * , data ) ;
state - > max_rdata_len = max_rdata_len ;
2011-06-01 05:51:15 +04:00
endtime = timeval_current_ofs_msec ( transp - > timeout ) ;
2010-12-14 20:20:25 +03:00
2011-07-28 14:54:31 +04:00
if ( tstream_is_cli_np ( transp - > stream ) ) {
use_trans = true ;
}
2011-07-28 16:15:15 +04:00
if ( tevent_queue_length ( transp - > write_queue ) > 0 ) {
use_trans = false ;
}
if ( tevent_queue_length ( transp - > read_queue ) > 0 ) {
use_trans = false ;
}
2011-07-28 14:54:31 +04:00
if ( use_trans ) {
tstream_cli_np_use_trans ( transp - > stream ) ;
}
2010-12-14 20:20:25 +03:00
subreq = tstream_writev_queue_send ( state , ev ,
transp - > stream ,
transp - > write_queue ,
& state - > req , 1 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
if ( ! tevent_req_set_endtime ( subreq , ev , endtime ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , rpc_tstream_trans_writev , req ) ;
subreq = tstream_readv_pdu_queue_send ( state , ev ,
transp - > stream ,
transp - > read_queue ,
rpc_tstream_trans_next_vector ,
state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
if ( ! tevent_req_set_endtime ( subreq , ev , endtime ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , rpc_tstream_trans_readv_pdu , req ) ;
return req ;
}
static void rpc_tstream_trans_writev ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct rpc_tstream_trans_state * state =
tevent_req_data ( req ,
struct rpc_tstream_trans_state ) ;
int ret ;
int err ;
ret = tstream_writev_queue_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
rpc_tstream_disconnect ( state - > transp ) ;
tevent_req_nterror ( req , map_nt_error_from_unix ( err ) ) ;
return ;
}
}
static int rpc_tstream_trans_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * count )
{
struct rpc_tstream_trans_state * state =
talloc_get_type_abort ( private_data ,
struct rpc_tstream_trans_state ) ;
struct iovec * vector ;
if ( state - > max_rdata_len = = state - > rep . iov_len ) {
* _vector = NULL ;
* count = 0 ;
return 0 ;
}
state - > rep . iov_base = talloc_array ( state , uint8_t ,
state - > max_rdata_len ) ;
if ( state - > rep . iov_base = = NULL ) {
return - 1 ;
}
state - > rep . iov_len = state - > max_rdata_len ;
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( ! vector ) {
return - 1 ;
}
vector [ 0 ] = state - > rep ;
* _vector = vector ;
* count = 1 ;
return 0 ;
}
static void rpc_tstream_trans_readv_pdu ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct rpc_tstream_trans_state * state =
tevent_req_data ( req ,
struct rpc_tstream_trans_state ) ;
int ret ;
int err ;
ret = tstream_readv_pdu_queue_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
rpc_tstream_disconnect ( state - > transp ) ;
tevent_req_nterror ( req , map_nt_error_from_unix ( err ) ) ;
return ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS rpc_tstream_trans_recv ( struct tevent_req * req , TALLOC_CTX * mem_ctx ,
uint8_t * * prdata , uint32_t * prdata_len )
{
struct rpc_tstream_trans_state * state =
tevent_req_data ( req ,
struct rpc_tstream_trans_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* prdata = ( uint8_t * ) talloc_move ( mem_ctx , & state - > rep . iov_base ) ;
* prdata_len = state - > rep . iov_len ;
return NT_STATUS_OK ;
}
2010-06-23 02:01:45 +04:00
/**
* @ brief Initialize a tstream transport facility
* NOTE : this function will talloc_steal , the stream and the queues .
*
* @ param mem_ctx - memory context used to allocate the transport
* @ param stream - a ready to use tstream
* @ param presult - the transport structure
*
* @ return - a NT Status error code .
*/
NTSTATUS rpc_transport_tstream_init ( TALLOC_CTX * mem_ctx ,
2010-06-23 02:01:45 +04:00
struct tstream_context * * stream ,
struct rpc_cli_transport * * presult )
2010-06-23 02:01:45 +04:00
{
struct rpc_cli_transport * result ;
struct rpc_tstream_state * state ;
result = talloc ( mem_ctx , struct rpc_cli_transport ) ;
if ( result = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
state = talloc ( result , struct rpc_tstream_state ) ;
if ( state = = NULL ) {
TALLOC_FREE ( result ) ;
return NT_STATUS_NO_MEMORY ;
}
result - > priv = state ;
2010-06-23 02:01:45 +04:00
state - > read_queue = tevent_queue_create ( state , " read_queue " ) ;
if ( state - > read_queue = = NULL ) {
TALLOC_FREE ( result ) ;
return NT_STATUS_NO_MEMORY ;
}
state - > write_queue = tevent_queue_create ( state , " write_queue " ) ;
if ( state - > write_queue = = NULL ) {
TALLOC_FREE ( result ) ;
return NT_STATUS_NO_MEMORY ;
}
state - > stream = talloc_move ( state , stream ) ;
2010-06-23 02:01:45 +04:00
state - > timeout = 10000 ; /* 10 seconds. */
2010-12-14 20:20:25 +03:00
if ( tstream_is_cli_np ( state - > stream ) ) {
result - > trans_send = rpc_tstream_trans_send ;
result - > trans_recv = rpc_tstream_trans_recv ;
} else {
result - > trans_send = NULL ;
result - > trans_recv = NULL ;
}
2010-06-23 02:01:45 +04:00
result - > write_send = rpc_tstream_write_send ;
result - > write_recv = rpc_tstream_write_recv ;
result - > read_send = rpc_tstream_read_send ;
result - > read_recv = rpc_tstream_read_recv ;
result - > is_connected = rpc_tstream_is_connected ;
result - > set_timeout = rpc_tstream_set_timeout ;
* presult = result ;
return NT_STATUS_OK ;
}
2010-09-06 19:31:15 +04:00
struct cli_state * rpc_pipe_np_smb_conn ( struct rpc_pipe_client * p )
{
struct rpc_tstream_state * transp =
talloc_get_type_abort ( p - > transport - > priv ,
struct rpc_tstream_state ) ;
bool ok ;
ok = rpccli_is_connected ( p ) ;
if ( ! ok ) {
return NULL ;
}
if ( ! tstream_is_cli_np ( transp - > stream ) ) {
return NULL ;
}
return tstream_cli_np_get_cli_state ( transp - > stream ) ;
}