/
channel.go
142 lines (130 loc) · 2.83 KB
/
channel.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
package main
/*
* channel.go
* Handle MitMing channels
* By J. Stuart McMurray
* Created 20160122
* Last Modified 20160122
*/
import (
"fmt"
"io"
"log"
"os"
"golang.org/x/crypto/ssh"
)
/* handleNewChannels handles proxying channel requests read from chans to the
SSH connection sc. info is used for logging. */
func handleNewChannels(
chans <-chan ssh.NewChannel,
sc ssh.Conn,
info string,
) {
for cr := range chans {
go handleNewChannel(cr, sc, info)
}
}
/* handleChannel proxies a channel request command or shell to the ssh
connection sc. */
func handleNewChannel(cr ssh.NewChannel, sc ssh.Conn, info string) {
log.Printf(
"%v Type:%q Data:%q NewChannel",
info,
cr.ChannelType(),
cr.ExtraData(),
)
/* Make the same request to the other side */
och, oreqs, err := sc.OpenChannel(cr.ChannelType(), cr.ExtraData())
if nil != err {
/* If we can't log it, and reject the client */
oe, ok := err.(*ssh.OpenChannelError)
var (
reason ssh.RejectionReason
message string
)
if !ok {
log.Printf(
"%v Type:%q Data:%q Unable to open channel: "+
"%v",
info,
cr.ChannelType(),
cr.ExtraData(),
err,
)
reason = ssh.ConnectionFailed
message = "Fail"
message = err.Error()
} else {
log.Printf(
"%v Type:%q Data:%q Reason:%q Message:%q "+
"Unable to open channel",
info,
cr.ChannelType(),
cr.ExtraData(),
oe.Reason.String(),
oe.Message,
)
reason = oe.Reason
message = oe.Message
}
if err := cr.Reject(reason, message); nil != err {
log.Printf(
"%v Unable to pass on channel rejecton "+
"request: %v",
info,
err,
)
}
return
}
defer och.Close()
/* Accept the channel request from the requestor */
rch, rreqs, err := cr.Accept()
if nil != err {
log.Printf(
"%v Unable to accept request for a channel of type "+
"%q: %v",
cr.ChannelType(),
info,
err,
)
return
}
defer rch.Close()
/* Handle passing requests between channels */
hcrinfo := fmt.Sprintf(" %v ChannelType:%q", info, cr.ChannelType())
go handleChannelRequests(
rreqs,
och,
hcrinfo+" ReqDir:AsDirection",
)
go handleChannelRequests(
oreqs,
rch,
hcrinfo+" ReqDir:AgainstDirection",
)
log.Printf(
"%v Type:%q Data:%q Opened",
info,
cr.ChannelType(),
cr.ExtraData(),
)
/* For now, print out read data */
done := make(chan struct{}, 4)
go copyOut(och, rch, done)
go copyOut(rch, och, done)
go copyOut(och.Stderr(), rch.Stderr(), done)
go copyOut(rch.Stderr(), och.Stderr(), done)
/* Wait for a pipe to break */
<-done
fmt.Printf("\nDone.\n")
}
/* copyOut Copies src to dst and to stdout, and nonblockingly sends on done
when there's no more left to copy */
func copyOut(dst io.Writer, src io.Reader, done chan<- struct{}) {
io.Copy(io.MultiWriter(os.Stdout, dst), src)
select {
case done <- struct{}{}:
default:
}
}