dce/rpc "client" api

Luke Kenneth Casson Leighton lkcl at samba.org
Fri Aug 18 02:20:09 GMT 2000

On Thu, 17 Aug 2000, Jeremy Allison wrote:

> On Thu, Aug 17, 2000 at 03:40:25PM +1000, Luke Kenneth Casson Leighton wrote:
> > i'm adding the following api into cli_connect.c, it's basically replacing
> > all the switch statements with a function table.  it will make it easier
> > to add DCE/RPC over TCP.
> Luke, after all our recent discussions this is the wrong way to
> propose a change to HEAD (if, that is, you're proposing a change to
> HEAD). "I'm adding" - ie. the implication being there's
> no discussion over it, is not the correct phrasing to use here.

ok, then i rephrase: i am proposing the following api in cli_connect.c.

if anyone can think of any improvements, suggestions, caveats, please do
so [i notice some below - thx jeremy].

> > also, The Plan Is, to make these function tables dynamically loadable.
> > cli_ncacn_np.so, for example, to handle DCE/RPC over SMB.  cli_ncalrpc.so
> > to handle DCE/RPC over loop-back (currently implemented as unix domain
> > sockets, only accessible by/as root).
> Again, we're not yet ready to add .so support into HEAD until
> we determine how this should work across all supported OS'es.

ok, thanks for letting me know, i shan't .so-itise them, then.
> > typedef struct cli_connect_fns
> > {
> > 	/* create new connection.  strictly speaking, one arg should be
> > 	 * full dce/rpc format: e.g "ncacn_np:\\server\pipe\pipename" */
> > 	void *cli_connect_add(const char *pipe_name,
> > 				  const vuser_key *key,
> > 				  const char *srv_name,
> > 				  const struct ntuser_creds *ntc,
> > 				  BOOL reuse, BOOL *is_new_connection);
> > 
> > 	/* terminate client connection */
> > 	void cli_connection_free(void *con);
> > 
> > 	/* get nt creds associated with an msrpc session. */
> > 	struct ntdom_info *cli_conn_get_ntinfo(void *con);
> > 
> > 	/* get a server name associated with a connection */
> > 	BOOL cli_con_get_srvname(void *con, char *srv_name);
> > 
> > 	/* write to a pipe */
> > 	BOOL rpc_api_write(void *con, prs_struct *data);
> > 
> > 	/* read from a pipe */
> > 	BOOL rpc_api_rcv_pdu(void *con, prs_struct *rdata);
> > 
> > 	/* detect dead servers. The fd is set to -1 when we get an error */
> > 	BOOL rpc_con_ok(void *con);
> > 
> > 	/* read and write to a pipe */
> > 	BOOL rpc_api_send_rcv_pdu(void *con, prs_struct *data,
> > 			  prs_struct *rdata);
> > 
> > } cli_connect_fns;

> This idea looks interesting, can you explain exactly
> how it would be used please, as I'm not sure of the
> exact semantics of these calls.

ok.  the concept is taken directly from cli_connect.c: it was... um... 2
hours' work to create a cli_ncacn_np.c and a cli_ncalrpc.c which are each
about 140 lines long, including the GPL license :)

the purpose of this api is to act as a transport-independent DCE/RPC
communication system, one that can be asked at run-time to make decisions
on which transport is to be used.  e.g, ncacn_np, ncalrpc, ncacn_tcp,
ncacn_http (which are SMB, loop-back, TCP and HTTP respectively).

[the _reason_ for adding it is to make it clearer what is going on.  i
spent an hour and a half explaining the purpose of and the difference
between the ncacn_np and ncalrpc implementations.  because it was not
clear, he removed large segments of code, in the interests of simplicity].

so, this api marks a clear delineation between transport-specific jobs and
the _use_ of the transports, which was otherwise embedded/hard-coded into
cli_connect.c with damn-stupid switch statements.


the cli_ncacn_np.c cli_connect_add function calls ncacn_np_use_add().  
this function will initiate an SMB IPC$ and perform an SMBopenX /
NTSMBcreateX on the DCE/RPC pipe name.

by contrast, the cli_ncalrpc.c cli_connect_add function calls
msrpc_use_add.  this function will initiate a unix domain socket to

by way of a further contrast, a cli_ncacn_tcp.c cli_connect_add function
will make a TCP connection to a [specified] port/ip address.

