Skip to content

Commit

Permalink
net: support blank host and port as specified by curl connect-to flag
Browse files Browse the repository at this point in the history
Implement the following

"HOST1" and "PORT1" may be the empty string, meaning "any host/port".
"HOST2" and "PORT2" may also be the empty string, meaning "use the request's original host/port".
  • Loading branch information
mmatczuk committed Sep 10, 2024
1 parent 7137032 commit 4421f84
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 7 deletions.
27 changes: 20 additions & 7 deletions net.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"crypto/tls"
"fmt"
"net"
"slices"
"sync"
"sync/atomic"
"syscall"
Expand All @@ -23,15 +24,27 @@ import (
type DialRedirectFunc func(network, address string) (targetNetwork, targetAddress string)

func DialRedirectFromHostPortPairs(subs []HostPortPair) DialRedirectFunc {
m := make(map[string]string, len(subs))
for _, h := range subs {
m[net.JoinHostPort(h.Src.Host, h.Src.Port)] = net.JoinHostPort(h.Dst.Host, h.Dst.Port)
}

subs = slices.Clone(subs)
return func(network, address string) (string, string) {
if target, ok := m[address]; ok {
return network, target
host, port, err := net.SplitHostPort(address)
if err != nil {
return network, address
}

for _, s := range subs {
if (s.Src.Host == "" || s.Src.Host == host) && (s.Src.Port == "" || s.Src.Port == port) { //nolint:gocritic // nestingReduce: invert if cond, replace body with `continue`, move old body after the statement
h := s.Dst.Host
if h == "" {
h = host
}
p := s.Dst.Port
if p == "" {
p = port
}
return network, net.JoinHostPort(h, p)
}
}

return network, address
}
}
Expand Down
65 changes: 65 additions & 0 deletions net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,71 @@ import (
"github.com/saucelabs/forwarder/utils/golden"
)

func TestDialRedirectFromHostPortPairs(t *testing.T) {
tests := []struct {
name string
hp HostPortPair
input string
want string
}{
{
name: "basic",
hp: HostPortPair{
Src: HostPort{"a", "80"},
Dst: HostPort{"b", "443"},
},
input: "a:80",
want: "b:443",
},
{
name: "blank src host",
hp: HostPortPair{
Src: HostPort{"", "80"},
Dst: HostPort{"b", "443"},
},
input: "a:80",
want: "b:443",
},
{
name: "blank src port",
hp: HostPortPair{
Src: HostPort{"a", ""},
Dst: HostPort{"b", "443"},
},
input: "a:80",
want: "b:443",
},
{
name: "blank dst host",
hp: HostPortPair{
Src: HostPort{"a", "80"},
Dst: HostPort{"", "443"},
},
input: "a:80",
want: "a:443",
},
{
name: "blank dst port",
hp: HostPortPair{
Src: HostPort{"a", "80"},
Dst: HostPort{"b", ""},
},
input: "a:80",
want: "b:80",
},
}

for i := range tests {
tc := tests[i]
t.Run(tc.name, func(t *testing.T) {
_, got := DialRedirectFromHostPortPairs([]HostPortPair{tc.hp})("tcp", tc.input)
if got != tc.want {
t.Fatalf("DialRedirectFromHostPortPairs(): got %v, want %v", got, tc.want)
}
})
}
}

func TestDialerRedirect(t *testing.T) {
l := Listener{
Address: "localhost:0",
Expand Down

0 comments on commit 4421f84

Please sign in to comment.