ndr
import "github.com/TheManticoreProject/Manticore/network/dcerpc/ndr"
Package ndr implements the DCE/RPC Network Data Representation (NDR) transfer syntax, version 2.0 (the “NDR20” little-endian encoding used by Windows RPC), with a declarative, reflection-driven API for marshalling RPC call structures.
The low-level codec (Encoder/Decoder) handles NDR’s stream-relative alignment and primitive encoding; the reflection walker in marshal.go drives it from struct tags so callers can declare an RPC call as a Go struct (see Call, Marshal, Unmarshal).
The codec is transfer-syntax aware: an Encoder/Decoder carries a Syntax (NDR20 or NDR64) that governs the width and alignment of NDR counts and referent ids (4 octets 4-aligned for NDR20, 8 octets 8-aligned for NDR64; [MS-RPCE] section 2.2.5). Scalar primitives keep their natural width in both syntaxes. The declarative walker (Marshal/Unmarshal) and the v5 client still default to NDR20; the NDR64 path is being introduced incrementally and is reachable through NewEncoderForSyntax/ NewDecoderForSyntax. Both syntaxes are little-endian (the standard Windows data representation, DREP 0x10); big-endian is out of scope.
References:
- [C706] DCE 1.1: RPC, Chapter 14 “Transfer Syntax NDR”: https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
- [MS-RPCE] Transfer Syntax NDR types: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/b6090c2b-f44a-47a1-a13b-b82ade0137b2
Index
- func Marshal(v any) ([]byte, error)
- func MarshalAs(v any, syntax Syntax) ([]byte, error)
- func Request(call Call) ([]byte, error)
- func RequestAs(call Call, syntax Syntax) ([]byte, error)
- func Response(stub []byte, out any) error
- func ResponseAs(stub []byte, out any, syntax Syntax) error
- func Unmarshal(data []byte, v any) error
- func UnmarshalAs(data []byte, v any, syntax Syntax) error
- type BOOL
- type BOOLEAN
- type BYTE
- type Call
- type DWORD
- type DWORD64
- type Decoder
- func NewDecoder(data []byte) *Decoder
- func NewDecoderForSyntax(data []byte, s Syntax) *Decoder
- func (d *Decoder) Align(n int)
- func (d *Decoder) Pos() int
- func (d *Decoder) ReadBytes(n int) ([]byte, error)
- func (d *Decoder) ReadUint16() (uint16, error)
- func (d *Decoder) ReadUint32() (uint32, error)
- func (d *Decoder) ReadUint64() (uint64, error)
- func (d *Decoder) ReadUint8() (uint8, error)
- func (d *Decoder) Remaining() int
- func (d *Decoder) Syntax() Syntax
- type Encoder
- func NewEncoder() *Encoder
- func NewEncoderForSyntax(s Syntax) *Encoder
- func (e *Encoder) Align(n int)
- func (e *Encoder) Bytes() []byte
- func (e *Encoder) Len() int
- func (e *Encoder) Syntax() Syntax
- func (e *Encoder) WriteBytes(b []byte)
- func (e *Encoder) WriteUint16(v uint16)
- func (e *Encoder) WriteUint32(v uint32)
- func (e *Encoder) WriteUint64(v uint64)
- func (e *Encoder) WriteUint8(v uint8)
- type Invoker
- type LONG
- type Marshaler
- type STR
- type Syntax
- type WORD
- type WSTR
func Marshal
func Marshal(v any) ([]byte, error)
Marshal encodes v (a struct or pointer to struct) into an NDR octet stream. Each exported field is marshalled in declaration order according to its `ndr:"…"` tag.
Pointers (Go pointer fields, or value fields tagged “unique”/“ptr”) are encoded as a referent id inline with their referent body deferred to the end of the enclosing construction, per the deferral rule in [C706] section 14.3.10: https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
func MarshalAs
func MarshalAs(v any, syntax Syntax) ([]byte, error)
MarshalAs is Marshal under an explicit transfer syntax. NDR64 widens counts and referent ids to 8 octets and aligns them to 8 ([MS-RPCE] section 2.2.5). NDR64 unions and pipes are not yet supported and marshalling a value that contains one returns an error rather than emitting an unverified encoding.
func Request
func Request(call Call) ([]byte, error)
Request marshals an RPC call’s [in] parameters into a stub buffer suitable for network/dcerpc/client.Client.Call(call.Opnum(), stub).
func RequestAs
func RequestAs(call Call, syntax Syntax) ([]byte, error)
RequestAs is Request under an explicit transfer syntax, used by a client that has negotiated NDR64 ([MS-RPCE] section 2.2.5).
func Response
func Response(stub []byte, out any) error
Response unmarshals an RPC response stub into out (a pointer to the [out] parameter structure).
func ResponseAs
func ResponseAs(stub []byte, out any, syntax Syntax) error
ResponseAs is Response under an explicit transfer syntax.
func Unmarshal
func Unmarshal(data []byte, v any) error
Unmarshal decodes an NDR octet stream into v, which must be a non-nil pointer to a struct.
func UnmarshalAs
func UnmarshalAs(data []byte, v any, syntax Syntax) error
UnmarshalAs is Unmarshal under an explicit transfer syntax. See MarshalAs for the NDR64 semantics and the union/pipe limitation.
type BOOL
BOOL is the Windows BOOL data type, a 4-octet integer ([MS-DTYP] 2.2.3). It is distinct from the 1-octet NDR boolean: for the latter, use Go bool (or BOOLEAN).
type BOOL = int32
type BOOLEAN
BOOLEAN is the 1-octet NDR boolean ([C706] section 14.2.4).
type BOOLEAN = bool
type BYTE
Windows-style NDR type aliases, mirroring the names used in [MS-DTYP] so declarations read like the IDL. References:
- [MS-DTYP] 2.2 Common Data Types: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/cca27429-5689-4a16-b2b4-9325d93e4ba2
type BYTE = uint8
type Call
Call is implemented by a request structure to provide its operation number. Its exported fields are the [in] parameters, marshalled in declaration order.
type Call interface {
Opnum() uint16
}
type DWORD
Windows-style NDR type aliases, mirroring the names used in [MS-DTYP] so declarations read like the IDL. References:
- [MS-DTYP] 2.2 Common Data Types: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/cca27429-5689-4a16-b2b4-9325d93e4ba2
type DWORD = uint32
type DWORD64
Windows-style NDR type aliases, mirroring the names used in [MS-DTYP] so declarations read like the IDL. References:
- [MS-DTYP] 2.2 Common Data Types: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/cca27429-5689-4a16-b2b4-9325d93e4ba2
type DWORD64 = uint64
type Decoder
Decoder reads an NDR octet stream produced under the little-endian data representation.
type Decoder struct {
// contains filtered or unexported fields
}
func NewDecoder
func NewDecoder(data []byte) *Decoder
NewDecoder returns a decoder over data for the NDR20 transfer syntax.
func NewDecoderForSyntax
func NewDecoderForSyntax(data []byte, s Syntax) *Decoder
NewDecoderForSyntax returns a decoder over data for the given transfer syntax.
func (*Decoder) Align
func (d *Decoder) Align(n int)
Align advances the read position to the next multiple of n.
func (*Decoder) Pos
func (d *Decoder) Pos() int
Pos returns the current read offset.
func (*Decoder) ReadBytes
func (d *Decoder) ReadBytes(n int) ([]byte, error)
ReadBytes returns the next n octets.
func (*Decoder) ReadUint16
func (d *Decoder) ReadUint16() (uint16, error)
ReadUint16 reads a 2-octet little-endian value, 2-aligned.
func (*Decoder) ReadUint32
func (d *Decoder) ReadUint32() (uint32, error)
ReadUint32 reads a 4-octet little-endian value, 4-aligned.
func (*Decoder) ReadUint64
func (d *Decoder) ReadUint64() (uint64, error)
ReadUint64 reads an 8-octet little-endian value, 8-aligned.
func (*Decoder) ReadUint8
func (d *Decoder) ReadUint8() (uint8, error)
ReadUint8 reads a 1-octet value.
func (*Decoder) Remaining
func (d *Decoder) Remaining() int
Remaining returns the number of unread octets.
func (*Decoder) Syntax
func (d *Decoder) Syntax() Syntax
Syntax reports the transfer syntax the decoder operates under.
type Encoder
Encoder builds an NDR octet stream. Alignment is computed relative to the start of the stream, as required by [C706] section 14.2.2.
type Encoder struct {
// contains filtered or unexported fields
}
func NewEncoder
func NewEncoder() *Encoder
NewEncoder returns an empty encoder for the NDR20 transfer syntax.
func NewEncoderForSyntax
func NewEncoderForSyntax(s Syntax) *Encoder
NewEncoderForSyntax returns an empty encoder for the given transfer syntax.
func (*Encoder) Align
func (e *Encoder) Align(n int)
Align pads the stream with zero octets so the next write begins on a multiple of n.
func (*Encoder) Bytes
func (e *Encoder) Bytes() []byte
Bytes returns the accumulated octet stream.
func (*Encoder) Len
func (e *Encoder) Len() int
Len returns the current stream length, which is the offset alignment is measured against.
func (*Encoder) Syntax
func (e *Encoder) Syntax() Syntax
Syntax reports the transfer syntax the encoder operates under.
func (*Encoder) WriteBytes
func (e *Encoder) WriteBytes(b []byte)
WriteBytes appends raw octets with no alignment.
func (*Encoder) WriteUint16
func (e *Encoder) WriteUint16(v uint16)
WriteUint16 appends a 2-octet little-endian value, 2-aligned.
func (*Encoder) WriteUint32
func (e *Encoder) WriteUint32(v uint32)
WriteUint32 appends a 4-octet little-endian value, 4-aligned.
func (*Encoder) WriteUint64
func (e *Encoder) WriteUint64(v uint64)
WriteUint64 appends an 8-octet little-endian value, 8-aligned.
func (*Encoder) WriteUint8
func (e *Encoder) WriteUint8(v uint8)
WriteUint8 appends a 1-octet value.
type Invoker
Invoker executes an RPC call: it marshals in (an ndr.Call) to a request stub, transmits it on opnum in.Opnum(), and unmarshals the response stub into out. It is the transport-version-neutral surface that interface method stubs depend on, so the stubs stay independent of any concrete client or wire-protocol version. Both the connection-oriented (network/dcerpc/v5/client) and, in future, connectionless (network/dcerpc/v4) DCE/RPC clients satisfy it.
type Invoker interface {
Invoke(in Call, out any) error
}
type LONG
Windows-style NDR type aliases, mirroring the names used in [MS-DTYP] so declarations read like the IDL. References:
- [MS-DTYP] 2.2 Common Data Types: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/cca27429-5689-4a16-b2b4-9325d93e4ba2
type LONG = int32
type Marshaler
Marshaler is implemented by types that encode themselves as NDR, bypassing the reflection walker. It is the escape hatch for unions and any layout the declarative tags do not cover.
type Marshaler interface {
// AlignmentNDR reports the type's NDR alignment (1, 2, 4, or 8).
AlignmentNDR() int
// MarshalNDR appends the type's NDR representation to the encoder.
MarshalNDR(*Encoder) error
// UnmarshalNDR reads the type's NDR representation from the decoder.
UnmarshalNDR(*Decoder) error
}
type STR
STR is a char string ([string]). A field of this type marshals as a conformant+varying ASCII array. See WSTR for pointer semantics.
type STR string
type Syntax
Syntax selects the NDR transfer syntax an Encoder/Decoder operates under. It governs the on-the-wire width and alignment of NDR counts (maximum_count, offset, actual_count) and referent ids, which differ between the two syntaxes ([MS-RPCE] section 2.2.5). NDR20 is the zero value, so an Encoder/Decoder built without an explicit syntax behaves as the classic NDR 2.0 codec.
type Syntax int
const (
// NDR20 is NDR transfer syntax version 2.0: 4-octet counts and referent ids.
NDR20 Syntax = iota
// NDR64 is the NDR64 transfer syntax: 8-octet counts and referent ids.
NDR64
)
func (Syntax) String
func (s Syntax) String() string
String returns the syntax name for diagnostics.
type WORD
Windows-style NDR type aliases, mirroring the names used in [MS-DTYP] so declarations read like the IDL. References:
- [MS-DTYP] 2.2 Common Data Types: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/cca27429-5689-4a16-b2b4-9325d93e4ba2
type WORD = uint16
type WSTR
WSTR is a wchar_t string ([string] wide). A field of this type marshals as a conformant+varying UTF-16LE array without needing an explicit “wstr” tag.
Pointer semantics are explicit and controlled by the tag: a bare WSTR/STR/string field is encoded inline with no referent id (matching a top-level [ref] string parameter), while one tagged “unique” or “ptr” is encoded as a referent id with its body deferred (matching an embedded [unique,string] wchar_t*). Embedded strings in MS-RPC structures are almost always unique; tag them accordingly.
type WSTR string