server
import "github.com/TheManticoreProject/Manticore/network/llmnr/server"
Index
- Constants
- func HandlerDescribePacket(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *message.Message) bool
- func HandlerDescribePacketJson(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *message.Message) bool
- func InterfaceAddresses(ifaceName string) (ipv4 net.IP, ipv6 net.IP, err error)
- func MessageToJson(remoteAddr net.Addr, msg *message.Message) ([]byte, error)
- type Handler
- type HandlerFunc
- type MatchMode
- type ResponseWriter
- type Server
- func NewIPv4Server() (*Server, error)
- func NewIPv4ServerWithHandlers(handlers []Handler) (*Server, error)
- func NewIPv6Server() (*Server, error)
- func NewIPv6ServerWithHandlers(handlers []Handler) (*Server, error)
- func NewServer(network string, handlers []Handler) (*Server, error)
- func (s *Server) Close() error
- func (s *Server) IsIPv4() bool
- func (s *Server) IsIPv6() bool
- func (s *Server) ListenAndServe() error
- func (s *Server) ListenAndServeTCP() error
- func (s *Server) Listening() bool
- func (s *Server) ListeningTCP() bool
- func (s *Server) RegisterHandler(handler Handler)
- func (s *Server) Serve() error
- func (s *Server) SetDebug(debug bool)
- func (s *Server) TCPAddr() net.Addr
- type SpoofConfig
- type SpoofHandler
- func NewSpoofHandler(cfg SpoofConfig) (*SpoofHandler, error)
- func (h *SpoofHandler) BuildResponse(msg *message.Message) (*message.Message, bool)
- func (h *SpoofHandler) Matches(name string) bool
- func (h *SpoofHandler) Run(server *Server, remoteAddr net.Addr, writer ResponseWriter, msg *message.Message) bool
Constants
DefaultSpoofTTL is the TTL (in seconds) placed in spoofed answer records when no explicit TTL is configured. RFC 4795 §2.8 RECOMMENDS a default of 30 seconds, matching what Windows LLMNR responders emit.
const DefaultSpoofTTL = 30
func HandlerDescribePacket
func HandlerDescribePacket(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *message.Message) bool
HandlerDescribePacket logs detailed information about an LLMNR packet received by the
Parameters: - server: A pointer to the Server that received the packet. - remoteAddr: The address of the remote client that sent the packet. - writer: A ResponseWriter to send responses back to the client. - message: The LLMNR message received from the client.
The function logs the following details about the received LLMNR packet: - The remote address of the client that sent the packet. - The number of questions in the packet, and for each question:
- The class of the question.
- The type of the question.
- The name in the question.
- The number of answers in the packet, and for each answer:
- The class of the answer.
- The type of the answer.
- The name in the answer.
- The TTL (Time to Live) of the answer.
- The RDLENGTH (length of the RDATA field) of the answer.
- The RDATA (resource data) of the answer.
- The number of authority records in the packet, and for each authority record:
- The class of the authority record.
- The type of the authority record.
- The name in the authority record.
- The TTL (Time to Live) of the authority record.
- The RDLENGTH (length of the RDATA field) of the authority record.
- The RDATA (resource data) of the authority record.
The function uses a logger to output the information in a structured format, with indentation to represent the hierarchy of the packet’s contents. The logger is locked during the function execution to ensure thread-safe logging.
func HandlerDescribePacketJson
func HandlerDescribePacketJson(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *message.Message) bool
HandlerDescribePacketJson logs the details of the LLMNR message in JSON format.
Parameters: - server: A pointer to the Server that received the packet. - remoteAddr: The address of the remote client that sent the packet. - writer: A ResponseWriter to send responses back to the client. - message: The LLMNR message received from the client.
The function marshals the decoded message into an indented JSON document (see MessageToJson) and logs it via the same logger used by HandlerDescribePacket. Like its sibling it always returns false so that packet processing continues with the next handler.
func InterfaceAddresses
func InterfaceAddresses(ifaceName string) (ipv4 net.IP, ipv6 net.IP, err error)
InterfaceAddresses returns the first usable IPv4 and IPv6 unicast addresses configured on the named interface, which a poisoner uses to auto-detect the address it should hand out so a coerced victim connects back to this host.
When ifaceName is empty the addresses are taken from the first interface that is up, is not loopback, and carries a global-unicast address of the corresponding family. Either return value may be nil if the interface owns no address of that family; an error is returned only when no suitable interface or address could be found at all.
func MessageToJson
func MessageToJson(remoteAddr net.Addr, msg *message.Message) ([]byte, error)
MessageToJson builds an indented JSON representation of a decoded LLMNR message. It renders the header (including the individually decoded flag bits and the section counts), the questions (name/type/class), and the answer, authority and additional resource records (name/type/class/ttl plus typed RDATA where the record type is recognized). The raw RDATA bytes are always included as a hex string so that unmodeled record types stay fully represented. The build is robust: a record whose typed decode errors simply falls back to its raw bytes and never panics.
Parameters: - remoteAddr: The address of the remote peer that sent the message. - msg: The decoded LLMNR message to render.
Returns: - The indented JSON document. - An error if JSON marshalling fails.
type Handler
Handler defines the interface for processing LLMNR queries
type Handler interface {
Run(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *message.Message) bool
}
type HandlerFunc
HandlerFunc is an adapter to allow regular functions to serve as LLMNR handlers
type HandlerFunc func(*Server, net.Addr, ResponseWriter, *message.Message) bool
func (HandlerFunc) Run
func (f HandlerFunc) Run(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *message.Message) bool
Run executes the handler function to process an LLMNR query.
Parameters: - server: The Server instance that received the query. - remoteAddr: The address of the client that sent the query. - writer: The ResponseWriter used to send responses back to the client. - message: The Message received from the client.
The function calls the handler function with the provided ResponseWriter and Message. It allows regular functions to be used as LLMNR handlers by adapting them to the Handler interface.
Example usage:
handlerFunc := func(w ResponseWriter, r *message.Message) {
// Process the LLMNR query and write a response
}
handler := HandlerFunc(handlerFunc)
RegisterHandler(handler)
This function is typically called internally by the Server when a new LLMNR query is received.
type MatchMode
MatchMode selects how a SpoofHandler decides whether a queried name should be answered with a spoofed address.
type MatchMode int
const (
// MatchAll answers every queried name (subject to own-hostname suppression).
// This is the aggressive default an operator uses to poison a whole segment.
MatchAll MatchMode = iota
// MatchList answers only names present in the case-insensitive allowlist
// (SpoofHandler.Names). Any other name is ignored so it can still be resolved
// by a legitimate responder on the link.
MatchList
// MatchRegex answers only names matching the configured regular expression
// (SpoofHandler.Regex), e.g. `^(wpad|proxy)$`.
MatchRegex
)
type ResponseWriter
ResponseWriter interface is used by an LLMNR handler to construct a response
type ResponseWriter interface {
WriteMessage(*message.Message) error
GetRemoteAddr() net.Addr
}
func NewResponseWriter
func NewResponseWriter(server *Server, remoteAddr net.Addr) ResponseWriter
NewResponseWriter creates a new ResponseWriter instance.
Parameters: - server: The Server instance that received the query. - remoteAddr: The address of the client that sent the query.
Returns: - A new ResponseWriter instance.
type Server
Server represents an LLMNR server.
The Server struct contains the necessary fields and methods to handle LLMNR (Link-Local Multicast Name Resolution) requests and responses. It supports both IPv4 and IPv6 communication over UDP.
Fields:
- Handlers: A slice of Handler interfaces that process incoming LLMNR messages.
- Network: A string representing the network type. Known networks are “tcp”, “tcp4” (IPv4-only), “tcp6” (IPv6-only), “udp”, “udp4” (IPv4-only), “udp6” (IPv6-only), “ip”, “ip4” (IPv4-only), “ip6” (IPv6-only), “unix”, “unixgram”, and “unixpacket”.
- Address: A pointer to a net.UDPAddr struct representing the server’s address.
- Conn: A pointer to a net.UDPConn struct representing the server’s UDP connection.
- CloseOnce: A sync.Once struct to ensure the server is closed only once.
- Closed: A channel that is closed when the server is shut down.
- Debug: A boolean flag indicating whether debug mode is enabled.
type Server struct {
// Handlers is a slice of Handler interfaces that process incoming LLMNR messages.
// It is guarded by handlersMu: RegisterHandler appends under the write lock
// while the per-packet dispatch path reads a snapshot under the read lock, so
// a handler may be registered concurrently with Serve without a data race.
Handlers []Handler
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
// "unixpacket".
Network string
// Address is the address of the server.
Address *net.UDPAddr
// Conn is the connection of the server.
Conn *net.UDPConn
// TCPListenAddr is the address the optional TCP responder binds to when
// ListenAndServeTCP is used. RFC 4795 §2.4 requires a responder to listen on
// TCP port 5355 on the unicast address(es) it answers from, so this defaults
// to ":5355" (all interfaces) when left empty. It is overridable so a caller
// can pin the responder to a specific unicast address, and so tests can bind
// an ephemeral loopback port.
TCPListenAddr string
// Interface is the network interface on which the multicast group is joined.
// When nil the group is joined on the system-default multicast interface
// (preserving the previous behavior); setting it lets the server listen on a
// specific link, which matters on multi-homed hosts where the default
// multicast interface is not the one carrying the traffic of interest.
Interface *net.Interface
// CloseOnce is a sync.Once struct to ensure the server is closed only once.
CloseOnce sync.Once
// Closed is a channel that is closed when the server is shut down.
Closed chan struct{}
// Debug is a boolean flag indicating whether debug mode is enabled.
Debug bool
// contains filtered or unexported fields
}
func NewIPv4Server
func NewIPv4Server() (*Server, error)
NewIPv4Server creates a new LLMNR server for IPv4.
This function initializes a new Server instance configured to use the “udp4” network type for IPv4 communication. It does not accept any handlers and initializes the server with an empty list of The server’s internal state, including the handlers, closed channel, and debug flag, is initialized.
Returns: - A pointer to the newly created Server instance. - An error if the server creation fails.
Example usage:
server, err := llmnr.NewIPv4Server()
if err != nil {
log.Fatalf("Failed to create IPv4 server: %v", err)
}
if server.IsIPv4() {
fmt.Println("The server is using an IPv4 address.")
} else {
fmt.Println("The server is not using an IPv4 address.")
}
func NewIPv4ServerWithHandlers
func NewIPv4ServerWithHandlers(handlers []Handler) (*Server, error)
NewIPv4ServerWithHandlers creates a new LLMNR server for IPv4 with the specified
This function initializes a new Server instance configured to use the “udp4” network type for IPv4 communication. It accepts a list of handlers that will be used to process incoming LLMNR requests. The server’s internal state, including the handlers, closed channel, and debug flag, is initialized.
Parameters: - handlers: A slice of Handler instances to handle incoming LLMNR requests.
Returns: - A pointer to the newly created Server instance. - An error if the server creation fails.
Example usage:
handlers := []llmnr.Handler{
llmnr.HandlerFunc(myHandlerFunc),
}
server, err := llmnr.NewIPv4ServerWithHandlers(handlers)
if err != nil {
log.Fatalf("Failed to create IPv4 server: %v", err)
}
if server.IsIPv4() {
fmt.Println("The server is using an IPv4 address.")
} else {
fmt.Println("The server is not using an IPv4 address.")
}
func NewIPv6Server
func NewIPv6Server() (*Server, error)
NewIPv6Server creates a new LLMNR server for IPv6.
This function initializes a new Server instance configured to use the “udp6” network type for IPv6 communication. It does not accept any handlers and initializes the server with an empty list of The server’s internal state, including the handlers, closed channel, and debug flag, is initialized.
Returns: - A pointer to the newly created Server instance. - An error if the server creation fails.
Example usage:
server, err := llmnr.NewIPv6Server()
if err != nil {
log.Fatalf("Failed to create IPv6 server: %v", err)
}
if server.IsIPv6() {
fmt.Println("The server is using an IPv6 address.")
} else {
fmt.Println("The server is not using an IPv6 address.")
}
func NewIPv6ServerWithHandlers
func NewIPv6ServerWithHandlers(handlers []Handler) (*Server, error)
NewIPv6ServerWithHandlers creates a new LLMNR server for IPv6 with the specified
This function initializes a new Server instance configured to use the “udp6” network type for IPv6 communication. It accepts a list of handlers that will be used to process incoming LLMNR requests. The server’s internal state, including the handlers, closed channel, and debug flag, is initialized.
Parameters: - handlers: A slice of Handler instances to handle incoming LLMNR requests.
Returns: - A pointer to the newly created Server instance. - An error if the server creation fails.
Example usage:
handlers := []llmnr.Handler{
llmnr.HandlerFunc(myHandlerFunc),
}
server, err := llmnr.NewIPv6ServerWithHandlers(handlers)
if err != nil {
log.Fatalf("Failed to create IPv6 server: %v", err)
}
if server.IsIPv6() {
fmt.Println("The server is using an IPv6 address.")
} else {
fmt.Println("The server is not using an IPv6 address.")
}
func NewServer
func NewServer(network string, handlers []Handler) (*Server, error)
NewServer creates a new LLMNR server with the specified network and
This function initializes a new Server instance with the provided network type and a list of The server is configured to use the specified network (e.g., “udp4” for IPv4, “udp6” for IPv6) and initializes its internal state, including the handlers, closed channel, and debug flag.
Parameters: - network: A string specifying the network type (e.g., “udp4”, “udp6”). - handlers: A slice of Handler instances to handle incoming LLMNR requests.
Returns: - A pointer to the newly created Server instance. - An error if the server creation fails.
Example usage:
handlers := []llmnr.Handler{
llmnr.HandlerFunc(myHandlerFunc),
}
server, err := llmnr.NewServer("udp4", handlers)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
if server.IsIPv4() {
fmt.Println("The server is using an IPv4 address.")
} else {
fmt.Println("The server is not using an IPv4 address.")
}
func (*Server) Close
func (s *Server) Close() error
Close gracefully shuts down the LLMNR server by closing the UDP connection and signaling the server to stop. It ensures that the server is closed only once, even if Close is called multiple times.
The function uses a sync.Once to guarantee that the server’s resources are released only once. It closes the server’s closed channel to signal any goroutines to stop, and if the server has an active UDP connection, it closes the connection.
Returns: - An error if the server fails to close the UDP connection or encounters an error during the shutdown process.
Example usage: server, err := llmnr.NewServer(handler)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
err = server.ListenAndServe()
if err != nil {
log.Fatalf("Server encountered an error: %v", err)
}
// When you want to stop the server err = server.Close()
if err != nil {
log.Fatalf("Failed to close server: %v", err)
}
func (*Server) IsIPv4
func (s *Server) IsIPv4() bool
IsIPv4 checks if the server’s address is an IPv4 address.
Returns: - A boolean value: true if the server’s address is an IPv4 address, false otherwise.
The function uses the net.IP.To4 method to determine if the IP address is an IPv4 address. If the IP address is not an IPv4 address, the method will return nil, and the function will return false.
Example usage: server, err := llmnr.NewIPv4Server()
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
if server.IsIPv4() {
fmt.Println("The server is using an IPv4 address.")
} else {
fmt.Println("The server is not using an IPv4 address.")
}
func (*Server) IsIPv6
func (s *Server) IsIPv6() bool
IsIPv6 checks if the server’s address is an IPv6 address.
Returns: - A boolean value: true if the server’s address is an IPv6 address, false otherwise.
An address is considered IPv6 when it has a valid 16-byte representation and cannot be represented as an IPv4 address (net.IP.To4 returns nil). Checking only To16() would incorrectly classify IPv4 addresses as IPv6, because net.IP.To16() also returns a non-nil 16-byte representation for IPv4.
Example usage: server, err := llmnr.NewIPv6Server()
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
if server.IsIPv6() {
fmt.Println("The server is using an IPv6 address.")
} else {
fmt.Println("The server is not using an IPv6 address.")
}
func (*Server) ListenAndServe
func (s *Server) ListenAndServe() error
ListenAndServe starts the LLMNR server and begins listening for incoming UDP packets on the IPv4 multicast address. It creates a UDP connection and assigns it to the server’s connection field. The function then enters a loop to continuously read from the UDP connection, decode incoming messages, and pass them to the server’s handler.
Returns: - An error if the server fails to start listening on the UDP connection or encounters an error during execution.
Example usage: server, err := llmnr.NewServer(handler)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
err = server.ListenAndServe()
if err != nil {
log.Fatalf("Server encountered an error: %v", err)
}
func (*Server) ListenAndServeTCP
func (s *Server) ListenAndServeTCP() error
ListenAndServeTCP starts the optional LLMNR TCP responder and blocks serving it until the server is closed. RFC 4795 §2.4 requires a responder to listen on TCP port 5355 on the unicast address(es) it answers from, so that a querying host whose UDP response was truncated (TC bit set) can retry the query over TCP and receive the complete answer.
It binds s.TCPListenAddr (defaulting to “:5355” when empty), then accepts connections and serves each with the same handler chain as the UDP path. It is independent of and non-breaking to ListenAndServe: a caller that wants both transports starts ListenAndServe and ListenAndServeTCP in separate goroutines, and Close shuts down both. Readiness can be observed with ListeningTCP.
Returns:
- An error if no handlers are registered or the TCP socket cannot be bound. A nil error is returned after a clean Close.
func (*Server) Listening
func (s *Server) Listening() bool
Listening reports whether the server has bound its multicast socket and is ready to receive queries. It is safe to call concurrently with ListenAndServe, so a caller that starts the server in a background goroutine can poll it to learn when the server is up without racing on the Conn field.
func (*Server) ListeningTCP
func (s *Server) ListeningTCP() bool
ListeningTCP reports whether the server has bound its TCP responder socket and is ready to accept connections. Like Listening, it is safe to call concurrently with ListenAndServeTCP so a caller that started the TCP responder in a background goroutine can poll for readiness without racing on the tcpListener field.
func (*Server) RegisterHandler
func (s *Server) RegisterHandler(handler Handler)
RegisterHandler registers a new handler to the LLMNR
Parameters: - handler: The Handler to be registered. It must implement the Handler interface.
The function appends the provided handler to the server’s list of handlers. These handlers will be invoked to process incoming LLMNR queries. Handlers are executed in the order they are registered.
Example usage:
handler := &MyHandler{}
RegisterHandler(handler)
This function is typically called before starting the server to ensure that all necessary handlers are in place to process incoming queries.
func (*Server) Serve
func (s *Server) Serve() error
Serve handles incoming LLMNR requests and processes them in a loop until the server is closed.
The function reads packets from the UDP connection, decodes the messages, and handles LLMNR queries. It uses a buffer to read the incoming packets and processes each packet in a separate goroutine.
The function also supports debugging, printing detailed information about the received packets and errors.
Returns: - An error if the server encounters an issue while reading from the UDP connection.
Example usage: server, err := llmnr.NewServer(handler)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
err = server.ListenAndServe()
if err != nil {
log.Fatalf("Server encountered an error: %v", err)
}
// When you want to stop the server err = server.Close()
if err != nil {
log.Fatalf("Failed to close server: %v", err)
}
func (*Server) SetDebug
func (s *Server) SetDebug(debug bool)
SetDebug enables or disables debug mode for the LLMNR server.
Parameters: - debug: A boolean value indicating whether to enable (true) or disable (false) debug mode.
When debug mode is enabled, the server will print detailed information about incoming packets, decoded messages, and any errors encountered during processing. This can be useful for troubleshooting and understanding the server’s behavior.
Example usage: server, err := llmnr.NewServer(handler)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
server.SetDebug(true)
err = server.ListenAndServe()
if err != nil {
log.Fatalf("Server encountered an error: %v", err)
}
func (*Server) TCPAddr
func (s *Server) TCPAddr() net.Addr
TCPAddr returns the address the TCP responder is bound to, or nil when the TCP responder has not been started. It is primarily useful when TCPListenAddr requested an ephemeral port (e.g. “127.0.0.1:0” in tests) and the caller needs the concrete port that was assigned.
type SpoofConfig
SpoofConfig configures a SpoofHandler.
A SpoofHandler answers LLMNR queries for names it does not own with an attacker-chosen address, the classic name-resolution poisoning primitive used to coerce a victim into connecting to the operator’s host. Which names are answered is controlled by Mode (answer-all, an exact allowlist, or a regex), and the address returned is taken from SpoofIPv4/SpoofIPv6 (typically a local interface address so the victim connects back to the operator).
type SpoofConfig struct {
// Mode selects the name-matching strategy (MatchAll, MatchList or MatchRegex).
Mode MatchMode
// Names is the case-insensitive allowlist consulted when Mode is MatchList.
// Names are compared as single, dot-trimmed labels.
Names []string
// Regex is the regular expression consulted when Mode is MatchRegex. It is
// tested against the dot-trimmed queried name.
Regex *regexp.Regexp
// SpoofIPv4 is the IPv4 address returned in A answers. When nil, A queries
// are not answered regardless of AnswerA.
SpoofIPv4 net.IP
// SpoofIPv6 is the IPv6 address returned in AAAA answers. When nil, AAAA
// queries are not answered regardless of AnswerAAAA.
SpoofIPv6 net.IP
// AnswerA and AnswerAAAA gate which query types are answered. Answering the
// type that was asked (A for an A query, AAAA for an AAAA query) matches how
// a real responder behaves.
AnswerA bool
AnswerAAAA bool
// TTL is the TTL (seconds) placed in answer records. Zero selects
// DefaultSpoofTTL.
TTL uint32
// SuppressOwnHostname, when set, declines to answer queries for OwnHostname
// so the poisoner does not shadow legitimate resolution of the host it runs
// on (which is both noisy and self-defeating).
SuppressOwnHostname bool
// OwnHostname is the single-label hostname to suppress when
// SuppressOwnHostname is set. It is compared case-insensitively.
OwnHostname string
// Verbose logs every query that is answered.
Verbose bool
}
type SpoofHandler
SpoofHandler is an LLMNR Handler that answers matched query names with a spoofed address. It implements the Handler interface and is registered on a Server like any other handler; because the LLMNR server imposes no authoritative-name restriction, the handler can answer for arbitrary names, which is exactly what a poisoner requires.
type SpoofHandler struct {
// contains filtered or unexported fields
}
func NewSpoofHandler
func NewSpoofHandler(cfg SpoofConfig) (*SpoofHandler, error)
NewSpoofHandler builds a SpoofHandler from cfg, validating that the configuration can actually answer something.
It returns an error when no answerable address family is configured (neither a usable A nor a usable AAAA answer), when MatchList is selected with an empty allowlist, or when MatchRegex is selected without a compiled regex, so a misconfigured poisoner fails fast instead of silently answering nothing.
func (*SpoofHandler) BuildResponse
func (h *SpoofHandler) BuildResponse(msg *message.Message) (*message.Message, bool)
BuildResponse assembles the spoofed response for msg without transmitting it.
The returned message copies the query’s transaction ID and sets the QR (response) flag (via CreateResponseFromMessage), then for every question whose name matches and whose type is one the handler answers, it appends an answer record carrying the configured spoof address. Each answer builder re-adds the corresponding question, so the response echoes the question section a real responder (and this project’s own validating resolver) expects.
It returns (nil, false) when no question matched, so the caller can leave the query for another handler or another responder on the link.
func (*SpoofHandler) Matches
func (h *SpoofHandler) Matches(name string) bool
Matches reports whether name should be answered under the handler’s match mode and own-hostname suppression. It is exported to make the matching logic directly testable and reusable.
func (*SpoofHandler) Run
func (h *SpoofHandler) Run(server *Server, remoteAddr net.Addr, writer ResponseWriter, msg *message.Message) bool
Run implements the Handler interface. It builds a spoofed response for the incoming query and, if a name matched, unicasts it back to the querying host via the ResponseWriter (which directs it to the source port the query came from, per RFC 4795 §2.3).
It returns false to stop the handler chain when the query was answered, and true to let subsequent handlers run when nothing matched.