1 package state
2 3 import (
4 "crypto/tls"
5 "errors"
6 "fmt"
7 "io/ioutil"
8 "math/rand"
9 "net"
10 "os"
11 "path/filepath"
12 "strconv"
13 "strings"
14 "time"
15 16 "github.com/btcsuite/go-socks/socks"
17 "github.com/p9c/p9/pkg/qu"
18 "go.uber.org/atomic"
19 20 "github.com/p9c/p9/pkg/log"
21 "github.com/p9c/p9/cmd/node/active"
22 "github.com/p9c/p9/pkg/amt"
23 "github.com/p9c/p9/pkg/apputil"
24 "github.com/p9c/p9/pkg/chaincfg"
25 "github.com/p9c/p9/pkg/chainrpc"
26 "github.com/p9c/p9/pkg/connmgr"
27 "github.com/p9c/p9/pkg/fork"
28 "github.com/p9c/p9/pkg/pipe"
29 "github.com/p9c/p9/pkg/util"
30 "github.com/p9c/p9/pkg/util/routeable"
31 "github.com/p9c/p9/pod/config"
32 )
33 34 // GetNew returns a fresh new context
35 func GetNew(
36 config *config.Config, hf func(ifc interface{}) error,
37 quit qu.C,
38 ) (s *State, e error) {
39 // after this, all the configurations are set and mostly sanitized
40 if e = config.Initialize(hf); E.Chk(e) {
41 // return
42 panic(e)
43 }
44 log.SetLogLevel(config.LogLevel.V())
45 chainClientReady := qu.T()
46 rand.Seed(time.Now().UnixNano())
47 rand.Seed(rand.Int63())
48 s = &State{
49 ChainClientReady: chainClientReady,
50 KillAll: quit,
51 Config: config,
52 ConfigMap: config.Map,
53 StateCfg: new(active.Config),
54 NodeChan: make(chan *chainrpc.Server),
55 Syncing: atomic.NewBool(true),
56 }
57 // everything in the configuration is set correctly up to this point, except for settings based on the running
58 // network, so after this is when those settings are elaborated
59 T.Ln("setting active network:", s.Config.Network.V())
60 switch s.Config.Network.V() {
61 case "testnet", "testnet3", "t":
62 s.ActiveNet = &chaincfg.TestNet3Params
63 fork.IsTestnet = true
64 case "regtestnet", "regressiontest", "r":
65 fork.IsTestnet = true
66 s.ActiveNet = &chaincfg.RegressionTestParams
67 case "simnet", "s":
68 fork.IsTestnet = true
69 s.ActiveNet = &chaincfg.SimNetParams
70 default:
71 if s.Config.Network.V() != "mainnet" &&
72 s.Config.Network.V() != "m" {
73 D.Ln("using mainnet for node")
74 }
75 s.ActiveNet = &chaincfg.MainNetParams
76 }
77 if (s.Config.LAN.True() || s.Config.Solo.True()) && s.ActiveNet.Name == "mainnet" {
78 if e = fmt.Errorf("neither Solo or LAN can be active on mainnet for obvious reasons"); F.Chk(e) {
79 return
80 }
81 }
82 // if pipe logging is enabled, start it up
83 if s.Config.PipeLog.True() {
84 D.Ln("starting up pipe logger")
85 pipe.LogServe(s.KillAll, fmt.Sprint(os.Args))
86 }
87 I.Ln("set to write logs in the network specific directory")
88 if e = s.Config.LogDir.Set(
89 filepath.Join(s.Config.DataDir.V(), s.ActiveNet.Name)); E.Chk(e) {
90 }
91 I.Ln("enable log file writing")
92 if e = log.SetLogWriteToFile(s.Config.LogDir.V(),
93 s.Config.RunningCommand.Name); E.Chk(e) {
94 }
95 // set up TLS stuff if it hasn't been set up yet. We assume if the configured values correspond to files the files
96 // are valid TLS cert/pairs, and that the key will be absent if onetimetlskey was set
97 if (s.Config.ClientTLS.True() || s.Config.ServerTLS.True()) &&
98 (
99 (!apputil.FileExists(s.Config.RPCKey.V()) && s.Config.OneTimeTLSKey.False()) ||
100 !apputil.FileExists(s.Config.RPCCert.V()) ||
101 !apputil.FileExists(s.Config.CAFile.V())) {
102 D.Ln("generating TLS certificates")
103 I.Ln(s.Config.RPCKey.V(), s.Config.RPCCert.V(), s.Config.RPCKey.V())
104 // Create directories for cert and key files if they do not yet exist.
105 certDir, _ := filepath.Split(s.Config.RPCCert.V())
106 keyDir, _ := filepath.Split(s.Config.RPCKey.V())
107 e = os.MkdirAll(certDir, 0700)
108 if e != nil {
109 E.Ln(e)
110 return
111 }
112 e = os.MkdirAll(keyDir, 0700)
113 if e != nil {
114 E.Ln(e)
115 return
116 }
117 // Generate cert pair.
118 org := "pod/wallet autogenerated cert"
119 validUntil := time.Now().Add(time.Hour * 24 * 365 * 10)
120 var cert, key []byte
121 cert, key, e = util.NewTLSCertPair(org, validUntil, nil)
122 if e != nil {
123 E.Ln(e)
124 return
125 }
126 _, e = tls.X509KeyPair(cert, key)
127 if e != nil {
128 E.Ln(e)
129 return
130 }
131 // Write cert and (potentially) the key files.
132 e = ioutil.WriteFile(s.Config.RPCCert.V(), cert, 0600)
133 if e != nil {
134 rmErr := os.Remove(s.Config.RPCCert.V())
135 if rmErr != nil {
136 E.Ln("cannot remove written certificates:", rmErr)
137 }
138 return
139 }
140 e = ioutil.WriteFile(s.Config.CAFile.V(), cert, 0600)
141 if e != nil {
142 rmErr := os.Remove(s.Config.RPCCert.V())
143 if rmErr != nil {
144 E.Ln("cannot remove written certificates:", rmErr)
145 }
146 return
147 }
148 e = ioutil.WriteFile(s.Config.RPCKey.V(), key, 0600)
149 if e != nil {
150 E.Ln(e)
151 rmErr := os.Remove(s.Config.RPCCert.V())
152 if rmErr != nil {
153 E.Ln("cannot remove written certificates:", rmErr)
154 }
155 rmErr = os.Remove(s.Config.CAFile.V())
156 if rmErr != nil {
157 E.Ln("cannot remove written certificates:", rmErr)
158 }
159 return
160 }
161 D.Ln("done generating TLS certificates")
162 }
163 164 // Validate profile port number
165 T.Ln("validating profile port number")
166 if s.Config.Profile.V() != "" {
167 var profilePort int
168 profilePort, e = strconv.Atoi(s.Config.Profile.V())
169 if e != nil || profilePort < 1024 || profilePort > 65535 {
170 e = fmt.Errorf("the profile port must be between 1024 and 65535, disabling profiling")
171 E.Ln(e)
172 return
173 // if e = s.Config.Profile.Set(""); E.Chk(e) {
174 // }
175 }
176 }
177 178 T.Ln("checking addpeer and connectpeer lists")
179 if s.Config.AddPeers.Len() > 0 && s.Config.ConnectPeers.Len() > 0 {
180 e = fmt.Errorf("the addpeers and connectpeers options can not be both set")
181 _, _ = fmt.Fprintln(os.Stderr, e)
182 return
183 }
184 185 T.Ln("checking proxy/connect for disabling listening")
186 if (s.Config.ProxyAddress.V() != "" || s.Config.ConnectPeers.Len() > 0) && s.Config.P2PListeners.Len() == 0 {
187 s.Config.DisableListen.T()
188 }
189 190 T.Ln("checking relay/reject nonstandard policy settings")
191 switch {
192 case s.Config.RelayNonStd.True() && s.Config.RejectNonStd.True():
193 e = fmt.Errorf("rejectnonstd and relaynonstd cannot be used together" +
194 " -- choose only one")
195 E.Ln(e)
196 return
197 }
198 199 // Chk to make sure limited and admin users don't have the same username
200 T.Ln("checking admin and limited username is different")
201 if !s.Config.Username.Empty() &&
202 s.Config.Username.V() == s.Config.LimitUser.V() {
203 e := fmt.Errorf("--username and --limituser must not specify the same username")
204 _, _ = fmt.Fprintln(os.Stderr, e)
205 os.Exit(1)
206 }
207 // Chk to make sure limited and admin users don't have the same password
208 T.Ln("checking limited and admin passwords are not the same")
209 if !s.Config.Password.Empty() &&
210 s.Config.Password.V() == s.Config.LimitPass.V() {
211 e := fmt.Errorf("password and limitpass must not specify the same password")
212 _, _ = fmt.Fprintln(os.Stderr, e)
213 os.Exit(1)
214 }
215 216 T.Ln("checking user agent comments", s.Config.UserAgentComments)
217 for _, uaComment := range s.Config.UserAgentComments.S() {
218 if strings.ContainsAny(uaComment, "/:()") {
219 e = fmt.Errorf(
220 "the following characters must not " +
221 "appear in user agent comments: '/', ':', '(', ')'",
222 )
223 _, _ = fmt.Fprintln(os.Stderr, e)
224 os.Exit(1)
225 }
226 }
227 228 T.Ln("checking min relay tx fee")
229 s.StateCfg.ActiveMinRelayTxFee, e = amt.NewAmount(s.Config.MinRelayTxFee.V())
230 if e != nil {
231 E.Ln(e)
232 str := "invalid minrelaytxfee: %v"
233 e = fmt.Errorf(str, e)
234 _, _ = fmt.Fprintln(os.Stderr, e)
235 os.Exit(0)
236 }
237 I.Ln("autolisten", s.Config.AutoListen.True())
238 // if autolisten is set, set default ports on all p2p listeners discovered to be available
239 if s.Config.AutoListen.True() {
240 I.Ln("autolisten is enabled")
241 _, allAddresses := routeable.GetAddressesAndInterfaces()
242 p2pAddresses := []string{}
243 for addr := range allAddresses {
244 p2pAddresses = append(p2pAddresses, net.JoinHostPort(addr, s.ActiveNet.DefaultPort))
245 }
246 if e = s.Config.P2PConnect.Set(p2pAddresses); E.Chk(e) {
247 return
248 }
249 if e = s.Config.P2PListeners.Set(p2pAddresses); E.Chk(e) {
250 return
251 }
252 s.StateCfg.Save = true
253 }
254 255 // if autoports is set, in addition, set all listeners to random automatic ports, this and autolisten can be
256 // combined for full auto, this enables multiple instances on one local host
257 var fP int
258 if s.Config.AutoPorts.True() {
259 I.Ln("autoports is enabled")
260 p2pl := [][]string{
261 s.Config.P2PListeners.V(), s.Config.RPCListeners.V(),
262 s.Config.WalletRPCListeners.V(),
263 }
264 for i := range p2pl {
265 for j := range p2pl[i] {
266 var h string
267 if h, _, e = net.SplitHostPort(p2pl[i][j]); E.Chk(e) {
268 return
269 }
270 if fP, e = GetFreePort(); E.Chk(e) {
271 return
272 }
273 addy := net.JoinHostPort(h, fmt.Sprint(fP))
274 p2pl[i][j] = addy
275 }
276 }
277 if e = s.Config.P2PListeners.Set(p2pl[0]); E.Chk(e) {
278 return
279 }
280 if e = s.Config.RPCListeners.Set(p2pl[1]); E.Chk(e) {
281 return
282 }
283 if e = s.Config.WalletRPCListeners.Set(p2pl[2]); E.Chk(e) {
284 return
285 }
286 287 s.StateCfg.Save = true
288 }
289 // if LAN or Solo are active, disable DNS seeding
290 if s.Config.LAN.True() || s.Config.Solo.True() {
291 W.Ln("disabling DNS seeding due to test mode settings active")
292 s.Config.DisableDNSSeed.T()
293 }
294 295 T.Ln("checking rpc server has a login enabled")
296 if (s.Config.Username.Empty() || s.Config.Password.Empty()) &&
297 (s.Config.LimitUser.Empty() || s.Config.LimitPass.Empty()) {
298 W.Ln("disabling RPC due to empty login credentials")
299 s.Config.DisableRPC.T()
300 }
301 T.Ln("checking rpc max concurrent requests")
302 if s.Config.RPCMaxConcurrentReqs.V() < 0 {
303 str := "The rpcmaxwebsocketconcurrentrequests opt may not be" +
304 " less than 0 -- parsed [%d]"
305 e = fmt.Errorf(str, s.Config.RPCMaxConcurrentReqs.V())
306 _, _ = fmt.Fprintln(os.Stderr, e)
307 // os.Exit(1)
308 return
309 }
310 // Setup dial and DNS resolution (lookup) functions depending on the specified
311 // options. The default is to use the standard net.DialTimeout function as well
312 // as the system DNS resolver. When a proxy is specified, the dial function is
313 // set to the proxy specific dial function and the lookup is set to use tor
314 // (unless --noonion is specified in which case the system DNS resolver is
315 // used).
316 T.Ln("setting network dialer and lookup")
317 s.StateCfg.Dial = net.DialTimeout
318 s.StateCfg.Lookup = net.LookupIP
319 if !s.Config.ProxyAddress.Empty() {
320 T.Ln("we are loading a proxy!")
321 _, _, e = net.SplitHostPort(s.Config.ProxyAddress.V())
322 if e != nil {
323 E.Ln(e)
324 str := "proxy address '%s' is invalid: %v"
325 e = fmt.Errorf(str, s.Config.ProxyAddress.V(), e)
326 fmt.Fprintln(os.Stderr, e)
327 // os.Exit(1)
328 return
329 }
330 // TODO: this is kinda stupid hm? switch *and* toggle by presence of flag value, one should be enough
331 if s.Config.OnionEnabled.True() && !s.Config.OnionProxyAddress.Empty() {
332 E.Ln("onion enabled but no onionproxy has been configured")
333 T.Ln("halting to avoid exposing IP address")
334 }
335 // Tor stream isolation requires either proxy or onion proxy to be set.
336 if s.Config.TorIsolation.True() &&
337 s.Config.ProxyAddress.Empty() &&
338 s.Config.OnionProxyAddress.Empty() {
339 e = fmt.Errorf("%s: Tor stream isolation requires either proxy or onionproxy to be set")
340 _, _ = fmt.Fprintln(os.Stderr, e)
341 return
342 // os.Exit(1)
343 }
344 if s.Config.OnionEnabled.False() {
345 if e = s.Config.OnionProxyAddress.Set(""); E.Chk(e) {
346 }
347 }
348 349 // Tor isolation flag means proxy credentials will be overridden unless there is
350 // also an onion proxy configured in which case that one will be overridden.
351 torIsolation := false
352 if s.Config.TorIsolation.True() && s.Config.OnionProxyAddress.Empty() &&
353 (!s.Config.ProxyUser.Empty() || !s.Config.ProxyPass.Empty()) {
354 torIsolation = true
355 W.Ln(
356 "Tor isolation set -- overriding specified" +
357 " proxy user credentials",
358 )
359 }
360 proxy := &socks.Proxy{
361 Addr: s.Config.ProxyAddress.V(),
362 Username: s.Config.ProxyUser.V(),
363 Password: s.Config.ProxyPass.V(),
364 TorIsolation: torIsolation,
365 }
366 s.StateCfg.Dial = proxy.DialTimeout
367 // Treat the proxy as tor and perform DNS resolution through it unless the
368 // --noonion flag is set or there is an onion-specific proxy configured.
369 if s.Config.OnionEnabled.True() &&
370 s.Config.OnionProxyAddress.Empty() {
371 s.StateCfg.Lookup = func(host string) ([]net.IP, error) {
372 return connmgr.TorLookupIP(host, s.Config.ProxyAddress.V())
373 }
374 }
375 }
376 // Setup onion address dial function depending on the specified options. The
377 // default is to use the same dial function selected above. However, when an
378 // onion-specific proxy is specified, the onion address dial function is set to
379 // use the onion-specific proxy while leaving the normal dial function as
380 // selected above. This allows .onion address traffic to be routed through a
381 // different proxy than normal traffic.
382 T.Ln("setting up tor proxy if enabled")
383 if !s.Config.OnionProxyAddress.Empty() {
384 if _, _, e = net.SplitHostPort(s.Config.OnionProxyAddress.V()); E.Chk(e) {
385 e = fmt.Errorf("onion proxy address '%s' is invalid: %v",
386 s.Config.OnionProxyAddress.V(), e,
387 )
388 // _, _ = fmt.Fprintln(os.Stderr, e)
389 }
390 // Tor isolation flag means onion proxy credentials will be overridden.
391 if s.Config.TorIsolation.True() &&
392 (!s.Config.OnionProxyUser.Empty() || !s.Config.OnionProxyPass.Empty()) {
393 W.Ln(
394 "Tor isolation set - overriding specified onionproxy user" +
395 " credentials",
396 )
397 }
398 }
399 T.Ln("setting onion dialer")
400 s.StateCfg.Oniondial =
401 func(network, addr string, timeout time.Duration) (net.Conn, error) {
402 proxy := &socks.Proxy{
403 Addr: s.Config.OnionProxyAddress.V(),
404 Username: s.Config.OnionProxyUser.V(),
405 Password: s.Config.OnionProxyPass.V(),
406 TorIsolation: s.Config.TorIsolation.True(),
407 }
408 return proxy.DialTimeout(network, addr, timeout)
409 }
410 411 // When configured in bridge mode (both --onion and --proxy are configured), it
412 // means that the proxy configured by --proxy is not a tor proxy, so override
413 // the DNS resolution to use the onion-specific proxy.
414 T.Ln("setting proxy lookup")
415 if !s.Config.ProxyAddress.Empty() {
416 s.StateCfg.Lookup = func(host string) ([]net.IP, error) {
417 return connmgr.TorLookupIP(host, s.Config.OnionProxyAddress.V())
418 }
419 } else {
420 s.StateCfg.Oniondial = s.StateCfg.Dial
421 }
422 // Specifying --noonion means the onion address dial function results in an error.
423 if s.Config.OnionEnabled.False() {
424 s.StateCfg.Oniondial = func(a, b string, t time.Duration) (
425 net.Conn, error,
426 ) {
427 return nil, errors.New("tor has been disabled")
428 }
429 }
430 if s.StateCfg.Save || !apputil.FileExists(s.Config.ConfigFile.V()) {
431 s.StateCfg.Save = false
432 if s.Config.RunningCommand.Name == "kopach" {
433 return
434 }
435 D.Ln("saving configuration")
436 var e error
437 if e = s.Config.WriteToFile(s.Config.ConfigFile.V()); E.Chk(e) {
438 }
439 }
440 if s.ActiveNet.Name == chaincfg.TestNet3Params.Name {
441 fork.IsTestnet = true
442 }
443 444 return
445 }
446 447 // GetFreePort asks the kernel for free open ports that are ready to use.
448 func GetFreePort() (int, error) {
449 var port int
450 addr, e := net.ResolveTCPAddr("tcp", "localhost:0")
451 if e != nil {
452 return 0, e
453 }
454 var l *net.TCPListener
455 l, e = net.ListenTCP("tcp", addr)
456 if e != nil {
457 return 0, e
458 }
459 defer func() {
460 if e := l.Close(); E.Chk(e) {
461 }
462 }()
463 port = l.Addr().(*net.TCPAddr).Port
464 return port, nil
465 }
466