/
server.go
171 lines (142 loc) · 4.04 KB
/
server.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
package chserver
import (
"log"
"net/http"
"time"
"github.com/jpillora/chisel/share"
"golang.org/x/crypto/ssh"
"golang.org/x/net/websocket"
"io"
"net"
)
type Config struct {
KeySeed string
Remote string
}
type Server struct {
*chshare.Logger
Remote string
fingerprint string
wsCount int
wsServer websocket.Server
httpServer *chshare.HTTPServer
sshConfig *ssh.ServerConfig
}
func NewServer(config *Config) (*Server, error) {
s := &Server{
Logger: chshare.NewLogger("server"),
wsServer: websocket.Server{},
httpServer: chshare.NewHTTPServer(),
}
s.wsServer.Handler = websocket.Handler(s.handleWS)
//generate private key (optionally using seed)
key, _ := chshare.GenerateKey(config.KeySeed)
//convert into ssh.PrivateKey
private, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatal("Failed to parse key")
}
//fingerprint this key
s.fingerprint = chshare.FingerprintKey(private.PublicKey())
//create ssh config
s.sshConfig = &ssh.ServerConfig{
ServerVersion: chshare.ProtocolVersion + "-server",
PasswordCallback: s.authUser,
}
s.sshConfig.AddHostKey(private)
s.Remote = config.Remote
return s, nil
}
func (s *Server) Close() error {
//this should cause an error in the open websockets
return s.httpServer.Close()
}
func (s *Server) HandleHTTP(w http.ResponseWriter, r *http.Request) {
// websockets upgrade AND has chisel prefix
if r.Header.Get("Upgrade") == "websocket" &&
r.Header.Get("Sec-WebSocket-Protocol") == chshare.ProtocolVersion {
s.wsServer.ServeHTTP(w, r)
return
}
//missing :O
w.WriteHeader(404)
}
func (s *Server) authUser(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
return nil, nil
}
func (s *Server) handleWS(ws *websocket.Conn) {
// Before use, a handshake must be performed on the incoming net.Conn.
sshConn, chans, reqs, err := ssh.NewServerConn(ws, s.sshConfig)
if err != nil {
s.Debugf("Failed to handshake (%s)", err)
return
}
//verify configuration
s.Debugf("Verifying configuration")
//wait for request, with timeout
var r *ssh.Request
select {
case r = <-reqs:
case <-time.After(10 * time.Second):
sshConn.Close()
return
}
failed := func(err error) {
r.Reply(false, []byte(err.Error()))
}
if r.Type != "config" {
failed(s.Errorf("expecting config request"))
return
}
_, err = chshare.DecodeConfig(r.Payload)
if err != nil {
failed(s.Errorf("invalid config"))
return
}
//success!
r.Reply(true, nil)
//prepare connection logger
s.wsCount++
id := s.wsCount
l := s.Fork("session#%d", id)
l.Debugf("Open")
go func() {
for r := range reqs {
switch r.Type {
case "ping":
r.Reply(true, nil)
default:
l.Debugf("Unknown request: %s", r.Type)
}
}
}()
go ConnectStreams(l, chans, s.Remote)
sshConn.Wait()
l.Debugf("Close")
}
func ConnectStreams(l *chshare.Logger, chans <-chan ssh.NewChannel, remote string) {
var streamCount int
for ch := range chans {
// string(ch.ExtraData())
stream, reqs, err := ch.Accept()
if err != nil {
l.Debugf("Failed to accept stream: %s", err)
continue
}
streamCount++
id := streamCount
go ssh.DiscardRequests(reqs)
go handleStream(l.Fork("stream#%d", id), stream, remote)
}
}
func handleStream(l *chshare.Logger, src io.ReadWriteCloser, remote string) {
dst, err := net.Dial("tcp", remote)
if err != nil {
l.Debugf("%s", err)
src.Close()
return
}
l.Debugf("Open")
s, r := chshare.Pipe(src, dst)
l.Debugf("Close (sent %d received %d)", s, r)
}