dnstap

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 1, 2019 License: Apache-2.0 Imports: 15 Imported by: 0

README

dnstap: flexible, structured event replication format for DNS servers
---------------------------------------------------------------------

dnstap implements an encoding format for DNS server events. It uses a
lightweight framing on top of event payloads encoded using Protocol Buffers and
is transport neutral.

dnstap can represent internal state inside a DNS server that is difficult to
obtain using techniques based on traditional packet capture or unstructured
textual format logging.

This repository contains a command-line tool named "dnstap" developed in the
Go programming language. It can be installed with the following command:

    go get -u github.com/dnstap/golang-dnstap/dnstap

Documentation

Overview

Package dnstap is a generated protocol buffer package.

It is generated from these files:

dnstap.proto

It has these top-level messages:

Dnstap
Message

Index

Constants

This section is empty.

Variables

View Source
var Dnstap_Type_name = map[int32]string{
	1: "MESSAGE",
}
View Source
var Dnstap_Type_value = map[string]int32{
	"MESSAGE": 1,
}
View Source
var FSContentType = []byte("protobuf:dnstap.Dnstap")

FSContentType is the FrameStream content type for dnstap protobuf data.

View Source
var MaxPayloadSize uint32 = 96 * 1024

MaxPayloadSize sets the upper limit on input Dnstap payload sizes. If an Input receives a Dnstap payload over this size limit, ReadInto will log an error and return.

EDNS0 and DNS over TCP use 2 octets for DNS message size, imposing a maximum size of 65535 octets for the DNS message, which is the bulk of the data carried in a Dnstap message. Protobuf encoding overhead and metadata with some size guidance (e.g., identity and version being DNS strings, which have a maximum length of 255) add up to less than 1KB. The default 96KiB size of the buffer allows a bit over 30KB space for "extra" metadata.

View Source
var Message_Type_name = map[int32]string{
	1:  "AUTH_QUERY",
	2:  "AUTH_RESPONSE",
	3:  "RESOLVER_QUERY",
	4:  "RESOLVER_RESPONSE",
	5:  "CLIENT_QUERY",
	6:  "CLIENT_RESPONSE",
	7:  "FORWARDER_QUERY",
	8:  "FORWARDER_RESPONSE",
	9:  "STUB_QUERY",
	10: "STUB_RESPONSE",
	11: "TOOL_QUERY",
	12: "TOOL_RESPONSE",
}
View Source
var Message_Type_value = map[string]int32{
	"AUTH_QUERY":         1,
	"AUTH_RESPONSE":      2,
	"RESOLVER_QUERY":     3,
	"RESOLVER_RESPONSE":  4,
	"CLIENT_QUERY":       5,
	"CLIENT_RESPONSE":    6,
	"FORWARDER_QUERY":    7,
	"FORWARDER_RESPONSE": 8,
	"STUB_QUERY":         9,
	"STUB_RESPONSE":      10,
	"TOOL_QUERY":         11,
	"TOOL_RESPONSE":      12,
}
View Source
var SocketFamily_name = map[int32]string{
	1: "INET",
	2: "INET6",
}
View Source
var SocketFamily_value = map[string]int32{
	"INET":  1,
	"INET6": 2,
}
View Source
var SocketProtocol_name = map[int32]string{
	1: "UDP",
	2: "TCP",
}
View Source
var SocketProtocol_value = map[string]int32{
	"UDP": 1,
	"TCP": 2,
}

Functions

func JSONFormat added in v0.2.0

func JSONFormat(dt *Dnstap) (out []byte, ok bool)

JSONFormat renders a Dnstap message in JSON format. Any encapsulated DNS messages are rendered as strings in a format similar to 'dig' output.

func TextFormat

func TextFormat(dt *Dnstap) (out []byte, ok bool)

TextFormat renders a dnstap message in a compact human-readable text form.

func YamlFormat

func YamlFormat(dt *Dnstap) (out []byte, ok bool)

YamlFormat renders a dnstap message in YAML format. Any encapsulated DNS messages are rendered as strings in a format similar to 'dig' output.

Types

type Dnstap

