summaryrefslogtreecommitdiff
path: root/vendor/github.com/cretz/bine/tor/listen.go
blob: 6b142d2a835c08fef63d9a462af12ccb00fdd406 (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
package tor

import (
	"context"
	"crypto"
	"crypto/rsa"
	"fmt"
	"net"
	"strconv"

	"github.com/cretz/bine/control"
	"github.com/cretz/bine/torutil/ed25519"
	othered25519 "golang.org/x/crypto/ed25519"
)

// OnionService implements net.Listener and net.Addr for an onion service.
type OnionService struct {
	// ID is the service ID for this onion service.
	ID string

	// Key is the private key for this service. It is either the set key, the
	// generated key, or nil if asked to discard the key. If present, it is
	// *crypto/rsa.PrivateKey (1024 bit) when Version3 is false or
	// github.com/cretz/bine/torutil/ed25519.KeyPair when Version3 is true.
	Key crypto.PrivateKey

	// Version3 says whether or not this service is a V3 service.
	Version3 bool

	// ClientAuths is the credential set for clients. The keys are username and
	// the values are credentials. The credentials will always be present even
	// if Tor had to generate them.
	ClientAuths map[string]string

	// LocalListener is the local TCP listener. This is always present.
	LocalListener net.Listener

	// RemotePorts is the set of remote ports that are forwarded to the local
	// listener. This will always have at least one value.
	RemotePorts []int

	// CloseLocalListenerOnClose is true if the local listener should be closed
	// on Close. This is set to true if a listener was created by Listen and set
	// to false of an existing LocalListener was provided to Listen.
	CloseLocalListenerOnClose bool

	// The Tor object that created this. Needed for Close.
	Tor *Tor
}

// ListenConf is the configuration for Listen calls.
type ListenConf struct {
	// LocalPort is the local port to create a TCP listener on. If the port is
	// 0, it is automatically chosen. This is ignored if LocalListener is set.
	LocalPort int

	// LocalListener is the specific local listener to back the onion service.
	// If this is nil (the default), then a listener is created with LocalPort.
	LocalListener net.Listener

	// RemotePorts are the remote ports to serve the onion service on. If empty,
	// it is the same as the local port or local listener. This must have at
	// least one value if the local listener is not a *net.TCPListener.
	RemotePorts []int

	// Key is the private key to use. If not present, a key is generated based
	// on whether Version3 is true or false. If present, it must be a
	// *crypto/rsa.PrivateKey (1024 bit), a
	// github.com/cretz/bine/torutil/ed25519.KeyPair, a
	// golang.org/x/crypto/ed25519.PrivateKey, or a
	// github.com/cretz/bine/control.Key.
	Key crypto.PrivateKey

	// Version3 determines whether, when Key is nil, a version 2 or version 3
	// service/key will be generated. If true it is version 3 (an ed25519 key
	// and v3 onion service) and if false it is version 2 (a RSA-1024 key and v2
	// onion service). If Key is not nil, this value is ignored.
	Version3 bool

	// ClientAuths is the set of usernames and credentials for client
	// authentication. The keys are usernames and the values are credentials. If
	// a username is present but the credential is empty, a credential is
	// generated by Tor for that user. If this is empty there is no
	// authentication.
	ClientAuths map[string]string

	// MaxStreams is the maximum number of streams the service will accept. 0
	// means unlimited.
	MaxStreams int

	// DiscardKey, if true and Key is nil (meaning a private key is generated),
	// tells Tor not to return the generated private key. This value is ignored
	// if Key is not nil.
	DiscardKey bool

	// Detach, if true, prevents the default behavior of the onion service being
	// deleted when this controller connection is closed.
	Detach bool

	// NonAnonymous must be true if Tor options HiddenServiceSingleHopMode and
	// HiddenServiceNonAnonymousMode are set. Otherwise, it must be false.
	NonAnonymous bool

	// MaxStreamsCloseCircuit determines whether to close the circuit when the
	// maximum number of streams is exceeded. If true, the circuit is closed. If
	// false, the stream is simply not connected but the circuit stays open.
	MaxStreamsCloseCircuit bool

	// NoWait if true will not wait until the onion service is published. If
	// false, the network will be enabled if it's not and then we will wait
	// until the onion service is published.
	NoWait bool
}

// Listen creates an onion service and local listener. The context can be nil.
// If conf is nil, the default struct value is used. Note, if this errors, any
// listeners created here are closed but if a LocalListener is provided it may remain open.
func (t *Tor) Listen(ctx context.Context, conf *ListenConf) (*OnionService, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	// Create the service up here and make sure we close it no matter the error within
	svc := &OnionService{Tor: t, CloseLocalListenerOnClose: conf.LocalListener == nil}
	var err error

	// Create the local listener if necessary
	svc.LocalListener = conf.LocalListener
	if svc.LocalListener == nil {
		if svc.LocalListener, err = net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(conf.LocalPort)); err != nil {
			return nil, err
		}
	}

	// Henceforth, any error requires we close the svc

	// Build the onion request
	req := &control.AddOnionRequest{MaxStreams: conf.MaxStreams, ClientAuths: conf.ClientAuths}
	// Set flags
	if conf.DiscardKey {
		req.Flags = append(req.Flags, "DiscardPK")
	}
	if conf.Detach {
		req.Flags = append(req.Flags, "Detach")
	}
	if len(conf.ClientAuths) > 0 {
		req.Flags = append(req.Flags, "BasicAuth")
	}
	if conf.NonAnonymous {
		req.Flags = append(req.Flags, "NonAnonymous")
	}
	if conf.MaxStreamsCloseCircuit {
		req.Flags = append(req.Flags, "MaxStreamsCloseCircuit")
	}
	// Set the key
	switch key := conf.Key.(type) {
	case nil:
		svc.Version3 = conf.Version3
		if conf.Version3 {
			req.Key = control.GenKey(control.KeyAlgoED25519V3)
		} else {
			req.Key = control.GenKey(control.KeyAlgoRSA1024)
		}
	case control.GenKey:
		svc.Version3 = conf.Version3
		req.Key = key
	case *rsa.PrivateKey:
		svc.Key = key
		svc.Version3 = false
		if key.N == nil || key.N.BitLen() != 1024 {
			err = fmt.Errorf("RSA key must be 1024 bits")
		} else {
			req.Key = &control.RSAKey{PrivateKey: key}
		}
	case *control.RSAKey:
		svc.Key = key.PrivateKey
		svc.Version3 = false
		if key.N == nil || key.N.BitLen() != 1024 {
			err = fmt.Errorf("RSA key must be 1024 bits")
		} else {
			req.Key = key
		}
	case ed25519.KeyPair:
		svc.Key = key
		svc.Version3 = true
		req.Key = &control.ED25519Key{key}
	case othered25519.PrivateKey:
		properKey := ed25519.FromCryptoPrivateKey(key)
		svc.Key = properKey
		svc.Version3 = true
		req.Key = &control.ED25519Key{properKey}
	case *control.ED25519Key:
		svc.Key = key.KeyPair
		svc.Version3 = true
		req.Key = key
	default:
		err = fmt.Errorf("Unrecognized key type: %T", key)
	}

	// Apply the remote ports
	if err == nil {
		if len(conf.RemotePorts) == 0 {
			tcpAddr, ok := svc.LocalListener.Addr().(*net.TCPAddr)
			if !ok {
				err = fmt.Errorf("Unable to derive local TCP port")
			} else {
				svc.RemotePorts = []int{tcpAddr.Port}
			}
		} else {
			svc.RemotePorts = make([]int, len(conf.RemotePorts))
			copy(svc.RemotePorts, conf.RemotePorts)
		}
	}
	// Apply the local ports with the remote ports
	if err == nil {
		localAddr := svc.LocalListener.Addr().String()
		if _, ok := svc.LocalListener.(*net.UnixListener); ok {
			localAddr = "unix:" + localAddr
		}
		for _, remotePort := range svc.RemotePorts {
			req.Ports = append(req.Ports, &control.KeyVal{Key: strconv.Itoa(remotePort), Val: localAddr})
		}
	}

	// Create the onion service
	var resp *control.AddOnionResponse
	if err == nil {
		resp, err = t.Control.AddOnion(req)
	}

	// Apply the response to the service
	if err == nil {
		svc.ID = resp.ServiceID
		switch key := resp.Key.(type) {
		case nil:
			// Do nothing
		case *control.RSAKey:
			svc.Key = key.PrivateKey
		case *control.ED25519Key:
			svc.Key = key.KeyPair
		default:
			err = fmt.Errorf("Unrecognized result key type: %T", key)
		}
		// Client auths are the conf and then overridden by results
		if len(conf.ClientAuths) > 0 {
			svc.ClientAuths = make(map[string]string, len(conf.ClientAuths))
			for k, v := range conf.ClientAuths {
				svc.ClientAuths[k] = v
			}
			for k, v := range resp.ClientAuths {
				svc.ClientAuths[k] = v
			}
		}
	}

	// Wait if necessary
	if err == nil && !conf.NoWait {
		t.Debugf("Enabling network before waiting for publication")
		// First make sure network is enabled
		if err = t.EnableNetwork(ctx, true); err == nil {
			t.Debugf("Waiting for publication")
			// Now we'll take a similar approach to Stem. Several UPLOADs are sent out, so we count em. If we see
			// UPLOADED, we succeeded. If we see failed, we count those. If there are as many failures as uploads, they
			// all failed and it's a failure. NOTE: unlike Stem's comments that say they don't, we are actually seeing
			// the service IDs for UPLOADED so we don't keep a map.
			uploadsAttempted := 0
			failures := []string{}
			_, err = t.Control.EventWait(ctx, []control.EventCode{control.EventCodeHSDesc},
				func(evt control.Event) (bool, error) {
					hs, _ := evt.(*control.HSDescEvent)
					if hs != nil && hs.Address == svc.ID {
						switch hs.Action {
						case "UPLOAD":
							uploadsAttempted++
						case "FAILED":
							failures = append(failures,
								fmt.Sprintf("Failed uploading to dir %v - reason: %v", hs.HSDir, hs.Reason))
							if len(failures) == uploadsAttempted {
								return false, fmt.Errorf("Failed all uploads, reasons: %v", failures)
							}
						case "UPLOADED":
							return true, nil
						}
					}
					return false, nil
				})
		}
	}

	// Give back err and close if there is an err
	if err != nil {
		if closeErr := svc.Close(); closeErr != nil {
			err = fmt.Errorf("Error on listen: %v (also got error trying to close: %v)", err, closeErr)
		}
		return nil, err
	}
	return svc, nil
}