exactly what happens with ncacn_http, i am uncertain: we do have Netmon
traces and a sample program to generate ncacn_http (and we also have matty


the cli_ncacn_np.c rpc_api_rcv_pdu function calls cli_read() which sends
an SMBreadX.

the cli_ncalrpc.c rpc_api_rcv_pdu function calls msrpc_read() which calls
read_data(16), unmarshalls the PDU header and calls read_data(PDUsize) on
the unix domain socket to the DCE/RPC daemon.

> The only thing I'm not keen on here is the returning
> of (void *) as the type of a "con". I know it's the
> correct abstract data type thing to do, but I'd like
> to initially see the internal data type of "con" before
> deciding.

cli_connect_add() returns a void*.  this is the state info required for
the connection.  each impl., cli_ncacn_np.c, cli_ncalrpc.c,
cli_ncacn_tcp.c etc. will typecast its state to-and-fro.  with 8 functions
and under 100 lines of code, it is hardly difficult to verify that the
typecasts are being done correctly:

/* create new connection.  strictly speaking, one arg should be
 * full dce/rpc format: e.g "ncacn_np:\\server\pipe\pipename" */
static void *ncacn_np_connect_add(const char *pipe_name,
			     const vuser_key * key,
			     const char *srv_name,
			     const struct ntuser_creds *ntc,
			     BOOL reuse, BOOL *is_new_connection)
	return ncacn_np_use_add(pipe_name, NULL, srv_name,
				ntc, reuse, is_new_connection);


terminate client connection
static void ncacn_np_connection_free(void *con_info)
	struct ncacn_np *msrpc = (struct ncacn_np*)con_info;
	BOOL closed = False;
	DEBUG(10, ("msrpc smb connection\n"));
			 &msrpc->smb->nt.key, False, &closed);

 get nt creds (HACK ALERT!) associated with an msrpc session.
static struct ntdom_info *ncacn_np_conn_get_ntinfo(void *con_info)
	struct ncacn_np *msrpc = (struct ncacn_np*)con_info;
	return &msrpc->smb->nt;

get a server name associated with a connection associated with a
policy handle.
static const char *ncacn_np_con_get_srvname(void *con_info)
	struct ncacn_np *msrpc = (struct ncacn_np*)con_info;
	return msrpc->smb->desthost;

 write to a pipe
static BOOL ncacn_np_api_write(void *con_info, prs_struct *data)
	struct ncacn_np *msrpc = (struct ncacn_np*)con_info;
	struct cli_state *cli = msrpc->smb;
	int fnum = msrpc->fnum;
	return cli_write(cli, fnum, 0x0008,
			 data->data, 0,
			 prs_data_size(data), prs_data_size(data)) > 0;

static BOOL ncacn_np_api_rcv_pdu(struct cli_connection *con,
		void *con_info, prs_struct *rdata)
	struct ncacn_np *msrpc = (struct ncacn_np*)con_info;
	struct cli_state *cli = msrpc->smb;
	int fnum = msrpc->fnum;
	return cli_rcv_pdu(con, cli, fnum, rdata);

/* this allows us to detect dead servers. The cli->fd is set to -1 when
   we get an error */
static BOOL ncacn_np_con_ok(void *con_info)
	struct ncacn_np *msrpc = (struct ncacn_np*)con_info;
	struct cli_state *cli;
	if (msrpc == NULL)
		return False;
	cli = msrpc->smb;
	if (cli->fd == -1)
		return False;
	return True;

static BOOL ncacn_np_api_send_rcv_pdu(struct cli_connection *con,
		void *con_info, prs_struct *data,
			  prs_struct *rdata)
	struct ncacn_np *msrpc = (struct ncacn_np*)con_info;
	struct ntdom_info *nt = ncacn_np_conn_get_ntinfo(con_info);
	struct cli_state *cli = msrpc->smb;
	int fnum = msrpc->fnum;
	if (cli->fd == -1)
		return False;
	return cli_send_and_rcv_pdu(con, cli, fnum, data,
				    rdata, nt->max_xmit_frag);

static cli_connect_fns ncacn_np_fns = 


cli_connect_fns *ncacn_np_get_fns(void)
	return &ncacn_np_fns;

More information about the samba-technical mailing list