![Marc Fielding](/assets/img/avatar_default.png)
* Support port forwarding of literal IPv6 addresses To disambiguate between colons as host:port separators and as IPv6 address separators, literal IPv6 addresses use square brackets around the address (https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers). So host ::1, port 22 is written as [::1]:22, and therefore a simple concatenation of host, colon, and port doesn't work. Fortunately net.JoinHostPort already implements this functionality, so with a bit of type gymnastics we can generate dest in an IPv6-safe way. * Support port forwarding of literal IPv6 addresses To disambiguate between colons as host:port separators and as IPv6 address separators, literal IPv6 addresses use square brackets around the address (https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers). So host ::1, port 22 is written as [::1]:22, and therefore a simple concatenation of host, colon, and port doesn't work. Fortunately net.JoinHostPort already implements this functionality, so with a bit of type gymnastics we can generate dest in an IPv6-safe way.
59 lines
1.3 KiB
Go
59 lines
1.3 KiB
Go
package ssh
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
|
|
gossh "golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// direct-tcpip data struct as specified in RFC4254, Section 7.2
|
|
type forwardData struct {
|
|
DestinationHost string
|
|
DestinationPort uint32
|
|
|
|
OriginatorHost string
|
|
OriginatorPort uint32
|
|
}
|
|
|
|
func directTcpipHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) {
|
|
d := forwardData{}
|
|
if err := gossh.Unmarshal(newChan.ExtraData(), &d); err != nil {
|
|
newChan.Reject(gossh.ConnectionFailed, "error parsing forward data: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if srv.LocalPortForwardingCallback == nil || !srv.LocalPortForwardingCallback(ctx, d.DestinationHost, d.DestinationPort) {
|
|
newChan.Reject(gossh.Prohibited, "port forwarding is disabled")
|
|
return
|
|
}
|
|
|
|
dest := net.JoinHostPort(d.DestinationHost, strconv.FormatInt(int64(d.DestinationPort), 10))
|
|
|
|
var dialer net.Dialer
|
|
dconn, err := dialer.DialContext(ctx, "tcp", dest)
|
|
if err != nil {
|
|
newChan.Reject(gossh.ConnectionFailed, err.Error())
|
|
return
|
|
}
|
|
|
|
ch, reqs, err := newChan.Accept()
|
|
if err != nil {
|
|
dconn.Close()
|
|
return
|
|
}
|
|
go gossh.DiscardRequests(reqs)
|
|
|
|
go func() {
|
|
defer ch.Close()
|
|
defer dconn.Close()
|
|
io.Copy(ch, dconn)
|
|
}()
|
|
go func() {
|
|
defer ch.Close()
|
|
defer dconn.Close()
|
|
io.Copy(dconn, ch)
|
|
}()
|
|
}
|