type Dnstap struct {
	// DNS server identity.
	// If enabled, this is the identity string of the DNS server which generated
	// this message. Typically this would be the same string as returned by an
	// "NSID" (RFC 5001) query.
	Identity []byte `protobuf:"bytes,1,opt,name=identity" json:"identity,omitempty"`
	// DNS server version.
	// If enabled, this is the version string of the DNS server which generated
	// this message. Typically this would be the same string as returned by a
	// "version.bind" query.
	Version []byte `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
	// Extra data for this payload.
	// This field can be used for adding an arbitrary byte-string annotation to
	// the payload. No encoding or interpretation is applied or enforced.
	Extra []byte       `protobuf:"bytes,3,opt,name=extra" json:"extra,omitempty"`
	Type  *Dnstap_Type `protobuf:"varint,15,req,name=type,enum=dnstap.Dnstap_Type" json:"type,omitempty"`
	// One of the following will be filled in.
	Message          *Message `protobuf:"bytes,14,opt,name=message" json:"message,omitempty"`
	XXX_unrecognized []byte   `json:"-"`
}

"Dnstap": this is the top-level dnstap type, which is a "union" type that contains other kinds of dnstap payloads, although currently only one type of dnstap payload is defined. See: https://developers.google.com/protocol-buffers/docs/techniques#union

func (*Dnstap) GetExtra

func (m *Dnstap) GetExtra() []byte

func (*Dnstap) GetIdentity

func (m *Dnstap) GetIdentity() []byte

func (*Dnstap) GetMessage

func (m *Dnstap) GetMessage() *Message

func (*Dnstap) GetType

func (m *Dnstap) GetType() Dnstap_Type

func (*Dnstap) GetVersion

func (m *Dnstap) GetVersion() []byte

func (*Dnstap) ProtoMessage

func (*Dnstap) ProtoMessage()

func (*Dnstap) Reset

func (m *Dnstap) Reset()

func (*Dnstap) String

func (m *Dnstap) String() string

type Dnstap_Type

type Dnstap_Type int32

Identifies which field below is filled in.

const (
	Dnstap_MESSAGE Dnstap_Type = 1
)

func (Dnstap_Type) Enum

func (x Dnstap_Type) Enum() *Dnstap_Type

func (Dnstap_Type) String

func (x Dnstap_Type) String() string

func (*Dnstap_Type) UnmarshalJSON

func (x *Dnstap_Type) UnmarshalJSON(data []byte) error

type FrameStreamInput

type FrameStreamInput struct {
	// contains filtered or unexported fields
}

A FrameStreamInput reads dnstap data from an io.ReadWriter.

func NewFrameStreamInput

func NewFrameStreamInput(r io.ReadWriter, bi bool) (input *FrameStreamInput, err error)

NewFrameStreamInput creates a FrameStreamInput reading data from the given io.ReadWriter. If bi is true, the input will use the bidirectional framestream protocol suitable for TCP and unix domain socket connections.

func NewFrameStreamInputFromFilename

func NewFrameStreamInputFromFilename(fname string) (input *FrameStreamInput, err error)

NewFrameStreamInputFromFilename creates a FrameStreamInput reading from the named file.

func NewFrameStreamInputTimeout added in v0.2.0

func NewFrameStreamInputTimeout(r io.ReadWriter, bi bool, timeout time.Duration) (input *FrameStreamInput, err error)

NewFrameStreamInputTimeout creates a FramestreamInput reading data from the given io.ReadWriter with a timeout applied to reading and (for bidirectional inputs) writing control messages.

func (*FrameStreamInput) ReadInto

func (input *FrameStreamInput) ReadInto(output chan []byte)

ReadInto reads data from the FrameStreamInput into the output channel.

ReadInto satisfies the dnstap Input interface.

func (*FrameStreamInput) Wait

func (input *FrameStreamInput) Wait()

Wait reeturns when ReadInto has finished.

Wait satisfies the dnstap Input interface.

type FrameStreamOutput

type FrameStreamOutput struct {
	// contains filtered or unexported fields
}

FrameStreamOutput implements a dnstap Output to an io.Writer.

func NewFrameStreamOutput

func NewFrameStreamOutput(w io.Writer) (o *FrameStreamOutput, err error)

NewFrameStreamOutput creates a FrameStreamOutput writing dnstap data to the given io.Writer.

func NewFrameStreamOutputFromFilename

func NewFrameStreamOutputFromFilename(fname string) (o *FrameStreamOutput, err error)

NewFrameStreamOutputFromFilename creates a file with the namee fname, truncates it if it exists, and returns a FrameStreamOutput writing to the newly created or truncated file.

func (*FrameStreamOutput) Close

func (o *FrameStreamOutput) Close()

Close closes the channel returned from GetOutputChannel, and flushes all pending output.

Close satisifies the dnstap Output interface.

func (*FrameStreamOutput) GetOutputChannel

func (o *FrameStreamOutput) GetOutputChannel() chan []byte

GetOutputChannel returns the channel on which the FrameStreamOutput accepts data.

GetOutputData satisfies the dnstap Output interface.

func (*FrameStreamOutput) RunOutputLoop

func (o *FrameStreamOutput) RunOutputLoop()

RunOutputLoop processes data received on the channel returned by GetOutputChannel, returning after the CLose method is called. If there is an error writing to the Output's writer, RunOutputLoop() logs a fatal error exits the program.

RunOutputLoop satisfies the dnstap Output interface.

type FrameStreamSockInput

type FrameStreamSockInput struct {
	// contains filtered or unexported fields
}

A FrameStreamSockInput collects dnstap data from one or more clients of a listening socket.

func NewFrameStreamSockInput

func NewFrameStreamSockInput(listener net.Listener) (input *FrameStreamSockInput)

NewFrameStreamSockInput creates a FrameStreamSockInput collecting dnstap data from clients which connect to the given listener.

func NewFrameStreamSockInputFromPath

func NewFrameStreamSockInputFromPath(socketPath string) (input *FrameStreamSockInput, err error)

NewFrameStreamSockInputFromPath creates a unix domain socket at the given socketPath and returns a FrameStreamSockInput collecting dnstap data from clients connecting to this socket.

If a socket or other file already exists at socketPath, NewFrameStreamSockInputFromPath removes it before creating the socket.

func (*FrameStreamSockInput) ReadInto

func (input *FrameStreamSockInput) ReadInto(output chan []byte)

ReadInto accepts connections to the FrameStreamSockInput's listening socket and sends all dnstap data read from these connections to the output channel.

ReadInto satisfies the dnstap Input interface.

func (*FrameStreamSockInput) SetTimeout added in v0.2.0

func (input *FrameStreamSockInput) SetTimeout(timeout time.Duration)

SetTimeout sets the timeout for reading the initial handshake and writing response control messages to clients of the FrameStreamSockInput's listener.

The timeout is effective only for connections accepted after the call to FrameStreamSockInput.

func (*FrameStreamSockInput) Wait

func (input *FrameStreamSockInput) Wait()

Wait satisfies the dnstap Input interface.

The FrameSTreamSocketInput Wait method never returns, because the corresponding Readinto method also never returns.

type FrameStreamSockOutput added in v0.2.0

type FrameStreamSockOutput struct {
	// contains filtered or unexported fields
}

A FrameStreamSockOutput manages a socket connection and sends dnstap data over a framestream connection on that socket.

func NewFrameStreamSockOutput added in v0.2.0

func NewFrameStreamSockOutput(address net.Addr) (*FrameStreamSockOutput, error)

NewFrameStreamSockOutput creates a FrameStreamSockOutput manaaging a connection to the given address.

func (*FrameStreamSockOutput) Close added in v0.2.0

func (o *FrameStreamSockOutput) Close()

Close shuts down the FrameStreamSockOutput's output channel and returns after all pending data has been flushed and the connection has been closed.

Close satisifes the dnstap Output interface

func (*FrameStreamSockOutput) GetOutputChannel added in v0.2.0

func (o *FrameStreamSockOutput) GetOutputChannel() chan []byte

GetOutputChannel returns the channel on which the FrameStreamSockOutput accepts data.

GetOutputChannel satisifes the dnstap Output interface.

func (*FrameStreamSockOutput) RunOutputLoop added in v0.2.0

func (o *FrameStreamSockOutput) RunOutputLoop()

RunOutputLoop reads data from the output channel and sends it over a connections to the FrameStreamSockOutput's address, establishing the connection as needed.

RunOutputLoop satisifes the dnstap Output interface.

func (*FrameStreamSockOutput) SetDialer added in v0.2.0

func (o *FrameStreamSockOutput) SetDialer(dialer *net.Dialer)

SetDialer replaces the default net.Dialer for re-establishing the the FrameStreamSockOutput connection. This can be used to set the timeout for connection establishment and enable keepalives new connections.

FrameStreamSockOutput uses a default dialer with a 30 second timeout.

func (*FrameStreamSockOutput) SetFlushTimeout added in v0.2.0

func (o *FrameStreamSockOutput) SetFlushTimeout(timeout time.Duration)

SetFlushTimeout sets the maximum time data will be kept in the output buffer.

The default flush timeout is five seconds.

func (*FrameStreamSockOutput) SetRetryInterval added in v0.2.0

func (o *FrameStreamSockOutput) SetRetryInterval(retry time.Duration)

SetRetryInterval specifies how long the FrameStreamSockOutput will wait before re-establishing a failed connection. The default retry interval is 10 seconds.

func (*FrameStreamSockOutput) SetTimeout added in v0.2.0

func (o *FrameStreamSockOutput) SetTimeout(timeout time.Duration)

SetTimeout sets the write timeout for data and control messages and the read timeout for handshake responses on the FrameStreamSockOutput's connection. The default timeout is zero, for no timeout.

type Input

type Input interface {
	ReadInto(chan []byte)
	Wait()
}

An Input is a source of dnstap data. It provides validation of the content type and will present any data read or received on the channel provided to the ReadInto method.

type Message

type Message struct {
	// One of the Type values described above.
	Type *Message_Type `protobuf:"varint,1,req,name=type,enum=dnstap.Message_Type" json:"type,omitempty"`
	// One of the SocketFamily values described above.
	SocketFamily *SocketFamily `protobuf:"varint,2,opt,name=socket_family,enum=dnstap.SocketFamily" json:"socket_family,omitempty"`
	// One of the SocketProtocol values described above.
	SocketProtocol *SocketProtocol `protobuf:"varint,3,opt,name=socket_protocol,enum=dnstap.SocketProtocol" json:"socket_protocol,omitempty"`
	// The network address of the message initiator.
	// For SocketFamily INET, this field is 4 octets (IPv4 address).
	// For SocketFamily INET6, this field is 16 octets (IPv6 address).
	QueryAddress []byte `protobuf:"bytes,4,opt,name=query_address" json:"query_address,omitempty"`
	// The network address of the message responder.
	// For SocketFamily INET, this field is 4 octets (IPv4 address).
	// For SocketFamily INET6, this field is 16 octets (IPv6 address).
	ResponseAddress []byte `protobuf:"bytes,5,opt,name=response_address" json:"response_address,omitempty"`
	// The transport port of the message initiator.
	// This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
	QueryPort *uint32 `protobuf:"varint,6,opt,name=query_port" json:"query_port,omitempty"`
	// The transport port of the message responder.
	// This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
	ResponsePort *uint32 `protobuf:"varint,7,opt,name=response_port" json:"response_port,omitempty"`
	// The time at which the DNS query message was sent or received, depending
	// on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY.
	// This is the number of seconds since the UNIX epoch.
	QueryTimeSec *uint64 `protobuf:"varint,8,opt,name=query_time_sec" json:"query_time_sec,omitempty"`
	// The time at which the DNS query message was sent or received.
	// This is the seconds fraction, expressed as a count of nanoseconds.
	QueryTimeNsec *uint32 `protobuf:"fixed32,9,opt,name=query_time_nsec" json:"query_time_nsec,omitempty"`
	// The initiator's original wire-format DNS query message, verbatim.
	QueryMessage []byte `protobuf:"bytes,10,opt,name=query_message" json:"query_message,omitempty"`
	// The "zone" or "bailiwick" pertaining to the DNS query message.
	// This is a wire-format DNS domain name.
	QueryZone []byte `protobuf:"bytes,11,opt,name=query_zone" json:"query_zone,omitempty"`
	// The time at which the DNS response message was sent or received,
	// depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or
	// CLIENT_RESPONSE.
	// This is the number of seconds since the UNIX epoch.
	ResponseTimeSec *uint64 `protobuf:"varint,12,opt,name=response_time_sec" json:"response_time_sec,omitempty"`
	// The time at which the DNS response message was sent or received.
	// This is the seconds fraction, expressed as a count of nanoseconds.
	ResponseTimeNsec *uint32 `protobuf:"fixed32,13,opt,name=response_time_nsec" json:"response_time_nsec,omitempty"`
	// The responder's original wire-format DNS response message, verbatim.
	ResponseMessage  []byte `protobuf:"bytes,14,opt,name=response_message" json:"response_message,omitempty"`
	XXX_unrecognized []byte `json:"-"`
}

Message: a wire-format (RFC 1035 section 4) DNS message and associated metadata. Applications generating "Message" payloads should follow certain requirements based on the MessageType, see below.

func (*Message) GetQueryAddress

func (m *Message) GetQueryAddress() []byte

func (*Message) GetQueryMessage

func (m *Message) GetQueryMessage() []byte

func (*Message) GetQueryPort

func (m *Message) GetQueryPort() uint32

func (*Message) GetQueryTimeNsec

func (m *Message) GetQueryTimeNsec() uint32

func (*Message) GetQueryTimeSec

func (m *Message) GetQueryTimeSec() uint64

func (*Message) GetQueryZone

func (m *Message) GetQueryZone() []byte

func (*Message) GetResponseAddress

func (m *Message) GetResponseAddress() []byte

func (*Message) GetResponseMessage

func (m *Message) GetResponseMessage() []byte

func (*Message) GetResponsePort

func (m *Message) GetResponsePort() uint32

func (*Message) GetResponseTimeNsec

func (m *Message) GetResponseTimeNsec() uint32

func (*Message) GetResponseTimeSec

func (m *Message) GetResponseTimeSec() uint64

func (*Message) GetSocketFamily

func (m *Message) GetSocketFamily() SocketFamily

func (*Message) GetSocketProtocol

func (m *Message) GetSocketProtocol() SocketProtocol

func (*Message) GetType

func (m *Message) GetType() Message_Type

func (*Message) ProtoMessage

func (*Message) ProtoMessage()

func (*Message) Reset

func (m *Message) Reset()

func (*Message) String

func (m *Message) String() string

type Message_Type

type Message_Type int32
const (
	// AUTH_QUERY is a DNS query message received from a resolver by an
	// authoritative name server, from the perspective of the authorative
	// name server.
	Message_AUTH_QUERY Message_Type = 1
	// AUTH_RESPONSE is a DNS response message sent from an authoritative
	// name server to a resolver, from the perspective of the authoritative
	// name server.
	Message_AUTH_RESPONSE Message_Type = 2
	// RESOLVER_QUERY is a DNS query message sent from a resolver to an
	// authoritative name server, from the perspective of the resolver.
	// Resolvers typically clear the RD (recursion desired) bit when
	// sending queries.
	Message_RESOLVER_QUERY Message_Type = 3
	// RESOLVER_RESPONSE is a DNS response message received from an
	// authoritative name server by a resolver, from the perspective of
	// the resolver.
	Message_RESOLVER_RESPONSE Message_Type = 4
	// CLIENT_QUERY is a DNS query message sent from a client to a DNS
	// server which is expected to perform further recursion, from the
	// perspective of the DNS server. The client may be a stub resolver or
	// forwarder or some other type of software which typically sets the RD
	// (recursion desired) bit when querying the DNS server. The DNS server
	// may be a simple forwarding proxy or it may be a full recursive
	// resolver.
	Message_CLIENT_QUERY Message_Type = 5
	// CLIENT_RESPONSE is a DNS response message sent from a DNS server to
	// a client, from the perspective of the DNS server. The DNS server
	// typically sets the RA (recursion available) bit when responding.
	Message_CLIENT_RESPONSE Message_Type = 6
	// FORWARDER_QUERY is a DNS query message sent from a downstream DNS
	// server to an upstream DNS server which is expected to perform
	// further recursion, from the perspective of the downstream DNS
	// server.
	Message_FORWARDER_QUERY Message_Type = 7
	// FORWARDER_RESPONSE is a DNS response message sent from an upstream
	// DNS server performing recursion to a downstream DNS server, from the
	// perspective of the downstream DNS server.
	Message_FORWARDER_RESPONSE Message_Type = 8
	// STUB_QUERY is a DNS query message sent from a stub resolver to a DNS
	// server, from the perspective of the stub resolver.
	Message_STUB_QUERY Message_Type = 9
	// STUB_RESPONSE is a DNS response message sent from a DNS server to a
	// stub resolver, from the perspective of the stub resolver.
	Message_STUB_RESPONSE Message_Type = 10
	// TOOL_QUERY is a DNS query message sent from a DNS software tool to a
	// DNS server, from the perspective of the tool.
	Message_TOOL_QUERY Message_Type = 11
	// TOOL_RESPONSE is a DNS response message received by a DNS software
	// tool from a DNS server, from the perspective of the tool.
	Message_TOOL_RESPONSE Message_Type = 12
)

func (Message_Type) Enum

func (x Message_Type) Enum() *Message_Type

func (Message_Type) String

func (x Message_Type) String() string

func (*Message_Type) UnmarshalJSON

func (x *Message_Type) UnmarshalJSON(data []byte) error

type Output

type Output interface {
	GetOutputChannel() chan []byte
	RunOutputLoop()
	Close()
}

An Output is a desintaion for dnstap data. It accepts data on the channel returned from the GetOutputChannel method. The RunOutputLoop() method processes data received on this channel, and returns after the Close() method is called.

type SocketFamily

type SocketFamily int32

SocketFamily: the network protocol family of a socket. This specifies how to interpret "network address" fields.

const (
	SocketFamily_INET  SocketFamily = 1
	SocketFamily_INET6 SocketFamily = 2
)

func (SocketFamily) Enum

func (x SocketFamily) Enum() *SocketFamily

func (SocketFamily) String

func (x SocketFamily) String() string

func (*SocketFamily) UnmarshalJSON

func (x *SocketFamily) UnmarshalJSON(data []byte) error

type SocketProtocol

type SocketProtocol int32

SocketProtocol: the transport protocol of a socket. This specifies how to interpret "transport port" fields.

const (
	SocketProtocol_UDP SocketProtocol = 1
	SocketProtocol_TCP SocketProtocol = 2
)

func (SocketProtocol) Enum

func (x SocketProtocol) Enum() *SocketProtocol

func (SocketProtocol) String

func (x SocketProtocol) String() string

func (*SocketProtocol) UnmarshalJSON

func (x *SocketProtocol) UnmarshalJSON(data []byte) error

type TextFormatFunc

type TextFormatFunc func(*Dnstap) ([]byte, bool)

A TextFormatFunc renders a dnstap message into a human readable format.

type TextOutput

type TextOutput struct {
	// contains filtered or unexported fields
}

TextOutput implements a dnstap Output rendering dnstap data as text.

func NewTextOutput

func NewTextOutput(writer io.Writer, format TextFormatFunc) (o *TextOutput)

NewTextOutput creates a TextOutput writing dnstap data to the given io.Writer in the text format given by the TextFormatFunc format.

func NewTextOutputFromFilename

func NewTextOutputFromFilename(fname string, format TextFormatFunc, doAppend bool) (o *TextOutput, err error)

NewTextOutputFromFilename creates a TextOutput writing dnstap data to a file with the given filename in the format given by format. If doAppend is false, the file is truncated if it already exists, otherwise the file is opened for appending.

func (*TextOutput) Close

func (o *TextOutput) Close()

Close closes the output channel and returns when all pending data has been written.

Close satisfies the dnstap Output interface.

func (*TextOutput) GetOutputChannel

func (o *TextOutput) GetOutputChannel() chan []byte

GetOutputChannel returns the channel on which the TextOutput accepts dnstap data.

GetOutputChannel satisfies the dnstap Output interface.

func (*TextOutput) RunOutputLoop

func (o *TextOutput) RunOutputLoop()

RunOutputLoop receives dnstap data sent on the output channel, formats it with the configured TextFormatFunc, and writes it to the file or io.Writer of the TextOutput.

RunOutputLoop satisfies the dnstap Output interface.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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