llmnr

package
v1.0.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 12, 2025 License: MIT Imports: 10 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// LLMNR uses port 5355 as specified in RFC 4795
	LLMNRPort = 5355

	// Multicast addresses for LLMNR
	IPv4MulticastAddr = "224.0.0.252"
	IPv6MulticastAddr = "FF02::1:3"

	MaxLabelLength  = 63  // Maximum length of a single label
	MaxDomainLength = 255 // Maximum length of entire domain name
	HeaderSize      = 12  // Size of LLMNR header in bytes

	MaxPacketSize = 512
)
View Source
const (
	FlagQR = 1 << 15 // Query/Response flag
	FlagOP = 1 << 14 // Operation code
	FlagC  = 1 << 13 // Conflict flag
	FlagTC = 1 << 12 // Truncation flag
	FlagT  = 1 << 11 // Tentative flag
)

LLMNR Header Flags

View Source
const (
	TypeA     uint16 = 1   // IPv4 address
	TypeNS    uint16 = 2   // Authoritative name server
	TypeCNAME uint16 = 5   // Canonical name for an alias
	TypeSOA   uint16 = 6   // Start of authority
	TypePTR   uint16 = 12  // Domain name pointer
	TypeMX    uint16 = 15  // Mail exchange
	TypeTXT   uint16 = 16  // Text strings
	TypeAAAA  uint16 = 28  // IPv6 address
	TypeSRV   uint16 = 33  // Service locator
	TypeOPT   uint16 = 41  // OPT pseudo-RR, RFC 2671
	TypeAXFR  uint16 = 252 // Transfer of entire zone
	TypeALL   uint16 = 255 // All records
)

Resource Record Types

View Source
const (
	ClassIN      uint16 = 1      // Internet
	ClassCS      uint16 = 2      // CSNET (Obsolete)
	ClassCH      uint16 = 3      // CHAOS
	ClassHS      uint16 = 4      // Hesiod
	ClassNONE    uint16 = 254    // Used in dynamic update messages
	ClassANY     uint16 = 255    // Any class
	ClassQU      uint16 = 1      // QU flag for LLMNR (same as IN)
	ClassUNICAST uint16 = 0x8001 // LLMNR Unicast response
)

Resource Record Classes

Variables

View Source
var (
	ErrNameTooLong  = errors.New("domain name too long")
	ErrLabelTooLong = errors.New("label too long")
)

Common errors

View Source
var (
	ErrInvalidQuestion = errors.New("invalid question format")
	ErrInvalidMessage  = errors.New("invalid message format")
)

Common errors

Functions

func ClassToString

func ClassToString(c uint16) string

func DecodeDomainName

func DecodeDomainName(data []byte, offset int) (string, int, error)

DecodeDomainName decodes a byte slice into a domain name string in the wire format as specified by the LLMNR protocol. The function processes the byte slice to extract the sequence of labels, handling pointers if present, and reconstructs the original domain name.

Parameters: - data: A byte slice containing the encoded domain name in wire format. - offset: The starting position in the byte slice from which to begin decoding.

Returns:

  • A string containing the decoded domain name.
  • An integer representing the new offset position after decoding.
  • An error if the decoding fails at any point, such as if the data is too short, if there is an invalid pointer, or if a label is too long.

Usage example:

data := []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 5, 'l', 'o', 'c', 'a', 'l', 0}
offset := 0
domainName, newOffset, err := DecodeDomainName(data, offset)
if err != nil {
    log.Fatalf("Failed to decode domain name: %v", err)
}
fmt.Printf("Decoded domain name: %s, New offset: %d\n", domainName, newOffset)

The resulting domain name for the given byte slice would be "example.local" and the new offset would be 14.

func EncodeDomainName

func EncodeDomainName(name string) ([]byte, error)

EncodeDomainName encodes a domain name into a byte slice in the wire format as specified by the LLMNR protocol. The function converts the domain name into a sequence of labels, each prefixed by its length, and ends with a zero byte.

Parameters: - name: The domain name to be encoded.

Returns:

  • A byte slice containing the encoded domain name.
  • An error if the encoding fails, such as if a label is too long.

Usage example:

encodedName, err := EncodeDomainName("example.local")
if err != nil {
    log.Fatalf("Failed to encode domain name: %v", err)
}

The resulting byte slice for "example.local" would be:

[]byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 5, 'l', 'o', 'c', 'a', 'l', 0}

func EncodeQuestion

func EncodeQuestion(q Question) ([]byte, error)

Usage:

buf, err := EncodeQuestion(buf, question)
if err != nil {
    // handle error
}

