-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.go
More file actions
210 lines (183 loc) · 5.78 KB
/
Copy pathserver.go
File metadata and controls
210 lines (183 loc) · 5.78 KB
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
204
205
206
207
208
209
210
// SPDX-License-Identifier: AGPL-3.0-or-later
package nameserver
import (
"fmt"
"log/slog"
"net"
"github.com/pilot-protocol/common/coreapi"
"github.com/pilot-protocol/common/protocol"
)
// PortListener abstracts the ability to listen on a Pilot overlay port.
// Satisfied by *driver.Driver (via a thin wrapper in cmd/nameserver).
type PortListener interface {
Listen(port uint16) (net.Listener, error)
}
// Server is the Pilot Protocol nameserver. It runs on the overlay
// network itself, listening on port 53.
//
// Trust boundary note: the nameserver responds to DNS queries from any registered
// node without trust gating. This is intentional — DNS is a public lookup service
// (like real-world DNS), and hostname→address mappings are not considered private.
// Private nodes are protected at the resolve/connect layer, not at name resolution.
type Server struct {
store *RecordStore
listener PortListener
ln net.Listener
ready chan struct{}
}
// New creates a nameserver backed by a fresh record store.
// If storePath is non-empty, records are persisted to that file.
func New(pl PortListener, storePath string) *Server {
store := NewRecordStore()
if storePath != "" {
store.SetStorePath(storePath)
}
return &Server{
store: store,
listener: pl,
ready: make(chan struct{}),
}
}
// Ready returns a channel that is closed once the server is listening.
func (s *Server) Ready() <-chan struct{} {
return s.ready
}
// Store returns the underlying record store for external manipulation.
func (s *Server) Store() *RecordStore {
return s.store
}
// ListenAndServe listens on Pilot port 53 and handles name queries.
func (s *Server) ListenAndServe() error {
ln, err := s.listener.Listen(protocol.PortNameserver)
if err != nil {
return fmt.Errorf("listen port %d: %w", protocol.PortNameserver, err)
}
s.ln = ln
close(s.ready)
slog.Info("nameserver listening", "port", protocol.PortNameserver)
for {
conn, err := ln.Accept()
if err != nil {
return fmt.Errorf("accept: %w", err)
}
go s.handleConn(conn)
}
}
// Close shuts down the nameserver.
func (s *Server) Close() error {
if s.ln != nil {
return s.ln.Close()
}
return nil
}
func (s *Server) handleConn(conn net.Conn) {
// L11 panic boundary: tear down THIS conn only.
// TODO(03-INVARIANTS.md §8): the standalone nameserver binary has
// no event bus today; once the plugin is wired into cmd/daemon's
// plugin runtime, thread the bus through here.
defer coreapi.RecoverPlugin("nameserver", "handleConn", nil, nil)
defer conn.Close()
buf := make([]byte, 65535)
n, err := conn.Read(buf)
if err != nil {
return
}
line := string(buf[:n])
req, err := ParseRequest(line)
if err != nil {
_, _ = conn.Write([]byte(FormatResponseErr(err.Error())))
return
}
resp := s.handleRequest(req, conn.RemoteAddr())
_, _ = conn.Write([]byte(resp))
}
func (s *Server) handleRequest(req Request, remoteAddr net.Addr) string {
switch req.Command {
case "QUERY":
return s.handleQuery(req)
case "REGISTER":
return s.handleRegister(req, remoteAddr)
default:
return FormatResponseErr("unknown command: " + req.Command)
}
}
func (s *Server) handleQuery(req Request) string {
switch req.RecordType {
case RecordA:
addr, err := s.store.LookupA(req.Name)
if err != nil {
return FormatResponseErr(err.Error())
}
return FormatResponseA(req.Name, addr)
case RecordN:
netID, err := s.store.LookupN(req.Name)
if err != nil {
return FormatResponseErr(err.Error())
}
return FormatResponseN(req.Name, netID)
case RecordS:
entries := s.store.LookupS(req.NetID, req.Port)
return FormatResponseS(entries)
default:
return FormatResponseErr("unknown record type: " + req.RecordType)
}
}
func (s *Server) handleRegister(req Request, remoteAddr net.Addr) string {
// Reject excessively long names to prevent memory DoS.
if len(req.Name) > MaxNameLength {
return FormatResponseErr(fmt.Sprintf("name too long: %d bytes (max %d)", len(req.Name), MaxNameLength))
}
// Extract caller's node ID from RemoteAddr for source validation
callerNode := extractCallerNode(remoteAddr)
switch req.RecordType {
case RecordA:
addr, err := protocol.ParseAddr(req.Address)
if err != nil {
return FormatResponseErr(err.Error())
}
// Validate: caller can only register addresses for their own node
if callerNode != 0 && addr.Node != callerNode {
return FormatResponseErr("cannot register address for another node")
}
s.store.RegisterA(req.Name, addr)
slog.Debug("nameserver registered A record", "name", req.Name, "addr", addr)
return FormatResponseOK()
case RecordN:
s.store.RegisterN(req.Name, req.NetID)
slog.Debug("nameserver registered N record", "name", req.Name, "network_id", req.NetID)
return FormatResponseOK()
case RecordS:
addr, err := protocol.ParseAddr(req.Address)
if err != nil {
return FormatResponseErr(err.Error())
}
// Validate: caller can only register services for their own node
if callerNode != 0 && addr.Node != callerNode {
return FormatResponseErr("cannot register service for another node")
}
s.store.RegisterS(req.Name, addr, req.NetID, req.Port)
slog.Debug("nameserver registered S record", "name", req.Name, "addr", addr, "port", req.Port, "network_id", req.NetID)
return FormatResponseOK()
default:
return FormatResponseErr("unknown record type: " + req.RecordType)
}
}
// extractCallerNode gets the node ID from a driver.Conn RemoteAddr().
// RemoteAddr format: "N:NNNN.HHHH.LLLL:port" — we parse the address part.
func extractCallerNode(addr net.Addr) uint32 {
if addr == nil {
return 0
}
s := addr.String()
// driver.Conn.RemoteAddr returns "addr:port"
// Try to parse the address portion
host, _, err := net.SplitHostPort(s)
if err != nil {
host = s
}
pilotAddr, err := protocol.ParseAddr(host)
if err != nil {
return 0
}
return pilotAddr.Node
}