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
|
package server4
import (
"fmt"
"log"
"net"
"sync"
"time"
"github.com/insomniacslk/dhcp/dhcpv4"
)
/*
To use the DHCPv4 server code you have to call NewServer with two arguments:
- a handler function, that will be called every time a valid DHCPv4 packet is
received, and
- an address to listen on.
The handler is a function that takes as input a packet connection, that can be
used to reply to the client; a peer address, that identifies the client sending
the request, and the DHCPv4 packet itself. Just implement your custom logic in
the handler.
The address to listen on is used to know IP address, port and optionally the
scope to create and UDP socket to listen on for DHCPv4 traffic.
Example program:
package main
import (
"log"
"net"
"github.com/insomniacslk/dhcp/dhcpv4"
)
func handler(conn net.PacketConn, peer net.Addr, m dhcpv4.DHCPv4) {
// this function will just print the received DHCPv4 message, without replying
log.Print(m.Summary())
}
func main() {
laddr := net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 67,
}
server := dhcpv4.NewServer(laddr, handler)
defer server.Close()
if err := server.ActivateAndServe(); err != nil {
log.Panic(err)
}
}
*/
// Handler is a type that defines the handler function to be called every time a
// valid DHCPv4 message is received
type Handler func(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4)
// Server represents a DHCPv4 server object
type Server struct {
conn net.PacketConn
connMutex sync.Mutex
shouldStop chan bool
Handler Handler
localAddr net.UDPAddr
}
// LocalAddr returns the local address of the listening socket, or nil if not
// listening
func (s *Server) LocalAddr() net.Addr {
s.connMutex.Lock()
defer s.connMutex.Unlock()
if s.conn == nil {
return nil
}
return s.conn.LocalAddr()
}
// ActivateAndServe starts the DHCPv4 server. The listener will run in
// background, and can be interrupted with `Server.Close`.
func (s *Server) ActivateAndServe() error {
s.connMutex.Lock()
if s.conn != nil {
// this may panic if s.conn is closed but not reset properly. For that
// you should use `Server.Close`.
s.Close()
}
conn, err := net.ListenUDP("udp4", &s.localAddr)
if err != nil {
s.connMutex.Unlock()
return err
}
s.conn = conn
s.connMutex.Unlock()
var (
pc *net.UDPConn
ok bool
)
if pc, ok = s.conn.(*net.UDPConn); !ok {
return fmt.Errorf("error: not an UDPConn")
}
if pc == nil {
return fmt.Errorf("ActivateAndServe: invalid nil PacketConn")
}
log.Printf("Server listening on %s", pc.LocalAddr())
log.Print("Ready to handle requests")
for {
select {
case <-s.shouldStop:
break
case <-time.After(time.Millisecond):
}
pc.SetReadDeadline(time.Now().Add(time.Second))
rbuf := make([]byte, 4096) // FIXME this is bad
n, peer, err := pc.ReadFrom(rbuf)
if err != nil {
switch err.(type) {
case net.Error:
if !err.(net.Error).Timeout() {
return err
}
// if timeout, silently skip and continue
default:
// complain and continue
log.Printf("Error reading from packet conn: %v", err)
}
continue
}
log.Printf("Handling request from %v", peer)
m, err := dhcpv4.FromBytes(rbuf[:n])
if err != nil {
log.Printf("Error parsing DHCPv4 request: %v", err)
continue
}
go s.Handler(pc, peer, m)
}
}
// Close sends a termination request to the server, and closes the UDP listener
func (s *Server) Close() error {
s.shouldStop <- true
s.connMutex.Lock()
defer s.connMutex.Unlock()
if s.conn != nil {
ret := s.conn.Close()
s.conn = nil
return ret
}
return nil
}
// NewServer initializes and returns a new Server object
func NewServer(addr net.UDPAddr, handler Handler) *Server {
return &Server{
localAddr: addr,
Handler: handler,
shouldStop: make(chan bool, 1),
}
}
|