forked from czerwonk/bird_socket
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbird_socket.go
129 lines (106 loc) · 2.73 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
123
124
125
126
127
128
129
package birdsocket
import (
"bytes"
"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.
birdReturnCodeRegex = regexp.MustCompile(`(?m)^(\d{4})`)
}
// 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(b)
}
return b, nil
}
func containsActionCompletedCode(b []byte) bool {
codes := birdReturnCodeRegex.FindAll(b, -1)
for _, c := range codes {
// Reply codes starting with 0 stand for
// `action successfully completed' messages
if bytes.HasPrefix(c, []byte("0")) {
return true
}
}
return false
}