summaryrefslogtreecommitdiff
path: root/obfsproxy/main.go
blob: a35a26d14a7899c7f7d87c9e2cac8624c35ce238 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// The obfsproxy command creates a SOCKS5 obfuscating proxy.
package main

import (
	"context"
	"encoding/json"
	"flag"
	"io"
	"log"
	"net"
	"os"
	"os/signal"

	"0xacab.org/leap/obfsvpn"

	"git.torproject.org/pluggable-transports/goptlib.git"
)

const transportName = "obfs4"

func main() {
	// Setup logging.
	logger := log.New(os.Stderr, "", log.LstdFlags)
	debug := log.New(io.Discard, "DEBUG ", log.LstdFlags)

	// Setup command line flags.
	var (
		verbose  bool
		addr     string = "[::1]:0"
		vpnAddr  string
		cfgFile  string
		stateDir string
	)
	flags := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	flags.BoolVar(&verbose, "v", verbose, "Enable verbose logging")
	flags.StringVar(&addr, "addr", addr, "The address to listen on for client connections")
	flags.StringVar(&vpnAddr, "vpn", vpnAddr, "The address of the OpenVPN server to connect to")
	flags.StringVar(&cfgFile, "c", cfgFile, "The JSON config file to load")
	flags.StringVar(&stateDir, "state", stateDir, "A directory in which to store bridge state")
	err := flags.Parse(os.Args[1:])
	if err != nil {
		logger.Fatalf("error parsing flags: %v", err)
	}

	if vpnAddr == "" {
		flags.PrintDefaults()
		logger.Fatal("must specify -vpn")
	}

	tcpVPNAddr, err := net.ResolveTCPAddr("tcp", vpnAddr)
	if err != nil {
		logger.Fatalf("error resolving VPN address: %v", err)
	}

	fd, err := os.Open(cfgFile)
	if err != nil {
		logger.Fatalf("error opening config file: %v", err)
	}
	var cfg Config
	err = json.NewDecoder(fd).Decode(&cfg)
	if err != nil {
		logger.Fatalf("error decoding config: %v", err)
	}

	// Configure logging.
	if verbose {
		debug.SetOutput(os.Stderr)
	}

	// Setup graceful shutdown.
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)

	listenConfig := obfsvpn.ListenConfig{
		NodeID:     cfg.NodeID,
		PrivateKey: cfg.PrivateKey,
		Seed:       cfg.DRBGSeed,
		StateDir:   stateDir,
	}
	ln, err := listenConfig.Listen(ctx, "tcp", addr)
	if err != nil {
		logger.Fatalf("error binding to %s: %v", addr, err)
	}

	go func() {
		<-ctx.Done()
		// Stop releases the signal handling and falls back to the default behavior,
		// so sending another interrupt will immediately terminate.
		stop()
		logger.Printf("shutting down…")
		err := ln.Close()
		if err != nil {
			logger.Printf("error closing listener: %v", err)
		}
	}()

	info := &pt.ServerInfo{
		OrAddr: tcpVPNAddr,
	}

	logger.Printf("listening on %s…", ln.Addr())
	for {
		conn, err := ln.Accept()
		if err != nil {
			debug.Printf("error accepting connection: %v", err)
			return
		}
		debug.Printf("accepted connection %v…", conn)
		go proxyConn(ctx, info, conn, net.Dialer{}, logger, debug)
	}
}

func proxyConn(ctx context.Context, info *pt.ServerInfo, conn net.Conn, d net.Dialer, logger, debug *log.Logger) {
	defer func() {
		err := conn.Close()
		if err != nil {
			debug.Printf("error closing connection: %v", err)
		}
	}()

	// TODO: do we actually want to send the USERADDR/TRANSPORT ExtOrPort
	// commands? I don't understand how this works, so I'm unsure.
	remote, err := pt.DialOr(info, conn.RemoteAddr().String(), transportName)
	if err != nil {
		logger.Printf("error dialing remote: %v", err)
		return
	}
	defer func() {
		err := remote.Close()
		if err != nil {
			debug.Printf("error closing remote connection: %v", err)
		}
	}()

	go func() {
		_, err := io.Copy(remote, conn)
		if err != nil {
			logger.Printf("error proxying client data to remote: %v", err)
			return
		}
	}()
	go func() {
		_, err := io.Copy(conn, remote)
		if err != nil {
			logger.Printf("error proxying remote data to client: %v", err)
			return
		}
	}()
}