librpc: Shorten dcerpc_binding_handle_call a bit
[Samba/gebeck_regimport.git] / source4 / scripting / bin / autoidl
blob313ab4babdab40d480d7babc342ce98ae8fc15be
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 # Unix SMB/CIFS implementation.
5 # Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 import sys
23 MAX_OPNUM = 1000
24 MAX_BASE_SIZE = 0x1000
25 MAX_IFACE_VERSION = 1000
27 DCERPC_FAULT_OP_RNG_ERROR = 0x1c010002
28 DCERPC_FAULT_NDR = 0x6f7
29 NT_STATUS_NET_WRITE_FAULT = -1073741614
32 sys.path.insert(0, "bin/python")
34 from samba.dcerpc import ClientConnection
36 def find_num_funcs(conn):
37 for i in xrange(MAX_OPNUM):
38 try:
39 conn.request(i, "")
40 except RuntimeError, (num, msg):
41 if num == DCERPC_FAULT_OP_RNG_ERROR:
42 return i
43 raise Exception("More than %d functions" % MAX_OPNUM)
45 class Function:
46 def __init__(self, conn, opnum):
47 self.conn = conn
48 self.opnum = opnum
50 def request(self, data):
51 assert isinstance(data, str)
52 self.conn.request(self.opnum, data)
54 def valid_ndr(self, data):
55 try:
56 self.request(data)
57 except RuntimeError, (num, msg):
58 if num == DCERPC_FAULT_NDR:
59 return False
60 return True
62 def find_base_request(self, start=""):
63 """Find the smallest possible request that we can do that is
64 valid.
66 This generally means sending all zeroes so we get NULL pointers where
67 possible."""
68 # TODO: Don't try with just 0's as there may be switch_is() variables
69 # for which 0 is not a valid value or variables with range() set
70 # See how much input bytes we need
71 for i in range(MAX_BASE_SIZE):
72 data = start + chr(0) * i
73 if self.valid_ndr(data):
74 return data
75 return None
77 def check_decision_byte(self, base_request, i):
78 """Check whether the specified byte is a possible "decision" byte,
79 e.g. a byte that is part of a pointer, array size variable or
80 discriminant.
82 Note that this function only checks if a byte is definitely a decision
83 byte. It may return False in some cases while the byte is actually
84 a decision byte."""
85 data = list(base_request)
86 data[i] = chr(1)
87 return not self.valid_ndr("".join(data))
89 def find_deferrant_data(self, base_request, idx):
90 data = list(base_request)
91 data[idx*4] = chr(1)
92 # Just check that this is a pointer to something non-empty:
93 assert not self.valid_ndr("".join(data))
95 newdata = self.find_base_request("".join(data))
97 if newdata is None:
98 return None
100 assert newdata.startswith(data)
102 return newdata[len(data):]
104 def find_idl(self):
105 base_request = self.find_base_request()
107 if base_request is None:
108 raise Exception("Unable to determine base size for opnum %d" % self.opnum)
110 print "\tBase request is %r" % base_request
112 decision_byte_map = map(lambda x: self.check_decision_byte(base_request, x), range(len(base_request)))
114 print decision_byte_map
116 # find pointers
117 possible_pointers = map(all,
118 [decision_byte_map[i*4:(i+1)*4] for i in range(int(len(base_request)/4))])
119 print possible_pointers
121 pointer_deferrant_bases = map(
122 lambda x: self.find_deferrant_data(base_request, x) if possible_pointers[x] else None, range(len(possible_pointers)))
124 print pointer_deferrant_bases
127 if len(sys.argv) < 3:
128 print "Usage: autoidl <binding> <UUID> [<version>]"
129 sys.exit(1)
131 (binding, uuid) = sys.argv[1:3]
132 if len(sys.argv) == 4:
133 version = sys.argv[3]
134 else:
135 version = None
137 if version is None:
138 for i in range(MAX_IFACE_VERSION):
139 try:
140 conn = ClientConnection(binding, (uuid, i))
141 except RuntimeError, (num, msg):
142 if num == NT_STATUS_NET_WRITE_FAULT:
143 continue
144 raise
145 else:
146 break
147 else:
148 conn = ClientConnection(binding, (uuid, version))
150 print "Figuring out number of connections...",
151 num_funcs = find_num_funcs(conn)
152 print "%d" % num_funcs
154 # Figure out the syntax for each one
155 for i in range(num_funcs):
156 print "Function %d" % i
157 data = Function(conn, i)
158 try:
159 data.find_idl()
160 except Exception, e:
161 print "Error: %r" % e