summaryrefslogtreecommitdiff
path: root/listener.go
blob: 593032f90e070c268c7ac325f6b7e8866a222903 (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
package obfsvpn

import (
	"bytes"
	"context"
	"crypto/rand"
	"encoding/hex"
	"net"

	pt "git.torproject.org/pluggable-transports/goptlib.git"
	"gitlab.com/yawning/obfs4.git/common/ntor"
	"gitlab.com/yawning/obfs4.git/transports/base"
	"gitlab.com/yawning/obfs4.git/transports/obfs4"
)

// ListenConfig contains options for listening to an address.
// If Seed is not set it defaults to a randomized value.
// If StateDir is not set the current working directory is used.
type ListenConfig struct {
	ListenConfig net.ListenConfig

	NodeID     *ntor.NodeID
	PrivateKey *ntor.PrivateKey
	Seed       [ntor.KeySeedLength]byte
	StateDir   string
}

// NewListenConfigCert creates a listener config by unpacking the node ID from
// its certificate.
// The private key must still be specified.
func NewListenConfigCert(cert string) (*ListenConfig, error) {
	nodeID, _, err := unpackCert(cert)
	if err != nil {
		return nil, err
	}
	return &ListenConfig{
		NodeID: nodeID,
	}, nil
}

// Wrap takes an existing net.Listener and wraps it in a listener that is
// configured to perform the ntor handshake.
// Values from the inner net.ListenConfig are ignored.
func (lc *ListenConfig) Wrap(ctx context.Context, ln net.Listener) (*Listener, error) {
	args := make(pt.Args)
	args.Add("node-id", lc.NodeID.Hex())
	args.Add("private-key", lc.PrivateKey.Hex())
	seed := ntor.KeySeed{}
	if bytes.Equal(lc.Seed[:], seed[:]) {
		_, err := rand.Read(seed[:])
		if err != nil {
			return nil, err
		}
	} else {
		seed = lc.Seed
	}
	args.Add("drbg-seed", hex.EncodeToString(seed[:]))
	sf, err := (&obfs4.Transport{}).ServerFactory(lc.StateDir, &args)
	if err != nil {
		return nil, err
	}
	return &Listener{sf: sf, ln: ln}, nil
}

// Listen announces on the local network address.
//
// See func net.Dial for a description of the network and address parameters.
func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (*Listener, error) {
	ln, err := lc.ListenConfig.Listen(ctx, network, address)
	if err != nil {
		return nil, err
	}
	return lc.Wrap(ctx, ln)
}

// Listener is a network listener that accepts obfuscated connections and
// performs the ntor handshake on them.
type Listener struct {
	sf base.ServerFactory
	ln net.Listener
}

// Accept waits for and returns the next connection to the listener.
func (l *Listener) Accept() (net.Conn, error) {
	conn, err := l.ln.Accept()
	if err != nil {
		return nil, err
	}
	conn, err = l.sf.WrapConn(conn)
	return conn, err
}

// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
func (l *Listener) Close() error {
	return l.ln.Close()
}

// Addr returns the listener's network address.
func (l *Listener) Addr() net.Addr {
	return l.ln.Addr()
}