mirror of
https://github.com/soypat/lneto.git
synced 2026-07-26 10:38:47 +00:00
a2970b923d
* add ipv6 to xnet.StackAsync * dns improvements * improve DNS workings of StackAsync * add tentative ICMPv6 * work on prefixes and fix some small bugs, plan UDP/TCP6 * fix bugs in StackAsync and ipv4.Prefix.Contains * update arpsubtable * completely remove legacy internet.StackIP for StackIPv4/v6 * ipv4/ipv6 tcp/udp * add TCP6/UDP6 dialing APIs * add xnet.Stack6 interface * more ipv6 integration into StackAsync; various tweaks to lneto and documentation+TODOs * add stack6 tests * replace netip.Prefix with ipv4.Prefix where it makes sense
150 lines
3.5 KiB
Go
150 lines
3.5 KiB
Go
package dns
|
|
|
|
import (
|
|
"log/slog"
|
|
"math"
|
|
"net"
|
|
"net/netip"
|
|
|
|
"github.com/soypat/lneto"
|
|
"github.com/soypat/lneto/internal"
|
|
)
|
|
|
|
type Client struct {
|
|
connID uint64
|
|
txid uint16
|
|
lport uint16
|
|
msg Message
|
|
respFlags HeaderFlags
|
|
state StateClientQuery
|
|
enableRecursion bool
|
|
}
|
|
|
|
type ResolveConfig struct {
|
|
Questions []Question
|
|
Additional []Resource
|
|
EnableRecursion bool
|
|
}
|
|
|
|
func (sudp *Client) Protocol() uint64 { return uint64(lneto.IPProtoUDP) }
|
|
|
|
func (sudp *Client) LocalPort() uint16 { return sudp.lport }
|
|
|
|
func (sudp *Client) ConnectionID() *uint64 { return &sudp.connID }
|
|
|
|
func (c *Client) StartResolve(localPort, txid uint16, cfg ResolveConfig) error {
|
|
nd := len(cfg.Questions)
|
|
if nd > math.MaxUint16 {
|
|
return lneto.ErrInvalidConfig
|
|
}
|
|
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)
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (int, error) {
|
|
if c.isClosed() {
|
|
return 0, net.ErrClosed
|
|
} else if c.state != CQueryPending {
|
|
return 0, nil
|
|
}
|
|
|
|
msg := &c.msg
|
|
frame := carrierData[offsetToFrame:]
|
|
msglen := msg.Len()
|
|
if msglen > uint16(len(frame)) {
|
|
return 0, errCalcLen
|
|
}
|
|
|
|
data, err := msg.AppendTo(frame[:0], c.txid, NewClientHeaderFlags(OpCodeQuery, c.enableRecursion))
|
|
if err != nil {
|
|
return 0, err
|
|
} else if len(data) > int(msglen) {
|
|
internal.LogAttrs(nil, slog.LevelError, "dns:unexpected-write", slog.Int("got", len(data)), slog.Int("want", int(msglen)))
|
|
return 0, lneto.ErrBug
|
|
}
|
|
c.state = CQueryOutstanding
|
|
// Unset don't frag since DNS requests go through LOTS of nodes.
|
|
// if frameOffset >= 28 {
|
|
// version := carrierData[0] >> 4
|
|
// if version == 4 {
|
|
// carrierData[6], carrierData[7] = 0, 0 // unset IP Flags.
|
|
// }
|
|
// }
|
|
return len(data), nil
|
|
}
|
|
|
|
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
|
if c.isClosed() {
|
|
return net.ErrClosed
|
|
} else if c.state != CQueryOutstanding {
|
|
return nil
|
|
}
|
|
frame := carrierData[frameOffset:]
|
|
f, err := NewFrame(frame)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
flags := f.Flags()
|
|
if f.TxID() != c.txid || !flags.IsResponse() {
|
|
return nil // Not meant for our client.
|
|
}
|
|
c.respFlags = flags
|
|
c.state = CQueryDone
|
|
msg := &c.msg
|
|
_, incompleteButOK, err := msg.Decode(frame)
|
|
if err != nil && !incompleteButOK {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) isClosed() bool {
|
|
return c.state == CQueryIdle || c.state == CQueryAborted
|
|
}
|
|
|
|
func (c *Client) ResponseCopyTo(dst *Message) (done bool, err error) {
|
|
if !c.respFlags.IsResponse() {
|
|
return false, nil
|
|
}
|
|
dst.CopyFrom(c.msg)
|
|
rcode := c.respFlags.ResponseCode()
|
|
if rcode != 0 {
|
|
return true, rcode
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (c *Client) ResponseAnswerLookup(dst []netip.Addr, host string) (uint16, error) {
|
|
if !c.respFlags.IsResponse() {
|
|
return 0, nil
|
|
}
|
|
rcode := c.respFlags.ResponseCode()
|
|
if rcode != 0 {
|
|
return 0, rcode
|
|
}
|
|
return c.msg.WriteAnswers(dst, host)
|
|
}
|
|
|
|
func (c *Client) ResponseFlags() (HeaderFlags, bool) {
|
|
return c.respFlags, c.respFlags.IsResponse()
|
|
}
|
|
|
|
func (c *Client) Abort() {
|
|
c.reset(0, 0, CQueryAborted, false)
|
|
}
|
|
|
|
func (c *Client) reset(lport, txid uint16, state StateClientQuery, enableRecursion bool) {
|
|
*c = Client{
|
|
connID: c.connID + 1,
|
|
lport: lport,
|
|
txid: txid,
|
|
msg: c.msg,
|
|
state: state,
|
|
enableRecursion: enableRecursion,
|
|
}
|
|
c.msg.Reset()
|
|
}
|