|
1 |
| -package main |
| 1 | +package shushtar |
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "crypto/tls" |
| 5 | + "fmt" |
| 6 | + "net" |
| 7 | + "os" |
| 8 | + "path/filepath" |
| 9 | + "strings" |
| 10 | + |
4 | 11 | "github.com/jessevdk/go-flags"
|
| 12 | + "github.com/lightninglabs/faraday" |
| 13 | + "github.com/lightninglabs/loop/loopd" |
| 14 | + "github.com/lightningnetwork/lnd" |
| 15 | + "github.com/lightningnetwork/lnd/build" |
| 16 | + "github.com/lightningnetwork/lnd/cert" |
| 17 | + "github.com/lightningnetwork/lnd/lncfg" |
| 18 | + "github.com/mwitkow/go-conntrack/connhelpers" |
5 | 19 | )
|
6 | 20 |
|
7 |
| -var ( |
8 |
| - defaultHTTPSListen = "localhost:8443" |
9 |
| - defaultLndHost = "localhost:10009" |
10 |
| - defaultLoopHost = "localhost:10010" |
11 |
| - defaultTLSCertPath = "https.cert" |
12 |
| - defaultTLSKeyPath = "https.key" |
13 |
| -) |
| 21 | +// Config is the main configuration struct of shushtar. It contains all config |
| 22 | +// items of its enveloping subservers, each prefixed with their daemon's short |
| 23 | +// name. |
| 24 | +type Config struct { |
| 25 | + HTTPSListen string `long:"httpslisten" description:"host:port to listen for incoming HTTP/2 connections on"` |
| 26 | + Lnd *lnd.Config `group:"lnd" namespace:"lnd"` |
| 27 | + Faraday *faraday.Config `group:"faraday" namespace:"faraday"` |
| 28 | + Loop *loopd.Config `group:"loop" namespace:"loop"` |
| 29 | +} |
| 30 | + |
| 31 | +// loadLndConfig loads and sanitizes the lnd main configuration and hooks up all |
| 32 | +// loggers. |
| 33 | +func loadLndConfig(preCfg *Config) (*lnd.Config, error) { |
| 34 | + // Show the version and exit if the version flag was specified. |
| 35 | + appName := filepath.Base(os.Args[0]) |
| 36 | + appName = strings.TrimSuffix(appName, filepath.Ext(appName)) |
| 37 | + usageMessage := fmt.Sprintf("Use %s -h to show usage", appName) |
| 38 | + if preCfg.Lnd.ShowVersion { |
| 39 | + fmt.Println(appName, "version", build.Version(), |
| 40 | + "commit="+build.Commit) |
| 41 | + os.Exit(0) |
| 42 | + } |
| 43 | + |
| 44 | + // If the config file path has not been modified by the user, then we'll |
| 45 | + // use the default config file path. However, if the user has modified |
| 46 | + // their lnddir, then we should assume they intend to use the config |
| 47 | + // file within it. |
| 48 | + configFileDir := lnd.CleanAndExpandPath(preCfg.Lnd.LndDir) |
| 49 | + configFilePath := lnd.CleanAndExpandPath(preCfg.Lnd.ConfigFile) |
| 50 | + if configFileDir != lnd.DefaultLndDir { |
| 51 | + if configFilePath == lnd.DefaultConfigFile { |
| 52 | + configFilePath = filepath.Join( |
| 53 | + configFileDir, lncfg.DefaultConfigFilename, |
| 54 | + ) |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + // Next, load any additional configuration options from the file. |
| 59 | + var configFileError error |
| 60 | + cfg := preCfg |
| 61 | + if err := flags.IniParse(configFilePath, cfg); err != nil { |
| 62 | + // If it's a parsing related error, then we'll return |
| 63 | + // immediately, otherwise we can proceed as possibly the config |
| 64 | + // file doesn't exist which is OK. |
| 65 | + if _, ok := err.(*flags.IniError); ok { |
| 66 | + return nil, err |
| 67 | + } |
| 68 | + |
| 69 | + configFileError = err |
| 70 | + } |
14 | 71 |
|
15 |
| -type config struct { |
16 |
| - HTTPSListen string `long:"httpslisten" description:"host:port to listen for incoming HTTP/2 connections on"` |
17 |
| - LNDHost string `long:"lndhost" description:"host:port that LND listens for RPC connections on"` |
18 |
| - LoopHost string `long:"loophost" description:"host:port that Loop listens for RPC connections on"` |
19 |
| - TLSCertPath string `long:"tlscertpath" description:"path to the TLS cert to use for HTTPS requests"` |
20 |
| - TLSKeyPath string `long:"tlskeypath" description:"path to the TLS key to use for HTTPS requests"` |
21 |
| -} |
22 |
| - |
23 |
| -// loadConfig starts with a skeleton default config, and reads in user provided |
24 |
| -// configuration from the command line. It does not provide a full set of |
25 |
| -// defaults or validate user input. |
26 |
| -func loadConfig() (*config, error) { |
27 |
| - // Start with a default config. |
28 |
| - config := &config{ |
29 |
| - HTTPSListen: defaultHTTPSListen, |
30 |
| - LNDHost: defaultLndHost, |
31 |
| - LoopHost: defaultLoopHost, |
32 |
| - TLSCertPath: defaultTLSCertPath, |
33 |
| - TLSKeyPath: defaultTLSKeyPath, |
34 |
| - } |
35 |
| - |
36 |
| - // Parse command line options to obtain user specified values. |
37 |
| - if _, err := flags.Parse(config); err != nil { |
| 72 | + // Finally, parse the remaining command line options again to ensure |
| 73 | + // they take precedence. |
| 74 | + if _, err := flags.Parse(cfg); err != nil { |
38 | 75 | return nil, err
|
39 | 76 | }
|
40 | 77 |
|
41 |
| - return config, nil |
| 78 | + // Make sure everything we just loaded makes sense. |
| 79 | + cleanCfg, err := lnd.ValidateConfig(*cfg.Lnd, usageMessage) |
| 80 | + if err != nil { |
| 81 | + return nil, err |
| 82 | + } |
| 83 | + |
| 84 | + // With the validated config obtained, we now know that the root logging |
| 85 | + // system of lnd is initialized and we can hook up our own loggers now. |
| 86 | + SetupLoggers(cleanCfg.LogWriter) |
| 87 | + |
| 88 | + // Warn about missing config file only after all other configuration is |
| 89 | + // done. This prevents the warning on help messages and invalid options. |
| 90 | + // Note this should go directly before the return. |
| 91 | + if configFileError != nil { |
| 92 | + log.Warnf("%v", configFileError) |
| 93 | + } |
| 94 | + |
| 95 | + return cleanCfg, nil |
| 96 | +} |
| 97 | + |
| 98 | +func getNetwork(cfg *lncfg.Chain) (string, error) { |
| 99 | + switch { |
| 100 | + case cfg.MainNet: |
| 101 | + return "mainnet", nil |
| 102 | + |
| 103 | + case cfg.TestNet3: |
| 104 | + return "testnet", nil |
| 105 | + |
| 106 | + case cfg.RegTest: |
| 107 | + return "regtest", nil |
| 108 | + |
| 109 | + case cfg.SimNet: |
| 110 | + return "simnet", nil |
| 111 | + |
| 112 | + default: |
| 113 | + return "", fmt.Errorf("no network selected") |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +func buildTLSConfigForHttp2(config *lnd.Config) (*tls.Config, error) { |
| 118 | + tlsCert, _, err := cert.LoadCert(config.TLSCertPath, config.TLSKeyPath) |
| 119 | + if err != nil { |
| 120 | + return nil, fmt.Errorf("failed reading TLS server keys: %v", |
| 121 | + err) |
| 122 | + } |
| 123 | + tlsConfig := cert.TLSConfFromCert(tlsCert) |
| 124 | + tlsConfig.CipherSuites = append( |
| 125 | + tlsConfig.CipherSuites, |
| 126 | + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, |
| 127 | + ) |
| 128 | + tlsConfig, err = connhelpers.TlsConfigWithHttp2Enabled(tlsConfig) |
| 129 | + if err != nil { |
| 130 | + return nil, fmt.Errorf("can't configure h2 handling: %v", err) |
| 131 | + } |
| 132 | + return tlsConfig, nil |
| 133 | +} |
| 134 | + |
| 135 | +// onDemandListener is a net.Listener that only actually starts to listen on a |
| 136 | +// network port once the Accept method is called. |
| 137 | +type onDemandListener struct { |
| 138 | + addr net.Addr |
| 139 | + lis net.Listener |
| 140 | +} |
| 141 | + |
| 142 | +// Accept waits for and returns the next connection to the listener. |
| 143 | +func (l *onDemandListener) Accept() (net.Conn, error) { |
| 144 | + if l.lis == nil { |
| 145 | + var err error |
| 146 | + l.lis, err = net.Listen(parseNetwork(l.addr), l.addr.String()) |
| 147 | + if err != nil { |
| 148 | + return nil, err |
| 149 | + } |
| 150 | + } |
| 151 | + return l.lis.Accept() |
| 152 | +} |
| 153 | + |
| 154 | +// Close closes the listener. |
| 155 | +// Any blocked Accept operations will be unblocked and return errors. |
| 156 | +func (l *onDemandListener) Close() error { |
| 157 | + return l.lis.Close() |
| 158 | +} |
| 159 | + |
| 160 | +// Addr returns the listener's network address. |
| 161 | +func (l *onDemandListener) Addr() net.Addr { |
| 162 | + return l.addr |
| 163 | +} |
| 164 | + |
| 165 | +// parseNetwork parses the network type of the given address. |
| 166 | +func parseNetwork(addr net.Addr) string { |
| 167 | + switch addr := addr.(type) { |
| 168 | + // TCP addresses resolved through net.ResolveTCPAddr give a default |
| 169 | + // network of "tcp", so we'll map back the correct network for the given |
| 170 | + // address. This ensures that we can listen on the correct interface |
| 171 | + // (IPv4 vs IPv6). |
| 172 | + case *net.TCPAddr: |
| 173 | + if addr.IP.To4() != nil { |
| 174 | + return "tcp4" |
| 175 | + } |
| 176 | + return "tcp6" |
| 177 | + |
| 178 | + default: |
| 179 | + return addr.Network() |
| 180 | + } |
42 | 181 | }
|
0 commit comments