|
|
@ -20,12 +20,20 @@ import ( |
|
|
|
// addr. Incoming connections will be available by calling Accept on
|
|
|
|
// addr. Incoming connections will be available by calling Accept on
|
|
|
|
// the returned net.Listener. The listener must be serviced, or the
|
|
|
|
// the returned net.Listener. The listener must be serviced, or the
|
|
|
|
// SSH connection may hang.
|
|
|
|
// SSH connection may hang.
|
|
|
|
|
|
|
|
// N must be "tcp", "tcp4", "tcp6", or "unix".
|
|
|
|
func (c *Client) Listen(n, addr string) (net.Listener, error) { |
|
|
|
func (c *Client) Listen(n, addr string) (net.Listener, error) { |
|
|
|
|
|
|
|
switch n { |
|
|
|
|
|
|
|
case "tcp", "tcp4", "tcp6": |
|
|
|
laddr, err := net.ResolveTCPAddr(n, addr) |
|
|
|
laddr, err := net.ResolveTCPAddr(n, addr) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
return c.ListenTCP(laddr) |
|
|
|
return c.ListenTCP(laddr) |
|
|
|
|
|
|
|
case "unix": |
|
|
|
|
|
|
|
return c.ListenUnix(addr) |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Automatic port allocation is broken with OpenSSH before 6.0. See
|
|
|
|
// Automatic port allocation is broken with OpenSSH before 6.0. See
|
|
|
@ -116,7 +124,7 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Register this forward, using the port number we obtained.
|
|
|
|
// Register this forward, using the port number we obtained.
|
|
|
|
ch := c.forwards.add(*laddr) |
|
|
|
ch := c.forwards.add(laddr) |
|
|
|
|
|
|
|
|
|
|
|
return &tcpListener{laddr, c, ch}, nil |
|
|
|
return &tcpListener{laddr, c, ch}, nil |
|
|
|
} |
|
|
|
} |
|
|
@ -131,7 +139,7 @@ type forwardList struct { |
|
|
|
// forwardEntry represents an established mapping of a laddr on a
|
|
|
|
// forwardEntry represents an established mapping of a laddr on a
|
|
|
|
// remote ssh server to a channel connected to a tcpListener.
|
|
|
|
// remote ssh server to a channel connected to a tcpListener.
|
|
|
|
type forwardEntry struct { |
|
|
|
type forwardEntry struct { |
|
|
|
laddr net.TCPAddr |
|
|
|
laddr net.Addr |
|
|
|
c chan forward |
|
|
|
c chan forward |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -140,15 +148,15 @@ type forwardEntry struct { |
|
|
|
// the original forward-request.
|
|
|
|
// the original forward-request.
|
|
|
|
type forward struct { |
|
|
|
type forward struct { |
|
|
|
newCh NewChannel // the ssh client channel underlying this forward
|
|
|
|
newCh NewChannel // the ssh client channel underlying this forward
|
|
|
|
raddr *net.TCPAddr // the raddr of the incoming connection
|
|
|
|
raddr net.Addr // the raddr of the incoming connection
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *forwardList) add(addr net.TCPAddr) chan forward { |
|
|
|
func (l *forwardList) add(addr net.Addr) chan forward { |
|
|
|
l.Lock() |
|
|
|
l.Lock() |
|
|
|
defer l.Unlock() |
|
|
|
defer l.Unlock() |
|
|
|
f := forwardEntry{ |
|
|
|
f := forwardEntry{ |
|
|
|
addr, |
|
|
|
laddr: addr, |
|
|
|
make(chan forward, 1), |
|
|
|
c: make(chan forward, 1), |
|
|
|
} |
|
|
|
} |
|
|
|
l.entries = append(l.entries, f) |
|
|
|
l.entries = append(l.entries, f) |
|
|
|
return f.c |
|
|
|
return f.c |
|
|
@ -176,8 +184,15 @@ func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) { |
|
|
|
|
|
|
|
|
|
|
|
func (l *forwardList) handleChannels(in <-chan NewChannel) { |
|
|
|
func (l *forwardList) handleChannels(in <-chan NewChannel) { |
|
|
|
for ch := range in { |
|
|
|
for ch := range in { |
|
|
|
|
|
|
|
var ( |
|
|
|
|
|
|
|
laddr net.Addr |
|
|
|
|
|
|
|
raddr net.Addr |
|
|
|
|
|
|
|
err error |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
switch channelType := ch.ChannelType(); channelType { |
|
|
|
|
|
|
|
case "forwarded-tcpip": |
|
|
|
var payload forwardedTCPPayload |
|
|
|
var payload forwardedTCPPayload |
|
|
|
if err := Unmarshal(ch.ExtraData(), &payload); err != nil { |
|
|
|
if err = Unmarshal(ch.ExtraData(), &payload); err != nil { |
|
|
|
ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) |
|
|
|
ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
@ -187,33 +202,51 @@ func (l *forwardList) handleChannels(in <-chan NewChannel) { |
|
|
|
// format. It is implied that this should be an IP
|
|
|
|
// format. It is implied that this should be an IP
|
|
|
|
// address, as it would be impossible to connect to it
|
|
|
|
// address, as it would be impossible to connect to it
|
|
|
|
// otherwise.
|
|
|
|
// otherwise.
|
|
|
|
laddr, err := parseTCPAddr(payload.Addr, payload.Port) |
|
|
|
laddr, err = parseTCPAddr(payload.Addr, payload.Port) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
ch.Reject(ConnectionFailed, err.Error()) |
|
|
|
ch.Reject(ConnectionFailed, err.Error()) |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort) |
|
|
|
raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
ch.Reject(ConnectionFailed, err.Error()) |
|
|
|
ch.Reject(ConnectionFailed, err.Error()) |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ok := l.forward(*laddr, *raddr, ch); !ok { |
|
|
|
case "forwarded-streamlocal@openssh.com": |
|
|
|
|
|
|
|
var payload forwardedStreamLocalPayload |
|
|
|
|
|
|
|
if err = Unmarshal(ch.ExtraData(), &payload); err != nil { |
|
|
|
|
|
|
|
ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error()) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
laddr = &net.UnixAddr{ |
|
|
|
|
|
|
|
Name: payload.SocketPath, |
|
|
|
|
|
|
|
Net: "unix", |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
raddr = &net.UnixAddr{ |
|
|
|
|
|
|
|
Name: "@", |
|
|
|
|
|
|
|
Net: "unix", |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
panic(fmt.Errorf("ssh: unknown channel type %s", channelType)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if ok := l.forward(laddr, raddr, ch); !ok { |
|
|
|
// Section 7.2, implementations MUST reject spurious incoming
|
|
|
|
// Section 7.2, implementations MUST reject spurious incoming
|
|
|
|
// connections.
|
|
|
|
// connections.
|
|
|
|
ch.Reject(Prohibited, "no forward for address") |
|
|
|
ch.Reject(Prohibited, "no forward for address") |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// remove removes the forward entry, and the channel feeding its
|
|
|
|
// remove removes the forward entry, and the channel feeding its
|
|
|
|
// listener.
|
|
|
|
// listener.
|
|
|
|
func (l *forwardList) remove(addr net.TCPAddr) { |
|
|
|
func (l *forwardList) remove(addr net.Addr) { |
|
|
|
l.Lock() |
|
|
|
l.Lock() |
|
|
|
defer l.Unlock() |
|
|
|
defer l.Unlock() |
|
|
|
for i, f := range l.entries { |
|
|
|
for i, f := range l.entries { |
|
|
|
if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { |
|
|
|
if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() { |
|
|
|
l.entries = append(l.entries[:i], l.entries[i+1:]...) |
|
|
|
l.entries = append(l.entries[:i], l.entries[i+1:]...) |
|
|
|
close(f.c) |
|
|
|
close(f.c) |
|
|
|
return |
|
|
|
return |
|
|
@ -231,12 +264,12 @@ func (l *forwardList) closeAll() { |
|
|
|
l.entries = nil |
|
|
|
l.entries = nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool { |
|
|
|
func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool { |
|
|
|
l.Lock() |
|
|
|
l.Lock() |
|
|
|
defer l.Unlock() |
|
|
|
defer l.Unlock() |
|
|
|
for _, f := range l.entries { |
|
|
|
for _, f := range l.entries { |
|
|
|
if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port { |
|
|
|
if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() { |
|
|
|
f.c <- forward{ch, &raddr} |
|
|
|
f.c <- forward{newCh: ch, raddr: raddr} |
|
|
|
return true |
|
|
|
return true |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -262,7 +295,7 @@ func (l *tcpListener) Accept() (net.Conn, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
go DiscardRequests(incoming) |
|
|
|
go DiscardRequests(incoming) |
|
|
|
|
|
|
|
|
|
|
|
return &tcpChanConn{ |
|
|
|
return &chanConn{ |
|
|
|
Channel: ch, |
|
|
|
Channel: ch, |
|
|
|
laddr: l.laddr, |
|
|
|
laddr: l.laddr, |
|
|
|
raddr: s.raddr, |
|
|
|
raddr: s.raddr, |
|
|
@ -277,7 +310,7 @@ func (l *tcpListener) Close() error { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// this also closes the listener.
|
|
|
|
// this also closes the listener.
|
|
|
|
l.conn.forwards.remove(*l.laddr) |
|
|
|
l.conn.forwards.remove(l.laddr) |
|
|
|
ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) |
|
|
|
ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) |
|
|
|
if err == nil && !ok { |
|
|
|
if err == nil && !ok { |
|
|
|
err = errors.New("ssh: cancel-tcpip-forward failed") |
|
|
|
err = errors.New("ssh: cancel-tcpip-forward failed") |
|
|
@ -293,6 +326,9 @@ func (l *tcpListener) Addr() net.Addr { |
|
|
|
// Dial initiates a connection to the addr from the remote host.
|
|
|
|
// Dial initiates a connection to the addr from the remote host.
|
|
|
|
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
|
|
|
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
|
|
|
func (c *Client) Dial(n, addr string) (net.Conn, error) { |
|
|
|
func (c *Client) Dial(n, addr string) (net.Conn, error) { |
|
|
|
|
|
|
|
var ch Channel |
|
|
|
|
|
|
|
switch n { |
|
|
|
|
|
|
|
case "tcp", "tcp4", "tcp6": |
|
|
|
// Parse the address into host and numeric port.
|
|
|
|
// Parse the address into host and numeric port.
|
|
|
|
host, portString, err := net.SplitHostPort(addr) |
|
|
|
host, portString, err := net.SplitHostPort(addr) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -302,20 +338,40 @@ func (c *Client) Dial(n, addr string) (net.Conn, error) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
// Use a zero address for local and remote address.
|
|
|
|
// Use a zero address for local and remote address.
|
|
|
|
zeroAddr := &net.TCPAddr{ |
|
|
|
zeroAddr := &net.TCPAddr{ |
|
|
|
IP: net.IPv4zero, |
|
|
|
IP: net.IPv4zero, |
|
|
|
Port: 0, |
|
|
|
Port: 0, |
|
|
|
} |
|
|
|
} |
|
|
|
ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port)) |
|
|
|
return &chanConn{ |
|
|
|
|
|
|
|
Channel: ch, |
|
|
|
|
|
|
|
laddr: zeroAddr, |
|
|
|
|
|
|
|
raddr: zeroAddr, |
|
|
|
|
|
|
|
}, nil |
|
|
|
|
|
|
|
case "unix": |
|
|
|
|
|
|
|
var err error |
|
|
|
|
|
|
|
ch, err = c.dialStreamLocal(addr) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
return &tcpChanConn{ |
|
|
|
return &chanConn{ |
|
|
|
Channel: ch, |
|
|
|
Channel: ch, |
|
|
|
laddr: zeroAddr, |
|
|
|
laddr: &net.UnixAddr{ |
|
|
|
raddr: zeroAddr, |
|
|
|
Name: "@", |
|
|
|
|
|
|
|
Net: "unix", |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
raddr: &net.UnixAddr{ |
|
|
|
|
|
|
|
Name: addr, |
|
|
|
|
|
|
|
Net: "unix", |
|
|
|
|
|
|
|
}, |
|
|
|
}, nil |
|
|
|
}, nil |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DialTCP connects to the remote address raddr on the network net,
|
|
|
|
// DialTCP connects to the remote address raddr on the network net,
|
|
|
@ -332,7 +388,7 @@ func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
return &tcpChanConn{ |
|
|
|
return &chanConn{ |
|
|
|
Channel: ch, |
|
|
|
Channel: ch, |
|
|
|
laddr: laddr, |
|
|
|
laddr: laddr, |
|
|
|
raddr: raddr, |
|
|
|
raddr: raddr, |
|
|
@ -366,26 +422,26 @@ type tcpChan struct { |
|
|
|
Channel // the backing channel
|
|
|
|
Channel // the backing channel
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// tcpChanConn fulfills the net.Conn interface without
|
|
|
|
// chanConn fulfills the net.Conn interface without
|
|
|
|
// the tcpChan having to hold laddr or raddr directly.
|
|
|
|
// the tcpChan having to hold laddr or raddr directly.
|
|
|
|
type tcpChanConn struct { |
|
|
|
type chanConn struct { |
|
|
|
Channel |
|
|
|
Channel |
|
|
|
laddr, raddr net.Addr |
|
|
|
laddr, raddr net.Addr |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// LocalAddr returns the local network address.
|
|
|
|
// LocalAddr returns the local network address.
|
|
|
|
func (t *tcpChanConn) LocalAddr() net.Addr { |
|
|
|
func (t *chanConn) LocalAddr() net.Addr { |
|
|
|
return t.laddr |
|
|
|
return t.laddr |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// RemoteAddr returns the remote network address.
|
|
|
|
// RemoteAddr returns the remote network address.
|
|
|
|
func (t *tcpChanConn) RemoteAddr() net.Addr { |
|
|
|
func (t *chanConn) RemoteAddr() net.Addr { |
|
|
|
return t.raddr |
|
|
|
return t.raddr |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// SetDeadline sets the read and write deadlines associated
|
|
|
|
// SetDeadline sets the read and write deadlines associated
|
|
|
|
// with the connection.
|
|
|
|
// with the connection.
|
|
|
|
func (t *tcpChanConn) SetDeadline(deadline time.Time) error { |
|
|
|
func (t *chanConn) SetDeadline(deadline time.Time) error { |
|
|
|
if err := t.SetReadDeadline(deadline); err != nil { |
|
|
|
if err := t.SetReadDeadline(deadline); err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
@ -396,12 +452,14 @@ func (t *tcpChanConn) SetDeadline(deadline time.Time) error { |
|
|
|
// A zero value for t means Read will not time out.
|
|
|
|
// A zero value for t means Read will not time out.
|
|
|
|
// After the deadline, the error from Read will implement net.Error
|
|
|
|
// After the deadline, the error from Read will implement net.Error
|
|
|
|
// with Timeout() == true.
|
|
|
|
// with Timeout() == true.
|
|
|
|
func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { |
|
|
|
func (t *chanConn) SetReadDeadline(deadline time.Time) error { |
|
|
|
|
|
|
|
// for compatibility with previous version,
|
|
|
|
|
|
|
|
// the error message contains "tcpChan"
|
|
|
|
return errors.New("ssh: tcpChan: deadline not supported") |
|
|
|
return errors.New("ssh: tcpChan: deadline not supported") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// SetWriteDeadline exists to satisfy the net.Conn interface
|
|
|
|
// SetWriteDeadline exists to satisfy the net.Conn interface
|
|
|
|
// but is not implemented by this type. It always returns an error.
|
|
|
|
// but is not implemented by this type. It always returns an error.
|
|
|
|
func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { |
|
|
|
func (t *chanConn) SetWriteDeadline(deadline time.Time) error { |
|
|
|
return errors.New("ssh: tcpChan: deadline not supported") |
|
|
|
return errors.New("ssh: tcpChan: deadline not supported") |
|
|
|
} |
|
|
|
} |
|
|
|