1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-09 20:23:51 +03:00
Files
samba-mirror/source/build/pidl/server.pm
Andrew Tridgell a4df5c7cf8 r464: a big improvement to the API for writing server-side RPC
servers. Previously the server pipe code needed to return the RPC
level status (nearly always "OK") and separately set the function call
return using r->out.result. All the programmers writing servers
(metze, jelmer and me) were often getting this wrong, by doing things
like "return NT_STATUS_NO_MEMORY" which was really quite meaningless
as there is no code like that at the dcerpc level.

I have now modified pidl to generate the necessary boilerplate so that
just returning the status you want from the function will work. So for
a NTSTATUS function you return NT_STATUS_XXX and from a WERROR
function you return WERR_XXX. If you really want to generate a DCERPC
level fault rather than just a return value in your function then you
should use the DCESRV_FAULT() macro which will correctly generate a
fault for you.

As a side effect, this also adds automatic type checking of all of our
server side rpc functions, which was impossible with the old API. When
I changed the API I found and fixed quite a few functions with the
wrong type information, so this is definately useful.

I have also changed the server side template generation to generate a
DCERPC "operation range error" by default when you have not yet filled
in a server side function. This allows us to correctly implement
functions in any order in our rpc pipe servers and give the client the
right information about the fault.
2007-10-10 12:51:44 -05:00

216 lines
4.5 KiB
Perl

###################################################
# server boilerplate generator
# Copyright tridge@samba.org 2003
# Copyright metze@samba.org 2004
# released under the GNU GPL
package IdlServer;
use strict;
my($res);
sub pidl($)
{
$res .= shift;
}
#####################################################
# generate the switch statement for function dispatch
sub gen_dispatch_switch($)
{
my $data = shift;
my $count = 0;
foreach my $d (@{$data}) {
next if ($d->{TYPE} ne "FUNCTION");
pidl "\tcase $count: {\n";
pidl "\t\tstruct $d->{NAME} *r2 = r;\n";
if ($d->{RETURN_TYPE} && $d->{RETURN_TYPE} ne "void") {
pidl "\t\tr2->out.result = $d->{NAME}(dce_call, mem_ctx, r2);\n";
} else {
pidl "\t\t$d->{NAME}(dce_call, mem_ctx, r2);\n";
}
pidl "\t\tbreak;\n\t}\n";
$count++;
}
}
#####################################################################
# produce boilerplate code for a interface
sub Boilerplate_Iface($)
{
my($interface) = shift;
my($data) = $interface->{DATA};
my $count = 0;
my $name = $interface->{NAME};
my $uname = uc $name;
foreach my $d (@{$data}) {
if ($d->{TYPE} eq "FUNCTION") { $count++; }
}
if ($count == 0) {
return;
}
pidl "
static NTSTATUS $name\__op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface)
{
#ifdef DCESRV_INTERFACE_$uname\_BIND
return DCESRV_INTERFACE_$uname\_BIND(dce_call,iface);
#else
return NT_STATUS_OK;
#endif
}
static void $name\__op_unbind(struct dcesrv_connection *dce_conn, const struct dcesrv_interface *iface)
{
#ifdef DCESRV_INTERFACE_$uname\_UNBIND
DCESRV_INTERFACE_$uname\_UNBIND(dce_conn,iface);
#else
return;
#endif
}
static NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
{
uint16 opnum = dce_call->pkt.u.request.opnum;
dce_call->fault_code = 0;
switch (opnum) {
";
gen_dispatch_switch($data);
pidl "
default:
dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
break;
}
if (dce_call->fault_code != 0) {
return NT_STATUS_NET_WRITE_FAULT;
}
return NT_STATUS_OK;
}
static const struct dcesrv_interface $name\_interface = {
&dcerpc_table_$name,
$name\__op_bind,
$name\__op_unbind,
$name\__op_dispatch
};
";
}
#####################################################################
# produce boilerplate code for an endpoint server
sub Boilerplate_Ep_Server($)
{
my($interface) = shift;
my($data) = $interface->{DATA};
my $count = 0;
my $name = $interface->{NAME};
my $uname = uc $name;
foreach my $d (@{$data}) {
if ($d->{TYPE} eq "FUNCTION") { $count++; }
}
if ($count == 0) {
return;
}
pidl "
static NTSTATUS $name\__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)
{
int i;
for (i=0;i<$name\_interface.ndr->endpoints->count;i++) {
NTSTATUS ret;
const char *name = $name\_interface.ndr->endpoints->names[i];
ret = dcesrv_interface_register(dce_ctx, name, &$name\_interface, NULL);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(1,(\"$name\_op_init_server: failed to register endpoint \'%s\'\\n\",name));
return ret;
}
}
return NT_STATUS_OK;
}
static BOOL $name\__op_interface_by_uuid(struct dcesrv_interface *iface, const char *uuid, uint32 if_version)
{
if ($name\_interface.ndr->if_version == if_version &&
strcmp($name\_interface.ndr->uuid, uuid)==0) {
memcpy(iface,&$name\_interface, sizeof(*iface));
return True;
}
return False;
}
static BOOL $name\__op_interface_by_name(struct dcesrv_interface *iface, const char *name)
{
if (strcmp($name\_interface.ndr->name, name)==0) {
memcpy(iface,&$name\_interface, sizeof(*iface));
return True;
}
return False;
}
NTSTATUS dcerpc_$name\_init(void)
{
NTSTATUS ret;
struct dcesrv_endpoint_server ep_server;
/* fill in our name */
ep_server.name = \"$name\";
/* fill in all the operations */
ep_server.init_server = $name\__op_init_server;
ep_server.interface_by_uuid = $name\__op_interface_by_uuid;
ep_server.interface_by_name = $name\__op_interface_by_name;
/* register ourselves with the DCERPC subsystem. */
ret = register_backend(\"dcerpc\", &ep_server);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,(\"Failed to register \'$name\' endpoint server!\\n\"));
return ret;
}
return ret;
}
";
}
#####################################################################
# parse a parsed IDL structure back into an IDL file
sub Parse($)
{
my($idl) = shift;
$res = "/* dcerpc server boilerplate generated by pidl */\n\n";
foreach my $x (@{$idl}) {
if ($x->{TYPE} eq "INTERFACE") {
Boilerplate_Iface($x);
Boilerplate_Ep_Server($x);
}
}
return $res;
}
1;