mirror of
https://github.com/samba-team/samba.git
synced 2025-01-13 13:18:06 +03:00
Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-pyirpc
(This used to be commit 0a9330ee2a
)
This commit is contained in:
commit
521354e4b3
@ -18,10 +18,13 @@
|
||||
*/
|
||||
|
||||
#ifdef SWIGPYTHON
|
||||
%{
|
||||
#include "libcli/util/pyerrors.h"
|
||||
%}
|
||||
|
||||
%typemap(out,noblock=1) WERROR {
|
||||
if (!W_ERROR_IS_OK($1)) {
|
||||
PyObject *obj = Py_BuildValue((char *)"(i,s)", W_ERROR_V($1), win_errstr($1));
|
||||
PyErr_SetObject(PyExc_RuntimeError, obj);
|
||||
PyErr_SetWERROR($1);
|
||||
SWIG_fail;
|
||||
} else if ($result == NULL) {
|
||||
$result = Py_None;
|
||||
@ -30,8 +33,7 @@
|
||||
|
||||
%typemap(out,noblock=1) NTSTATUS {
|
||||
if (NT_STATUS_IS_ERR($1)) {
|
||||
PyObject *obj = Py_BuildValue((char *)"(i,s)", NT_STATUS_V($1), nt_errstr($1));
|
||||
PyErr_SetObject(PyExc_RuntimeError, obj);
|
||||
PyErr_SetNTSTATUS($1);
|
||||
SWIG_fail;
|
||||
} else if ($result == NULL) {
|
||||
$result = Py_None;
|
||||
|
33
source4/libcli/util/pyerrors.h
Normal file
33
source4/libcli/util/pyerrors.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba utility functions
|
||||
Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef __PYERRORS_H__
|
||||
#define __PYERRORS_H__
|
||||
|
||||
#define PyErr_FromWERROR(err) Py_BuildValue("(i,s)", W_ERROR_V(err), discard_const_p(char, win_errstr(err)))
|
||||
|
||||
#define PyErr_FromNTSTATUS(status) Py_BuildValue("(i,s)", NT_STATUS_V(status), discard_const_p(char, nt_errstr(status)))
|
||||
|
||||
#define PyErr_SetWERROR(err) \
|
||||
PyErr_SetObject(PyExc_RuntimeError, PyErr_FromWERROR(err))
|
||||
|
||||
#define PyErr_SetNTSTATUS(status) \
|
||||
PyErr_SetObject(PyExc_RuntimeError, PyErr_FromNTSTATUS(status))
|
||||
|
||||
#endif /* __PYERRORS_H__ */
|
@ -607,103 +607,102 @@ PUBLIC_HEADERS += $(addprefix $(librpcsrcdir)/, rpc/dcerpc.h \
|
||||
gen_ndr/epmapper.h gen_ndr/ndr_epmapper.h gen_ndr/ndr_epmapper_c.h)
|
||||
|
||||
|
||||
[PYTHON::swig_dcerpc]
|
||||
LIBRARY_REALNAME = samba/dcerpc/_dcerpc.$(SHLIBEXT)
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_SMB NDR_MISC LIBSAMBA-UTIL LIBSAMBA-HOSTCONFIG dcerpc_samr RPC_NDR_LSA DYNCONFIG
|
||||
[PYTHON::python_dcerpc]
|
||||
LIBRARY_REALNAME = samba/dcerpc/base.$(SHLIBEXT)
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_SMB NDR_MISC LIBSAMBA-UTIL LIBSAMBA-HOSTCONFIG dcerpc_samr RPC_NDR_LSA DYNCONFIG swig_credentials param
|
||||
|
||||
swig_dcerpc_OBJ_FILES = $(dcerpcsrcdir)/dcerpc_wrap.o
|
||||
python_dcerpc_OBJ_FILES = $(dcerpcsrcdir)/pyrpc.o
|
||||
|
||||
$(eval $(call python_py_module_template,samba/dcerpc/__init__.py,$(dcerpcsrcdir)/dcerpc.py))
|
||||
|
||||
$(swig_dcerpc_OBJ_FILES): CFLAGS+=$(CFLAG_NO_UNUSED_MACROS) $(CFLAG_NO_CAST_QUAL)
|
||||
|
||||
[PYTHON::python_echo]
|
||||
LIBRARY_REALNAME = samba/dcerpc/echo.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_ECHO PYTALLOC param swig_credentials
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_ECHO PYTALLOC param swig_credentials python_dcerpc
|
||||
|
||||
python_echo_OBJ_FILES = $(gen_ndrsrcdir)/py_echo.o
|
||||
|
||||
[PYTHON::python_winreg]
|
||||
LIBRARY_REALNAME = samba/dcerpc/winreg.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_WINREG python_misc PYTALLOC param swig_credentials python_dcerpc_misc python_lsa
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_WINREG python_misc PYTALLOC param swig_credentials python_dcerpc_misc python_lsa python_dcerpc
|
||||
|
||||
python_winreg_OBJ_FILES = $(gen_ndrsrcdir)/py_winreg.o
|
||||
|
||||
[PYTHON::python_dcerpc_misc]
|
||||
LIBRARY_REALNAME = samba/dcerpc/misc.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = PYTALLOC
|
||||
PRIVATE_DEPENDENCIES = PYTALLOC python_dcerpc
|
||||
|
||||
python_dcerpc_misc_OBJ_FILES = $(gen_ndrsrcdir)/py_misc.o
|
||||
|
||||
[PYTHON::python_initshutdown]
|
||||
LIBRARY_REALNAME = samba/dcerpc/initshutdown.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_INITSHUTDOWN PYTALLOC param swig_credentials python_lsa python_dcerpc_security
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_INITSHUTDOWN PYTALLOC param swig_credentials python_lsa python_dcerpc_security python_dcerpc
|
||||
|
||||
python_initshutdown_OBJ_FILES = $(gen_ndrsrcdir)/py_initshutdown.o
|
||||
|
||||
[PYTHON::python_epmapper]
|
||||
LIBRARY_REALNAME = samba/dcerpc/epmapper.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = dcerpc PYTALLOC param swig_credentials python_dcerpc_misc
|
||||
PRIVATE_DEPENDENCIES = dcerpc PYTALLOC param swig_credentials python_dcerpc_misc python_dcerpc
|
||||
|
||||
python_epmapper_OBJ_FILES = $(gen_ndrsrcdir)/py_epmapper.o
|
||||
|
||||
[PYTHON::python_mgmt]
|
||||
LIBRARY_REALNAME = samba/dcerpc/mgmt.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = PYTALLOC param swig_credentials dcerpc python_dcerpc_misc
|
||||
PRIVATE_DEPENDENCIES = PYTALLOC param swig_credentials dcerpc python_dcerpc_misc python_dcerpc
|
||||
|
||||
python_mgmt_OBJ_FILES = $(gen_ndrsrcdir)/py_mgmt.o
|
||||
|
||||
[PYTHON::python_atsvc]
|
||||
LIBRARY_REALNAME = samba/dcerpc/atsvc.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = dcerpc_atsvc PYTALLOC param swig_credentials
|
||||
PRIVATE_DEPENDENCIES = dcerpc_atsvc PYTALLOC param swig_credentials python_dcerpc
|
||||
|
||||
python_atsvc_OBJ_FILES = $(gen_ndrsrcdir)/py_atsvc.o
|
||||
|
||||
[PYTHON::python_samr]
|
||||
LIBRARY_REALNAME = samba/dcerpc/samr.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = dcerpc_samr PYTALLOC python_dcerpc_security python_lsa python_dcerpc_misc swig_credentials param
|
||||
PRIVATE_DEPENDENCIES = dcerpc_samr PYTALLOC python_dcerpc_security python_lsa python_dcerpc_misc swig_credentials param python_dcerpc
|
||||
|
||||
python_samr_OBJ_FILES = $(gen_ndrsrcdir)/py_samr.o
|
||||
|
||||
[PYTHON::python_svcctl]
|
||||
LIBRARY_REALNAME = samba/dcerpc/svcctl.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_SVCCTL PYTALLOC param swig_credentials python_dcerpc_misc
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_SVCCTL PYTALLOC param swig_credentials python_dcerpc_misc python_dcerpc
|
||||
|
||||
python_svcctl_OBJ_FILES = $(gen_ndrsrcdir)/py_svcctl.o
|
||||
|
||||
[PYTHON::python_lsa]
|
||||
LIBRARY_REALNAME = samba/dcerpc/lsa.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_LSA PYTALLOC param swig_credentials python_dcerpc_security
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_LSA PYTALLOC param swig_credentials python_dcerpc_security python_dcerpc
|
||||
|
||||
python_lsa_OBJ_FILES = $(gen_ndrsrcdir)/py_lsa.o
|
||||
|
||||
[PYTHON::python_wkssvc]
|
||||
LIBRARY_REALNAME = samba/dcerpc/wkssvc.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_WKSSVC PYTALLOC param swig_credentials python_lsa python_dcerpc_security
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_WKSSVC PYTALLOC param swig_credentials python_lsa python_dcerpc_security python_dcerpc
|
||||
|
||||
python_wkssvc_OBJ_FILES = $(gen_ndrsrcdir)/py_wkssvc.o
|
||||
|
||||
[PYTHON::python_dfs]
|
||||
LIBRARY_REALNAME = samba/dcerpc/dfs.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_DFS PYTALLOC param swig_credentials python_dcerpc_misc
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_DFS PYTALLOC param swig_credentials python_dcerpc_misc python_dcerpc
|
||||
|
||||
python_dfs_OBJ_FILES = $(gen_ndrsrcdir)/py_dfs.o
|
||||
|
||||
[PYTHON::python_unixinfo]
|
||||
LIBRARY_REALNAME = samba/dcerpc/unixinfo.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_UNIXINFO PYTALLOC param swig_credentials python_dcerpc_security python_dcerpc_misc
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_UNIXINFO PYTALLOC param swig_credentials python_dcerpc_security python_dcerpc_misc python_dcerpc
|
||||
|
||||
python_unixinfo_OBJ_FILES = $(gen_ndrsrcdir)/py_unixinfo.o
|
||||
|
||||
[PYTHON::python_drsuapi]
|
||||
LIBRARY_REALNAME = samba/dcerpc/drsuapi.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_DRSUAPI PYTALLOC param swig_credentials python_dcerpc_misc python_dcerpc_security
|
||||
PRIVATE_DEPENDENCIES = RPC_NDR_DRSUAPI PYTALLOC param swig_credentials python_dcerpc_misc python_dcerpc_security python_dcerpc
|
||||
|
||||
python_drsuapi_OBJ_FILES = $(gen_ndrsrcdir)/py_drsuapi.o
|
||||
|
||||
[PYTHON::python_dcerpc_security]
|
||||
LIBRARY_REALNAME = samba/dcerpc/security.$(SHLIBEXT)
|
||||
PRIVATE_DEPENDENCIES = PYTALLOC python_dcerpc_misc
|
||||
PRIVATE_DEPENDENCIES = PYTALLOC python_dcerpc_misc python_dcerpc
|
||||
|
||||
python_dcerpc_security_OBJ_FILES = $(gen_ndrsrcdir)/py_security.o
|
||||
|
||||
|
@ -38,7 +38,7 @@ interface rpcecho
|
||||
|
||||
|
||||
/* test some alignment issues */
|
||||
typedef struct {
|
||||
typedef [public] struct {
|
||||
uint8 v;
|
||||
} echo_info1;
|
||||
|
||||
|
@ -362,5 +362,13 @@ NTSTATUS dcerpc_binding_from_tower(TALLOC_CTX *mem_ctx,
|
||||
struct epm_tower *tower,
|
||||
struct dcerpc_binding **b_out);
|
||||
|
||||
NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
|
||||
struct GUID *object,
|
||||
uint16_t opnum,
|
||||
bool async,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *stub_data_in,
|
||||
DATA_BLOB *stub_data_out);
|
||||
|
||||
|
||||
#endif /* __DCERPC_H__ */
|
||||
|
@ -1,125 +0,0 @@
|
||||
/* Tastes like -*- C -*- */
|
||||
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Swig interface to librpc functions.
|
||||
|
||||
Copyright (C) Tim Potter 2004
|
||||
Copyright (C) Jelmer Vernooij 2007
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
%define DOCSTRING
|
||||
"DCE/RPC protocol implementation"
|
||||
%enddef
|
||||
|
||||
%module(docstring=DOCSTRING) dcerpc
|
||||
|
||||
%{
|
||||
|
||||
/* This symbol is used in both includes.h and Python.h which causes an
|
||||
annoying compiler warning. */
|
||||
|
||||
#ifdef HAVE_FSTAT
|
||||
#undef HAVE_FSTAT
|
||||
#endif
|
||||
|
||||
#include "includes.h"
|
||||
#include "dynconfig.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
#include "param/param.h"
|
||||
|
||||
#undef strcpy
|
||||
|
||||
%}
|
||||
|
||||
%import "../../lib/talloc/talloc.i"
|
||||
%import "../../auth/credentials/credentials.i"
|
||||
|
||||
%typemap(in,noblock=1, numinputs=0) struct dcerpc_pipe **OUT (struct dcerpc_pipe *temp_dcerpc_pipe) {
|
||||
$1 = &temp_dcerpc_pipe;
|
||||
}
|
||||
|
||||
%typemap(argout,noblock=1) struct dcerpc_pipe ** {
|
||||
/* Set REF_ALLOC flag so we don't have to do too much extra
|
||||
mucking around with ref variables in ndr unmarshalling. */
|
||||
|
||||
(*$1)->conn->flags |= DCERPC_NDR_REF_ALLOC;
|
||||
|
||||
/* Return swig handle on dcerpc_pipe */
|
||||
|
||||
$result = SWIG_NewPointerObj(*$1, SWIGTYPE_p_dcerpc_pipe, 0);
|
||||
}
|
||||
|
||||
%types(struct dcerpc_pipe *);
|
||||
|
||||
%rename(pipe_connect) dcerpc_pipe_connect;
|
||||
|
||||
NTSTATUS dcerpc_pipe_connect(TALLOC_CTX *parent_ctx,
|
||||
struct dcerpc_pipe **pp,
|
||||
const char *binding,
|
||||
const struct ndr_interface_table *table,
|
||||
struct cli_credentials *credentials,
|
||||
struct event_context *ev,
|
||||
struct loadparm_context *lp_ctx);
|
||||
|
||||
%typemap(in,noblock=1) DATA_BLOB * (DATA_BLOB temp_data_blob) {
|
||||
temp_data_blob.data = PyString_AsString($input);
|
||||
temp_data_blob.length = PyString_Size($input);
|
||||
$1 = &temp_data_blob;
|
||||
}
|
||||
|
||||
const char *dcerpc_server_name(struct dcerpc_pipe *p);
|
||||
|
||||
/* Some typemaps for easier access to resume handles. Really this can
|
||||
also be done using the uint32 carray functions, but it's a bit of a
|
||||
hassle. TODO: Fix memory leak here. */
|
||||
|
||||
%typemap(in,noblock=1) uint32_t *resume_handle {
|
||||
$1 = malloc(sizeof(*$1));
|
||||
*$1 = PyLong_AsLong($input);
|
||||
}
|
||||
|
||||
%typemap(out,noblock=1) uint32_t *resume_handle {
|
||||
$result = PyLong_FromLong(*$1);
|
||||
}
|
||||
|
||||
%typemap(in,noblock=1) struct policy_handle * {
|
||||
|
||||
if ((SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor,
|
||||
SWIG_POINTER_EXCEPTION)) == -1)
|
||||
return NULL;
|
||||
|
||||
if ($1 == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "None is not a valid policy handle");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* When returning a policy handle to Python we need to make a copy of
|
||||
as the talloc context it is created under is destroyed after the
|
||||
wrapper function returns. TODO: Fix memory leak created here. */
|
||||
|
||||
%typemap(out,noblock=1) struct policy_handle * {
|
||||
if ($1) {
|
||||
struct policy_handle *temp = (struct policy_handle *)malloc(sizeof(struct policy_handle));
|
||||
memcpy(temp, $1, sizeof(struct policy_handle));
|
||||
$result = SWIG_NewPointerObj(temp, SWIGTYPE_p_policy_handle, 0);
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
$result = Py_None;
|
||||
}
|
||||
}
|
@ -1,67 +1,20 @@
|
||||
# This file was automatically generated by SWIG (http://www.swig.org).
|
||||
# Version 1.3.35
|
||||
#!/usr/bin/python
|
||||
|
||||
# Unix SMB/CIFS implementation.
|
||||
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
# Don't modify this file, modify the SWIG interface instead.
|
||||
|
||||
"""
|
||||
DCE/RPC protocol implementation
|
||||
"""
|
||||
|
||||
import _dcerpc
|
||||
import new
|
||||
new_instancemethod = new.instancemethod
|
||||
try:
|
||||
_swig_property = property
|
||||
except NameError:
|
||||
pass # Python < 2.2 doesn't have 'property'.
|
||||
def _swig_setattr_nondynamic(self,class_type,name,value,static=1):
|
||||
if (name == "thisown"): return self.this.own(value)
|
||||
if (name == "this"):
|
||||
if type(value).__name__ == 'PySwigObject':
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
method = class_type.__swig_setmethods__.get(name,None)
|
||||
if method: return method(self,value)
|
||||
if (not static) or hasattr(self,name):
|
||||
self.__dict__[name] = value
|
||||
else:
|
||||
raise AttributeError("You cannot add attributes to %s" % self)
|
||||
|
||||
def _swig_setattr(self,class_type,name,value):
|
||||
return _swig_setattr_nondynamic(self,class_type,name,value,0)
|
||||
|
||||
def _swig_getattr(self,class_type,name):
|
||||
if (name == "thisown"): return self.this.own()
|
||||
method = class_type.__swig_getmethods__.get(name,None)
|
||||
if method: return method(self)
|
||||
raise AttributeError,name
|
||||
|
||||
def _swig_repr(self):
|
||||
try: strthis = "proxy of " + self.this.__repr__()
|
||||
except: strthis = ""
|
||||
return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
|
||||
|
||||
import types
|
||||
try:
|
||||
_object = types.ObjectType
|
||||
_newclass = 1
|
||||
except AttributeError:
|
||||
class _object : pass
|
||||
_newclass = 0
|
||||
del types
|
||||
|
||||
|
||||
def _swig_setattr_nondynamic_method(set):
|
||||
def set_attr(self,name,value):
|
||||
if (name == "thisown"): return self.this.own(value)
|
||||
if hasattr(self,name) or (name == "this"):
|
||||
set(self,name,value)
|
||||
else:
|
||||
raise AttributeError("You cannot add attributes to %s" % self)
|
||||
return set_attr
|
||||
|
||||
|
||||
pipe_connect = _dcerpc.pipe_connect
|
||||
dcerpc_server_name = _dcerpc.dcerpc_server_name
|
||||
|
||||
|
||||
from base import *
|
||||
|
File diff suppressed because it is too large
Load Diff
350
source4/librpc/rpc/pyrpc.c
Normal file
350
source4/librpc/rpc/pyrpc.c
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba utility functions
|
||||
Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
|
||||
|
||||
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"
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
#include "librpc/rpc/pyrpc.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
#include "lib/events/events.h"
|
||||
|
||||
static bool PyString_AsGUID(PyObject *object, struct GUID *uuid)
|
||||
{
|
||||
NTSTATUS status;
|
||||
status = GUID_from_string(PyString_AsString(object), uuid);
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
PyErr_SetNTSTATUS(status);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ndr_syntax_from_py_object(PyObject *object, struct ndr_syntax_id *syntax_id)
|
||||
{
|
||||
ZERO_STRUCTP(syntax_id);
|
||||
|
||||
if (PyString_Check(object)) {
|
||||
return PyString_AsGUID(object, &syntax_id->uuid);
|
||||
} else if (PyTuple_Check(object)) {
|
||||
if (PyTuple_Size(object) < 1 || PyTuple_Size(object) > 2) {
|
||||
PyErr_SetString(PyExc_ValueError, "Syntax ID tuple has invalid size");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PyString_Check(PyTuple_GetItem(object, 0))) {
|
||||
PyErr_SetString(PyExc_ValueError, "Expected GUID as first element in tuple");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PyString_AsGUID(PyTuple_GetItem(object, 0), &syntax_id->uuid))
|
||||
return false;
|
||||
|
||||
if (!PyInt_Check(PyTuple_GetItem(object, 1))) {
|
||||
PyErr_SetString(PyExc_ValueError, "Expected version as second element in tuple");
|
||||
return false;
|
||||
}
|
||||
|
||||
syntax_id->if_version = PyInt_AsLong(PyTuple_GetItem(object, 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_TypeError, "Expected UUID or syntax id tuple");
|
||||
return false;
|
||||
}
|
||||
|
||||
static PyObject *py_iface_server_name(PyObject *obj, void *closure)
|
||||
{
|
||||
const char *server_name;
|
||||
dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
|
||||
|
||||
server_name = dcerpc_server_name(iface->pipe);
|
||||
if (server_name == NULL)
|
||||
return Py_None;
|
||||
|
||||
return PyString_FromString(server_name);
|
||||
}
|
||||
|
||||
static PyObject *py_ndr_syntax_id(struct ndr_syntax_id *syntax_id)
|
||||
{
|
||||
PyObject *ret;
|
||||
char *uuid_str;
|
||||
|
||||
uuid_str = GUID_string(NULL, &syntax_id->uuid);
|
||||
if (uuid_str == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = Py_BuildValue("(s,i)", uuid_str, syntax_id->if_version);
|
||||
|
||||
talloc_free(uuid_str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *py_iface_abstract_syntax(PyObject *obj, void *closure)
|
||||
{
|
||||
dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
|
||||
|
||||
return py_ndr_syntax_id(&iface->pipe->syntax);
|
||||
}
|
||||
|
||||
static PyObject *py_iface_transfer_syntax(PyObject *obj, void *closure)
|
||||
{
|
||||
dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
|
||||
|
||||
return py_ndr_syntax_id(&iface->pipe->transfer_syntax);
|
||||
}
|
||||
|
||||
static PyGetSetDef dcerpc_interface_getsetters[] = {
|
||||
{ discard_const_p(char, "server_name"), py_iface_server_name, NULL,
|
||||
discard_const_p(char, "name of the server, if connected over SMB") },
|
||||
{ discard_const_p(char, "abstract_syntax"), py_iface_abstract_syntax, NULL,
|
||||
discard_const_p(char, "syntax id of the abstract syntax") },
|
||||
{ discard_const_p(char, "transfer_syntax"), py_iface_transfer_syntax, NULL,
|
||||
discard_const_p(char, "syntax id of the transfersyntax") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static PyMemberDef dcerpc_interface_members[] = {
|
||||
{ discard_const_p(char, "request_timeout"), T_INT,
|
||||
offsetof(struct dcerpc_pipe, request_timeout), 0,
|
||||
discard_const_p(char, "request timeout, in seconds") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
void PyErr_SetDCERPCStatus(struct dcerpc_pipe *p, NTSTATUS status)
|
||||
{
|
||||
if (p != NULL && NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
|
||||
const char *errstr = dcerpc_errstr(NULL, p->last_fault_code);
|
||||
PyErr_SetObject(PyExc_RuntimeError,
|
||||
Py_BuildValue("(i,s)", p->last_fault_code,
|
||||
errstr));
|
||||
} else {
|
||||
PyErr_SetNTSTATUS(status);
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *py_iface_request(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
|
||||
int opnum;
|
||||
DATA_BLOB data_in, data_out;
|
||||
NTSTATUS status;
|
||||
char *in_data;
|
||||
int in_length;
|
||||
PyObject *ret;
|
||||
PyObject *object = NULL;
|
||||
struct GUID object_guid;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(NULL);
|
||||
const char *kwnames[] = { "opnum", "data", "object", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "is#|O:request",
|
||||
discard_const_p(char *, kwnames), &opnum, &in_data, &in_length, &object)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_in.data = (uint8_t *)talloc_memdup(mem_ctx, in_data, in_length);
|
||||
data_in.length = in_length;
|
||||
|
||||
ZERO_STRUCT(data_out);
|
||||
|
||||
if (object != NULL && !PyString_AsGUID(object, &object_guid)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = dcerpc_request(iface->pipe, object?&object_guid:NULL,
|
||||
opnum, false, mem_ctx, &data_in, &data_out);
|
||||
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
PyErr_SetDCERPCStatus(iface->pipe, status);
|
||||
talloc_free(mem_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = PyString_FromStringAndSize((char *)data_out.data, data_out.length);
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *py_iface_later_context(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
|
||||
NTSTATUS status;
|
||||
const char *kwnames[] = { "abstract_syntax", "transfer_syntax", NULL };
|
||||
PyObject *py_abstract_syntax = Py_None, *py_transfer_syntax = Py_None;
|
||||
struct ndr_syntax_id abstract_syntax, transfer_syntax;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:alter_context",
|
||||
discard_const_p(char *, kwnames), &py_abstract_syntax,
|
||||
&py_transfer_syntax)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!ndr_syntax_from_py_object(py_abstract_syntax, &abstract_syntax))
|
||||
return NULL;
|
||||
|
||||
if (py_transfer_syntax == Py_None) {
|
||||
transfer_syntax = ndr_transfer_syntax;
|
||||
} else {
|
||||
if (!ndr_syntax_from_py_object(py_transfer_syntax,
|
||||
&transfer_syntax))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = dcerpc_alter_context(iface->pipe, iface->pipe, &abstract_syntax,
|
||||
&transfer_syntax);
|
||||
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
PyErr_SetDCERPCStatus(iface->pipe, status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyMethodDef dcerpc_interface_methods[] = {
|
||||
{ "request", (PyCFunction)py_iface_request, METH_VARARGS|METH_KEYWORDS, "S.request(opnum, data, object=None) -> data\nMake a raw request" },
|
||||
{ "alter_context", (PyCFunction)py_iface_later_context, METH_VARARGS|METH_KEYWORDS, "S.alter_context(syntax)\nChange to a different interface" },
|
||||
{ NULL, NULL, 0, NULL },
|
||||
};
|
||||
|
||||
|
||||
static void dcerpc_interface_dealloc(PyObject* self)
|
||||
{
|
||||
dcerpc_InterfaceObject *interface = (dcerpc_InterfaceObject *)self;
|
||||
talloc_free(interface->pipe);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyObject *dcerpc_interface_new(PyTypeObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
dcerpc_InterfaceObject *ret;
|
||||
const char *binding_string;
|
||||
struct cli_credentials *credentials;
|
||||
struct loadparm_context *lp_ctx = NULL;
|
||||
PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None;
|
||||
TALLOC_CTX *mem_ctx = NULL;
|
||||
struct event_context *event_ctx;
|
||||
NTSTATUS status;
|
||||
|
||||
PyObject *syntax, *py_basis = Py_None;
|
||||
const char *kwnames[] = {
|
||||
"binding", "syntax", "lp_ctx", "credentials", "basis_connection", NULL
|
||||
};
|
||||
extern struct loadparm_context *lp_from_py_object(PyObject *py_obj);
|
||||
extern struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj);
|
||||
struct ndr_interface_table *table;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OOO:connect", discard_const_p(char *, kwnames), &binding_string, &syntax, &py_lp_ctx, &py_credentials, &py_basis)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lp_ctx = lp_from_py_object(py_lp_ctx);
|
||||
if (lp_ctx == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
credentials = cli_credentials_from_py_object(py_credentials);
|
||||
if (credentials == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected credentials");
|
||||
return NULL;
|
||||
}
|
||||
ret = PyObject_New(dcerpc_InterfaceObject, &dcerpc_InterfaceType);
|
||||
|
||||
event_ctx = event_context_init(mem_ctx);
|
||||
|
||||
/* Create a dummy interface table struct. TODO: In the future, we should rather just allow
|
||||
* connecting without requiring an interface table.
|
||||
*/
|
||||
|
||||
table = talloc_zero(mem_ctx, struct ndr_interface_table);
|
||||
|
||||
if (table == NULL) {
|
||||
PyErr_SetString(PyExc_MemoryError, "Allocating interface table");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!ndr_syntax_from_py_object(syntax, &table->syntax_id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->pipe = NULL;
|
||||
|
||||
if (py_basis != Py_None) {
|
||||
struct dcerpc_pipe *base_pipe;
|
||||
|
||||
if (!PyObject_TypeCheck(py_basis, &dcerpc_InterfaceType)) {
|
||||
PyErr_SetString(PyExc_ValueError, "basis_connection must be a DCE/RPC connection");
|
||||
talloc_free(mem_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
base_pipe = ((dcerpc_InterfaceObject *)py_basis)->pipe;
|
||||
|
||||
status = dcerpc_secondary_context(base_pipe, &ret->pipe,
|
||||
table);
|
||||
ret->pipe = talloc_steal(NULL, ret->pipe);
|
||||
} else {
|
||||
status = dcerpc_pipe_connect(NULL, &ret->pipe, binding_string,
|
||||
table, credentials, event_ctx, lp_ctx);
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
PyErr_SetDCERPCStatus(ret->pipe, status);
|
||||
talloc_free(mem_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
|
||||
return (PyObject *)ret;
|
||||
}
|
||||
|
||||
PyTypeObject dcerpc_InterfaceType = {
|
||||
PyObject_HEAD_INIT(NULL) 0,
|
||||
.tp_name = "dcerpc.ClientConnection",
|
||||
.tp_basicsize = sizeof(dcerpc_InterfaceObject),
|
||||
.tp_dealloc = dcerpc_interface_dealloc,
|
||||
.tp_getset = dcerpc_interface_getsetters,
|
||||
.tp_members = dcerpc_interface_members,
|
||||
.tp_methods = dcerpc_interface_methods,
|
||||
.tp_doc = "ClientConnection(binding, syntax, lp_ctx=None, credentials=None) -> connection\n"
|
||||
"\n"
|
||||
"binding should be a DCE/RPC binding string (for example: ncacn_ip_tcp:127.0.0.1)\n"
|
||||
"syntax should be a tuple with a GUID and version number of an interface\n"
|
||||
"lp_ctx should be a path to a smb.conf file or a param.LoadParm object\n"
|
||||
"credentials should be a credentials.Credentials object.\n\n",
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
.tp_new = dcerpc_interface_new,
|
||||
};
|
||||
|
||||
void initbase(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
if (PyType_Ready(&dcerpc_InterfaceType) < 0)
|
||||
return;
|
||||
|
||||
m = Py_InitModule3("base", NULL, "DCE/RPC protocol implementation");
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
Py_INCREF((PyObject *)&dcerpc_InterfaceType);
|
||||
PyModule_AddObject(m, "ClientConnection", (PyObject *)&dcerpc_InterfaceType);
|
||||
}
|
@ -20,6 +20,8 @@
|
||||
#ifndef _PYRPC_H_
|
||||
#define _PYRPC_H_
|
||||
|
||||
#include "libcli/util/pyerrors.h"
|
||||
|
||||
#define PY_CHECK_TYPE(type, var, fail) \
|
||||
if (!type ## _Check(var)) {\
|
||||
PyErr_Format(PyExc_TypeError, "Expected type %s", type ## _Type.tp_name); \
|
||||
@ -36,4 +38,18 @@
|
||||
# define PyAPI_DATA(RTYPE) extern RTYPE
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
struct dcerpc_pipe *pipe;
|
||||
} dcerpc_InterfaceObject;
|
||||
|
||||
PyAPI_DATA(PyTypeObject) dcerpc_InterfaceType;
|
||||
|
||||
#define PyErr_FromNdrError(err) PyErr_FromNTSTATUS(ndr_map_error2ntstatus(err))
|
||||
|
||||
#define PyErr_SetNdrError(err) \
|
||||
PyErr_SetObject(PyExc_RuntimeError, PyErr_FromNdrError(err))
|
||||
|
||||
void PyErr_SetDCERPCStatus(struct dcerpc_pipe *pipe, NTSTATUS status);
|
||||
|
||||
#endif /* _PYRPC_H_ */
|
@ -225,9 +225,64 @@ sub PythonStruct($$$$$$)
|
||||
$self->pidl("return py_talloc_import(&$name\_Type, ret);");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
|
||||
$self->pidl("");
|
||||
|
||||
my $py_methods = "NULL";
|
||||
|
||||
# If the struct is not public there ndr_pull/ndr_push functions will
|
||||
# be static so not callable from here
|
||||
if (has_property($d, "public")) {
|
||||
$self->pidl("static PyObject *py_$name\_ndr_pack(PyObject *py_obj)");
|
||||
$self->pidl("{");
|
||||
$self->indent;
|
||||
$self->pidl("$cname *object = py_talloc_get_ptr(py_obj);");
|
||||
$self->pidl("DATA_BLOB blob;");
|
||||
$self->pidl("enum ndr_err_code err;");
|
||||
$self->pidl("err = ndr_push_struct_blob(&blob, py_talloc_get_mem_ctx(py_obj), NULL, object, (ndr_push_flags_fn_t)ndr_push_$name);");
|
||||
$self->pidl("if (err != NDR_ERR_SUCCESS) {");
|
||||
$self->indent;
|
||||
$self->pidl("PyErr_SetNdrError(err);");
|
||||
$self->pidl("return NULL;");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->pidl("");
|
||||
$self->pidl("return PyString_FromStringAndSize((char *)blob.data, blob.length);");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->pidl("");
|
||||
|
||||
$self->pidl("static PyObject *py_$name\_ndr_unpack(PyObject *py_obj, PyObject *args)");
|
||||
$self->pidl("{");
|
||||
$self->indent;
|
||||
$self->pidl("$cname *object = py_talloc_get_ptr(py_obj);");
|
||||
$self->pidl("DATA_BLOB blob;");
|
||||
$self->pidl("enum ndr_err_code err;");
|
||||
$self->pidl("if (!PyArg_ParseTuple(args, \"s#:__ndr_unpack__\", &blob.data, &blob.length))");
|
||||
$self->pidl("\treturn NULL;");
|
||||
$self->pidl("");
|
||||
$self->pidl("err = ndr_pull_struct_blob_all(&blob, py_talloc_get_mem_ctx(py_obj), NULL, object, (ndr_pull_flags_fn_t)ndr_pull_$name);");
|
||||
$self->pidl("if (err != NDR_ERR_SUCCESS) {");
|
||||
$self->indent;
|
||||
$self->pidl("PyErr_SetNdrError(err);");
|
||||
$self->pidl("return NULL;");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->pidl("");
|
||||
$self->pidl("return Py_None;");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->pidl("");
|
||||
$py_methods = "py_$name\_methods";
|
||||
$self->pidl("static PyMethodDef $py_methods\[] = {");
|
||||
$self->indent;
|
||||
$self->pidl("{ \"__ndr_pack__\", (PyCFunction)py_$name\_ndr_pack, METH_NOARGS, \"S.pack() -> blob\\nNDR pack\" },");
|
||||
$self->pidl("{ \"__ndr_unpack__\", (PyCFunction)py_$name\_ndr_unpack, METH_VARARGS, \"S.unpack(blob) -> None\\nNDR unpack\" },");
|
||||
$self->pidl("{ NULL, NULL, 0, NULL }");
|
||||
$self->deindent;
|
||||
$self->pidl("};");
|
||||
$self->pidl("");
|
||||
}
|
||||
|
||||
$self->pidl_hdr("PyAPI_DATA(PyTypeObject) $name\_Type;\n");
|
||||
$self->pidl_hdr("#define $name\_Check(op) PyObject_TypeCheck(op, &$name\_Type)\n");
|
||||
$self->pidl_hdr("#define $name\_CheckExact(op) ((op)->ob_type == &$name\_Type)\n");
|
||||
@ -243,6 +298,7 @@ sub PythonStruct($$$$$$)
|
||||
$self->pidl(".tp_getset = $getsetters,");
|
||||
$self->pidl(".tp_repr = py_talloc_default_repr,");
|
||||
$self->pidl(".tp_doc = $docstring,");
|
||||
$self->pidl(".tp_methods = $py_methods,");
|
||||
$self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,");
|
||||
$self->pidl(".tp_new = py_$name\_new,");
|
||||
$self->deindent;
|
||||
@ -271,7 +327,7 @@ sub PythonFunctionBody($$$)
|
||||
{
|
||||
my ($self, $fn, $iface, $prettyname) = @_;
|
||||
|
||||
$self->pidl("$iface\_InterfaceObject *iface = ($iface\_InterfaceObject *)self;");
|
||||
$self->pidl("dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;");
|
||||
$self->pidl("NTSTATUS status;");
|
||||
$self->pidl("TALLOC_CTX *mem_ctx = talloc_new(NULL);");
|
||||
$self->pidl("struct $fn->{NAME} *r = talloc_zero(mem_ctx, struct $fn->{NAME});");
|
||||
@ -353,7 +409,14 @@ sub PythonFunctionBody($$$)
|
||||
}
|
||||
}
|
||||
$self->pidl("status = dcerpc_$fn->{NAME}(iface->pipe, mem_ctx, r);");
|
||||
$self->handle_ntstatus("status", "NULL", "mem_ctx");
|
||||
$self->pidl("if (NT_STATUS_IS_ERR(status)) {");
|
||||
$self->indent;
|
||||
$self->pidl("PyErr_SetDCERPCStatus(iface->pipe, status);");
|
||||
$self->pidl("talloc_free(mem_ctx);");
|
||||
$self->pidl("return NULL;");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->pidl("");
|
||||
|
||||
$env = GenerateFunctionOutEnv($fn, "r->");
|
||||
my $i = 0;
|
||||
@ -445,7 +508,7 @@ sub handle_werror($$$$)
|
||||
|
||||
$self->pidl("if (!W_ERROR_IS_OK($var)) {");
|
||||
$self->indent;
|
||||
$self->pidl("PyErr_SetString(PyExc_RuntimeError, win_errstr($var));");
|
||||
$self->pidl("PyErr_SetWERROR($var);");
|
||||
$self->pidl("talloc_free($mem_ctx);") if ($mem_ctx);
|
||||
$self->pidl("return $retval;");
|
||||
$self->deindent;
|
||||
@ -459,7 +522,7 @@ sub handle_ntstatus($$$$)
|
||||
|
||||
$self->pidl("if (NT_STATUS_IS_ERR($var)) {");
|
||||
$self->indent;
|
||||
$self->pidl("PyErr_SetString(PyExc_RuntimeError, nt_errstr($var));");
|
||||
$self->pidl("PyErr_SetNTSTATUS($var);");
|
||||
$self->pidl("talloc_free($mem_ctx);") if ($mem_ctx);
|
||||
$self->pidl("return $retval;");
|
||||
$self->deindent;
|
||||
@ -551,13 +614,6 @@ sub Interface($$$)
|
||||
|
||||
if (defined $interface->{PROPERTIES}->{uuid}) {
|
||||
$self->pidl_hdr("PyAPI_DATA(PyTypeObject) $interface->{NAME}_InterfaceType;\n");
|
||||
$self->pidl("typedef struct {");
|
||||
$self->indent;
|
||||
$self->pidl("PyObject_HEAD");
|
||||
$self->pidl("struct dcerpc_pipe *pipe;");
|
||||
$self->deindent;
|
||||
$self->pidl("} $interface->{NAME}_InterfaceObject;");
|
||||
|
||||
$self->pidl("");
|
||||
|
||||
my @fns = ();
|
||||
@ -587,37 +643,27 @@ sub Interface($$$)
|
||||
$self->pidl("};");
|
||||
$self->pidl("");
|
||||
|
||||
$self->pidl("static void interface_$interface->{NAME}_dealloc(PyObject* self)");
|
||||
$self->pidl("{");
|
||||
$self->indent;
|
||||
$self->pidl("$interface->{NAME}_InterfaceObject *interface = ($interface->{NAME}_InterfaceObject *)self;");
|
||||
$self->pidl("talloc_free(interface->pipe);");
|
||||
$self->pidl("PyObject_Del(self);");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->pidl("");
|
||||
|
||||
$self->pidl("static PyObject *interface_$interface->{NAME}_new(PyTypeObject *self, PyObject *args, PyObject *kwargs)");
|
||||
$self->pidl("{");
|
||||
$self->indent;
|
||||
$self->pidl("$interface->{NAME}_InterfaceObject *ret;");
|
||||
$self->pidl("dcerpc_InterfaceObject *ret;");
|
||||
$self->pidl("const char *binding_string;");
|
||||
$self->pidl("struct cli_credentials *credentials;");
|
||||
$self->pidl("struct loadparm_context *lp_ctx = NULL;");
|
||||
$self->pidl("PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None;");
|
||||
$self->pidl("PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None, *py_basis = Py_None;");
|
||||
$self->pidl("TALLOC_CTX *mem_ctx = NULL;");
|
||||
$self->pidl("struct event_context *event_ctx;");
|
||||
$self->pidl("NTSTATUS status;");
|
||||
$self->pidl("");
|
||||
$self->pidl("const char *kwnames[] = {");
|
||||
$self->indent;
|
||||
$self->pidl("\"binding\", \"lp_ctx\", \"credentials\", NULL");
|
||||
$self->pidl("\"binding\", \"lp_ctx\", \"credentials\", \"basis_connection\", NULL");
|
||||
$self->deindent;
|
||||
$self->pidl("};");
|
||||
$self->pidl("extern struct loadparm_context *lp_from_py_object(PyObject *py_obj);");
|
||||
$self->pidl("extern struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj);");
|
||||
$self->pidl("");
|
||||
$self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"s|OO:$interface->{NAME}\", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials)) {");
|
||||
$self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"s|OOO:$interface->{NAME}\", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials, &py_basis)) {");
|
||||
$self->indent;
|
||||
$self->pidl("return NULL;");
|
||||
$self->deindent;
|
||||
@ -640,13 +686,33 @@ sub Interface($$$)
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
|
||||
$self->pidl("ret = PyObject_New($interface->{NAME}_InterfaceObject, &$interface->{NAME}_InterfaceType);");
|
||||
$self->pidl("ret = PyObject_New(dcerpc_InterfaceObject, &$interface->{NAME}_InterfaceType);");
|
||||
$self->pidl("");
|
||||
$self->pidl("event_ctx = event_context_init(mem_ctx);");
|
||||
$self->pidl("");
|
||||
|
||||
$self->pidl("if (py_basis != Py_None) {");
|
||||
$self->indent;
|
||||
$self->pidl("struct dcerpc_pipe *base_pipe;");
|
||||
$self->pidl("");
|
||||
$self->pidl("if (!PyObject_TypeCheck(py_basis, &dcerpc_InterfaceType)) {");
|
||||
$self->indent;
|
||||
$self->pidl("PyErr_SetString(PyExc_ValueError, \"basis_connection must be a DCE/RPC connection\");");
|
||||
$self->pidl("talloc_free(mem_ctx);");
|
||||
$self->pidl("return NULL;");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->pidl("");
|
||||
$self->pidl("base_pipe = ((dcerpc_InterfaceObject *)py_basis)->pipe;");
|
||||
$self->pidl("");
|
||||
$self->pidl("status = dcerpc_secondary_context(base_pipe, &ret->pipe, &ndr_table_$interface->{NAME});");
|
||||
$self->deindent;
|
||||
$self->pidl("} else {");
|
||||
$self->indent;
|
||||
$self->pidl("status = dcerpc_pipe_connect(NULL, &ret->pipe, binding_string, ");
|
||||
$self->pidl(" &ndr_table_$interface->{NAME}, credentials, event_ctx, lp_ctx);");
|
||||
$self->deindent;
|
||||
$self->pidl("}");
|
||||
$self->handle_ntstatus("status", "NULL", "mem_ctx");
|
||||
|
||||
$self->pidl("ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;");
|
||||
@ -676,8 +742,8 @@ sub Interface($$$)
|
||||
$self->indent;
|
||||
$self->pidl("PyObject_HEAD_INIT(NULL) 0,");
|
||||
$self->pidl(".tp_name = \"$basename.$interface->{NAME}\",");
|
||||
$self->pidl(".tp_basicsize = sizeof($interface->{NAME}_InterfaceObject),");
|
||||
$self->pidl(".tp_dealloc = interface_$interface->{NAME}_dealloc,");
|
||||
$self->pidl(".tp_basicsize = sizeof(dcerpc_InterfaceObject),");
|
||||
$self->pidl(".tp_base = &dcerpc_InterfaceType,");
|
||||
$self->pidl(".tp_methods = interface_$interface->{NAME}_methods,");
|
||||
$self->pidl(".tp_doc = $docstring,");
|
||||
$self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,");
|
||||
@ -887,11 +953,11 @@ sub ConvertScalarToPython($$$)
|
||||
}
|
||||
|
||||
if ($ctypename eq "NTSTATUS") {
|
||||
return "PyInt_FromLong(NT_STATUS_V($cvar))";
|
||||
return "PyErr_FromNTSTATUS($cvar)";
|
||||
}
|
||||
|
||||
if ($ctypename eq "WERROR") {
|
||||
return "PyInt_FromLong(W_ERROR_V($cvar))";
|
||||
return "PyErr_FromWERROR($cvar)";
|
||||
}
|
||||
|
||||
if (($ctypename eq "string" or $ctypename eq "nbt_string" or $ctypename eq "nbt_name" or $ctypename eq "wrepl_nbt_name")) {
|
||||
@ -1029,7 +1095,7 @@ sub Parse($$$$$)
|
||||
#include <Python.h>
|
||||
#include \"librpc/rpc/dcerpc.h\"
|
||||
#include \"scripting/python/pytalloc.h\"
|
||||
#include \"scripting/python/pyrpc.h\"
|
||||
#include \"librpc/rpc/pyrpc.h\"
|
||||
#include \"lib/events/events.h\"
|
||||
#include \"$hdr\"
|
||||
#include \"$ndr_hdr\"
|
||||
|
@ -40,5 +40,3 @@ samba4.winbind.struct.*.SHOW_SEQUENCE # Not yet working in winbind
|
||||
samba4.winbind.struct.*.GETPWENT # Not yet working in winbind
|
||||
samba4.winbind.struct.*.SETPWENT # Not yet working in winbind
|
||||
samba4.winbind.struct.*.LOOKUP_NAME_SID # Not yet working in winbind
|
||||
|
||||
|
||||
|
161
source4/scripting/bin/autoidl.py
Executable file
161
source4/scripting/bin/autoidl.py
Executable file
@ -0,0 +1,161 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Unix SMB/CIFS implementation.
|
||||
# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
MAX_OPNUM = 1000
|
||||
MAX_BASE_SIZE = 0x1000
|
||||
MAX_IFACE_VERSION = 1000
|
||||
|
||||
DCERPC_FAULT_OP_RNG_ERROR = 0x1c010002
|
||||
DCERPC_FAULT_NDR = 0x6f7
|
||||
NT_STATUS_NET_WRITE_FAULT = -1073741614
|
||||
|
||||
|
||||
sys.path.insert(0, "bin/python")
|
||||
|
||||
from samba.dcerpc import ClientConnection
|
||||
|
||||
def find_num_funcs(conn):
|
||||
for i in xrange(MAX_OPNUM):
|
||||
try:
|
||||
conn.request(i, "")
|
||||
except RuntimeError, (num, msg):
|
||||
if num == DCERPC_FAULT_OP_RNG_ERROR:
|
||||
return i
|
||||
raise Exception("More than %d functions" % MAX_OPNUM)
|
||||
|
||||
class Function:
|
||||
def __init__(self, conn, opnum):
|
||||
self.conn = conn
|
||||
self.opnum = opnum
|
||||
|
||||
def request(self, data):
|
||||
assert isinstance(data, str)
|
||||
self.conn.request(self.opnum, data)
|
||||
|
||||
def valid_ndr(self, data):
|
||||
try:
|
||||
self.request(data)
|
||||
except RuntimeError, (num, msg):
|
||||
if num == DCERPC_FAULT_NDR:
|
||||
return False
|
||||
return True
|
||||
|
||||
def find_base_request(self, start=""):
|
||||
"""Find the smallest possible request that we can do that is
|
||||
valid.
|
||||
|
||||
This generally means sending all zeroes so we get NULL pointers where
|
||||
possible."""
|
||||
# TODO: Don't try with just 0's as there may be switch_is() variables
|
||||
# for which 0 is not a valid value or variables with range() set
|
||||
# See how much input bytes we need
|
||||
for i in range(MAX_BASE_SIZE):
|
||||
data = start + chr(0) * i
|
||||
if self.valid_ndr(data):
|
||||
return data
|
||||
return None
|
||||
|
||||
def check_decision_byte(self, base_request, i):
|
||||
"""Check whether the specified byte is a possible "decision" byte,
|
||||
e.g. a byte that is part of a pointer, array size variable or
|
||||
discriminant.
|
||||
|
||||
Note that this function only checks if a byte is definitely a decision
|
||||
byte. It may return False in some cases while the byte is actually
|
||||
a decision byte."""
|
||||
data = list(base_request)
|
||||
data[i] = chr(1)
|
||||
return not self.valid_ndr("".join(data))
|
||||
|
||||
def find_deferrant_data(self, base_request, idx):
|
||||
data = list(base_request)
|
||||
data[idx*4] = chr(1)
|
||||
# Just check that this is a pointer to something non-empty:
|
||||
assert not self.valid_ndr("".join(data))
|
||||
|
||||
newdata = self.find_base_request("".join(data))
|
||||
|
||||
if newdata is None:
|
||||
return None
|
||||
|
||||
assert newdata.startswith(data)
|
||||
|
||||
return newdata[len(data):]
|
||||
|
||||
def find_idl(self):
|
||||
base_request = self.find_base_request()
|
||||
|
||||
if base_request is None:
|
||||
raise Exception("Unable to determine base size for opnum %d" % self.opnum)
|
||||
|
||||
print "\tBase request is %r" % base_request
|
||||
|
||||
decision_byte_map = map(lambda x: self.check_decision_byte(base_request, x), range(len(base_request)))
|
||||
|
||||
print decision_byte_map
|
||||
|
||||
# find pointers
|
||||
possible_pointers = map(all,
|
||||
[decision_byte_map[i*4:(i+1)*4] for i in range(int(len(base_request)/4))])
|
||||
print possible_pointers
|
||||
|
||||
pointer_deferrant_bases = map(
|
||||
lambda x: self.find_deferrant_data(base_request, x) if possible_pointers[x] else None, range(len(possible_pointers)))
|
||||
|
||||
print pointer_deferrant_bases
|
||||
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print "Usage: autoidl <binding> <UUID> [<version>]"
|
||||
sys.exit(1)
|
||||
|
||||
(binding, uuid) = sys.argv[1:3]
|
||||
if len(sys.argv) == 4:
|
||||
version = sys.argv[3]
|
||||
else:
|
||||
version = None
|
||||
|
||||
if version is None:
|
||||
for i in range(MAX_IFACE_VERSION):
|
||||
try:
|
||||
conn = ClientConnection(binding, (uuid, i))
|
||||
except RuntimeError, (num, msg):
|
||||
if num == NT_STATUS_NET_WRITE_FAULT:
|
||||
continue
|
||||
raise
|
||||
else:
|
||||
break
|
||||
else:
|
||||
conn = ClientConnection(binding, (uuid, version))
|
||||
|
||||
print "Figuring out number of connections...",
|
||||
num_funcs = find_num_funcs(conn)
|
||||
print "%d" % num_funcs
|
||||
|
||||
# Figure out the syntax for each one
|
||||
for i in range(num_funcs):
|
||||
print "Function %d" % i
|
||||
data = Function(conn, i)
|
||||
try:
|
||||
data.find_idl()
|
||||
except Exception, e:
|
||||
print "Error: %r" % e
|
@ -35,7 +35,7 @@
|
||||
%import "../../auth/credentials/credentials.i"
|
||||
%import "../../param/param.i"
|
||||
%import "../../libcli/security/security.i"
|
||||
%import "../../libcli/util/errors.i"
|
||||
%include "../../libcli/util/errors.i"
|
||||
|
||||
%feature("docstring") generate_random_str "S.random_password(len) -> string\n" \
|
||||
"Generate random password with specified length.";
|
||||
@ -97,7 +97,6 @@ bool dsdb_set_ntds_invocation_id(struct ldb_context *ldb, const char *guid)
|
||||
}
|
||||
%}
|
||||
|
||||
char *private_path(TALLOC_CTX* mem_ctx,
|
||||
struct loadparm_context *lp_ctx,
|
||||
const char *name);
|
||||
char *private_path(TALLOC_CTX* mem_ctx, struct loadparm_context *lp_ctx,
|
||||
const char *name);
|
||||
|
||||
|
@ -2554,6 +2554,9 @@ static swig_module_info swig_module = {swig_types, 27, 0, 0, 0, 0};
|
||||
#include "librpc/ndr/libndr.h"
|
||||
|
||||
|
||||
#include "libcli/util/pyerrors.h"
|
||||
|
||||
|
||||
SWIGINTERN int
|
||||
SWIG_AsVal_double (PyObject *obj, double *val)
|
||||
{
|
||||
@ -3046,8 +3049,7 @@ SWIGINTERN PyObject *_wrap_dsdb_attach_schema_from_ldif_file(PyObject *SWIGUNUSE
|
||||
"ldb context must be non-NULL");
|
||||
result = dsdb_attach_schema_from_ldif_file(arg1,(char const *)arg2,(char const *)arg3);
|
||||
if (!W_ERROR_IS_OK(result)) {
|
||||
PyObject *obj = Py_BuildValue((char *)"(i,s)", W_ERROR_V(result), win_errstr(result));
|
||||
PyErr_SetObject(PyExc_RuntimeError, obj);
|
||||
PyErr_SetWERROR(result);
|
||||
SWIG_fail;
|
||||
} else if (resultobj == NULL) {
|
||||
resultobj = Py_None;
|
||||
|
28
source4/scripting/python/samba/ndr.py
Normal file
28
source4/scripting/python/samba/ndr.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Unix SMB/CIFS implementation.
|
||||
# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
def ndr_pack(object):
|
||||
return object.__ndr_pack__()
|
||||
|
||||
|
||||
def ndr_unpack(cls, data):
|
||||
object = cls()
|
||||
object.__ndr_unpack__(data)
|
||||
return object
|
46
source4/scripting/python/samba/tests/dcerpc/bare.py
Normal file
46
source4/scripting/python/samba/tests/dcerpc/bare.py
Normal file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Unix SMB/CIFS implementation.
|
||||
# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
from samba.dcerpc import ClientConnection
|
||||
from unittest import TestCase
|
||||
|
||||
class BareTestCase(TestCase):
|
||||
def test_bare(self):
|
||||
# Connect to the echo pipe
|
||||
x = ClientConnection("ncalrpc:localhost[DEFAULT]",
|
||||
("60a15ec5-4de8-11d7-a637-005056a20182", 1))
|
||||
self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4))
|
||||
|
||||
def test_alter_context(self):
|
||||
x = ClientConnection("ncalrpc:localhost[DEFAULT]",
|
||||
("12345778-1234-abcd-ef00-0123456789ac", 1))
|
||||
y = ClientConnection("ncalrpc:localhost",
|
||||
("60a15ec5-4de8-11d7-a637-005056a20182", 1),
|
||||
basis_connection=x)
|
||||
x.alter_context(("60a15ec5-4de8-11d7-a637-005056a20182", 1))
|
||||
# FIXME: self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4))
|
||||
|
||||
def test_two_connections(self):
|
||||
x = ClientConnection("ncalrpc:localhost[DEFAULT]",
|
||||
("60a15ec5-4de8-11d7-a637-005056a20182", 1))
|
||||
y = ClientConnection("ncalrpc:localhost",
|
||||
("60a15ec5-4de8-11d7-a637-005056a20182", 1),
|
||||
basis_connection=x)
|
||||
self.assertEquals("\x01\x00\x00\x00", y.request(0, chr(0) * 4))
|
@ -18,6 +18,7 @@
|
||||
#
|
||||
|
||||
from samba.dcerpc import echo
|
||||
from samba.ndr import ndr_pack, ndr_unpack
|
||||
import unittest
|
||||
from samba.tests import RpcInterfaceTestCase
|
||||
|
||||
@ -25,6 +26,14 @@ class RpcEchoTests(RpcInterfaceTestCase):
|
||||
def setUp(self):
|
||||
self.conn = echo.rpcecho("ncalrpc:", self.get_loadparm())
|
||||
|
||||
def test_two_contexts(self):
|
||||
self.conn2 = echo.rpcecho("ncalrpc", basis_connection=self.conn)
|
||||
self.assertEquals(3, self.conn2.AddOne(2))
|
||||
|
||||
def test_abstract_syntax(self):
|
||||
self.assertEquals(("60a15ec5-4de8-11d7-a637-005056a20182", 1),
|
||||
self.conn.abstract_syntax)
|
||||
|
||||
def test_addone(self):
|
||||
self.assertEquals(2, self.conn.AddOne(1))
|
||||
|
||||
@ -40,3 +49,19 @@ class RpcEchoTests(RpcInterfaceTestCase):
|
||||
surrounding_struct.surrounding = [1,2,3,4]
|
||||
y = self.conn.TestSurrounding(surrounding_struct)
|
||||
self.assertEquals(8 * [0], y.surrounding)
|
||||
|
||||
def test_manual_request(self):
|
||||
self.assertEquals("\x01\x00\x00\x00", self.conn.request(0, chr(0) * 4))
|
||||
|
||||
def test_server_name(self):
|
||||
self.assertEquals(None, self.conn.server_name)
|
||||
|
||||
class NdrEchoTests(unittest.TestCase):
|
||||
def test_info1_push(self):
|
||||
x = echo.info1()
|
||||
x.v = 42
|
||||
self.assertEquals("\x2a", ndr_pack(x))
|
||||
|
||||
def test_info1_pull(self):
|
||||
x = ndr_unpack(echo.info1, "\x42")
|
||||
self.assertEquals(x.v, 66)
|
||||
|
@ -64,7 +64,6 @@ SCRIPTDIR=$samba4srcdir/../testprogs/ejs
|
||||
smb4torture="$samba4bindir/smbtorture $TORTURE_OPTIONS"
|
||||
|
||||
plantest "js.base" dc "$SCRIPTDIR/base.js" $CONFIGURATION
|
||||
plantest "samr.python" dc "$samba4bindir/../scripting/bin/samr.py" ncalrpc:
|
||||
#plantest "ejsnet.js" dc "$SCRIPTDIR/ejsnet.js" $CONFIGURATION -U\$USERNAME%\$PASSWORD \$DOMAIN ejstestuser
|
||||
plantest "js.ldb" none "$SCRIPTDIR/ldb.js" `pwd` $CONFIGURATION -d 10
|
||||
plantest "js.winreg" dc $samba4srcdir/scripting/bin/winreg $CONFIGURATION ncalrpc: 'HKLM' -U\$USERNAME%\$PASSWORD
|
||||
@ -341,6 +340,7 @@ plantest "samba.python" none $SUBUNITRUN samba.tests
|
||||
plantest "provision.python" none $SUBUNITRUN samba.tests.provision
|
||||
plantest "samba3.python" none $SUBUNITRUN samba.tests.samba3
|
||||
plantest "samr.python" dc $SUBUNITRUN samba.tests.dcerpc.sam
|
||||
plantest "dcerpc.bare.python" dc $SUBUNITRUN samba.tests.dcerpc.bare
|
||||
plantest "samdb.python" dc $SUBUNITRUN samba.tests.samdb
|
||||
plantest "unixinfo.python" dc $SUBUNITRUN samba.tests.dcerpc.unix
|
||||
plantest "events.python" none PYTHONPATH="$PYTHONPATH:lib/events" $SUBUNITRUN tests
|
||||
|
Loading…
Reference in New Issue
Block a user