-
Notifications
You must be signed in to change notification settings - Fork 6
/
ipc.go
114 lines (99 loc) · 3.01 KB
/
ipc.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
package dcrdtest
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"os"
)
// ipcPipePair holds both ends of an IPC pipe used to communicate with dcrd.
type ipcPipePair struct {
r *os.File
w *os.File
// Whether to close the R and/or W ends.
closeR, closeW bool
}
// close closes the required ends of the pipe and returns the first error.
func (p ipcPipePair) close() error {
var errR, errW error
if p.closeR {
errR = p.r.Close()
}
if p.closeW {
errW = p.w.Close()
}
if errR == nil {
return errW
}
return errR
}
// newIPCPipePair creates a new IPC pipe pair.
func newIPCPipePair(closeR, closeW bool) (ipcPipePair, error) {
r, w, err := os.Pipe()
if err != nil {
return ipcPipePair{}, err
}
return ipcPipePair{r: r, w: w, closeR: closeR, closeW: closeW}, nil
}
// pipeMessage is a generic interface for dcrd pipe messages.
type pipeMessage interface{}
// boundP2PListenAddrEvent is a pipeMessage that tracks the P2P address of the
// underlying dcrd node.
type boundP2PListenAddrEvent string
// boundRPCListenAddrEvent is a pipeMessage that tracks the RPC address of the
// underlying dcrd node.
type boundRPCListenAddrEvent string
// nextIPCMessage returns the next dcrd IPC message read from the passed
// reading-end pipe.
//
// For unknown messages, this returns an empty pipeMessage instead of an error.
func nextIPCMessage(r io.Reader) (pipeMessage, error) {
var emptyMsg pipeMessage
const protocolVersion = 1
// Bufferize reads from the underlying file.
r = bufio.NewReader(r)
// Decode the header.
var bProto [1]byte
var bLenType [1]byte
var bType [255]byte
var bLenPay [4]byte
// Enforce the protocol version.
if _, err := io.ReadFull(r, bProto[:]); err != nil {
return emptyMsg, fmt.Errorf("unable to read protocol: %v", err)
}
gotProtoVersion := bProto[0]
if gotProtoVersion != protocolVersion {
return emptyMsg, fmt.Errorf("protocol version mismatch: %d != %d",
gotProtoVersion, protocolVersion)
}
// Decode rest of header.
if _, err := io.ReadFull(r, bLenType[:]); err != nil {
return emptyMsg, fmt.Errorf("unable to read type length: %v", err)
}
lenType := bLenType[0]
if _, err := io.ReadFull(r, bType[:lenType]); err != nil {
return emptyMsg, fmt.Errorf("unable to read type: %v", err)
}
if _, err := io.ReadFull(r, bLenPay[:]); err != nil {
return emptyMsg, fmt.Errorf("unable to read payload length: %v", err)
}
// The existing IPC messages are small, so reading the entire message
// in an in-memory buffer is feasible today.
lenPay := binary.LittleEndian.Uint32(bLenPay[:])
payload := make([]byte, lenPay)
if _, err := io.ReadFull(r, payload); err != nil {
return emptyMsg, fmt.Errorf("unable to read payload: %v", err)
}
// Decode the payload based on the type.
typ := string(bType[:lenType])
switch typ {
case "p2plistenaddr":
return boundP2PListenAddrEvent(string(payload)), nil
case "rpclistenaddr":
return boundRPCListenAddrEvent(string(payload)), nil
default:
// Other message types are unsupported but don't cause a read
// error.
return emptyMsg, nil
}
}