-
Notifications
You must be signed in to change notification settings - Fork 8
/
verify.go
203 lines (177 loc) · 5.86 KB
/
verify.go
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package main
// Copyright (C) 2013 Alexander Bauer, Luke Evers, Daniel Supernault,
// Dylan Whichard, and contributors; (GPLv3) see LICENSE or doc.go
import (
"database/sql"
"errors"
"fmt"
"math/rand"
"net"
"net/http"
"time"
)
// QueueNode inserts the given node into the verify queue with its
// expiration time set to the current time plus the grace period, its
// emailsent field set by the matching argument, and identified by the
// given ID.
func (db DB) QueueNode(id int64, emailsent bool, grace Duration, node *Node) (err error) {
_, err = db.Exec(`INSERT INTO nodes_verify_queue
(id, address, owner, email, contact, details, pgp,
lat, lon, status, verifysent, expiration)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
id, []byte(node.Addr), node.OwnerName, node.OwnerEmail,
node.Contact, node.Details, []byte(node.PGP),
node.Latitude, node.Longitude, node.Status,
emailsent, time.Now().Add(time.Duration(grace)))
return
}
// DeleteExpiredFromQueue removes expired nodes from the verify queue
// by checking if their expiration stamp is past the current time.
func (db DB) DeleteExpiredFromQueue() (err error) {
_, err = db.Exec(`DELETE FROM nodes_verify_queue
WHERE expiration <= ?;`, time.Now())
return
}
// VerifyQueuedNode removes a node (as identified by the id) from the
// queue, performs VerifyRequest checks, and inserts it into the nodes
// table. If it encounters an error, the node remains in the verify
// queue.
func (db DB) VerifyQueuedNode(id int64, r *http.Request) (addr IP, verifyerr error, err error) {
// Get the node via the id.
var node = new(Node)
contact := sql.NullString{}
details := sql.NullString{}
err = db.QueryRow(`
SELECT address,owner,email,contact,details,pgp,lat,lon,status
FROM nodes_verify_queue WHERE id = ?;`, id).Scan(
&node.Addr, &node.OwnerName, &node.OwnerEmail,
&contact, &details, &node.PGP,
&node.Latitude, &node.Longitude, &node.Status)
if err != nil {
return
}
node.Contact = contact.String
node.Details = details.String
// Perform VerifyRequest checks.
verifyerr = VerifyRequest(node, r)
if verifyerr != nil {
return
}
err = db.AddNode(node)
if err != nil {
return
}
_, err = db.Exec(`DELETE FROM nodes_verify_queue
WHERE id = ?;`, id)
if err != nil {
l.Errf("Could not clear verified node %d: %s", id, err)
}
// Add it to the RSS feed. The feed will be refreshed at the next
// heartbeat.
AddNodeToRSS(node, time.Now())
return node.Addr, nil, nil
}
var (
NodeAddrNotContainedByNetmaskError = "verify: Node address not within configured netmask: %s"
)
// VerifyRegistrant performs appropriate registration-time checks to
// ensure that a Node is fit to be placed in the verification
// queue. If the given Node is acceptable, then no error will be
// returned.
func VerifyRegistrant(node *Node) error {
// Ensure that the node's address is contained by the netmask.
if Conf.Verify.Netmask != nil {
if !(*net.IPNet)(Conf.Verify.Netmask).Contains(net.IP(node.Addr)) {
return fmt.Errorf(NodeAddrNotContainedByNetmaskError,
Conf.Verify.Netmask)
}
}
return nil
}
var (
RemoteAddressDoesNotMatchError = errors.New(
"verify: remote address does not match Node address")
)
// VerifyRequest performs appropriate verification checks for a Node
// based on a received http.Request, as follows. Checks are only
// performed if they are enabled in the configuration. If all checks
// are successful, it returns nil.
//
// - Ensure that remote address matches the Node's address, OR it is
// an address specified in AdminAddresses in the config.
func VerifyRequest(node *Node, r *http.Request) error {
// Ensure that r.RemoteAddr matches node.Addr.
if Conf.Verify.FromNode {
if !net.IP(node.Addr).Equal(net.ParseIP(r.RemoteAddr)) &&
!IsAdmin(r) {
// If the node address and remote address don't match,
// then this verify step has failed.
return RemoteAddressDoesNotMatchError
}
}
return nil
}
// SendVerificationEmail uses the fields in Conf.SMTP to send a
// templated email (verification.txt) to the given email address. If
// the email could not be sent, it returns an error.
func SendVerificationEmail(id int64, recipientEmail string) (err error) {
// Prepare an Email type.
e := &Email{
To: recipientEmail,
From: Conf.SMTP.EmailAddress,
Subject: Conf.Name + " Node Registration",
}
e.Data = make(map[string]interface{}, 5)
e.Data["Link"] = Conf.Web.Hostname + Conf.Web.Prefix
e.Data["VerificationID"] = id
e.Data["FromNode"] = Conf.Verify.FromNode
e.Data["Flags"] = Conf.ExtraVerificationFlags
// Generate a random number for use as a boundary marker in the
// multipart/alternative email.
e.Data["Boundary"] = rand.Int31()
if err = e.Send("verification.txt"); err == nil {
l.Debugf("Sent verification email to %d", id)
}
return
}
// ResendVerificationEmails attempts to resend a verification email to
// every node in the verification queue that is marked as not yet
// notified. It logs errors.
func ResendVerificationEmails() {
rows, err := Db.Query(`SELECT id,email
FROM nodes_verify_queue
WHERE verifysent = 0;`)
if err != nil {
l.Errf("Error resending verification emails: %s", err)
return
}
// Allocate slice so that rows can be updated later.
verifysent := make([]int64, 0)
for rows.Next() {
var (
id int64
email string
)
if err = rows.Scan(&id, &email); err != nil {
l.Errf("Error resending verification email: %s", err)
continue
}
if err = SendVerificationEmail(id, email); err != nil {
l.Warningf("Could not send verification email to %q: %s", email, err)
} else {
verifysent = append(verifysent, id)
}
}
setVerifysent, err := Db.Prepare(`UPDATE nodes_verify_queue
SET verifysent = 1
WHERE id = ?;`)
if err != nil {
l.Errf("Error preparing verifysent statement: %s", err)
return
}
for _, id := range verifysent {
if _, err = setVerifysent.Exec(id); err != nil {
l.Warningf("Could not set verifysent for %d: %s", id, err)
}
}
}