Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f637b0f916 | ||
|
|
06d425be3a | ||
|
|
17b31c5fc7 | ||
|
|
eee0bfc80a | ||
|
|
04dd90b25b | ||
|
|
2b116ffad6 | ||
|
|
6ab6551686 | ||
|
|
6bdeffe3f2 | ||
|
|
9224e79eea | ||
|
|
d1711b02b6 | ||
|
|
2fb865da94 | ||
|
|
00cb3dd99e | ||
|
|
0048d87cad | ||
|
|
564dfe2c79 | ||
|
|
f93e047f64 | ||
|
|
863440f936 | ||
|
|
e38b0155dd |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
liberapay: octeep
|
||||||
15
.github/workflows/wireproxy.yml
vendored
15
.github/workflows/wireproxy.yml
vendored
@@ -1,17 +1,20 @@
|
|||||||
name: Cross compile WirePorxy
|
name: Cross compile WireProxy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
create:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
WirePorxy:
|
WireProxy:
|
||||||
|
|
||||||
name: Cross compile WirePorxy
|
name: Cross compile WireProxy
|
||||||
|
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
env:
|
env:
|
||||||
workdir: ./WirePorxy
|
workdir: ./WireProxy
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -19,7 +22,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Git clone WirePorxy
|
- name: Git clone WireProxy
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/octeep/wireproxy.git ${{ env.workdir }}
|
git clone https://github.com/octeep/wireproxy.git ${{ env.workdir }}
|
||||||
cp ./.github/wireproxy-releaser.yml ${{ env.workdir }}/.goreleaser.yml
|
cp ./.github/wireproxy-releaser.yml ${{ env.workdir }}/.goreleaser.yml
|
||||||
@@ -43,4 +46,4 @@ jobs:
|
|||||||
tag_name: wireproxy
|
tag_name: wireproxy
|
||||||
files: ${{ env.workdir }}/dist/*.tar.gz
|
files: ${{ env.workdir }}/dist/*.tar.gz
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -1,4 +1,8 @@
|
|||||||
# wireproxy
|
# wireproxy
|
||||||
|
[](./LICENSE)
|
||||||
|
[](https://github.com/octeep/wireproxy/actions)
|
||||||
|
[](https://pkg.go.dev/github.com/octeep/wireproxy)
|
||||||
|
|
||||||
A wireguard client that exposes itself as a socks5 proxy or tunnels.
|
A wireguard client that exposes itself as a socks5 proxy or tunnels.
|
||||||
|
|
||||||
# What is this
|
# What is this
|
||||||
@@ -20,6 +24,10 @@ anything.
|
|||||||
- TCP static routing for client and server
|
- TCP static routing for client and server
|
||||||
- SOCKS5 proxy (currently only CONNECT is supported)
|
- SOCKS5 proxy (currently only CONNECT is supported)
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
- UDP Support in SOCKS5
|
||||||
|
- UDP static routing
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
```
|
```
|
||||||
./wireproxy -c [path to config]
|
./wireproxy -c [path to config]
|
||||||
@@ -108,6 +116,9 @@ WGConfig = <path to the wireguard config>
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Donation
|
||||||
|
<noscript><a href="https://liberapay.com/octeep/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||||||
|
|
||||||
|
|
||||||
## Stargazers over time
|
## Stargazers over time
|
||||||
|
|
||||||
|
|||||||
@@ -8,18 +8,56 @@ import (
|
|||||||
|
|
||||||
"github.com/akamensky/argparse"
|
"github.com/akamensky/argparse"
|
||||||
"github.com/octeep/wireproxy"
|
"github.com/octeep/wireproxy"
|
||||||
|
"suah.dev/protect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// an argument to denote that this process was spawned by -d
|
||||||
const daemonProcess = "daemon-process"
|
const daemonProcess = "daemon-process"
|
||||||
|
|
||||||
|
// attempts to pledge and panic if it fails
|
||||||
|
// this does nothing on non-OpenBSD systems
|
||||||
|
func pledgeOrPanic(promises string) {
|
||||||
|
err := protect.Pledge(promises)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempts to unveil and panic if it fails
|
||||||
|
// this does nothing on non-OpenBSD systems
|
||||||
|
func unveilOrPanic(path string, flags string) {
|
||||||
|
err := protect.Unveil(path, flags)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the executable path via syscalls or infer it from argv
|
||||||
|
func executablePath() string {
|
||||||
|
programPath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return os.Args[0]
|
||||||
|
}
|
||||||
|
return programPath
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
exePath := executablePath()
|
||||||
|
unveilOrPanic("/", "r")
|
||||||
|
unveilOrPanic(exePath, "x")
|
||||||
|
|
||||||
|
// only allow standard stdio operation, file reading, networking, and exec
|
||||||
|
// also remove unveil permission to lock unveil
|
||||||
|
pledgeOrPanic("stdio rpath inet dns proc exec")
|
||||||
|
|
||||||
isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess
|
isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess
|
||||||
args := os.Args
|
args := os.Args
|
||||||
if isDaemonProcess {
|
if isDaemonProcess {
|
||||||
|
// remove proc and exec if they are not needed
|
||||||
|
pledgeOrPanic("stdio rpath inet dns")
|
||||||
args = []string{args[0]}
|
args = []string{args[0]}
|
||||||
args = append(args, os.Args[2:]...)
|
args = append(args, os.Args[2:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
parser := argparse.NewParser("wireproxy", "Userspace wireguard client for proxying")
|
parser := argparse.NewParser("wireproxy", "Userspace wireguard client for proxying")
|
||||||
|
|
||||||
config := parser.String("c", "config", &argparse.Options{Required: true, Help: "Path of configuration file"})
|
config := parser.String("c", "config", &argparse.Options{Required: true, Help: "Path of configuration file"})
|
||||||
@@ -32,9 +70,14 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !*daemon {
|
||||||
|
// remove proc and exec if they are not needed
|
||||||
|
pledgeOrPanic("stdio rpath inet dns")
|
||||||
|
}
|
||||||
|
|
||||||
conf, err := wireproxy.ParseConfig(*config)
|
conf, err := wireproxy.ParseConfig(*config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *configTest {
|
if *configTest {
|
||||||
@@ -49,14 +92,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *daemon {
|
if *daemon {
|
||||||
programPath, err := os.Executable()
|
args[0] = daemonProcess
|
||||||
if err != nil {
|
cmd := exec.Command(exePath, args...)
|
||||||
programPath = args[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
newArgs := []string{daemonProcess}
|
|
||||||
newArgs = append(newArgs, args[1:]...)
|
|
||||||
cmd := exec.Command(programPath, newArgs...)
|
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
@@ -64,9 +101,12 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no file access is allowed from now on, only networking
|
||||||
|
pledgeOrPanic("stdio inet dns")
|
||||||
|
|
||||||
tnet, err := wireproxy.StartWireguard(conf.Device)
|
tnet, err := wireproxy.StartWireguard(conf.Device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, spawner := range conf.Routines {
|
for _, spawner := range conf.Routines {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"golang.zx2c4.com/go118/netip"
|
"golang.zx2c4.com/go118/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DeviceConfig contains the information to initiate a wireguard connection
|
||||||
type DeviceConfig struct {
|
type DeviceConfig struct {
|
||||||
SelfSecretKey string
|
SelfSecretKey string
|
||||||
SelfEndpoint []netip.Addr
|
SelfEndpoint []netip.Addr
|
||||||
@@ -160,6 +161,7 @@ func resolveIPPAndPort(addr string) (string, error) {
|
|||||||
return net.JoinHostPort(ip.String(), port), nil
|
return net.JoinHostPort(ip.String(), port), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseInterface parses the [Interface] section and extract the information into `device`
|
||||||
func ParseInterface(cfg *ini.File, device *DeviceConfig) error {
|
func ParseInterface(cfg *ini.File, device *DeviceConfig) error {
|
||||||
sections, err := cfg.SectionsByName("Interface")
|
sections, err := cfg.SectionsByName("Interface")
|
||||||
if len(sections) != 1 || err != nil {
|
if len(sections) != 1 || err != nil {
|
||||||
@@ -197,6 +199,7 @@ func ParseInterface(cfg *ini.File, device *DeviceConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePeer parses the [Peer] section and extract the information into `device`
|
||||||
func ParsePeer(cfg *ini.File, device *DeviceConfig) error {
|
func ParsePeer(cfg *ini.File, device *DeviceConfig) error {
|
||||||
sections, err := cfg.SectionsByName("Peer")
|
sections, err := cfg.SectionsByName("Peer")
|
||||||
if len(sections) != 1 || err != nil {
|
if len(sections) != 1 || err != nil {
|
||||||
@@ -292,6 +295,8 @@ func parseSocks5Config(section *ini.Section) (RoutineSpawner, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Takes a function that parses an individual section into a config, and apply it on all
|
||||||
|
// specified sections
|
||||||
func parseRoutinesConfig(routines *[]RoutineSpawner, cfg *ini.File, sectionName string, f func(*ini.Section) (RoutineSpawner, error)) error {
|
func parseRoutinesConfig(routines *[]RoutineSpawner, cfg *ini.File, sectionName string, f func(*ini.Section) (RoutineSpawner, error)) error {
|
||||||
sections, err := cfg.SectionsByName(sectionName)
|
sections, err := cfg.SectionsByName(sectionName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -310,6 +315,7 @@ func parseRoutinesConfig(routines *[]RoutineSpawner, cfg *ini.File, sectionName
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseConfig takes the path of a configuration file and parses it into Configuration
|
||||||
func ParseConfig(path string) (*Configuration, error) {
|
func ParseConfig(path string) (*Configuration, error) {
|
||||||
iniOpt := ini.LoadOptions{
|
iniOpt := ini.LoadOptions{
|
||||||
Insensitive: true,
|
Insensitive: true,
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ RUN apk upgrade
|
|||||||
COPY --from=go-build /go/wireproxy/wireproxy /usr/bin/
|
COPY --from=go-build /go/wireproxy/wireproxy /usr/bin/
|
||||||
|
|
||||||
VOLUME [ "/etc/wireproxy"]
|
VOLUME [ "/etc/wireproxy"]
|
||||||
ENTRYPOINT [ "/usr/bin/wireproxy", "/etc/wireproxy/config" ]
|
ENTRYPOINT [ "/usr/bin/wireproxy", "--config", "/etc/wireproxy/config" ]
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -21,4 +21,5 @@ require (
|
|||||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||||
|
suah.dev/protect v1.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
3
go.sum
3
go.sum
@@ -744,6 +744,7 @@ golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210314195730-07df6a141424/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210314195730-07df6a141424/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -999,3 +1000,5 @@ sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:w
|
|||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
suah.dev/protect v1.2.0 h1:4G4V43yVYXCjLFzaE9QJR0fLo3rf5vNBS9YxyoI19DU=
|
||||||
|
suah.dev/protect v1.2.0/go.mod h1:Ocn1yqUskqe/is6N2bxQxtT+fegbvQsOFyHbJAQu9XE=
|
||||||
|
|||||||
161
routine.go
161
routine.go
@@ -4,11 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/armon/go-socks5"
|
"github.com/armon/go-socks5"
|
||||||
@@ -17,20 +17,33 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// errorLogger is the logger to print error message
|
||||||
|
var errorLogger = log.New(os.Stderr, "ERROR: ", log.LstdFlags)
|
||||||
|
|
||||||
|
// CredentialValidator stores the authentication data of a socks5 proxy
|
||||||
type CredentialValidator struct {
|
type CredentialValidator struct {
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VirtualTun stores a reference to netstack network and DNS configuration
|
||||||
type VirtualTun struct {
|
type VirtualTun struct {
|
||||||
tnet *netstack.Net
|
tnet *netstack.Net
|
||||||
systemDNS bool
|
systemDNS bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RoutineSpawner spawns a routine (e.g. socks5, tcp static routes) after the configuration is parsed
|
||||||
type RoutineSpawner interface {
|
type RoutineSpawner interface {
|
||||||
SpawnRoutine(vt *VirtualTun)
|
SpawnRoutine(vt *VirtualTun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type addressPort struct {
|
||||||
|
address string
|
||||||
|
port uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupAddr lookups a hostname.
|
||||||
|
// DNS traffic may or may not be routed depending on VirtualTun's setting
|
||||||
func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, error) {
|
func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, error) {
|
||||||
if d.systemDNS {
|
if d.systemDNS {
|
||||||
return net.DefaultResolver.LookupHost(ctx, name)
|
return net.DefaultResolver.LookupHost(ctx, name)
|
||||||
@@ -39,13 +52,10 @@ func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d VirtualTun) ResolveAddrPort(saddr string) (*netip.AddrPort, error) {
|
// ResolveAddrPortWithContext resolves a hostname and returns an AddrPort.
|
||||||
name, sport, err := net.SplitHostPort(saddr)
|
// DNS traffic may or may not be routed depending on VirtualTun's setting
|
||||||
if err != nil {
|
func (d VirtualTun) ResolveAddrWithContext(ctx context.Context, name string) (*netip.Addr, error) {
|
||||||
return nil, err
|
addrs, err := d.LookupAddr(ctx, name)
|
||||||
}
|
|
||||||
|
|
||||||
addrs, err := d.LookupAddr(context.Background(), name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -55,7 +65,38 @@ func (d VirtualTun) ResolveAddrPort(saddr string) (*netip.AddrPort, error) {
|
|||||||
return nil, errors.New("no address found for: " + name)
|
return nil, errors.New("no address found for: " + name)
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, err := netip.ParseAddr(addrs[rand.Intn(size)])
|
rand.Shuffle(size, func(i, j int) {
|
||||||
|
addrs[i], addrs[j] = addrs[j], addrs[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
var addr netip.Addr
|
||||||
|
for _, saddr := range addrs {
|
||||||
|
addr, err = netip.ParseAddr(saddr)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve resolves a hostname and returns an IP.
|
||||||
|
// DNS traffic may or may not be routed depending on VirtualTun's setting
|
||||||
|
func (d VirtualTun) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
|
||||||
|
addr, err := d.ResolveAddrWithContext(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, addr.AsSlice(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAddressPort(endpoint string) (*addressPort, error) {
|
||||||
|
name, sport, err := net.SplitHostPort(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -65,34 +106,20 @@ func (d VirtualTun) ResolveAddrPort(saddr string) (*netip.AddrPort, error) {
|
|||||||
return nil, &net.OpError{Op: "dial", Err: errors.New("port must be numeric")}
|
return nil, &net.OpError{Op: "dial", Err: errors.New("port must be numeric")}
|
||||||
}
|
}
|
||||||
|
|
||||||
addrPort := netip.AddrPortFrom(addr, uint16(port))
|
return &addressPort{address: name, port: uint16(port)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d VirtualTun) resolveToAddrPort(endpoint *addressPort) (*netip.AddrPort, error) {
|
||||||
|
addr, err := d.ResolveAddrWithContext(context.Background(), endpoint.address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addrPort := netip.AddrPortFrom(*addr, endpoint.port)
|
||||||
return &addrPort, nil
|
return &addrPort, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d VirtualTun) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
|
// Spawns a socks5 server.
|
||||||
var addrs []string
|
|
||||||
var err error
|
|
||||||
|
|
||||||
addrs, err = d.LookupAddr(ctx, name)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ctx, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
size := len(addrs)
|
|
||||||
if size == 0 {
|
|
||||||
return ctx, nil, errors.New("no address found for: " + name)
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := addrs[rand.Intn(size)]
|
|
||||||
ip := net.ParseIP(addr)
|
|
||||||
if ip == nil {
|
|
||||||
return ctx, nil, errors.New("invalid address: " + addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx, ip, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) {
|
func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) {
|
||||||
conf := &socks5.Config{Dial: vt.tnet.DialContext, Resolver: vt}
|
conf := &socks5.Config{Dial: vt.tnet.DialContext, Resolver: vt}
|
||||||
if username := config.Username; username != "" {
|
if username := config.Username; username != "" {
|
||||||
@@ -102,33 +129,46 @@ func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) {
|
|||||||
}
|
}
|
||||||
server, err := socks5.New(conf)
|
server, err := socks5.New(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := server.ListenAndServe("tcp", config.BindAddress); err != nil {
|
if err := server.ListenAndServe("tcp", config.BindAddress); err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Valid checks the authentication data in CredentialValidator and compare them
|
||||||
|
// to username and password in constant time.
|
||||||
func (c CredentialValidator) Valid(username, password string) bool {
|
func (c CredentialValidator) Valid(username, password string) bool {
|
||||||
u := subtle.ConstantTimeCompare([]byte(c.username), []byte(username))
|
u := subtle.ConstantTimeCompare([]byte(c.username), []byte(username))
|
||||||
p := subtle.ConstantTimeCompare([]byte(c.password), []byte(password))
|
p := subtle.ConstantTimeCompare([]byte(c.password), []byte(password))
|
||||||
return u&p == 1
|
return u&p == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connForward copy data from `from` to `to`, then close both stream.
|
||||||
func connForward(bufSize int, from io.ReadWriteCloser, to io.ReadWriteCloser) {
|
func connForward(bufSize int, from io.ReadWriteCloser, to io.ReadWriteCloser) {
|
||||||
buf := make([]byte, bufSize)
|
buf := make([]byte, bufSize)
|
||||||
_, err := io.CopyBuffer(to, from, buf)
|
_, err := io.CopyBuffer(to, from, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
to.Close()
|
errorLogger.Printf("Cannot forward traffic: %s\n", err.Error())
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
_ = from.Close()
|
||||||
|
_ = to.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) {
|
// tcpClientForward starts a new connection via wireguard and forward traffic from `conn`
|
||||||
sconn, err := tnet.DialTCP(target)
|
func tcpClientForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
|
||||||
|
target, err := vt.resolveToAddrPort(raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("[ERROR] TCP Client Tunnel to %s: %s\n", target, err.Error())
|
errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpAddr := TCPAddrFromAddrPort(*target)
|
||||||
|
|
||||||
|
sconn, err := vt.tnet.DialTCP(tcpAddr)
|
||||||
|
if err != nil {
|
||||||
|
errorLogger.Printf("TCP Client Tunnel to %s: %s\n", target, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,31 +176,40 @@ func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) {
|
|||||||
go connForward(1024, conn, sconn)
|
go connForward(1024, conn, sconn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spawns a local TCP server which acts as a proxy to the specified target
|
||||||
func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) {
|
func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) {
|
||||||
raddr, err := vt.ResolveAddrPort(conf.Target)
|
raddr, err := parseAddressPort(conf.Target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
tcpAddr := TCPAddrFromAddrPort(*raddr)
|
|
||||||
|
|
||||||
server, err := net.ListenTCP("tcp", conf.BindAddress)
|
server, err := net.ListenTCP("tcp", conf.BindAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
conn, err := server.Accept()
|
conn, err := server.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
go tcpClientForward(vt.tnet, tcpAddr, conn)
|
go tcpClientForward(vt, raddr, conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tcpServerForward(target *net.TCPAddr, conn net.Conn) {
|
// tcpServerForward starts a new connection locally and forward traffic from `conn`
|
||||||
sconn, err := net.DialTCP("tcp", nil, target)
|
func tcpServerForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
|
||||||
|
target, err := vt.resolveToAddrPort(raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("[ERROR] TCP Server Tunnel to %s: %s\n", target, err.Error())
|
errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpAddr := TCPAddrFromAddrPort(*target)
|
||||||
|
|
||||||
|
sconn, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||||
|
if err != nil {
|
||||||
|
errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,24 +217,24 @@ func tcpServerForward(target *net.TCPAddr, conn net.Conn) {
|
|||||||
go connForward(1024, conn, sconn)
|
go connForward(1024, conn, sconn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spawns a TCP server on wireguard which acts as a proxy to the specified target
|
||||||
func (conf *TCPServerTunnelConfig) SpawnRoutine(vt *VirtualTun) {
|
func (conf *TCPServerTunnelConfig) SpawnRoutine(vt *VirtualTun) {
|
||||||
raddr, err := vt.ResolveAddrPort(conf.Target)
|
raddr, err := parseAddressPort(conf.Target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
tcpAddr := TCPAddrFromAddrPort(*raddr)
|
|
||||||
|
|
||||||
addr := &net.TCPAddr{Port: conf.ListenPort}
|
addr := &net.TCPAddr{Port: conf.ListenPort}
|
||||||
server, err := vt.tnet.ListenTCP(addr)
|
server, err := vt.tnet.ListenTCP(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
conn, err := server.Accept()
|
conn, err := server.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
go tcpServerForward(tcpAddr, conn)
|
go tcpServerForward(vt, raddr, conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DeviceSetting contains the parameters for setting up a tun interface
|
||||||
type DeviceSetting struct {
|
type DeviceSetting struct {
|
||||||
ipcRequest string
|
ipcRequest string
|
||||||
dns []netip.Addr
|
dns []netip.Addr
|
||||||
@@ -16,18 +17,21 @@ type DeviceSetting struct {
|
|||||||
mtu int
|
mtu int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serialize the config into an IPC request and DeviceSetting
|
||||||
func createIPCRequest(conf *DeviceConfig) (*DeviceSetting, error) {
|
func createIPCRequest(conf *DeviceConfig) (*DeviceSetting, error) {
|
||||||
request := fmt.Sprintf(`private_key=%s
|
request := fmt.Sprintf(`private_key=%s
|
||||||
public_key=%s
|
public_key=%s
|
||||||
endpoint=%s
|
endpoint=%s
|
||||||
persistent_keepalive_interval=%d
|
persistent_keepalive_interval=%d
|
||||||
preshared_key=%s
|
preshared_key=%s
|
||||||
allowed_ip=0.0.0.0/0`, conf.SelfSecretKey, conf.PeerPublicKey, conf.PeerEndpoint, conf.KeepAlive, conf.PreSharedKey)
|
allowed_ip=0.0.0.0/0
|
||||||
|
allowed_ip=::0/0`, conf.SelfSecretKey, conf.PeerPublicKey, conf.PeerEndpoint, conf.KeepAlive, conf.PreSharedKey)
|
||||||
|
|
||||||
setting := &DeviceSetting{ipcRequest: request, dns: conf.DNS, deviceAddr: conf.SelfEndpoint, mtu: conf.MTU}
|
setting := &DeviceSetting{ipcRequest: request, dns: conf.DNS, deviceAddr: conf.SelfEndpoint, mtu: conf.MTU}
|
||||||
return setting, nil
|
return setting, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartWireguard creates a tun interface on netstack given a configuration
|
||||||
func StartWireguard(conf *DeviceConfig) (*VirtualTun, error) {
|
func StartWireguard(conf *DeviceConfig) (*VirtualTun, error) {
|
||||||
setting, err := createIPCRequest(conf)
|
setting, err := createIPCRequest(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user