forked from jcmturner/gokrb5
/
ndr.go
246 lines (224 loc) · 7.68 KB
/
ndr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Package ndr is a partial implementation of NDR encoding: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
package ndr
import (
"bytes"
"encoding/binary"
"fmt"
"math"
)
/*
Serialization Version 1
https://msdn.microsoft.com/en-us/library/cc243563.aspx
Common Header - https://msdn.microsoft.com/en-us/library/cc243890.aspx
8 bytes in total:
- First byte - Version: Must equal 1
- Second byte - 1st 4 bits: Endianess (0=Big; 1=Little); 2nd 4 bits: Character Encoding (0=ASCII; 1=EBCDIC)
- 3rd - Floating point representation
- 4th - Common Header Length: Must equal 8
- 5th - 8th - Filler: MUST be set to 0xcccccccc on marshaling, and SHOULD be ignored during unmarshaling.
Private Header - https://msdn.microsoft.com/en-us/library/cc243919.aspx
8 bytes in total:
- First 4 bytes - Indicates the length of a serialized top-level type in the octet stream. It MUST include the padding length and exclude the header itself.
- Second 4 bytes - Filler: MUST be set to 0 (zero) during marshaling, and SHOULD be ignored during unmarshaling.
*/
const (
protocolVersion = 1
commonHeaderBytes = 8
privateHeaderBytes = 8
bigEndian = 0
littleEndian = 1
ascii = 0
ebcdic = 1
ieee = 0
vax = 1
cray = 2
ibm = 3
)
// CommonHeader implements the NDR common header: https://msdn.microsoft.com/en-us/library/cc243889.aspx
type CommonHeader struct {
Version uint8
Endianness binary.ByteOrder
CharacterEncoding uint8
//FloatRepresentation uint8
HeaderLength uint16
Filler []byte
}
// PrivateHeader implements the NDR private header: https://msdn.microsoft.com/en-us/library/cc243919.aspx
type PrivateHeader struct {
ObjectBufferLength uint32
Filler []byte
}
// ReadHeaders processes the bytes to return the NDR Common and Private headers.
func ReadHeaders(b *[]byte) (CommonHeader, PrivateHeader, int, error) {
ch, p, err := GetCommonHeader(b)
if err != nil {
return CommonHeader{}, PrivateHeader{}, 0, err
}
ph, err := GetPrivateHeader(b, &p, &ch.Endianness)
if err != nil {
return CommonHeader{}, PrivateHeader{}, 0, err
}
return ch, ph, p, err
}
// GetCommonHeader processes the bytes to return the NDR Common header.
func GetCommonHeader(b *[]byte) (CommonHeader, int, error) {
//The first 8 bytes comprise the Common RPC Header for type marshalling.
if len(*b) < commonHeaderBytes {
return CommonHeader{}, 0, Malformed{EText: "Not enough bytes."}
}
if (*b)[0] != protocolVersion {
return CommonHeader{}, 0, Malformed{EText: fmt.Sprintf("Stream does not indicate a RPC Type serialization of version %v", protocolVersion)}
}
endian := int((*b)[1] >> 4 & 0xF)
if endian != 0 && endian != 1 {
return CommonHeader{}, 1, Malformed{EText: "Common header does not indicate a valid endianness"}
}
charEncoding := uint8((*b)[1] & 0xF)
if charEncoding != 0 && charEncoding != 1 {
return CommonHeader{}, 1, Malformed{EText: "Common header does not indicate a valid charater encoding"}
}
var bo binary.ByteOrder
switch endian {
case littleEndian:
bo = binary.LittleEndian
case bigEndian:
bo = binary.BigEndian
}
l := bo.Uint16((*b)[2:4])
if l != commonHeaderBytes {
return CommonHeader{}, 4, Malformed{EText: fmt.Sprintf("Common header does not indicate a valid length: %v instead of %v", uint8((*b)[3]), commonHeaderBytes)}
}
return CommonHeader{
Version: uint8((*b)[0]),
Endianness: bo,
CharacterEncoding: charEncoding,
//FloatRepresentation: uint8(b[2]),
HeaderLength: l,
Filler: (*b)[4:8],
}, 8, nil
}
// GetPrivateHeader processes the bytes to return the NDR Private header.
func GetPrivateHeader(b *[]byte, p *int, bo *binary.ByteOrder) (PrivateHeader, error) {
//The next 8 bytes comprise the RPC type marshalling private header for constructed types.
if len(*b) < (privateHeaderBytes) {
return PrivateHeader{}, Malformed{EText: "Not enough bytes."}
}
var l uint32
buf := bytes.NewBuffer((*b)[*p : *p+4])
binary.Read(buf, *bo, &l)
if l%8 != 0 {
return PrivateHeader{}, Malformed{EText: "Object buffer length not a multiple of 8"}
}
*p += 8
return PrivateHeader{
ObjectBufferLength: l,
Filler: (*b)[4:8],
}, nil
}
// ReadUint8 reads bytes representing a thirty two bit integer.
func ReadUint8(b *[]byte, p *int) (i uint8) {
if len((*b)[*p:]) < 1 {
return
}
ensureAlignment(p, 1)
i = uint8((*b)[*p])
*p++
return
}
// ReadUint16 reads bytes representing a thirty two bit integer.
func ReadUint16(b *[]byte, p *int, e *binary.ByteOrder) (i uint16) {
if len((*b)[*p:]) < 2 {
return
}
ensureAlignment(p, 2)
i = (*e).Uint16((*b)[*p : *p+2])
*p += 2
return
}
// ReadUint32 reads bytes representing a thirty two bit integer.
func ReadUint32(b *[]byte, p *int, e *binary.ByteOrder) (i uint32) {
if len((*b)[*p:]) < 4 {
return
}
ensureAlignment(p, 4)
i = (*e).Uint32((*b)[*p : *p+4])
*p += 4
return
}
// ReadUint64 reads bytes representing a thirty two bit integer.
func ReadUint64(b *[]byte, p *int, e *binary.ByteOrder) (i uint64) {
if len((*b)[*p:]) < 8 {
return
}
ensureAlignment(p, 8)
i = (*e).Uint64((*b)[*p : *p+8])
*p += 8
return
}
// ReadBytes reads the number of bytes specified.
func ReadBytes(b *[]byte, p *int, s int, e *binary.ByteOrder) (r []byte) {
if len((*b)[*p:]) < s {
return
}
buf := bytes.NewBuffer((*b)[*p : *p+s])
r = make([]byte, s)
binary.Read(buf, *e, &r)
*p += s
return r
}
// ReadBool reads bytes representing a boolean.
func ReadBool(b *[]byte, p *int) bool {
if len((*b)[*p:]) < 1 {
return false
}
if ReadUint8(b, p) != 0 {
return true
}
return false
}
// ReadIEEEfloat32 reads bytes representing a IEEE formatted 32 bit float.
func ReadIEEEfloat32(b *[]byte, p *int, e *binary.ByteOrder) float32 {
ensureAlignment(p, 4)
return math.Float32frombits(ReadUint32(b, p, e))
}
// ReadIEEEfloat64 reads bytes representing a IEEE formatted 64 bit float.
func ReadIEEEfloat64(b *[]byte, p *int, e *binary.ByteOrder) float64 {
ensureAlignment(p, 8)
return math.Float64frombits(ReadUint64(b, p, e))
}
// ReadConformantVaryingString reads a Conformant and Varying String from the bytes slice.
// A conformant and varying string is a string in which the maximum number of elements is not known beforehand and therefore is included in the representation of the string.
// NDR represents a conformant and varying string as an ordered sequence of representations of the string elements, preceded by three unsigned long integers.
// The first integer gives the maximum number of elements in the string, including the terminator.
// The second integer gives the offset from the first index of the string to the first index of the actual subset being passed.
// The third integer gives the actual number of elements being passed, including the terminator.
func ReadConformantVaryingString(b *[]byte, p *int, e *binary.ByteOrder) (string, error) {
m := ReadUint32(b, p, e) // Max element count
o := ReadUint32(b, p, e) // Offset
a := ReadUint32(b, p, e) // Actual count
if a > (m-o) || o > m {
return "", Malformed{EText: fmt.Sprintf("Not enough bytes. Max: %d, Offset: %d, Actual: %d", m, o, a)}
}
//Unicode string so each element is 2 bytes
//move position based on the offset
if o > 0 {
*p += int(o * 2)
}
s := make([]rune, a, a)
for i := 0; i < len(s); i++ {
s[i] = rune(ReadUint16(b, p, e))
}
ensureAlignment(p, 4)
return string(s), nil
}
// ReadUniDimensionalConformantArrayHeader reads a UniDimensionalConformantArrayHeader from the bytes slice.
func ReadUniDimensionalConformantArrayHeader(b *[]byte, p *int, e *binary.ByteOrder) int {
return int(ReadUint32(b, p, e))
}
func ensureAlignment(p *int, byteSize int) {
if byteSize > 0 {
if s := *p % byteSize; s != 0 {
*p += byteSize - s
}
}
}