forked from skycoin/dmsg-deprecated
-
Notifications
You must be signed in to change notification settings - Fork 0
/
frame.go
168 lines (136 loc) · 3.85 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
package dmsg
import (
"encoding/binary"
"fmt"
"io"
"math"
"sync/atomic"
"time"
"github.com/skycoin/dmsg/ioutil"
"github.com/skycoin/dmsg/cipher"
)
const (
// Type returns the transport type string.
Type = "dmsg"
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
)
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)
)
// 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
}