The function returns the updated byte slice with the encoded question appended to it, or an error if the domain name encoding fails.

func EncodeResourceRecord

func EncodeResourceRecord(rr ResourceRecord) ([]byte, error)

EncodeResourceRecord converts a ResourceRecord into a byte slice using the LLMNR protocol's wire format. It encodes the domain name, type, class, TTL, RDLength, and RData fields sequentially.

Parameters: - rr: The ResourceRecord to be encoded.

Returns:

  • A byte slice with the encoded resource record.
  • An error if encoding fails, such as with an invalid domain name.

Example usage:

rr := ResourceRecord{
    Name:     "example.local",
    Type:     TypeA,
    Class:    ClassIN,
    TTL:      300,
    RDLength: 4,
    RData:    []byte{192, 168, 1, 1},
}
encodedBuf, err := EncodeResourceRecord(rr)
if err != nil {
    log.Fatalf("Failed to encode resource record: %v", err)
}

func FlagToString

func FlagToString(f uint16) string

func HandlerDescribePacket

func HandlerDescribePacket(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *Message) bool

HandlerDescribePacket logs detailed information about an LLMNR packet received by the server.

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) bool

HandlerDescribePacketJson logs the details of the LLMNR message in JSON format.

func IPToRData

func IPToRData(ip string) []byte

IPToRData converts an IP address string to its corresponding RData byte slice representation. It determines whether the IP address is IPv4 or IPv6 and calls the appropriate conversion function.

Parameters: - ip: A string representing the IP address to be converted.

Returns: - A byte slice containing the RData representation of the IP address. - nil if the IP address is neither a valid IPv4 nor IPv6 address.

Usage example:

rdata := IPToRData("192.168.1.1")
if rdata == nil {
    log.Fatalf("Invalid IP address")
}

func IPv4ToRData

func IPv4ToRData(ip string) []byte

IPv4ToRData converts an IPv4 address string to its corresponding RData byte slice representation.

Parameters: - ip: A string representing the IPv4 address to be converted.

Returns: - A byte slice containing the RData representation of the IPv4 address.

Usage example:

rdata := IPv4ToRData("192.168.1.1")
if rdata == nil {
    log.Fatalf("Invalid IPv4 address")
}

func IPv6ToRData

func IPv6ToRData(ip string) []byte

IPv6ToRData converts an IPv6 address string to its corresponding RData byte slice representation.

Parameters: - ip: A string representing the IPv6 address to be converted.

Returns: - A byte slice containing the RData representation of the IPv6 address.

Usage example:

rdata := IPv6ToRData("2001:0db8:85a3:0000:0000:8a2e:0370:7334")
if rdata == nil {
    log.Fatalf("Invalid IPv6 address")
}

func TypeToString

func TypeToString(t uint16) string

func ValidateDomainName

func ValidateDomainName(name string) error

ValidateDomainName validates a domain name according to the rules specified by the LLMNR protocol. The function checks if the domain name exceeds the maximum allowed length and if any label within the domain name exceeds the maximum allowed label length. A domain name is composed of labels separated by dots.

Parameters: - name: The domain name to be validated.

Returns:

  • An error if the domain name is invalid, such as if it is too long or if any label is too long.
  • nil if the domain name is valid.

Usage example:

err := ValidateDomainName("example.local")
if err != nil {
    log.Fatalf("Invalid domain name: %v", err)
}

The function will return an error in the following cases: - If the domain name exceeds MaxDomainLength. - If any label within the domain name exceeds MaxLabelLength.

Types

type Client

type Client struct {
	Conn      *net.UDPConn
	Timeout   time.Duration
	Queries   sync.Map
	CloseOnce sync.Once
	Closed    chan struct{}
}

Client represents an LLMNR client that can send queries and receive responses.

The Client struct provides methods to create a new client, send queries, and close the client connection. It manages a UDP connection and uses a sync.Map to keep track of ongoing queries.

Fields: - conn: A pointer to the UDP connection used for sending and receiving LLMNR messages. - timeout: The duration to wait for a response before timing out. - queries: A sync.Map that maps query IDs to channels for receiving responses. - closeOnce: Ensures the client is closed only once. - closed: A channel that is closed when the client is closed.

Usage example:

