-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsession.go
164 lines (134 loc) · 3.8 KB
/
session.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
package godrop
import (
"bufio"
"compress/gzip"
"fmt"
"io"
"net"
"strconv"
"strings"
)
const (
AUTH_PACKET = 60
CLONE_PACKET = 61
CLONE_PACKET_ACK = 62
CLONE_PACKET_NAK = 63
AUTH_PACKET_LENGTH = 65
CLONE_ACKNACK_LENGTH = 65
BUF_SIZE = 1024
PADDING = "/"
)
// Session represents the connection between 2 peers
type Session struct {
conn net.Conn
reader *bufio.Reader
writer *bufio.Writer
DebugWriter io.Writer
LocalHost string
RemoteHost string
RemoteService string
RemotePort int
RemoteIP string
RemoteDroplet string
isAuthenticated bool
isEncrypted bool
}
// NewSession returns a new session instance.
// If private or public key pairs are nil, session will be unencrypted.
func NewSession(conn net.Conn, clientFlag, isEncrypted bool) (*Session, error) {
sesh := new(Session)
sesh.conn = conn
sesh.reader = bufio.NewReader(conn)
sesh.writer = bufio.NewWriter(conn)
sesh.isEncrypted = isEncrypted
sesh.RemoteIP = conn.RemoteAddr().String()
sesh.isAuthenticated = false
return sesh, nil
}
// IsEncrypted returns true if the underlying tcp connection is encrypted
func (s *Session) IsEncrypted() bool {
return s.isEncrypted
}
// Close closes the underlying tcp connection
func (s *Session) Close() {
s.conn.Close()
}
// Authenticate sends an AUTH_PACKET to the remote peer
//
// [1-Byte AUTH_PACKET][64 BYTES LOCAL HOST NAME]
func (s *Session) Authenticate() (Header, error) {
authType := []byte{AUTH_PACKET}
host := fillString(s.LocalHost, 64)
s.writer.Write(authType)
s.writer.Write([]byte(host))
s.writer.Flush()
response := make([]byte, 74)
if _, err := io.ReadFull(s.reader, response); err != nil {
return Header{}, err
}
size := response[:10]
name := response[10:]
contentLength, err := strconv.ParseInt(strings.Trim(string(size), PADDING), 10, 64)
if err != nil {
return Header{}, err
}
contentName := strings.Trim(string(name), PADDING)
header := Header{
Size: contentLength,
Name: contentName,
}
return header, nil
}
// SendHeader sends a header packet to the remote peer
//
// [10-byte content-length][64-byte content name]
func (s *Session) SendHeader(name string, size int64) {
contentLength := fillString(strconv.FormatInt(size, 10), 10)
contentName := fillString(name, 64)
s.writer.Write([]byte(contentLength))
s.writer.Write([]byte(contentName))
s.writer.Flush()
}
// TransferContent writes the contents of dir into a gzip writer.
// The underlying gzip writer writes into s.writer which represents the underlying
// tcp connection
//
// tarWriter ---> gzipWriter ---> bufioWriter ---> net.Conn
func (s *Session) TransferContent(dir string) error {
gzw := gzip.NewWriter(s.writer)
defer s.Close() // close underlying tcp conn
defer s.writer.Flush() // close buffio.Writer
defer gzw.Close() // close gzip writer
WriteTarball(gzw, dir)
return nil
}
// CloneDir clones the advertised directory into dir
//
// tarReader <--- gzipReader <--- bufioReader <--- net.Conn
func (s *Session) CloneDir(dir string) error {
// first send a packet to the peer indicating that we want to clone the content
clonePacket := []byte{CLONE_PACKET}
padding := fillString(PADDING, 64)
s.writer.Write(clonePacket)
s.writer.Write([]byte(padding))
s.writer.Flush()
// wait for the ack or nak
response := make([]byte, CLONE_ACKNACK_LENGTH)
if _, err := io.ReadFull(s.reader, response); err != nil {
return err
}
// ACCESS-DENIED
if response[0] == CLONE_PACKET_NAK {
reason := response[1:]
return fmt.Errorf(strings.Trim(string(reason), PADDING))
}
gzr, err := gzip.NewReader(s.reader)
if err != nil {
return err
}
if s.DebugWriter != nil {
tr := io.TeeReader(gzr, s.DebugWriter)
return ReadTarball(tr, dir)
}
return ReadTarball(gzr, dir)
}