nbdgm
import "github.com/TheManticoreProject/Manticore/network/netbios/nbdgm"
Package nbdgm implements the NetBIOS Datagram Service (RFC 1002 4.4, UDP port 138): the datagram header codec, the DIRECT_UNIQUE / DIRECT_GROUP / BROADCAST datagrams, the DATAGRAM ERROR packet, the DATAGRAM QUERY REQUEST and POSITIVE / NEGATIVE DATAGRAM QUERY RESPONSE packets, and FIRST/MORE fragmentation and reassembly of the USER_DATA payload.
This is the connectionless transport that the Browser protocol and mailslots ride on top of; only the datagram transport layer lives here. The NetBIOS name encoding for the SOURCE_NAME / DESTINATION_NAME fields is reused from the sibling name-service package (network/netbios/nbns).
Index
- Constants
- type Datagram
- type DatagramHandler
- type Listener
- type Name
- type Reassembler
- type Sender
- func (s *Sender) Fragment(msgType uint8, destName Name, userData []byte) ([][]byte, error)
- func (s *Sender) SendBroadcast(destName Name, userData []byte) error
- func (s *Sender) SendDirectGroup(dest string, destName Name, userData []byte) error
- func (s *Sender) SendDirectUnique(dest string, destName Name, userData []byte) error
Constants
Datagram MSG_TYPE values (RFC 1002 4.4.1). The message type is the first byte of every datagram and selects both the semantics and the trailer layout.
const (
MsgTypeDirectUnique uint8 = 0x10 // DIRECT_UNIQUE DATAGRAM
MsgTypeDirectGroup uint8 = 0x11 // DIRECT_GROUP DATAGRAM
MsgTypeBroadcast uint8 = 0x12 // BROADCAST DATAGRAM
MsgTypeError uint8 = 0x13 // DATAGRAM ERROR
MsgTypeQueryRequest uint8 = 0x14 // DATAGRAM QUERY REQUEST
MsgTypePositiveQueryResponse uint8 = 0x15 // DATAGRAM POSITIVE QUERY RESPONSE
MsgTypeNegativeQueryResponse uint8 = 0x16 // DATAGRAM NEGATIVE QUERY RESPONSE
)
FLAGS field bit layout (RFC 1002 4.4.1). The one-byte FLAGS field is drawn in the RFC as
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | SNT | F | M |
+---+---+---+---+---+---+---+---+
where bit 0 is the most-significant bit. In terms of the numeric byte value M (MORE) is therefore the least-significant bit (0x01), F (FIRST) is 0x02, and SNT (source end-node type) occupies bits 2-3 (mask 0x0C).
const (
FlagMore uint8 = 0x01 // M: set when more fragments of this datagram follow
FlagFirst uint8 = 0x02 // F: set on the first (or only) fragment of a datagram
)
SNT source end-node type values carried in the FLAGS field (RFC 1002 4.4.1).
const (
NodeTypeB uint8 = 0x00 // B node (broadcast)
NodeTypeP uint8 = 0x01 // P node (point-to-point)
NodeTypeM uint8 = 0x02 // M node (mixed)
NodeTypeNBDD uint8 = 0x03 // NetBIOS Datagram Distribution server
)
DATAGRAM ERROR ERROR_CODE values (RFC 1002 4.4.2).
const (
ErrorDestinationNameNotPresent uint8 = 0x82 // DESTINATION NAME NOT PRESENT
ErrorInvalidSourceNameFormat uint8 = 0x83 // INVALID SOURCE NAME FORMAT
ErrorInvalidDestNameFormat uint8 = 0x84 // INVALID DESTINATION NAME FORMAT
)
Listener socket timeouts and buffer size. MaxUDPSize is the RFC 1001 minimum reassembly buffer, the largest single UDP datagram a conforming datagram service must accept; larger logical datagrams arrive fragmented.
const (
UDPReadTimeout = 5 * time.Second
MaxUDPSize = 576
)
Reassembly bounds. The datagram service fragments a datagram whose names plus USER_DATA do not fit in a single UDP packet (RFC 1002 4.4.1); a receiver reassembles the pieces keyed by (source, DGM_ID). These constants bound the memory and lifetime of the reassembly state so a hostile or lossy sender cannot exhaust memory with partial datagrams.
const (
// MaxUserDataSize caps the reassembled USER_DATA of a single datagram. A
// DGM_ID whose fragments would exceed it is dropped.
MaxUserDataSize = 65535
// DefaultReassemblyTimeout is how long an incomplete datagram is retained
// before its fragments are discarded.
DefaultReassemblyTimeout = 30 * time.Second
// DefaultMaxPending bounds the number of distinct in-progress datagrams held
// at once. Once reached, a new datagram is only admitted after expired
// entries are evicted.
DefaultMaxPending = 1024
)
DefaultDatagramPort is the well-known UDP port of the NetBIOS Datagram Service (RFC 1002 4.4 / RFC 1001 5.4). All datagrams are exchanged on it.
const DefaultDatagramPort = 138
DefaultMaxFragmentSize is the maximum size of a single outbound UDP datagram (header plus trailer) when the sender fragments an oversized USER_DATA. It is the RFC 1001 minimum reassembly buffer size (576), which every conforming receiver must accept.
const DefaultMaxFragmentSize = 576
LimitedBroadcastAddr is the IPv4 limited-broadcast address a BROADCAST datagram is sent to (RFC 1002 5.4). With DefaultDatagramPort it forms the destination 255.255.255.255:138.
const LimitedBroadcastAddr = "255.255.255.255"
type Datagram
Datagram is a decoded NetBIOS datagram. Which trailer fields are meaningful depends on MsgType: the DIRECT/BROADCAST types carry DgmLength, PacketOffset, SourceName, DestinationName and UserData; DATAGRAM ERROR carries ErrorCode; the query request/response types carry only DestinationName.
type Datagram struct {
MsgType uint8
Flags uint8
DgmID uint16
SourceIP net.IP
SourcePort uint16
// DIRECT_UNIQUE / DIRECT_GROUP / BROADCAST fields.
DgmLength uint16 // length of the trailer (names + user data) in this packet
PacketOffset uint16 // offset of this fragment's USER_DATA in the reassembled datagram
SourceName Name
DestinationName Name
UserData []byte
// DATAGRAM ERROR field.
ErrorCode uint8
}
func (*Datagram) HasMore
func (d *Datagram) HasMore() bool
HasMore reports whether the MORE (M) flag is set, i.e. further fragments of this datagram follow.
func (*Datagram) IsFirst
func (d *Datagram) IsFirst() bool
IsFirst reports whether the FIRST (F) flag is set.
func (*Datagram) Marshal
func (d *Datagram) Marshal() ([]byte, error)
Marshal serialises the datagram to its RFC 1002 4.4 wire form. For the DIRECT/BROADCAST types the DGM_LENGTH field is computed as the length of the emitted trailer (names, when the FIRST flag is set, plus USER_DATA), matching how a receiver validates it; the caller-supplied DgmLength is ignored.
func (*Datagram) NodeType
func (d *Datagram) NodeType() uint8
NodeType returns the SNT source end-node type encoded in the FLAGS field.
func (*Datagram) SetNodeType
func (d *Datagram) SetNodeType(nt uint8)
SetNodeType sets the SNT source end-node type bits of the FLAGS field.
func (*Datagram) Unmarshal
func (d *Datagram) Unmarshal(data []byte) (int, error)
Unmarshal parses a datagram from data and returns the number of bytes consumed. It never panics on short or malformed input, reporting an error instead. For a DIRECT/BROADCAST datagram only the DGM_LENGTH trailer bytes are consumed, so trailing padding in an oversized buffer is ignored.
type DatagramHandler
DatagramHandler is invoked for each fully received datagram. For the DIRECT/BROADCAST types the datagram is delivered only once fully reassembled, with the complete USER_DATA and decoded SOURCE_NAME/DESTINATION_NAME; the query request/response and error types are delivered as they arrive. source is the sender’s UDP address.
type DatagramHandler func(source *net.UDPAddr, d *Datagram)
type Listener
Listener receives NetBIOS datagrams on a UDP socket (port 138 by default), reassembles fragmented DIRECT/BROADCAST datagrams, and hands each complete datagram to a callback. It mirrors the nbns UDP server: Start spawns a serve goroutine and Stop shuts it down. A Listener is the inbound counterpart to Sender.
type Listener struct {
// contains filtered or unexported fields
}
func NewListener
func NewListener(addr string, handler DatagramHandler) (*Listener, error)
NewListener creates a datagram Listener bound to addr (e.g. “:138” or “127.0.0.1:0” for an ephemeral port) that delivers received datagrams to handler.
func (*Listener) LocalAddr
func (l *Listener) LocalAddr() *net.UDPAddr
LocalAddr returns the address the listener is bound to, useful when it was started on an ephemeral port ("…:0").
func (*Listener) Start
func (l *Listener) Start() error
Start begins listening for datagrams.
func (*Listener) Stop
func (l *Listener) Stop()
Stop gracefully shuts down the listener.
type Name
Name is a NetBIOS name as carried in a datagram SOURCE_NAME or DESTINATION_NAME field: the up-to-15-character base name, the one-byte service suffix (the 16th byte of the padded NetBIOS name), and an optional scope. On the wire it is the RFC 1002 4.2.1.2 second-level encoding.
type Name struct {
Name string // base name, up to 15 characters (may be the "*" wildcard)
Suffix byte // service suffix (16th byte of the padded NetBIOS name)
Scope string // optional NetBIOS scope ID, dot-separated ("" for the default)
}
type Reassembler
Reassembler reassembles fragmented NetBIOS datagrams. It is safe for concurrent use; a single Reassembler is shared by a Listener across the goroutines that handle inbound packets.
type Reassembler struct {
// contains filtered or unexported fields
}
func NewReassembler
func NewReassembler() *Reassembler
NewReassembler returns a Reassembler using the default timeout.
func (*Reassembler) Add
func (r *Reassembler) Add(source string, d *Datagram) (assembled *Datagram, done bool, err error)
Add feeds one received DIRECT/BROADCAST datagram fragment into the reassembler. source identifies the sender (typically its “ip:port”). When the datagram is complete it returns the fully reassembled Datagram and done=true; otherwise it returns done=false and retains the fragment. A datagram that is both FIRST and not MORE is complete in a single packet and is returned directly without any retained state.
func (*Reassembler) SetTimeout
func (r *Reassembler) SetTimeout(d time.Duration)
SetTimeout overrides the retention time for incomplete datagrams. A non-positive value restores the default.
type Sender
Sender emits NetBIOS datagrams. It carries the source identity stamped into every datagram’s header (SOURCE_NAME, SOURCE_IP, SOURCE_PORT and the SNT node type) and fragments a USER_DATA payload too large for a single UDP datagram. A Sender is a lightweight value: each send opens its own short-lived UDP socket, so one Sender is safe to reuse across sequential sends.
type Sender struct {
// SourceName is the sending application's NetBIOS name, carried as the
// SOURCE_NAME of DIRECT/BROADCAST datagrams.
SourceName Name
// SourceIP and SourcePort populate the datagram header's SOURCE_IP and
// SOURCE_PORT. SourceIP must be an IPv4 address. SourcePort is typically
// DefaultDatagramPort.
SourceIP net.IP
SourcePort uint16
// NodeType is the SNT source end-node type stamped into the FLAGS field
// (NodeTypeB by default).
NodeType uint8
// MaxFragmentSize bounds the size of each emitted UDP datagram. A
// non-positive value uses DefaultMaxFragmentSize.
MaxFragmentSize int
}
func (*Sender) Fragment
func (s *Sender) Fragment(msgType uint8, destName Name, userData []byte) ([][]byte, error)
Fragment builds the wire-form UDP payloads for a DIRECT/BROADCAST datagram, splitting userData across as many packets as the per-packet size limit requires. The first fragment carries SOURCE_NAME and DESTINATION_NAME and the FIRST flag; every fragment but the last sets the MORE flag; PACKET_OFFSET on each fragment is the byte offset of its USER_DATA within the whole payload. All fragments share one freshly generated DGM_ID.
func (*Sender) SendBroadcast
func (s *Sender) SendBroadcast(destName Name, userData []byte) error
SendBroadcast sends a BROADCAST datagram carrying userData to the IPv4 limited-broadcast address on port 138. userData is fragmented as needed.
func (*Sender) SendDirectGroup
func (s *Sender) SendDirectGroup(dest string, destName Name, userData []byte) error
SendDirectGroup sends a DIRECT_GROUP datagram carrying userData to dest (see SendDirectUnique for the dest and fragmentation semantics).
func (*Sender) SendDirectUnique
func (s *Sender) SendDirectUnique(dest string, destName Name, userData []byte) error
SendDirectUnique sends a DIRECT_UNIQUE datagram carrying userData to dest, which may be a bare host or host:port (the port defaults to 138). userData is fragmented across as many UDP datagrams as needed.