2 # -*- coding: utf-8 -*-
4 # Unix SMB/CIFS implementation.
5 # Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
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/>.
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
):
40 except RuntimeError, (num
, msg
):
41 if num
== DCERPC_FAULT_OP_RNG_ERROR
:
43 raise Exception("More than %d functions" % MAX_OPNUM
)
46 def __init__(self
, conn
, opnum
):
50 def request(self
, data
):
51 assert isinstance(data
, str)
52 self
.conn
.request(self
.opnum
, data
)
54 def valid_ndr(self
, data
):
57 except RuntimeError, (num
, msg
):
58 if num
== DCERPC_FAULT_NDR
:
62 def find_base_request(self
, start
=""):
63 """Find the smallest possible request that we can do that is
66 This generally means sending all zeroes so we get NULL pointers where
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
):
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
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
85 data
= list(base_request
)
87 return not self
.valid_ndr("".join(data
))
89 def find_deferrant_data(self
, base_request
, idx
):
90 data
= list(base_request
)
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
))
100 assert newdata
.startswith(data
)
102 return newdata
[len(data
):]
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
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>]"
131 (binding
, uuid
) = sys
.argv
[1:3]
132 if len(sys
.argv
) == 4:
133 version
= sys
.argv
[3]
138 for i
in range(MAX_IFACE_VERSION
):
140 conn
= ClientConnection(binding
, (uuid
, i
))
141 except RuntimeError, (num
, msg
):
142 if num
== NT_STATUS_NET_WRITE_FAULT
:
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
)
161 print "Error: %r" % e