-
Notifications
You must be signed in to change notification settings - Fork 6
/
bird_socket.go
122 lines (98 loc) · 2.6 KB
/
bird_socket.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
package birdsocket
import (
"net"
"regexp"
"strings"
)
var birdReturnCodeRegex *regexp.Regexp
func init() {
// Requests are commands encoded as a single line of text,
// replies are sequences of lines starting with a four-digit code
// followed by either a space (if it's the last line of the reply)
// or a minus sign (when the reply is going to continue with the next line),
// the rest of the line contains a textual message semantics of which depends
// on the numeric code.
// https://gitlab.nic.cz/labs/bird/-/blob/master/doc/reply_codes
birdReturnCodeRegex = regexp.MustCompile(`(?m)^([089]\d{3})`)
}
// BirdSocket encapsulates communication with Bird routing daemon
type BirdSocket struct {
socketPath string
bufferSize int
conn net.Conn
}
// BirdSocketOption applies options to BirdSocket
type Option func(*BirdSocket)
// WithBufferSize sets the buffer size for BirdSocket
func WithBufferSize(bufferSize int) Option {
return func(s *BirdSocket) {
s.bufferSize = bufferSize
}
}
// NewSocket creates a new socket
func NewSocket(socketPath string, opts ...Option) *BirdSocket {
socket := &BirdSocket{socketPath: socketPath, bufferSize: 4096}
for _, o := range opts {
o(socket)
}
return socket
}
// Query sends an ad hoc query to Bird and waits for the reply
func Query(socketPath, qry string) ([]byte, error) {
s := NewSocket(socketPath)
_, err := s.Connect()
if err != nil {
return nil, err
}
defer s.Close()
return s.Query(qry)
}
// Connect connects to the Bird unix socket
func (s *BirdSocket) Connect() ([]byte, error) {
var err error
s.conn, err = net.Dial("unix", s.socketPath)
if err != nil {
return nil, err
}
buf := make([]byte, s.bufferSize)
n, err := s.conn.Read(buf[:])
if err != nil {
return nil, err
}
return buf[:n], err
}
// Close closes the connection to the socket
func (s *BirdSocket) Close() {
if s.conn != nil {
s.conn.Close()
}
}
// Query sends an query to Bird and waits for the reply
func (s *BirdSocket) Query(qry string) ([]byte, error) {
_, err := s.conn.Write([]byte(strings.Trim(qry, "\n") + "\n"))
if err != nil {
return nil, err
}
output, err := s.readFromSocket(s.conn)
if err != nil {
return nil, err
}
return output, nil
}
func (s *BirdSocket) readFromSocket(conn net.Conn) ([]byte, error) {
b := make([]byte, 0)
buf := make([]byte, s.bufferSize)
done := false
for !done {
n, err := conn.Read(buf[:])
if err != nil {
return nil, err
}
b = append(b, buf[:n]...)
done = containsActionCompletedCode(buf[:n])
}
return b, nil
}
func containsActionCompletedCode(b []byte) bool {
return birdReturnCodeRegex.Match(b)
}