Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 48 additions & 15 deletions cli/portforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"net/netip"
"os"
"os/signal"
"strconv"
Expand Down Expand Up @@ -47,6 +48,10 @@ func (r *RootCmd) portForward() *clibase.Cmd {
Description: "Port forward multiple ports (TCP or UDP) in condensed syntax",
Command: "coder port-forward <workspace> --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012",
},
example{
Description: "Port forward specifying the local address to bind to",
Command: "coder port-forward <workspace> --tcp 1.2.3.4:8080:8080",
},
),
Middleware: clibase.Chain(
clibase.RequireNArgs(1),
Expand Down Expand Up @@ -255,9 +260,9 @@ func parsePortForwards(tcpSpecs, udpSpecs []string) ([]portForwardSpec, error) {
for _, port := range ports {
specs = append(specs, portForwardSpec{
listenNetwork: "tcp",
listenAddress: fmt.Sprintf("127.0.0.1:%v", port.local),
listenAddress: port.local.String(),
dialNetwork: "tcp",
dialAddress: fmt.Sprintf("127.0.0.1:%v", port.remote),
dialAddress: port.remote.String(),
})
}
}
Expand All @@ -273,9 +278,9 @@ func parsePortForwards(tcpSpecs, udpSpecs []string) ([]portForwardSpec, error) {
for _, port := range ports {
specs = append(specs, portForwardSpec{
listenNetwork: "udp",
listenAddress: fmt.Sprintf("127.0.0.1:%v", port.local),
listenAddress: port.local.String(),
dialNetwork: "udp",
dialAddress: fmt.Sprintf("127.0.0.1:%v", port.remote),
dialAddress: port.remote.String(),
})
}
}
Expand Down Expand Up @@ -307,29 +312,57 @@ func parsePort(in string) (uint16, error) {
}

type parsedSrcDestPort struct {
local, remote uint16
local, remote netip.AddrPort
}

func parseSrcDestPorts(in string) ([]parsedSrcDestPort, error) {
parts := strings.Split(in, ":")
if len(parts) > 2 {
return nil, xerrors.Errorf("invalid port specification %q", in)
}
if len(parts) == 1 {
var (
err error
parts = strings.Split(in, ":")
localAddr = netip.AddrFrom4([4]byte{127, 0, 0, 1})
remoteAddr = netip.AddrFrom4([4]byte{127, 0, 0, 1})
)

switch len(parts) {
case 1:
// Duplicate the single part
parts = append(parts, parts[0])
case 2:
// Check to see if the first part is an IP address.
_localAddr, err := netip.ParseAddr(parts[0])
if err != nil {
break
}
// The first part is the local address, so duplicate the port.
localAddr = _localAddr
parts = []string{parts[1], parts[1]}

case 3:
_localAddr, err := netip.ParseAddr(parts[0])
if err != nil {
return nil, xerrors.Errorf("invalid port specification %q; invalid ip %q: %w", in, parts[0], err)
}
localAddr = _localAddr
parts = parts[1:]

default:
return nil, xerrors.Errorf("invalid port specification %q", in)
}

if !strings.Contains(parts[0], "-") {
local, err := parsePort(parts[0])
localPort, err := parsePort(parts[0])
if err != nil {
return nil, xerrors.Errorf("parse local port from %q: %w", in, err)
}
remote, err := parsePort(parts[1])
remotePort, err := parsePort(parts[1])
if err != nil {
return nil, xerrors.Errorf("parse remote port from %q: %w", in, err)
}

return []parsedSrcDestPort{{local: local, remote: remote}}, nil
return []parsedSrcDestPort{{
local: netip.AddrPortFrom(localAddr, localPort),
remote: netip.AddrPortFrom(remoteAddr, remotePort),
}}, nil
}

local, err := parsePortRange(parts[0])
Expand All @@ -346,8 +379,8 @@ func parseSrcDestPorts(in string) ([]parsedSrcDestPort, error) {
var out []parsedSrcDestPort
for i := range local {
out = append(out, parsedSrcDestPort{
local: local[i],
remote: remote[i],
local: netip.AddrPortFrom(localAddr, local[i]),
remote: netip.AddrPortFrom(remoteAddr, remote[i]),
})
}
return out, nil
Expand Down
19 changes: 19 additions & 0 deletions cli/portforward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ func TestPortForward(t *testing.T) {
return l.Addr().String(), port
},
},
{
name: "TCPWithAddress",
network: "tcp",
flag: "--tcp=%v:%v",
setupRemote: func(t *testing.T) net.Listener {
l, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err, "create TCP listener")
return l
},
setupLocal: func(t *testing.T) (string, string) {
l, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err, "create TCP listener to generate random port")
defer l.Close()

_, port, err := net.SplitHostPort(l.Addr().String())
require.NoErrorf(t, err, "split TCP address %q", l.Addr().String())
return l.Addr().String(), fmt.Sprint("0.0.0.0:", port)
},
},
}

// Setup agent once to be shared between test-cases (avoid expensive
Expand Down
4 changes: 4 additions & 0 deletions cli/testdata/coder_port-forward_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Aliases: tunnel

 $ coder port-forward <workspace> --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012 

- Port forward specifying the local address to bind to:

 $ coder port-forward <workspace> --tcp 1.2.3.4:8080:8080 

Options
-p, --tcp string-array, $CODER_PORT_FORWARD_TCP
Forward TCP port(s) from the workspace to the local machine.
Expand Down
4 changes: 4 additions & 0 deletions docs/cli/port-forward.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ coder port-forward [flags] <workspace>
- Port forward multiple ports (TCP or UDP) in condensed syntax:

$ coder port-forward <workspace> --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012

- Port forward specifying the local address to bind to:

$ coder port-forward <workspace> --tcp 1.2.3.4:8080:8080
```

## Options
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy