mirror of
https://github.com/soypat/lneto.git
synced 2026-09-03 21:39:04 +00:00
V2 Netbird integration - UDP MIMO/SIMO, ICMPv6, DHCPv6 implementations (#106)
* begin adding udp.MuxHandler * add udp MuxHandlerSIMO/MIMO * add tcp rx shutdown * icmpv6 client * icmpv6 Client shared NDP/Echo preparation * icmpv6 client ndp/echo split * icmpv6 client ndp/echo split done * icmpv6 adjustments * add dhcpv6 stubs * dhcpv4 preliminary revision * add dns.NextLabel * dns label name tweaks * dns begin work on TCP client * add dnstcp package * apply gofmt changes * add udp mux tests * clean up, remove StackBig for now * remove dnstcp so as to merged confident parts and we continue dnstcp work elsewhere
This commit is contained in:
+10
-20
@@ -15,7 +15,7 @@ type Client struct {
|
||||
lport uint16
|
||||
msg Message
|
||||
respFlags HeaderFlags
|
||||
state clientState
|
||||
state StateClientQuery
|
||||
enableRecursion bool
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (c *Client) StartResolve(localPort, txid uint16, cfg ResolveConfig) error {
|
||||
if nd > math.MaxUint16 {
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
c.reset(localPort, txid, dnsSendQuery, cfg.EnableRecursion)
|
||||
c.reset(localPort, txid, CQueryPending, cfg.EnableRecursion)
|
||||
c.msg.LimitResourceDecoding(uint16(nd), uint16(nd), 0, 0)
|
||||
c.msg.AddQuestions(cfg.Questions)
|
||||
c.msg.AddAdditionals(cfg.Additional)
|
||||
@@ -46,7 +46,7 @@ func (c *Client) StartResolve(localPort, txid uint16, cfg ResolveConfig) error {
|
||||
func (c *Client) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (int, error) {
|
||||
if c.isClosed() {
|
||||
return 0, net.ErrClosed
|
||||
} else if c.state != dnsSendQuery {
|
||||
} else if c.state != CQueryPending {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (c *Client) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
|
||||
internal.LogAttrs(nil, slog.LevelError, "dns:unexpected-write", slog.Int("got", len(data)), slog.Int("want", int(msglen)))
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
c.state = dnsAwaitResponse
|
||||
c.state = CQueryOutstanding
|
||||
// Unset don't frag since DNS requests go through LOTS of nodes.
|
||||
// if frameOffset >= 28 {
|
||||
// version := carrierData[0] >> 4
|
||||
@@ -78,7 +78,7 @@ func (c *Client) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
|
||||
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
if c.isClosed() {
|
||||
return net.ErrClosed
|
||||
} else if c.state != dnsAwaitResponse {
|
||||
} else if c.state != CQueryOutstanding {
|
||||
return nil
|
||||
}
|
||||
frame := carrierData[frameOffset:]
|
||||
@@ -91,7 +91,7 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
return nil // Not meant for our client.
|
||||
}
|
||||
c.respFlags = flags
|
||||
c.state = dnsDone
|
||||
c.state = CQueryDone
|
||||
msg := &c.msg
|
||||
_, incompleteButOK, err := msg.Decode(frame)
|
||||
if err != nil && !incompleteButOK {
|
||||
@@ -101,7 +101,7 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
}
|
||||
|
||||
func (c *Client) isClosed() bool {
|
||||
return c.state == dnsClosed || c.state == dnsAborted
|
||||
return c.state == CQueryIdle || c.state == CQueryAborted
|
||||
}
|
||||
|
||||
func (c *Client) MessageCopyTo(dst *Message) (done bool, err error) {
|
||||
@@ -117,17 +117,17 @@ func (c *Client) MessageCopyTo(dst *Message) (done bool, err error) {
|
||||
}
|
||||
|
||||
func (c *Client) Answers() []Resource {
|
||||
if c.state != dnsDone {
|
||||
if c.state != CQueryDone {
|
||||
return nil
|
||||
}
|
||||
return c.msg.Answers
|
||||
}
|
||||
|
||||
func (c *Client) Abort() {
|
||||
c.reset(0, 0, 0, false)
|
||||
c.reset(0, 0, CQueryAborted, false)
|
||||
}
|
||||
|
||||
func (c *Client) reset(lport, txid uint16, state clientState, enableRecursion bool) {
|
||||
func (c *Client) reset(lport, txid uint16, state StateClientQuery, enableRecursion bool) {
|
||||
*c = Client{
|
||||
connID: c.connID + 1,
|
||||
lport: lport,
|
||||
@@ -138,13 +138,3 @@ func (c *Client) reset(lport, txid uint16, state clientState, enableRecursion bo
|
||||
}
|
||||
c.msg.Reset()
|
||||
}
|
||||
|
||||
type clientState uint8
|
||||
|
||||
const (
|
||||
dnsClosed clientState = iota
|
||||
dnsSendQuery
|
||||
dnsAwaitResponse
|
||||
dnsDone
|
||||
dnsAborted
|
||||
)
|
||||
|
||||
@@ -238,6 +238,17 @@ const (
|
||||
RCodeRefused RCode = 5 // refused
|
||||
)
|
||||
|
||||
// StateClientQuery is the lifecycle state of a single DNS query.
|
||||
type StateClientQuery uint8
|
||||
|
||||
const (
|
||||
CQueryIdle StateClientQuery = iota // no active query (zero value)
|
||||
CQueryPending // query built, not yet transmitted
|
||||
CQueryOutstanding // transmitted; awaiting response (RFC 7766 §9.3)
|
||||
CQueryDone // response received and decoded
|
||||
CQueryAborted // query abandoned (connection error or caller abort)
|
||||
)
|
||||
|
||||
func b2u8(b bool) uint8 {
|
||||
if b {
|
||||
return 1
|
||||
|
||||
+76
-47
@@ -26,6 +26,11 @@ const (
|
||||
MaxSizeUDP = 512
|
||||
)
|
||||
|
||||
// Message is a convenience type for decoding DNS messages and storing results in a single object.
|
||||
// Message is designed for ease of memory reuse. All internal buffers in a Message are reused in methods:
|
||||
// - [Message.Decode]: Limited in decode size by [Message.LimitResourceDecoding] which must be called beforehand.
|
||||
// - [Message.CopyFrom]
|
||||
// - [Message.AddQuestions]
|
||||
type Message struct {
|
||||
Questions []Question
|
||||
Answers []Resource
|
||||
@@ -598,8 +603,6 @@ func append32(b []byte, v uint32) []byte {
|
||||
}
|
||||
|
||||
func visitAllLabels(msg []byte, off uint16, fn func(b []byte), allowCompression bool) (uint16, error) {
|
||||
// currOff is the current working offset.
|
||||
currOff := off
|
||||
if len(msg) > math.MaxUint16 {
|
||||
return off, errResTooLong
|
||||
}
|
||||
@@ -610,60 +613,86 @@ func visitAllLabels(msg []byte, off uint16, fn func(b []byte), allowCompression
|
||||
// the usage of this name.
|
||||
var newOff = off
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
if currOff >= uint16(len(msg)) {
|
||||
return off, lneto.ErrTruncatedFrame
|
||||
}
|
||||
c := uint16(msg[currOff])
|
||||
currOff++
|
||||
switch c & 0xc0 {
|
||||
case 0x00: // String label (segment).
|
||||
if c == 0x00 {
|
||||
break LOOP // Nominal end of name, always ends with null terminator.
|
||||
}
|
||||
endOff := currOff + c
|
||||
if endOff > uint16(len(msg)) {
|
||||
return off, errCalcLen
|
||||
}
|
||||
|
||||
// Reject names containing dots. See issue golang/go#56246
|
||||
if bytes.IndexByte(msg[currOff:endOff], '.') >= 0 {
|
||||
return off, errInvalidName
|
||||
}
|
||||
|
||||
fn(msg[currOff:endOff])
|
||||
currOff = endOff
|
||||
|
||||
case 0xc0: // Pointer.
|
||||
// https://cs.opensource.google/go/x/net/+/refs/tags/v0.19.0:dns/dnsmessage/message.go;l=2078
|
||||
if !allowCompression {
|
||||
return off, errCompressedSRV
|
||||
}
|
||||
if currOff >= uint16(len(msg)) {
|
||||
return off, errInvalidPtr
|
||||
}
|
||||
c1 := msg[currOff]
|
||||
currOff++
|
||||
start, end, isPtr, err := NextLabel(msg[off:])
|
||||
if err != nil {
|
||||
return off, err
|
||||
} else if start == end {
|
||||
if ptr == 0 {
|
||||
newOff = currOff
|
||||
newOff = off + 1 // advance past the null terminator byte
|
||||
}
|
||||
// Don't follow too many pointers, maybe there's a loop.
|
||||
if ptr++; ptr > 10 {
|
||||
return off, errTooManyPtr
|
||||
break
|
||||
} else if isPtr {
|
||||
if !allowCompression {
|
||||
return newOff, errCompressedSRV
|
||||
}
|
||||
currOff = (c^0xC0)<<8 | uint16(c1)
|
||||
default:
|
||||
// Prefixes 0x80 and 0x40 are reserved.
|
||||
return off, errReserved
|
||||
if ptr == 0 {
|
||||
newOff = off + 2 // next record follows the 2-byte pointer
|
||||
}
|
||||
off = start
|
||||
if int(off) >= len(msg) {
|
||||
return newOff, errInvalidPtr
|
||||
} else if ptr++; ptr > 10 {
|
||||
return newOff, errTooManyPtr
|
||||
}
|
||||
} else {
|
||||
// Is normal label; start/end are relative to msg[off:].
|
||||
fn(msg[off+start : off+end])
|
||||
off += end
|
||||
}
|
||||
}
|
||||
if ptr == 0 {
|
||||
newOff = currOff
|
||||
}
|
||||
return newOff, nil
|
||||
}
|
||||
|
||||
// NextLabel parses the first control byte of data and returns the position and extent of next DNS label.
|
||||
//
|
||||
// For a normal string label (RFC 1035 §3.1), isPointer==false and start/end are
|
||||
// byte indices into data: data[start:end] holds the raw label bytes.
|
||||
// A null terminator (c==0) signals the end of the name: start==end==1, err==nil.
|
||||
//
|
||||
// For a compression pointer (RFC 1035 §4.1.4), isPointer==true:
|
||||
// - start is the absolute target offset within the full DNS message to jump to.
|
||||
// - end==0 (sentinel; not a data range).
|
||||
//
|
||||
// Returns [lneto.ErrTruncatedFrame] if data is too short to read the full label or pointer.
|
||||
// Returns errReserved for the 0x40 and 0x80 reserved prefix classes.
|
||||
func NextLabel(data []byte) (start_RelOrAbs, endRel uint16, isAbsPointer bool, err error) {
|
||||
// Default invalid values
|
||||
start_RelOrAbs, endRel = 0, 0
|
||||
if len(data) == 0 {
|
||||
return start_RelOrAbs, endRel, false, lneto.ErrTruncatedFrame
|
||||
}
|
||||
c := uint16(data[0])
|
||||
switch c & 0xc0 {
|
||||
case 0:
|
||||
start_RelOrAbs = 1
|
||||
// String label segment.
|
||||
if c == 0 {
|
||||
return start_RelOrAbs, start_RelOrAbs, false, nil // Null terminator. String ended.
|
||||
}
|
||||
endRel = start_RelOrAbs + c
|
||||
if int(endRel) > len(data) {
|
||||
return start_RelOrAbs, endRel, false, lneto.ErrTruncatedFrame
|
||||
}
|
||||
// Reject names containing dots. See issue golang/go#56246
|
||||
if bytes.IndexByte(data[start_RelOrAbs:endRel], '.') >= 0 {
|
||||
return start_RelOrAbs, endRel, false, errInvalidName
|
||||
}
|
||||
// Correct label!
|
||||
case 0xc0:
|
||||
// Pointer. Start is absolute index in DNS message.
|
||||
isAbsPointer = true
|
||||
if len(data) < 2 {
|
||||
return start_RelOrAbs, endRel, isAbsPointer, lneto.ErrTruncatedFrame // Need more data to fully read pointer.
|
||||
}
|
||||
c1 := uint16(data[1])
|
||||
start_RelOrAbs = (c^0xC0)<<8 | c1
|
||||
default:
|
||||
err = errReserved
|
||||
}
|
||||
return start_RelOrAbs, endRel, isAbsPointer, err
|
||||
}
|
||||
|
||||
func (dst *Message) CopyFrom(m Message) {
|
||||
internal.SliceReuse(&dst.Questions, len(m.Questions))
|
||||
internal.SliceReuse(&dst.Answers, len(m.Answers))
|
||||
|
||||
Reference in New Issue
Block a user