@@ -11,6 +11,7 @@ import (
1111 "io"
1212 "math/rand"
1313 "net"
14+ "net/netip"
1415 "strconv"
1516 "strings"
1617 "sync"
@@ -22,14 +23,21 @@ import (
2223// the returned net.Listener. The listener must be serviced, or the
2324// SSH connection may hang.
2425// N must be "tcp", "tcp4", "tcp6", or "unix".
26+ //
27+ // If the address is a hostname, it is sent to the remote peer as-is, without
28+ // being resolved locally, and the Listener Addr method will return a zero IP.
2529func (c * Client ) Listen (n , addr string ) (net.Listener , error ) {
2630 switch n {
2731 case "tcp" , "tcp4" , "tcp6" :
28- laddr , err := net .ResolveTCPAddr (n , addr )
32+ host , portStr , err := net .SplitHostPort (addr )
33+ if err != nil {
34+ return nil , err
35+ }
36+ port , err := strconv .ParseInt (portStr , 10 , 32 )
2937 if err != nil {
3038 return nil , err
3139 }
32- return c .ListenTCP ( laddr )
40+ return c .listenTCPInternal ( host , int ( port ) )
3341 case "unix" :
3442 return c .ListenUnix (addr )
3543 default :
@@ -102,15 +110,24 @@ func (c *Client) handleForwards() {
102110// ListenTCP requests the remote peer open a listening socket
103111// on laddr. Incoming connections will be available by calling
104112// Accept on the returned net.Listener.
113+ //
114+ // ListenTCP accepts an IP address, to provide a hostname use [Client.Listen]
115+ // with "tcp", "tcp4", or "tcp6" network instead.
105116func (c * Client ) ListenTCP (laddr * net.TCPAddr ) (net.Listener , error ) {
106117 c .handleForwardsOnce .Do (c .handleForwards )
107118 if laddr .Port == 0 && isBrokenOpenSSHVersion (string (c .ServerVersion ())) {
108119 return c .autoPortListenWorkaround (laddr )
109120 }
110121
122+ return c .listenTCPInternal (laddr .IP .String (), laddr .Port )
123+ }
124+
125+ func (c * Client ) listenTCPInternal (host string , port int ) (net.Listener , error ) {
126+ c .handleForwardsOnce .Do (c .handleForwards )
127+
111128 m := channelForwardMsg {
112- laddr . IP . String () ,
113- uint32 (laddr . Port ),
129+ host ,
130+ uint32 (port ),
114131 }
115132 // send message
116133 ok , resp , err := c .SendRequest ("tcpip-forward" , true , Marshal (& m ))
@@ -123,20 +140,33 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
123140
124141 // If the original port was 0, then the remote side will
125142 // supply a real port number in the response.
126- if laddr . Port == 0 {
143+ if port == 0 {
127144 var p struct {
128145 Port uint32
129146 }
130147 if err := Unmarshal (resp , & p ); err != nil {
131148 return nil , err
132149 }
133- laddr . Port = int (p .Port )
150+ port = int (p .Port )
134151 }
152+ // Construct a local address placeholder for the remote listener. If the
153+ // original host is an IP address, preserve it so that Listener.Addr()
154+ // reports the same IP. If the host is a hostname or cannot be parsed as an
155+ // IP, fall back to IPv4zero. The port field is always set, even if the
156+ // original port was 0, because in that case the remote server will assign
157+ // one, allowing callers to determine which port was selected.
158+ ip := net .IPv4zero
159+ if parsed , err := netip .ParseAddr (host ); err == nil {
160+ ip = net .IP (parsed .AsSlice ())
161+ }
162+ laddr := & net.TCPAddr {
163+ IP : ip ,
164+ Port : port ,
165+ }
166+ addr := net .JoinHostPort (host , strconv .FormatInt (int64 (port ), 10 ))
167+ ch := c .forwards .add ("tcp" , addr )
135168
136- // Register this forward, using the port number we obtained.
137- ch := c .forwards .add (laddr )
138-
139- return & tcpListener {laddr , c , ch }, nil
169+ return & tcpListener {laddr , addr , c , ch }, nil
140170}
141171
142172// forwardList stores a mapping between remote
@@ -149,8 +179,9 @@ type forwardList struct {
149179// forwardEntry represents an established mapping of a laddr on a
150180// remote ssh server to a channel connected to a tcpListener.
151181type forwardEntry struct {
152- laddr net.Addr
153- c chan forward
182+ addr string // host:port or socket path
183+ network string // tcp or unix
184+ c chan forward
154185}
155186
156187// forward represents an incoming forwarded tcpip connection. The
@@ -161,12 +192,13 @@ type forward struct {
161192 raddr net.Addr // the raddr of the incoming connection
162193}
163194
164- func (l * forwardList ) add (addr net. Addr ) chan forward {
195+ func (l * forwardList ) add (n , addr string ) chan forward {
165196 l .Lock ()
166197 defer l .Unlock ()
167198 f := forwardEntry {
168- laddr : addr ,
169- c : make (chan forward , 1 ),
199+ addr : addr ,
200+ network : n ,
201+ c : make (chan forward , 1 ),
170202 }
171203 l .entries = append (l .entries , f )
172204 return f .c
@@ -185,19 +217,20 @@ func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
185217 if port == 0 || port > 65535 {
186218 return nil , fmt .Errorf ("ssh: port number out of range: %d" , port )
187219 }
188- ip := net . ParseIP ( string ( addr ) )
189- if ip = = nil {
220+ ip , err := netip . ParseAddr ( addr )
221+ if err ! = nil {
190222 return nil , fmt .Errorf ("ssh: cannot parse IP address %q" , addr )
191223 }
192- return & net.TCPAddr {IP : ip , Port : int (port )}, nil
224+ return & net.TCPAddr {IP : net . IP ( ip . AsSlice ()) , Port : int (port )}, nil
193225}
194226
195227func (l * forwardList ) handleChannels (in <- chan NewChannel ) {
196228 for ch := range in {
197229 var (
198- laddr net.Addr
199- raddr net.Addr
200- err error
230+ addr string
231+ network string
232+ raddr net.Addr
233+ err error
201234 )
202235 switch channelType := ch .ChannelType (); channelType {
203236 case "forwarded-tcpip" :
@@ -207,40 +240,34 @@ func (l *forwardList) handleChannels(in <-chan NewChannel) {
207240 continue
208241 }
209242
210- // RFC 4254 section 7.2 specifies that incoming
211- // addresses should list the address, in string
212- // format. It is implied that this should be an IP
213- // address, as it would be impossible to connect to it
214- // otherwise.
215- laddr , err = parseTCPAddr (payload .Addr , payload .Port )
216- if err != nil {
217- ch .Reject (ConnectionFailed , err .Error ())
218- continue
219- }
243+ // RFC 4254 section 7.2 specifies that incoming addresses should
244+ // list the address that was connected, in string format. It is the
245+ // same address used in the tcpip-forward request. The originator
246+ // address is an IP address instead.
247+ addr = net .JoinHostPort (payload .Addr , strconv .FormatUint (uint64 (payload .Port ), 10 ))
248+
220249 raddr , err = parseTCPAddr (payload .OriginAddr , payload .OriginPort )
221250 if err != nil {
222251 ch .Reject (ConnectionFailed , err .Error ())
223252 continue
224253 }
225-
254+ network = "tcp"
226255227256 var payload forwardedStreamLocalPayload
228257 if err = Unmarshal (ch .ExtraData (), & payload ); err != nil {
229258 ch .
Reject (
ConnectionFailed ,
"could not parse [email protected] payload: " + err .
Error ())
230259 continue
231260 }
232- laddr = & net.UnixAddr {
233- Name : payload .SocketPath ,
234- Net : "unix" ,
235- }
261+ addr = payload .SocketPath
236262 raddr = & net.UnixAddr {
237263 Name : "@" ,
238264 Net : "unix" ,
239265 }
266+ network = "unix"
240267 default :
241268 panic (fmt .Errorf ("ssh: unknown channel type %s" , channelType ))
242269 }
243- if ok := l .forward (laddr , raddr , ch ); ! ok {
270+ if ok := l .forward (network , addr , raddr , ch ); ! ok {
244271 // Section 7.2, implementations MUST reject spurious incoming
245272 // connections.
246273 ch .Reject (Prohibited , "no forward for address" )
@@ -252,11 +279,11 @@ func (l *forwardList) handleChannels(in <-chan NewChannel) {
252279
253280// remove removes the forward entry, and the channel feeding its
254281// listener.
255- func (l * forwardList ) remove (addr net. Addr ) {
282+ func (l * forwardList ) remove (n , addr string ) {
256283 l .Lock ()
257284 defer l .Unlock ()
258285 for i , f := range l .entries {
259- if addr . Network () == f .laddr . Network () && addr . String () == f .laddr . String () {
286+ if n == f .network && addr == f .addr {
260287 l .entries = append (l .entries [:i ], l .entries [i + 1 :]... )
261288 close (f .c )
262289 return
@@ -274,11 +301,11 @@ func (l *forwardList) closeAll() {
274301 l .entries = nil
275302}
276303
277- func (l * forwardList ) forward (laddr , raddr net.Addr , ch NewChannel ) bool {
304+ func (l * forwardList ) forward (n , addr string , raddr net.Addr , ch NewChannel ) bool {
278305 l .Lock ()
279306 defer l .Unlock ()
280307 for _ , f := range l .entries {
281- if laddr . Network () == f .laddr . Network () && laddr . String () == f .laddr . String () {
308+ if n == f .network && addr == f .addr {
282309 f .c <- forward {newCh : ch , raddr : raddr }
283310 return true
284311 }
@@ -288,6 +315,7 @@ func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
288315
289316type tcpListener struct {
290317 laddr * net.TCPAddr
318+ addr string
291319
292320 conn * Client
293321 in <- chan forward
@@ -314,13 +342,21 @@ func (l *tcpListener) Accept() (net.Conn, error) {
314342
315343// Close closes the listener.
316344func (l * tcpListener ) Close () error {
345+ host , port , err := net .SplitHostPort (l .addr )
346+ if err != nil {
347+ return err
348+ }
349+ rport , err := strconv .ParseUint (port , 10 , 32 )
350+ if err != nil {
351+ return err
352+ }
317353 m := channelForwardMsg {
318- l . laddr . IP . String () ,
319- uint32 (l . laddr . Port ),
354+ host ,
355+ uint32 (rport ),
320356 }
321357
322358 // this also closes the listener.
323- l .conn .forwards .remove (l . laddr )
359+ l .conn .forwards .remove ("tcp" , l . addr )
324360 ok , _ , err := l .conn .SendRequest ("cancel-tcpip-forward" , true , Marshal (& m ))
325361 if err == nil && ! ok {
326362 err = errors .New ("ssh: cancel-tcpip-forward failed" )
0 commit comments