44 "context"
55 "fmt"
66 "net"
7+ "net/netip"
78 "os"
89 "os/signal"
910 "strconv"
@@ -47,6 +48,10 @@ func (r *RootCmd) portForward() *clibase.Cmd {
4748 Description : "Port forward multiple ports (TCP or UDP) in condensed syntax" ,
4849 Command : "coder port-forward <workspace> --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012" ,
4950 },
51+ example {
52+ Description : "Port forward specifying the local address to bind to" ,
53+ Command : "coder port-forward <workspace> --tcp 1.2.3.4:8080:8080" ,
54+ },
5055 ),
5156 Middleware : clibase .Chain (
5257 clibase .RequireNArgs (1 ),
@@ -255,9 +260,9 @@ func parsePortForwards(tcpSpecs, udpSpecs []string) ([]portForwardSpec, error) {
255260 for _ , port := range ports {
256261 specs = append (specs , portForwardSpec {
257262 listenNetwork : "tcp" ,
258- listenAddress : fmt . Sprintf ( "127.0.0.1:%v" , port .local ),
263+ listenAddress : port .local . String ( ),
259264 dialNetwork : "tcp" ,
260- dialAddress : fmt . Sprintf ( "127.0.0.1:%v" , port .remote ),
265+ dialAddress : port .remote . String ( ),
261266 })
262267 }
263268 }
@@ -273,9 +278,9 @@ func parsePortForwards(tcpSpecs, udpSpecs []string) ([]portForwardSpec, error) {
273278 for _ , port := range ports {
274279 specs = append (specs , portForwardSpec {
275280 listenNetwork : "udp" ,
276- listenAddress : fmt . Sprintf ( "127.0.0.1:%v" , port .local ),
281+ listenAddress : port .local . String ( ),
277282 dialNetwork : "udp" ,
278- dialAddress : fmt . Sprintf ( "127.0.0.1:%v" , port .remote ),
283+ dialAddress : port .remote . String ( ),
279284 })
280285 }
281286 }
@@ -307,29 +312,57 @@ func parsePort(in string) (uint16, error) {
307312}
308313
309314type parsedSrcDestPort struct {
310- local , remote uint16
315+ local , remote netip. AddrPort
311316}
312317
313318func parseSrcDestPorts (in string ) ([]parsedSrcDestPort , error ) {
314- parts := strings .Split (in , ":" )
315- if len (parts ) > 2 {
316- return nil , xerrors .Errorf ("invalid port specification %q" , in )
317- }
318- if len (parts ) == 1 {
319+ var (
320+ err error
321+ parts = strings .Split (in , ":" )
322+ localAddr = netip .AddrFrom4 ([4 ]byte {127 , 0 , 0 , 1 })
323+ remoteAddr = netip .AddrFrom4 ([4 ]byte {127 , 0 , 0 , 1 })
324+ )
325+
326+ switch len (parts ) {
327+ case 1 :
319328 // Duplicate the single part
320329 parts = append (parts , parts [0 ])
330+ case 2 :
331+ // Check to see if the first part is an IP address.
332+ _localAddr , err := netip .ParseAddr (parts [0 ])
333+ if err != nil {
334+ break
335+ }
336+ // The first part is the local address, so duplicate the port.
337+ localAddr = _localAddr
338+ parts = []string {parts [1 ], parts [1 ]}
339+
340+ case 3 :
341+ _localAddr , err := netip .ParseAddr (parts [0 ])
342+ if err != nil {
343+ return nil , xerrors .Errorf ("invalid port specification %q; invalid ip %q: %w" , in , parts [0 ], err )
344+ }
345+ localAddr = _localAddr
346+ parts = parts [1 :]
347+
348+ default :
349+ return nil , xerrors .Errorf ("invalid port specification %q" , in )
321350 }
351+
322352 if ! strings .Contains (parts [0 ], "-" ) {
323- local , err := parsePort (parts [0 ])
353+ localPort , err := parsePort (parts [0 ])
324354 if err != nil {
325355 return nil , xerrors .Errorf ("parse local port from %q: %w" , in , err )
326356 }
327- remote , err := parsePort (parts [1 ])
357+ remotePort , err := parsePort (parts [1 ])
328358 if err != nil {
329359 return nil , xerrors .Errorf ("parse remote port from %q: %w" , in , err )
330360 }
331361
332- return []parsedSrcDestPort {{local : local , remote : remote }}, nil
362+ return []parsedSrcDestPort {{
363+ local : netip .AddrPortFrom (localAddr , localPort ),
364+ remote : netip .AddrPortFrom (remoteAddr , remotePort ),
365+ }}, nil
333366 }
334367
335368 local , err := parsePortRange (parts [0 ])
@@ -346,8 +379,8 @@ func parseSrcDestPorts(in string) ([]parsedSrcDestPort, error) {
346379 var out []parsedSrcDestPort
347380 for i := range local {
348381 out = append (out , parsedSrcDestPort {
349- local : local [i ],
350- remote : remote [i ],
382+ local : netip . AddrPortFrom ( localAddr , local [i ]) ,
383+ remote : netip . AddrPortFrom ( remoteAddr , remote [i ]) ,
351384 })
352385 }
353386 return out , nil
0 commit comments