aboutsummaryrefslogtreecommitdiff
path: root/middleware/state.go
blob: f4cd43542167f4e39b5a5b2f4d43412664b8bdbe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package middleware

import (
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/miekg/dns"
)

// This file contains the state nd functions available for use in the templates.

// State contains some connection state and is useful in middleware.
type State struct {
	Root http.FileSystem // TODO(miek): needed?
	Req  *dns.Msg
	W    dns.ResponseWriter
}

// Now returns the current timestamp in the specified format.
func (s State) Now(format string) string { return time.Now().Format(format) }

// NowDate returns the current date/time that can be used in other time functions.
func (s State) NowDate() time.Time { return time.Now() }

// Header gets the heaser of the request in State.
func (s State) Header() *dns.RR_Header {
	// TODO(miek)
	return nil
}

// IP gets the (remote) IP address of the client making the request.
func (s State) IP() string {
	ip, _, err := net.SplitHostPort(s.W.RemoteAddr().String())
	if err != nil {
		return s.W.RemoteAddr().String()
	}
	return ip
}

// Post gets the (remote) Port of the client making the request.
func (s State) Port() (string, error) {
	_, port, err := net.SplitHostPort(s.W.RemoteAddr().String())
	if err != nil {
		return "0", err
	}
	return port, nil
}

// Proto gets the protocol used as the transport. This
// will be udp or tcp.
func (s State) Proto() string {
	if _, ok := s.W.RemoteAddr().(*net.UDPAddr); ok {
		return "udp"
	}
	if _, ok := s.W.RemoteAddr().(*net.TCPAddr); ok {
		return "tcp"
	}
	return "udp"
}

// Family returns the family of the transport.
// 1 for IPv4 and 2 for IPv6.
func (s State) Family() int {
	var a net.IP
	ip := s.W.RemoteAddr()
	if i, ok := ip.(*net.UDPAddr); ok {
		a = i.IP
	}
	if i, ok := ip.(*net.TCPAddr); ok {
		a = i.IP
	}

	if a.To4() != nil {
		return 1
	}
	return 2
}

// Do returns if the request has the DO (DNSSEC OK) bit set.
func (s State) Do() bool {
	if o := s.Req.IsEdns0(); o != nil {
		return o.Do()
	}
	return false
}

// UDPSize returns if UDP buffer size advertised in the requests OPT record.
// Or when the request was over TCP, we return the maximum allowed size of 64K.
func (s State) Size() int {
	if s.Proto() == "tcp" {
		return dns.MaxMsgSize
	}
	if o := s.Req.IsEdns0(); o != nil {
		s := o.UDPSize()
		if s < dns.MinMsgSize {
			s = dns.MinMsgSize
		}
		return int(s)
	}
	return dns.MinMsgSize
}

// SizeAndDo returns a ready made OPT record that the reflects the intent
// from the state. This can be added to upstream requests that will then
// hopefully return a message that is understandable by the original client.
func (s State) SizeAndDo() *dns.OPT {
	size := s.Size()
	Do := s.Do()

	o := new(dns.OPT)
	o.Hdr.Name = "."
	o.Hdr.Rrtype = dns.TypeOPT
	o.SetUDPSize(uint16(size))
	if Do {
		o.SetDo()
	}
	return o
}

// Type returns the type of the question as a string.
func (s State) Type() string { return dns.Type(s.Req.Question[0].Qtype).String() }

// QType returns the type of the question as a uint16.
func (s State) QType() uint16 { return s.Req.Question[0].Qtype }

// Name returns the name of the question in the request. Note
// this name will always have a closing dot and will be lower cased.
func (s State) Name() string { return strings.ToLower(dns.Name(s.Req.Question[0].Name).String()) }

// QName returns the name of the question in the request.
func (s State) QName() string { return dns.Name(s.Req.Question[0].Name).String() }

// Class returns the class of the question in the request.
func (s State) Class() string { return dns.Class(s.Req.Question[0].Qclass).String() }

// QClass returns the class of the question in the request.
func (s State) QClass() uint16 { return s.Req.Question[0].Qclass }

// ErrorMessage returns an error message suitable for sending
// back to the client.
func (s State) ErrorMessage(rcode int) *dns.Msg {
	m := new(dns.Msg)
	m.SetRcode(s.Req, rcode)
	return m
}

// AnswerMessage returns an error message suitable for sending
// back to the client.
func (s State) AnswerMessage() *dns.Msg {
	m := new(dns.Msg)
	m.SetReply(s.Req)
	return m
}