-
Notifications
You must be signed in to change notification settings - Fork 4
/
backend.go
227 lines (200 loc) · 6.85 KB
/
backend.go
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
// go-coronanet - Coronavirus social distancing network
// Copyright (c) 2020 Péter Szilágyi. All rights reserved.
package coronanet
import (
"context"
"encoding/gob"
"path/filepath"
"strconv"
"sync"
"github.com/coronanet/go-coronanet/protocols"
"github.com/coronanet/go-coronanet/protocols/corona"
"github.com/coronanet/go-coronanet/protocols/events"
"github.com/coronanet/go-coronanet/protocols/pairing"
"github.com/coronanet/go-coronanet/tornet"
"github.com/cretz/bine/control"
"github.com/cretz/bine/tor"
"github.com/ethereum/go-ethereum/log"
"github.com/ipsn/go-libtor"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
)
// Backend represents the social network node that can connect to other nodes in
// the network and exchange information.
type Backend struct {
database *leveldb.DB // Database to avoid custom file formats for storage
network *tor.Tor // Proxy through the Tor network, nil when offline
// Social protocol and related fields
overlay *tornet.Node // Overlay network running the Corona protocol
dialer *scheduler // Dial scheduler to periodically connect to peers
pairing *pairing.Pairing // Currently active pairing session (nil if none)
peerset map[tornet.IdentityFingerprint]*gob.Encoder // Current active connections for updates
// Event protocol and related fields
hosted map[tornet.IdentityFingerprint]*events.Server // Locally hosted and maintained events
checkin map[tornet.IdentityFingerprint]*events.CheckinSession // Active checkin session per hosted event
joined map[tornet.IdentityFingerprint]*events.Client // Remotely joined and watched events
logger log.Logger // Contextual logger to embed outside tags
lock sync.RWMutex
}
// NewBackend creates a new social network node.
func NewBackend(datadir string, logger log.Logger) (*Backend, error) {
// Create the database for accessing locally stored data
db, err := leveldb.OpenFile(filepath.Join(datadir, "ldb"), &opt.Options{})
if err != nil {
return nil, err
}
// Create the Tor background process for accessing remote data
net, err := tor.Start(nil, &tor.StartConf{
ProcessCreator: libtor.Creator,
UseEmbeddedControlConn: true,
DataDir: filepath.Join(datadir, "tor"),
//DebugWriter: os.Stderr,
//NoHush: true,
})
if err != nil {
db.Close()
return nil, err
}
// Create an idle backend; if there's already a user profile, assemble the overlay
backend := &Backend{
database: db,
network: net,
peerset: make(map[tornet.IdentityFingerprint]*gob.Encoder),
logger: logger,
}
backend.dialer = newScheduler(backend)
if prof, err := backend.Profile(); err == nil {
if err := backend.initOverlay(*prof.KeyRing); err != nil {
net.Close()
db.Close()
return nil, err
}
}
return backend, nil
}
// initOverlay initializes the application layer networking protocols that will
// the within the backend.
//
// Note, this method assumes the write lock is held.
func (b *Backend) initOverlay(keyring tornet.SecretKeyRing) error {
// Create the social network node for the contact list
b.logger.Info("Creating social node", "addresses", len(keyring.Addresses), "contacts", len(keyring.Trusted))
if b.overlay != nil {
panic("overlay double initialized")
}
overlay, err := tornet.NewNode(tornet.NodeConfig{
Gateway: tornet.NewTorGateway(b.network),
KeyRing: keyring,
RingHandler: b.updateKeyring,
ConnHandler: protocols.MakeHandler(protocols.HandlerConfig{
Protocol: corona.Protocol,
Handlers: map[uint]protocols.Handler{
1: b.handleContactV1,
},
}),
ConnTimeout: connectionIdleTimeout,
Logger: b.logger,
})
if err != nil {
return err
}
b.overlay = overlay
// Create the event servers and clients for meetup tracking
if err := b.initEvents(); err != nil {
b.overlay.Close()
b.overlay = nil
return err
}
return nil
}
// nukeOverlay tears down the entire application overlay network.
//
// Note, this method assumes the write lock is held.
func (b *Backend) nukeOverlay() error {
// Tear down the event servers and clients
b.nukeEvents()
// Tear down the social networking
b.logger.Info("Deleting social node")
if b.overlay == nil {
return nil
}
err := b.overlay.Close()
b.overlay = nil
// Since the overlay was deleted, ping the scheduler to spin down
b.dialer.suspend()
return err
}
// Close tears down the backend. It's irreversible, it cannot be used afterwards.
func (b *Backend) Close() error {
// Stop initiating and accepting outbound connections, drop everyone
b.dialer.close()
b.nukeOverlay()
// Disable and tear down the Tor gateway
b.network.Close()
b.network = nil
// Close the database and return
b.database.Close()
b.database = nil
return nil
}
// EnableGateway opens up the network proxy into the Tor network and starts
// building out the P2P overlay network on top. The method is async.
func (b *Backend) EnableGateway() error {
b.logger.Info("Enabling gateway networking")
if err := b.network.EnableNetwork(context.Background(), false); err != nil {
return err
}
// Networking enabled, resume all scheduled dials
prof, err := b.Profile()
if err != nil {
return nil // No profile is fine
}
b.dialer.reinit(*prof.KeyRing)
b.lock.RLock()
for _, client := range b.joined {
client.Resume()
}
b.lock.RUnlock()
return nil
}
// DisableGateway tears down the P2P overlay network running on top of Tor, breaks
// all active connections and closes off he network proxy from Tor.
func (b *Backend) DisableGateway() error {
b.logger.Info("Disabling gateway networking")
if err := b.network.Control.SetConf(control.KeyVals("DisableNetwork", "1")...); err != nil {
return err
}
// Networking disabled, suspend all scheduled dials as pointless
b.dialer.suspend()
b.lock.RLock()
for _, client := range b.joined {
client.Suspend()
}
b.lock.RUnlock()
return nil
}
// GatewayStatus returns whether the backend has networking enabled, whether that
// works or not; and the download and upload traffic incurred since starting it.
func (b *Backend) GatewayStatus() (bool, bool, uint64, uint64, error) {
// Retrieve whether the network is enabled or not
res, err := b.network.Control.GetConf("DisableNetwork")
if err != nil {
return false, false, 0, 0, err
}
enabled := res[0].Val == "0"
// Retrieve some status metrics from Tor itself
res, err = b.network.Control.GetInfo("status/circuit-established", "traffic/read", "traffic/written", "network-liveness")
if err != nil {
return enabled, false, 0, 0, err
}
connected := res[0].Val == "1" // TODO(karalabe): this doesn't seem to detect going offline, help?
ingress, err := strconv.ParseUint(res[1].Val, 0, 64)
if err != nil {
return enabled, connected, 0, 0, err
}
egress, err := strconv.ParseUint(res[2].Val, 0, 64)
if err != nil {
return enabled, connected, ingress, 0, err
}
return enabled, connected, ingress, egress, nil
}