client, err := NewClient()
if err != nil {
    log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

resp, err := client.Query(ctx, "example.local", TypeA)
if err != nil {
    log.Fatalf("Query failed: %v", err)
}
fmt.Printf("Received response: %v\n", resp)

func NewClient

func NewClient() (*Client, error)

NewClient creates a new LLMNR client with a UDP connection.

The function initializes a UDP connection for the client to use for sending and receiving LLMNR messages. It sets a default timeout duration for queries and starts a read loop to handle incoming responses.

Returns:

  • A pointer to the newly created Client.
  • An error if the UDP connection could not be created.

Usage example:

client, err := NewClient()
if err != nil {
    log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

resp, err := client.Query(ctx, "example.local", TypeA)
if err != nil {
    log.Fatalf("Query failed: %v", err)
}
fmt.Printf("Received response: %v\n", resp)

func (*Client) Close

func (c *Client) Close() error

Close closes the client connection

func (*Client) Query

func (c *Client) Query(ctx context.Context, name string, qtype uint16) (*Message, error)

Query sends an LLMNR query and waits for a response

type Handler

type Handler interface {
	Run(server *Server, remoteAddr net.Addr, writer ResponseWriter, message *Message) bool
}

Handler defines the interface for processing LLMNR queries

type HandlerFunc

type HandlerFunc func(*Server, net.Addr, ResponseWriter, *Message) bool

HandlerFunc is an adapter to allow regular functions to serve as LLMNR handlers

func (HandlerFunc) Run

func (f HandlerFunc) Run(server *Server, remoteAddr net.Addr, writer ResponseWriter, 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 llmnr.ResponseWriter, r *llmnr.Message) {
    // Process the LLMNR query and write a response
}
handler := llmnr.HandlerFunc(handlerFunc)
server.RegisterHandler(handler)

This function is typically called internally by the Server when a new LLMNR query is received.

type Header struct {
	ID      uint16 `json:"id"`
	Flags   uint16 `json:"flags"`
	QDCount uint16 `json:"qd_count"` // Question count
	ANCount uint16 `json:"an_count"` // Answer count
	NSCount uint16 `json:"ns_count"` // Authority count
	ARCount uint16 `json:"ar_count"` // Additional count
}

Header represents the LLMNR message header.

The header contains essential information about the LLMNR message, including the message ID, flags, and counts of various sections such as questions, answers, authority records, and additional records.

Fields:

  • ID: A 16-bit identifier assigned by the program that generates any kind of query. This identifier is copied to the corresponding reply and can be used by the requester to match up replies to outstanding queries.
  • Flags: A 16-bit field containing various flags that control the message flow and interpretation. These flags include the Query/Response flag (QR), Operation code (OP), Conflict flag (C), Truncation flag (TC), and Tentative flag (T).
  • QDCount: An unsigned 16-bit integer specifying the number of entries in the question section of the message.
  • ANCount: An unsigned 16-bit integer specifying the number of resource records in the answer section of the message.
  • NSCount: An unsigned 16-bit integer specifying the number of name server resource records in the authority records section of the message.
  • ARCount: An unsigned 16-bit integer specifying the number of resource records in the additional records section of the message.

Usage example:

header := Header{
    ID:      12345,
    Flags:   FlagQR,
    QDCount: 1,
    ANCount: 0,
    NSCount: 0,
    ARCount: 0,
}

type Message

type Message struct {
	Header
	Questions  []Question       `json:"questions"`
	Answers    []ResourceRecord `json:"answers"`
	Authority  []ResourceRecord `json:"authority"`
	Additional []ResourceRecord `json:"additional"`
}

Message represents an LLMNR message.

An LLMNR (Link-Local Multicast Name Resolution) message consists of a header and four sections: Questions, Answers, Authority, and Additional. The header contains metadata about the message, such as the transaction ID and various flags. The Questions section contains the questions being asked in the message, while the Answers, Authority, and Additional sections contain resource records that provide answers, authority information, and additional information, respectively.

Fields: - Header: The header of the LLMNR message, containing metadata such as the transaction ID and flags. - Questions: A slice of Question structs representing the questions in the message. - Answers: A slice of ResourceRecord structs representing the answers in the message. - Authority: A slice of ResourceRecord structs representing the authority records in the message. - Additional: A slice of ResourceRecord structs representing the additional records in the message.

func CreateResponseFromMessage

func CreateResponseFromMessage(msg *Message) *Message

CreateResponseFromMessage creates a new LLMNR message that is a response to the given message.

Parameters: - msg: The message to create a response for.

Returns: - A pointer to the newly created Message instance.

func DecodeMessage

func DecodeMessage(data []byte) (*Message, error)

DecodeMessage decodes a byte slice into a Message struct. It expects the byte slice to be in the wire format as specified by the LLMNR protocol. The function first checks if the provided data is at least as long as the LLMNR header. It then proceeds to decode the header fields, followed by the question and answer sections.

Parameters: - data: A byte slice containing the LLMNR message in wire format.

Returns:

  • A pointer to a Message struct containing the decoded data.
  • An error if the decoding fails at any point, such as if the data is too short or if there is an error decoding the question or answer sections.

func NewMessage

func NewMessage() *Message

NewMessage creates a new LLMNR message with a randomly generated transaction ID and initializes the Questions, Answers, Authority, and Additional sections as empty slices. The Header of the message is also initialized with the generated transaction ID.

Returns: - A pointer to the newly created Message instance.

func (*Message) AddAnswer

func (m *Message) AddAnswer(rr ResourceRecord) error

AddAnswer adds a resource record to the Answers section of the LLMNR message and updates the answer count in the header. It validates the domain name of the resource record before adding it.

Parameters: - rr: The resource record to be added to the Answers section.

Returns: - An error if the domain name of the resource record is invalid. - nil if the resource record is successfully added.

func (*Message) AddAnswerClassINTypeA

func (m *Message) AddAnswerClassINTypeA(name, ip string) error

AddAnswerClassINTypeA adds a resource record with Class IN and Type A to the Answers section of the LLMNR message and updates the answer count in the header. It validates the domain name of the resource record before adding it.

Parameters: - name: The domain name for the resource record. - rdata: The resource data for the Type A record (e.g., an IPv4 address).

Returns: - An error if the domain name of the resource record is invalid. - nil if the resource record is successfully added.

func (*Message) AddAnswerClassINTypeAAAA

func (m *Message) AddAnswerClassINTypeAAAA(name, ip string) error

AddAnswerClassINTypeAAAA adds a resource record with Class IN and Type AAAA to the Answers section of the LLMNR message and updates the answer count in the header. It validates the domain name of the resource record before adding it.

Parameters: - name: The domain name for the resource record. - rdata: The resource data for the Type AAAA record (e.g., an IPv6 address).

Returns: - An error if the domain name of the resource record is invalid. - nil if the resource record is successfully added.

func (*Message) AddQuestion

func (m *Message) AddQuestion(name string, qtype, qclass uint16) error

AddQuestion adds a question to the Questions section of the LLMNR message and updates the question count in the header. It validates the domain name of the question before adding it.

Parameters: - name: The domain name for the question. - qtype: The type of the question (e.g., TypeA, TypeAAAA). - qclass: The class of the question (e.g., ClassIN).

Returns: - An error if the domain name is invalid. - nil if the question is successfully added.

func (*Message) Encode

func (m *Message) Encode() ([]byte, error)

Encode serializes the Message struct into a byte slice according to the LLMNR wire format. It encodes the header, questions, and answers sections of the message.

Returns: - A byte slice containing the encoded message. - An error if encoding fails at any point, such as if there is an error encoding the questions or answers.

func (*Message) IsQuery

func (m *Message) IsQuery() bool

IsQuery returns true if the message is a query.

This function checks the QR (Query/Response) flag in the message's Flags field. If the QR flag is not set, the message is considered a query and the function returns true. If the QR flag is set, the message is considered a response and the function returns false.

Returns:

  • A boolean value indicating whether the message is a query (true) or not (false).

func (*Message) IsResponse

func (m *Message) IsResponse() bool

IsResponse returns true if the message is a response.

This function checks the QR (Query/Response) flag in the message's Flags field. If the QR flag is set, the message is considered a response and the function returns true. If the QR flag is not set, the message is considered a query and the function returns false.

Returns:

  • A boolean value indicating whether the message is a response (true) or not (false).

func (*Message) SetQuery

func (m *Message) SetQuery()

SetQuery marks the message as a query.

This function clears the QR (Query/Response) flag in the message's Flags field. By clearing the QR flag, the message is considered a query.

Usage:

msg.SetQuery()

After calling this function, the message will be marked as a query.

Returns:

  • Nothing. This function modifies the message in place.

func (*Message) SetResponse

func (m *Message) SetResponse()

SetResponse marks the message as a response.

This function sets the QR (Query/Response) flag in the message's Flags field. By setting the QR flag, the message is considered a response.

Usage:

msg.SetResponse()

After calling this function, the message will be marked as a response.

Returns:

  • Nothing. This function modifies the message in place.

func (*Message) Validate

func (m *Message) Validate() error

Validate checks the integrity of the LLMNR message by ensuring that the counts in the header match the actual number of questions, answers, authority, and additional records. It also validates the domain names in the questions and answers sections.

Returns: - An error if any of the counts do not match or if any domain name is invalid. - nil if the message is valid.

type Question

type Question struct {
	Name  string `json:"name"`
	Type  uint16 `json:"type"`
	Class uint16 `json:"class"`
}

Question represents a question in an LLMNR message.

An LLMNR (Link-Local Multicast Name Resolution) question consists of a domain name, a type, and a class. The domain name specifies the name being queried, while the type and class specify the type of the query (e.g., TypeA, TypeAAAA) and the class of the query (e.g., ClassIN), respectively.

Fields: - Name: The domain name being queried. - Type: The type of the query (e.g., TypeA, TypeAAAA). - Class: The class of the query (e.g., ClassIN).

The Question struct is used in the Questions section of an LLMNR message to represent individual questions being asked in the message. Each question is encoded and decoded using the EncodeQuestion and DecodeQuestion functions, respectively.

func DecodeQuestion

func DecodeQuestion(data []byte, offset int) (Question, int, error)

DecodeQuestion decodes a byte slice into a Question struct.

This function takes a byte slice and an offset, and decodes the data starting from the offset into a Question struct. The domain name is decoded first, followed by the type and class fields. The function ensures that the data is not truncated and returns an error if any part of the decoding process fails.

Parameters: - data: A byte slice containing the encoded question in wire format. - offset: The starting position in the byte slice from which to begin decoding.

Returns:

  • A Question struct containing the decoded data.
  • An integer representing the new offset after decoding.
  • An error if the decoding fails at any point, such as if the data is truncated or if there is an error decoding the domain name.

Usage:

question, newOffset, err := DecodeQuestion(data, offset)
if err != nil {
    // handle error
}

The function returns the decoded Question struct, the new offset, and an error if any.

type ResourceRecord

type ResourceRecord struct {
	Name     string `json:"name"`
	Type     uint16 `json:"type"`
	Class    uint16 `json:"class"`
	TTL      uint32 `json:"ttl"`
	RDLength uint16 `json:"rdlength"`
	RData    []byte `json:"rdata"`
}

ResourceRecord represents a resource record in the LLMNR protocol.

A resource record is used to store information about a domain name, such as its IP address, mail server, or other related data. The ResourceRecord struct contains fields for the domain name, type, class, time-to-live (TTL), resource data length (RDLength), and resource data (RData).

Fields: - Name: The domain name associated with the resource record. - Type: The type of the resource record, indicating the kind of data stored (e.g., TypeA for IPv4 address). - Class: The class of the resource record, typically ClassIN for Internet. - TTL: The time-to-live value, indicating how long the record can be cached before it should be discarded. - RDLength: The length of the resource data in bytes. - RData: The resource data, which contains the actual information associated with the domain name (e.g., an IP address).

Usage example:

rr := ResourceRecord{
    Name:     "example.local",
    Type:     TypeA,
    Class:    ClassIN,
    TTL:      300,
    RDLength: 4,
    RData:    []byte{192, 168, 1, 1},
}

func DecodeResourceRecord

func DecodeResourceRecord(data []byte, offset int) (ResourceRecord, int, error)

DecodeResourceRecord decodes a byte slice into a ResourceRecord struct. It expects the byte slice to be in the wire format as specified by the LLMNR protocol. The function first decodes the domain name, followed by the type, class, TTL, RDLength, and RData fields.

Parameters: - data: A byte slice containing the resource record in wire format. - offset: The starting position in the byte slice from which to begin decoding.

Returns:

  • A ResourceRecord struct containing the decoded data.
  • An integer representing the new offset position after decoding.
  • An error if the decoding fails at any point, such as if the data is too short or if there is an error decoding the domain name.

Usage example:

data := []byte{...} // byte slice containing the resource record in wire format
offset := 0
rr, newOffset, err := DecodeResourceRecord(data, offset)
if err != nil {
    log.Fatalf("Failed to decode resource record: %v", err)
}

Start Generation Here

type ResponseWriter

type ResponseWriter interface {
	WriteMessage(*Message) error
	GetRemoteAddr() net.Addr
}

ResponseWriter interface is used by an LLMNR handler to construct a response

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

type Server struct {
	// Handlers is a slice of Handler interfaces that process incoming LLMNR messages.
	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

	// 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
}

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.

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 handlers. 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 handlers.

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 handlers. 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 handlers.

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 handlers.

This function initializes a new Server instance with the provided network type and a list of handlers. 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.

The function uses the net.IP.To16 method to determine if the IP address is an IPv6 address. If the IP address is not an IPv6 address, the method will return nil, and the function will return false.

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) RegisterHandler

func (s *Server) RegisterHandler(handler Handler)

RegisterHandler registers a new handler to the LLMNR server.

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{}
server.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)
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL