Skip to content

Commit

Permalink
ipn: impl rpn for warp
Browse files Browse the repository at this point in the history
  • Loading branch information
ignoramous committed Oct 9, 2024
1 parent dce6677 commit 3a88024
Show file tree
Hide file tree
Showing 9 changed files with 1,041 additions and 20 deletions.
9 changes: 9 additions & 0 deletions intra/backend/ipn_proxies.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const ( // see ipn/proxies.go
Base = "Base" // does not proxy traffic; in sync w dnsx.NetNoProxy
Exit = "Exit" // always connects to the Internet (exit node); in sync w dnsx.NetExitProxy
Ingress = "Ingress" // incoming connections
Auto = "rpn" // auto uses ipn.Exit or any of the RPN proxies
RpnWg = WG + RPN // RPN Warp
OrbotS5 = "OrbotSocks5" // Orbot: Base Tor-as-a-SOCKS5 proxy
OrbotH1 = "OrbotHttp1" // Orbot: Base Tor-as-a-HTTP/1.1 proxy

Expand All @@ -27,6 +29,7 @@ const ( // see ipn/proxies.go
PIPWS = "pipws" // PIP: WebSockets proxy
NOOP = "noop" // No proxy, ex: Base, Block
INTERNET = "net" // egress network, ex: Exit
RPN = "rpn" // Rethink Proxy Network

// status of proxies

Expand All @@ -38,6 +41,10 @@ const ( // see ipn/proxies.go
END = -3 // proxy stopped
)

type Rpn interface {
RegisterWarp(b64 string) ([]byte, error)
}

type Proxy interface {
// ID returns the ID of this proxy.
ID() string
Expand Down Expand Up @@ -68,6 +75,8 @@ type Proxies interface {
GetProxy(id string) (Proxy, error)
// Router returns a lowest common denomination router for this multi-transport.
Router() Router
// RPN returns the Rethink Proxy Network interface.
Rpn() Rpn
// Refresh re-registers proxies and returns a csv of active ones.
RefreshProxies() (string, error)
}
Expand Down
69 changes: 69 additions & 0 deletions intra/core/ip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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/.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// SPDX-License-Identifier: MIT

// from: https://github.com/bepass-org/warp-plus/blob/19ac233cc/iputils/iputils.go

package core

import (
"errors"
"fmt"
"math/big"
"math/rand"
"net/netip"
"time"
)

// RandomIPFromPrefix returns a random IP from the provided CIDR prefix.
// Supports IPv4 and IPv6. Does not support mapped inputs.
func RandomIPFromPrefix(cidr netip.Prefix) (netip.Addr, error) {
startingAddress := cidr.Masked().Addr()
if startingAddress.Is4In6() {
return netip.Addr{}, errors.New("mapped v4 addresses not supported")
}

prefixLen := cidr.Bits()
if prefixLen == -1 {
return netip.Addr{}, fmt.Errorf("invalid cidr: %s", cidr)
}

// Initialise rand number generator
rng := rand.New(rand.NewSource(time.Now().UnixNano()))

// Find the bit length of the Host portion of the provided CIDR
// prefix
hostLen := big.NewInt(int64(startingAddress.BitLen() - prefixLen))

// Find the max value for our random number
max := new(big.Int).Exp(big.NewInt(2), hostLen, nil)

// Generate the random number
randInt := new(big.Int).Rand(rng, max)

// Get the first address in the CIDR prefix in 16-bytes form
startingAddress16 := startingAddress.As16()

// Convert the first address into a decimal number
startingAddressInt := new(big.Int).SetBytes(startingAddress16[:])

// Add the random number to the decimal form of the starting address
// to get a random address in the desired range
randomAddressInt := new(big.Int).Add(startingAddressInt, randInt)

// Convert the random address from decimal form back into netip.Addr
randomAddress, ok := netip.AddrFromSlice(randomAddressInt.FillBytes(make([]byte, 16)))
if !ok {
return netip.Addr{}, fmt.Errorf("failed to generate random IP from CIDR: %s", cidr)
}

// Unmap any mapped v4 addresses before return
return randomAddress.Unmap(), nil
}
135 changes: 135 additions & 0 deletions intra/dialers/utls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// 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/.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// SPDX-License-Identifier: MIT

// from: github.com/bepass-org/warp-plus/blob/19ac233cc/warp/tlsdial.go

package dialers

import (
"io"
"net"

"github.com/celzero/firestack/intra/core"

utls "github.com/refraction-networking/utls"
)

const utlsExtSniCurveId uint16 = 0x15
const sniCurveSize = 1200
const utlsVer = utls.VersionTLS12

var utlsDefaultCypherSuites = []uint16{
utls.GREASE_PLACEHOLDER,
utls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
utls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
utls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
utls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
utls.TLS_AES_128_GCM_SHA256, // tls 1.3
utls.FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
utls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
utls.TLS_RSA_WITH_AES_256_CBC_SHA,
}

var utlsDefaultExt = []utls.TLSExtension{
&sniCurveExt{
curvelen: sniCurveSize,
pad: true,
},
&utls.SupportedCurvesExtension{Curves: []utls.CurveID{utls.X25519, utls.CurveP256}},
&utls.SupportedPointsExtension{SupportedPoints: []byte{0}}, // uncompressed
&utls.SessionTicketExtension{},
&utls.ALPNExtension{AlpnProtocols: []string{"http/1.1"}},
&utls.SignatureAlgorithmsExtension{
SupportedSignatureAlgorithms: []utls.SignatureScheme{
utls.ECDSAWithP256AndSHA256,
utls.ECDSAWithP384AndSHA384,
utls.ECDSAWithP521AndSHA512,
utls.PSSWithSHA256,
utls.PSSWithSHA384,
utls.PSSWithSHA512,
utls.PKCS1WithSHA256,
utls.PKCS1WithSHA384,
utls.PKCS1WithSHA512,
utls.ECDSAWithSHA1,
utls.PKCS1WithSHA1,
},
},
&utls.KeyShareExtension{KeyShares: []utls.KeyShare{
{Group: utls.CurveID(utls.GREASE_PLACEHOLDER), Data: []byte{0}},
{Group: utls.X25519},
}},
&utls.PSKKeyExchangeModesExtension{Modes: []uint8{1}}, // pskModeDHE
}

// sniCurveExt implements SNICurve (0x15) extension
type sniCurveExt struct {
*utls.GenericExtension
curvelen int
pad bool // enabled if true
}

// Len returns the length of the SNICurveExtension.
func (e *sniCurveExt) Len() int {
if e.pad {
return 4 + e.curvelen
} // extension disabled
return 0
}

// Read reads the SNICurveExtension.
func (e *sniCurveExt) Read(b []byte) (n int, err error) {
if !e.pad { // extension disabled
return 0, io.EOF
}
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// https://tools.ietf.org/html/rfc7627
b[0] = byte(utlsExtSniCurveId >> 8)
b[1] = byte(utlsExtSniCurveId)
b[2] = byte(e.curvelen >> 8)
b[3] = byte(e.curvelen)

bptr := core.Alloc()
buf := *bptr
buf = buf[:cap(buf)]
defer func() {
*bptr = buf
core.Recycle(bptr)
}()

copy(b[4:], buf)
return e.Len(), io.EOF
}

// utlsHello creates a TLS hello packet with SNICurve.
func utlsHello(conn net.Conn, config *utls.Config, sni string) (*utls.UConn, error) {
uconn := utls.UClient(conn, config, utls.HelloCustom)
spec := utls.ClientHelloSpec{
TLSVersMax: utlsVer,
TLSVersMin: utlsVer,
CipherSuites: utlsDefaultCypherSuites,
Extensions: utlsDefaultExt,
GetSessionID: nil,
}
spec.Extensions = append(spec.Extensions, &utls.SNIExtension{ServerName: sni})
err := uconn.ApplyPreset(&spec)
if err != nil {
return nil, err
}

err = uconn.Handshake()
if err != nil {
return nil, err
}

return uconn, nil
}
Loading

2 comments on commit 3a88024

@ignoramous
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ignoramous
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.