// Accept implements net.Listener.Accept.
func (o *OnionService) Accept() (net.Conn, error) {
	return o.LocalListener.Accept()
}

// Addr implements net.Listener.Addr just returning this object.
func (o *OnionService) Addr() net.Addr {
	return o
}

// Network implements net.Addr.Network always returning "tcp".
func (o *OnionService) Network() string {
	return "tcp"
}

// String implements net.Addr.String and returns "<serviceID>.onion:<virtport>".
func (o *OnionService) String() string {
	return fmt.Sprintf("%v.onion:%v", o.ID, o.RemotePorts[0])
}

// Close implements net.Listener.Close and deletes the onion service and closes
// the LocalListener if CloseLocalListenerOnClose is true.
func (o *OnionService) Close() (err error) {
	o.Tor.Debugf("Closing onion %v", o)
	// Delete the onion first
	if o.ID != "" {
		err = o.Tor.Control.DelOnion(o.ID)
		o.ID = ""
	}
	// Now if the local one needs to be closed, do it
	if o.CloseLocalListenerOnClose && o.LocalListener != nil {
		if closeErr := o.LocalListener.Close(); closeErr != nil {
			if err != nil {
				err = fmt.Errorf("Unable to close onion: %v (also unable to close local listener: %v)", err, closeErr)
			} else {
				err = closeErr
			}
		}
		o.LocalListener = nil
	}
	if err != nil {
		o.Tor.Debugf("Failed closing onion: %v", err)
	}
	return
}