sshnet / sshnet/SSH.NET

Unexpected behavior (a.k.a. bug) on local tunnel

Open
#117 4 comments 2 reactions 1 assignee View on GitHub

@drieseng is already working on this.

Since Nov 16, 2016.

enhancement
Dominant language
C#
Stars
4.4k
Forks
993
Avg merge
9d 21h
Merged PRs (30d)
1

Description

Bug as seen on the net

People try to use local tunnels and that fails like on:

Usually the user has found a workaround but not clarified what happens.

This bug report clarifies and suggests an improvements.

How to reproduce

OS is Win7 here but probably happens with other configurations.

From c# - Creating a forwarded port within an SSH tunnel - Stack Overflow:

  PrivateKeyFile file = new PrivateKeyFile(@" .. path to private key .. ");
  using (var client = new SshClient(" .. remote server .. ", "ubuntu", file))
  {

      client.Connect();
      var port = new ForwardedPortLocal(3306, "localhost", 3306);
      client.AddForwardedPort(port);
      port.Start();

      Thread.Sleep(10000000);

      client.Disconnect();
  }

Expected behaviour

OS accepts connections to any local IP (127.0.0.1, ::1), port 3306.

Observed behaviour

OS does not accept connections to local IP (127.0.0.1) (not sure if tester ::1), port 3306.

It does accept connection to a particular IPV6 address, though.

Technical analysis: what's happening

Expected behavior

When opening a local tunnel without specifying IP to bind, OpenSSH implementation binds to all local IPs. Here's an example on Linux:

ssh somehost -L 1234:127.0.0.1:1234

On another terminal:

netstat -n  -at | grep -i listen | grep 1234

tcp        0      0 127.0.0.1:1234          0.0.0.0:*               LISTEN     
tcp6       0      0 ::1:1234                :::*                    LISTEN     

Observed behavior

Instead, SSH.NET does this (from SSH.NET/ForwardedPortLocal.NET.cs at 5f6c3b0bc7a5916d92467931ecacdfc0917f83d7 · sshnet/SSH.NET):

var addr = DnsAbstraction.GetHostAddresses(BoundHost)[0];

which on local machine provides IP addresses provided by DHCP server, not 127.0.0.1 or ::1.

Taking only the first result from array returned by DnsAbstraction.GetHostAddresses(BoundHost) is code smell.

Suggested change

(1) Make a foreach loop that binds to all adresses returned by DnsAbstraction.GetHostAddresses(BoundHost)
(2) if String.IsNullOrEmpty(BoundHost), use something like IPAddress.Loopback (or something ensuring IPv4 and IPv6 compatibility).

Though I'm not using that at the moment, can SSH.NET bind to "any IP" (pseudo-address '*') like OpenSSH does:

ssh somehost -L *:localport:remotehost:remoteport

That suggests : (3) if ("*".Equals(BoundHost)) { ... }

Conclusion

IMHO such a change would make SSH.NET more inline with expected behavior and avoid much frustration for people.

Thank you for your attention.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.