Skip to content

Commit

Permalink
ipn/wg: proton as rpn
Browse files Browse the repository at this point in the history
  • Loading branch information
ignoramous committed Dec 14, 2024
1 parent 1187776 commit e7da105
Show file tree
Hide file tree
Showing 7 changed files with 581 additions and 24 deletions.
7 changes: 7 additions & 0 deletions intra/backend/ipn_proxies.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const ( // see ipn/proxies.go
Auto = "Auto" // auto uses ipn.Exit or any of the RPN proxies
RpnWg = WG + "w" + RPN // RPN Warp
RpnAmz = WG + "a" + RPN // RPN Amnezia
RpnPro = WG + "p" + RPN // RPN Proton
RpnWs = PIPWS + RPN // RPN WebSockets
RpnH2 = PIPH2 + RPN // RPN HTTP/2
Rpn64 = NAT64 + RPN // RPN Exit hopping over NAT64
Expand Down Expand Up @@ -61,16 +62,22 @@ type Rpn interface {
RegisterSE() error
// RegisterAmnezia registers a new Amnezia installation.
RegisterAmnezia(publicKeyBase64 string) (json []byte, err error)
// RegisterProton registers a new Proton installation.
RegisterProton(existingStateJson []byte, serversFile string) (json []byte, err error)
// TestWarp connects to some Warp IPs and returns reachable ones.
TestWarp() (ips string, errs error)
// TestAmnezia connects to the Amnezia gateway and returns its IP if reachable.
TestAmnezia() (ips string, errs error)
// TestProton connects to the Proton gateway and returns its IP if reachable.
TestProton() (ips string, errs error)
// TestSE connects to some SurfEasy IPs and returns reachable ones.
TestSE() (ips string, errs error)
// TestExit64 connects to public NAT64 endpoints and returns reachable ones.
TestExit64() (ips string, errs error)
// Warp returns a RpnWg proxy.
Warp() (wg Proxy, err error)
// Proton returns a Proton WireGuard proxy.
Proton() (wg Proxy, err error)
// Amnezia returns a Amnezia WireGuard proxy.
Amnezia() (awg Proxy, err error)
// Pip returns a RpnWs proxy.
Expand Down
9 changes: 8 additions & 1 deletion intra/backend/ipn_wgkeygen.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package backend

import (
"crypto/ed25519"
"crypto/rand"
"crypto/subtle"
"encoding/base64"
Expand All @@ -26,7 +27,7 @@ import (

// from: github.com/WireGuard/wireguard-windows/blob/dcc0eb72a/conf/parser.go#L121

const klen = 32
const klen = ed25519.SeedSize

type (
eckey [klen]byte
Expand Down Expand Up @@ -83,6 +84,12 @@ func NewWgPrivateKey() (WgKey, error) {
return k, nil
}

func NewWgPrivateKeyFrom(k [klen]byte) WgKey {
k[0] &= 248
k[31] = (k[31] & 127) | 64
return (*eckey)(&k)
}

func parseKeyBase64(s string) (*eckey, error) {
k, err := base64.StdEncoding.DecodeString(s)
if err != nil {
Expand Down
71 changes: 71 additions & 0 deletions intra/core/sched.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2024 RethinkDNS and its authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package core

import (
"context"
"errors"
"sync"
"time"
)

var errNewJob = errors.New("sched: replaced by newer job")

type Job func() error

type ctl struct {
who context.Context
cancel context.CancelCauseFunc
}

type Scheduler struct {
ctx context.Context

mu sync.Mutex
jobctl map[string]*ctl
}

func NewScheduler(ctx context.Context) *Scheduler {
return &Scheduler{
ctx: ctx,
jobctl: make(map[string]*ctl),
}
}

// At runs f at time t; accepts a Context to cancel it.
func (s *Scheduler) At(id string, t time.Time, f Job) context.Context {
s.mu.Lock()
ctx, done := context.WithCancelCause(s.ctx)
if c := s.jobctl[id]; c != nil {
c.cancel(errNewJob) // dispose existing job
}
s.jobctl[id] = &ctl{who: ctx, cancel: done}
s.mu.Unlock()

Go("at."+id, func() {
var cause error

defer func() {
done(cause)
s.mu.Lock()
if c := s.jobctl[id]; c != nil && c.who == ctx {
delete(s.jobctl, id)
}
s.mu.Unlock()
}()

select {
case <-s.ctx.Done():
cause = s.ctx.Err()
return
case <-time.After(time.Until(t)):
cause = f()
return
}
})
return ctx
}
76 changes: 59 additions & 17 deletions intra/ipn/auto.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (h *auto) dial(network, local, remote string) (protect.Conn, error) {
exit, exerr := h.pxr.ProxyFor(Exit)
exit64, ex64err := h.pxr.ProxyFor(Rpn64)
warp, waerr := h.pxr.ProxyFor(RpnWg)
pro, proerr := h.pxr.ProxyFor(RpnPro)
amz, amzerr := h.pxr.ProxyFor(RpnAmz)
sep, seerr := h.pxr.ProxyFor(RpnSE)

Expand Down Expand Up @@ -104,6 +105,25 @@ func (h *auto) dial(network, local, remote string) (protect.Conn, error) {
return h.dialIfReachable(exit, network, local, remote)
}, func(ctx context.Context) (protect.Conn, error) {
const myidx = 1
if pro == nil { // exit must always be present
return nil, proerr
}
if recent {
if previdx != myidx {
return nil, errNotPinned
}
// ip pinned to this proxy
h.dialIfHealthy(pro, network, local, remote)
}

select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay * myidx): // 100ms
}
return h.dialIfReachable(pro, network, local, remote)
}, func(ctx context.Context) (protect.Conn, error) {
const myidx = 2
if warp == nil {
return nil, waerr
}
Expand All @@ -118,11 +138,11 @@ func (h *auto) dial(network, local, remote string) (protect.Conn, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay): // 100ms
case <-time.After(shortdelay * myidx): // 200ms
}
return h.dialIfHealthy(warp, network, local, remote)
}, func(ctx context.Context) (protect.Conn, error) {
const myidx = 2
const myidx = 3
if exit64 == nil {
return nil, ex64err
}
Expand All @@ -137,11 +157,11 @@ func (h *auto) dial(network, local, remote string) (protect.Conn, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay * 2): // 200ms
case <-time.After(shortdelay * myidx): // 300ms
}
return h.dialIfHealthy(exit64, network, local, remote)
}, func(ctx context.Context) (protect.Conn, error) {
const myidx = 3
const myidx = 4
if amz == nil {
return nil, amzerr
}
Expand All @@ -156,11 +176,11 @@ func (h *auto) dial(network, local, remote string) (protect.Conn, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay * 3): // 300ms
case <-time.After(shortdelay * myidx): // 400ms
}
return h.dialIfHealthy(amz, network, local, remote)
}, func(ctx context.Context) (protect.Conn, error) {
const myidx = 4
const myidx = 5
if sep == nil {
return nil, seerr
}
Expand All @@ -175,7 +195,7 @@ func (h *auto) dial(network, local, remote string) (protect.Conn, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay * 4): // 400ms
case <-time.After(shortdelay * myidx): // 500ms
}
return h.dialIfHealthy(sep, network, local, remote)
},
Expand All @@ -201,6 +221,7 @@ func (h *auto) Announce(network, local string) (protect.PacketConn, error) {

exit, exerr := h.pxr.ProxyFor(Exit)
warp, waerr := h.pxr.ProxyFor(RpnWg)
pro, proerr := h.pxr.ProxyFor(RpnPro)
amz, amzerr := h.pxr.ProxyFor(RpnAmz)

previdx, recent := h.exp.Get(local)
Expand All @@ -224,6 +245,24 @@ func (h *auto) Announce(network, local string) (protect.PacketConn, error) {
return h.announceIfHealthy(exit, network, local)
}, func(ctx context.Context) (protect.PacketConn, error) {
const myidx = 1
if pro == nil {
return nil, proerr
}
if recent {
if previdx != myidx {
return nil, errNotPinned
}
// ip pinned to this proxy
return h.announceIfHealthy(pro, network, local)
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay * myidx): // 100ms
}
return h.announceIfHealthy(pro, network, local)
}, func(ctx context.Context) (protect.PacketConn, error) {
const myidx = 2
if warp == nil {
return nil, waerr
}
Expand All @@ -237,11 +276,11 @@ func (h *auto) Announce(network, local string) (protect.PacketConn, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay): // 100ms
case <-time.After(shortdelay * myidx): // 200ms
}
return h.announceIfHealthy(warp, network, local)
}, func(ctx context.Context) (protect.PacketConn, error) {
const myidx = 2
const myidx = 3
if amz == nil {
return nil, amzerr
}
Expand All @@ -255,7 +294,7 @@ func (h *auto) Announce(network, local string) (protect.PacketConn, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(shortdelay * 2): // 200ms
case <-time.After(shortdelay * myidx): // 300ms
}
return h.announceIfHealthy(amz, network, local)
}, // seasy-proxy does not support udp?
Expand Down Expand Up @@ -286,7 +325,7 @@ func (h *auto) Probe(network, local string) (pc protect.PacketConn, err error) {
if h.status.Load() == END {
return nil, errProxyStopped
}
// todo: rpnwg, rpnamz
// todo: rpnwg, rpnamz, rpnpro
exit, err := h.pxr.ProxyFor(Exit)
if err == nil {
pc, err = exit.Dialer().Probe(network, local)
Expand Down Expand Up @@ -332,21 +371,24 @@ func (h *auto) Hop(p Proxy) error {
return errProxyStopped
}

var warp, sep, amz Proxy
var waerr, seerr, amzerr error
var warp, sep, amz, pro Proxy
var waerr, seerr, amzerr, proerr error
old := h.via.Tango(p)
if warp, waerr = h.pxr.ProxyFor(RpnWg); warp != nil {
warp.Hop(p)
waerr = warp.Hop(p)
}
if pro, proerr = h.pxr.ProxyFor(RpnPro); pro != nil {
proerr = pro.Hop(p)
}
if amz, amzerr = h.pxr.ProxyFor(RpnAmz); amz != nil {
amz.Hop(p)
amzerr = amz.Hop(p)
}
if sep, seerr = h.pxr.ProxyFor(RpnSE); sep != nil {
sep.Hop(p)
seerr = sep.Hop(p)
}

log.I("proxy: auto: hop(%s) => %s; errs? %v",
idhandle(old), idhandle(p), core.JoinErr(waerr, seerr, amzerr))
idhandle(old), idhandle(p), core.JoinErr(waerr, seerr, amzerr, proerr))
return nil
}

Expand Down
Loading

0 comments on commit e7da105

Please sign in to comment.