Skip to content

Commit

Permalink
ipn: impl Router.Reaches(ipport or hostport)
Browse files Browse the repository at this point in the history
  • Loading branch information
ignoramous committed Oct 14, 2024
1 parent 4c0fd4a commit 3634107
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 28 deletions.
6 changes: 6 additions & 0 deletions intra/backend/ipn_proxies.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ const ( // see ipn/proxies.go
)

type Rpn interface {
// RegisterWarp registers a new Warp public key.
RegisterWarp(publicKeyBase64 string) (json []byte, err error)
// TestWarp pings random Warp endpoints and returns reachable ones.
TestWarp() (ips string, errs error)
// Warp returns a RpnWg proxy.
Warp() (rpn Proxy, err error)
// Pip returns a RpnWs proxy.
Pip() (rpn Proxy, err error)
}

Expand Down Expand Up @@ -95,6 +99,8 @@ type Router interface {
MTU() (int, error)
// Stats returns the stats of this router.
Stat() *RouterStats
// Reaches returns true if any host:port or ip:port is dialable.
Reaches(hostportOrIPPortCsv string) bool
// Contains returns true if this router can route ipprefix.
Contains(ipprefix string) bool
}
Expand Down
10 changes: 8 additions & 2 deletions intra/ipn/auto.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var ttl30s = 30 * time.Second
type auto struct {
protoagnostic
skiprefresh
gw
pxr Proxies
addr string
exp *core.ExpMap[string, int]
Expand Down Expand Up @@ -192,8 +193,13 @@ func (h *auto) Type() string {
return RPN
}

func (*auto) Router() x.Router {
return PROXYGATEWAY
func (h *auto) Router() x.Router {
return h
}

// Reaches implements x.Router.
func (h *auto) Reaches(hostportOrIPPortCsv string) bool {
return Reaches(h, hostportOrIPPortCsv)
}

func (h *auto) GetAddr() string {
Expand Down
10 changes: 8 additions & 2 deletions intra/ipn/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
type base struct {
protoagnostic // Dial is proto aware
skiprefresh // no rebinding necessary on refresh
gw // dual stack gateway
outbound *protect.RDial // outbound dialer
addr string
status *core.Volatile[int]
Expand Down Expand Up @@ -102,8 +103,13 @@ func (h *base) Type() string {
return NOOP
}

func (*base) Router() x.Router {
return PROXYGATEWAY
func (h *base) Router() x.Router {
return h
}

// Reaches implements x.Router.
func (h *base) Reaches(hostportOrIPPortCsv string) bool {
return Reaches(h, hostportOrIPPortCsv)
}

func (h *base) GetAddr() string {
Expand Down
10 changes: 8 additions & 2 deletions intra/ipn/exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
type exit struct {
protoagnostic
skiprefresh
gw
outbound *protect.RDial // outbound dialer
addr string
status *core.Volatile[int]
Expand Down Expand Up @@ -96,8 +97,13 @@ func (h *exit) Type() string {
return INTERNET
}

func (*exit) Router() x.Router {
return PROXYGATEWAY
func (h *exit) Router() x.Router {
return h
}

// Reaches implements x.Router.
func (h *exit) Reaches(hostportOrIPPortCsv string) bool {
return Reaches(h, hostportOrIPPortCsv)
}

func (h *exit) GetAddr() string {
Expand Down
4 changes: 3 additions & 1 deletion intra/ipn/ground.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
type ground struct {
protoagnostic
skiprefresh
gw
addr string
}

Expand All @@ -23,6 +24,7 @@ var _ Proxy = (*ground)(nil)
// NewGroundProxy returns a new ground proxy.
func NewGroundProxy() *ground {
h := &ground{
gw: proxynogateway,
addr: "[::]:0",
}
return h
Expand Down Expand Up @@ -65,7 +67,7 @@ func (h *ground) Type() string {
}

func (h *ground) Router() x.Router {
return PROXYNOGATEWAY
return h
}

func (h *ground) GetAddr() string {
Expand Down
10 changes: 8 additions & 2 deletions intra/ipn/http1.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
type http1 struct {
nofwd // no forwarding/listening
skiprefresh // no refresh
gw // dual stack gateway
outbound proxy.Dialer
id string
opts *settings.ProxyOptions
Expand Down Expand Up @@ -108,8 +109,13 @@ func (h *http1) Type() string {
return HTTP1
}

func (*http1) Router() x.Router {
return PROXYGATEWAY
func (h *http1) Router() x.Router {
return h
}

// Reaches implements x.Router.
func (h *http1) Reaches(hostportOrIPPortCsv string) bool {
return Reaches(h, hostportOrIPPortCsv)
}

func (h *http1) GetAddr() string {
Expand Down
43 changes: 34 additions & 9 deletions intra/ipn/nop.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@
package ipn

import (
"net/netip"

x "github.com/celzero/firestack/intra/backend"
"github.com/celzero/firestack/intra/dialers"
"github.com/celzero/firestack/intra/protect"
)

// gw is a no-op/stub gateway that is either dualstack or not and has dummy stats.
type gw struct {
dual bool // is dualstack
stats x.RouterStats // zero stats
nov4, nov6 bool // is dualstack
stats x.RouterStats // zero stats
}

var _ x.Router = (*gw)(nil)

// IP4 implements Router.
func (w *gw) IP4() bool { return w.dual }
func (w *gw) IP4() bool { return !w.nov4 }

// IP6 implements Router.
func (w *gw) IP6() bool { return w.dual }
func (w *gw) IP6() bool { return !w.nov6 }

// MTU implements Router.
func (w *gw) MTU() (int, error) { return NOMTU, errNoMtu }
Expand All @@ -30,13 +35,33 @@ func (w *gw) MTU() (int, error) { return NOMTU, errNoMtu }
func (w *gw) Stat() *x.RouterStats { return &w.stats }

// Contains implements Router.
func (w *gw) Contains(string) bool { return w.dual }
func (w *gw) Contains(prefix string) bool {
ipnet, err := netip.ParsePrefix(prefix)
if err != nil {
return false
}
return (w.ok(ipnet.Addr()))
}

// PROXYGATEWAY is a stub Router that routes everything.
var PROXYGATEWAY = &gw{dual: true}
func (w *gw) ok(ip netip.Addr) bool { return w.ok4(ip) || w.ok6(ip) }
func (w *gw) ok4(ip netip.Addr) bool { return w.IP4() && ip.IsValid() && ip.Is4() }
func (w *gw) ok6(ip netip.Addr) bool { return w.IP6() && ip.IsValid() && ip.Is6() }

func (w *gw) Reaches(hostportOrIPPortCsv string) bool {
if len(hostportOrIPPortCsv) <= 0 {
return true
}
ips := dialers.For(hostportOrIPPortCsv)
for _, ip := range ips {
if w.ok(ip) {
return true
}
}
return false
}

// PROXYNOGATEWAY is a stub Router that routes nothing.
var PROXYNOGATEWAY = &gw{dual: false}
// proxynogateway is a Router that routes nothing.
var proxynogateway = gw{nov4: true, nov6: true}

// protoagnostic is a proxy that does not care about protocol changes.
type protoagnostic struct{}
Expand Down
11 changes: 8 additions & 3 deletions intra/ipn/piph2.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type piph2 struct {
nofwd // no forwarding/listening
protoagnostic // since dial, dialts are proto aware
skiprefresh // no refresh
id string // some unique identifier
gw // dual stack gateway
url string // h2 proxy url
hostname string // h2 proxy hostname
port int // h2 proxy port
Expand Down Expand Up @@ -240,8 +240,13 @@ func (t *piph2) GetAddr() string {
return t.hostname + ":" + strconv.Itoa(t.port)
}

func (*piph2) Router() x.Router {
return PROXYGATEWAY
func (t *piph2) Router() x.Router {
return t
}

// Reaches implements x.Router.
func (t *piph2) Reaches(hostportOrIPPortCsv string) bool {
return Reaches(t, hostportOrIPPortCsv)
}

func (t *piph2) Stop() error {
Expand Down
11 changes: 8 additions & 3 deletions intra/ipn/pipws.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type pipws struct {
nofwd // no forwarding/listening
protoagnostic // since dial is proto aware
skiprefresh // no refresh
id string // some unique identifier
gw // dual stack gateway
url string // ws proxy url
hostname string // ws proxy hostname
port int // ws proxy port
Expand Down Expand Up @@ -174,8 +174,13 @@ func (t *pipws) GetAddr() string {
return t.hostname + ":" + strconv.Itoa(t.port)
}

func (*pipws) Router() x.Router {
return PROXYGATEWAY
func (t *pipws) Router() x.Router {
return t
}

// Reaches implements x.Router.
func (t *pipws) Reaches(hostportOrIPPortCsv string) bool {
return Reaches(t, hostportOrIPPortCsv)
}

func (t *pipws) Stop() error {
Expand Down
5 changes: 5 additions & 0 deletions intra/ipn/proxies.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,11 @@ func (px *proxifier) Contains(ipprefix string) bool {
return false
}

func (px *proxifier) Reaches(ippcsv string) bool {
// TODO: stub
return px.Contains(ippcsv)
}

// Implements x.Rpn.
func (px *proxifier) RegisterWarp(pub string) ([]byte, error) {
id, err := px.warpc.Make(pub, "")
Expand Down
69 changes: 69 additions & 0 deletions intra/ipn/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@
package ipn

import (
"errors"
"fmt"
"net"
"net/netip"
"net/url"
"os"
"strconv"
"strings"
"syscall"

x "github.com/celzero/firestack/intra/backend"
"github.com/celzero/firestack/intra/core"
"github.com/celzero/firestack/intra/dialers"
"github.com/celzero/firestack/intra/log"
"github.com/celzero/firestack/intra/settings"
)
Expand Down Expand Up @@ -128,3 +136,64 @@ func (pxr *proxifier) fromOpts(id string, opts *settings.ProxyOptions) (Proxy, e
}
return p, err
}

func Reaches(p Proxy, hostportOrIPPortCsv string) bool {
if p == nil {
return false
}
if len(hostportOrIPPortCsv) <= 0 {
return true
}
// upstream := dnsx.Default
// if pdns := p.DNS(); len(pdns) > 0 {
// upstream = pdns
// }
ipps := make([]netip.AddrPort, 0)
for _, x := range strings.Split(hostportOrIPPortCsv, ",") {
host, port, err := net.SplitHostPort(x)
if err != nil {
port = "80"
x = host
}
on, err := strconv.ParseUint(port, 10, 16)
if err != nil {
log.W("wg: %s router: %s port %s; err: %v",
p.ID(), x, port, err)
on = 80
}
if len(x) > 0 { // x may be ip, host
ips := dialers.For(x)
for _, ip := range ips {
ipp := netip.AddrPortFrom(ip, uint16(on))
ipps = append(ipps, ipp)
}
}
}
tests := make([]core.Work[bool], 0)
for _, ipp := range ipps {
addr := ipp.String()
tests = append(tests, func() (bool, error) {
c, err := p.Dial("tcp", addr)
defer core.CloseConn(c)

ok := err == nil
if syserr := new(os.SyscallError); errors.As(err, &syserr) {
ok = syserr.Err == syscall.ECONNREFUSED
}
log.V("wg: %s router: %s reaches? %t; err? %v", p.ID(), addr, ok, err)
return ok, err
})
}

if len(tests) <= 0 {
log.W("wg: %s router: %v; no tests", p.ID(), hostportOrIPPortCsv)
return false
}

ok, who, err := core.Race("reach."+p.ID(), getproxytimeout, tests...)

log.D("wg: %s router: %v => %v reaches? %t; who: %d, err? %v",
p.ID(), hostportOrIPPortCsv, ipps, ok, who, err)

return ok
}
8 changes: 7 additions & 1 deletion intra/ipn/socks5.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
type socks5 struct {
nofwd // no forwarding/listening
skiprefresh // no refresh
gw // dual stack gateway
outbound []proxy.Dialer // outbound dialers connecting unto upstream proxy
id string // unique identifier
opts *settings.ProxyOptions // connect options
Expand Down Expand Up @@ -198,7 +199,12 @@ func (h *socks5) Type() string {
}

func (h *socks5) Router() x.Router {
return PROXYGATEWAY
return h
}

// Reaches implements x.Router.
func (h *socks5) Reaches(hostportOrIPPortCsv string) bool {
return Reaches(h, hostportOrIPPortCsv)
}

func (h *socks5) GetAddr() string {
Expand Down
Loading

0 comments on commit 3634107

Please sign in to comment.