forked from ivcosla/dmsg-1
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathframe.go
185 lines (151 loc) · 4.44 KB
/
frame.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
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
package dmsg
import (
"encoding/binary"
"fmt"
"io"
"math"
"sync/atomic"
"time"
"github.com/SkycoinProject/dmsg/ioutil"
"github.com/SkycoinProject/dmsg/cipher"
)
const (
// Type returns the transport type string.
Type = "dmsg"
// HandshakePayloadVersion contains payload version to maintain compatibility with future versions
// of HandshakePayload format.
HandshakePayloadVersion = "1"
tpBufCap = math.MaxUint16
tpBufFrameCap = math.MaxUint8
tpAckCap = math.MaxUint8
headerLen = 5 // fType(1 byte), chID(2 byte), payLen(2 byte)
)
var (
// TransportHandshakeTimeout defines the duration a transport handshake should take.
TransportHandshakeTimeout = time.Second * 10
// AcceptBufferSize defines the size of the accepts buffer.
AcceptBufferSize = 20
)
// HandshakePayload represents format of payload sent with REQUEST frames.
// TODO(evanlinjin): Use 'dmsg.Addr' for PK:Port pair.
type HandshakePayload struct {
Version string `json:"version"` // just in case the struct changes.
InitPK cipher.PubKey `json:"init_pk"`
RespPK cipher.PubKey `json:"resp_pk"`
Port uint16 `json:"port"`
}
func isInitiatorID(tpID uint16) bool { return tpID%2 == 0 }
func randID(initiator bool) uint16 {
var id uint16
for {
id = binary.BigEndian.Uint16(cipher.RandByte(2))
if initiator && id%2 == 0 || !initiator && id%2 != 0 {
return id
}
}
}
var serveCount int64
func incrementServeCount() int64 { return atomic.AddInt64(&serveCount, 1) }
func decrementServeCount() int64 { return atomic.AddInt64(&serveCount, -1) }
// FrameType represents the frame type.
type FrameType byte
func (ft FrameType) String() string {
var names = []string{
RequestType: "REQUEST",
AcceptType: "ACCEPT",
CloseType: "CLOSE",
FwdType: "FWD",
AckType: "ACK",
OkType: "OK",
}
if int(ft) >= len(names) {
return fmt.Sprintf("UNKNOWN:%d", ft)
}
return names[ft]
}
// Frame types.
const (
OkType = FrameType(0x0)
RequestType = FrameType(0x1)
AcceptType = FrameType(0x2)
CloseType = FrameType(0x3)
FwdType = FrameType(0xa)
AckType = FrameType(0xb)
)
// Reasons for closing frames
const (
PlaceholderReason = iota
)
// Frame is the dmsg data unit.
type Frame []byte
// MakeFrame creates a new Frame.
func MakeFrame(ft FrameType, chID uint16, pay []byte) Frame {
f := make(Frame, headerLen+len(pay))
f[0] = byte(ft)
binary.BigEndian.PutUint16(f[1:3], chID)
binary.BigEndian.PutUint16(f[3:5], uint16(len(pay)))
copy(f[5:], pay)
return f
}
// Type returns the frame's type.
func (f Frame) Type() FrameType { return FrameType(f[0]) }
// TpID returns the frame's tp_id.
func (f Frame) TpID() uint16 { return binary.BigEndian.Uint16(f[1:3]) }
// PayLen returns the expected payload len.
func (f Frame) PayLen() int { return int(binary.BigEndian.Uint16(f[3:5])) }
// Pay returns the payload.
func (f Frame) Pay() []byte { return f[headerLen:] }
// Disassemble splits the frame into fields.
func (f Frame) Disassemble() (ft FrameType, id uint16, p []byte) {
return f.Type(), f.TpID(), f.Pay()
}
// String implements io.Stringer
func (f Frame) String() string {
var p string
switch f.Type() {
case FwdType, AckType:
p = fmt.Sprintf("<seq:%d>", ioutil.DecodeUint16Seq(f.Pay()))
}
return fmt.Sprintf("<type:%s><id:%d><size:%d>%s", f.Type(), f.TpID(), f.PayLen(), p)
}
func readFrame(r io.Reader) (Frame, error) {
f := make(Frame, headerLen)
if _, err := io.ReadFull(r, f); err != nil {
return nil, err
}
f = append(f, make([]byte, f.PayLen())...)
_, err := io.ReadFull(r, f[headerLen:])
return f, err
}
type writeError struct{ error }
func (e *writeError) Error() string { return "write error: " + e.error.Error() }
func isWriteError(err error) bool {
_, ok := err.(*writeError)
return ok
}
func writeFrame(w io.Writer, f Frame) error {
_, err := w.Write(f)
if err != nil {
return &writeError{err}
}
return nil
}
func writeFwdFrame(w io.Writer, id uint16, seq ioutil.Uint16Seq, p []byte) error {
return writeFrame(w, MakeFrame(FwdType, id, append(seq.Encode(), p...)))
}
func writeCloseFrame(w io.Writer, id uint16, reason byte) error {
return writeFrame(w, MakeFrame(CloseType, id, []byte{reason}))
}
func combinePKs(initPK, respPK cipher.PubKey) []byte {
return append(initPK[:], respPK[:]...)
}
func splitPKs(b []byte) (initPK, respPK cipher.PubKey, ok bool) {
const pkLen = 33
if len(b) != pkLen*2 {
ok = false
return
}
copy(initPK[:], b[:pkLen])
copy(respPK[:], b[pkLen:])
return initPK, respPK, true
}