2021-11-10 19:06:12 +00:00
package setting
import (
2021-11-29 17:16:27 +00:00
"bytes"
"errors"
2021-11-10 19:06:12 +00:00
"flag"
"fmt"
"os"
"time"
"github.com/dcarrillo/whatismyip/internal/core"
2024-04-12 17:26:48 +00:00
"gopkg.in/yaml.v3"
2021-11-10 19:06:12 +00:00
)
type geodbPath struct {
City string
ASN string
}
type serverSettings struct {
ReadTimeout time . Duration
WriteTimeout time . Duration
}
2024-04-12 17:26:48 +00:00
type resolver struct {
Domain string ` yaml:"domain" `
ResourceRecords [ ] string ` yaml:"resource_records" `
RedirectPort string ` yaml:"redirect_port,omitempty" `
Ipv4 [ ] string ` yaml:"ipv4,omitempty" `
Ipv6 [ ] string ` yaml:"ipv6,omitempty" `
}
2021-11-10 19:06:12 +00:00
type settings struct {
2022-04-02 16:10:48 +00:00
GeodbPath geodbPath
TemplatePath string
BindAddress string
TLSAddress string
TLSCrtPath string
TLSKeyPath string
TrustedHeader string
2022-05-01 17:47:27 +00:00
TrustedPortHeader string
2022-04-02 16:10:48 +00:00
EnableSecureHeaders bool
2023-03-18 19:38:18 +00:00
EnableHTTP3 bool
2022-04-02 16:10:48 +00:00
Server serverSettings
2024-04-12 17:26:48 +00:00
Resolver resolver
2022-04-02 16:10:48 +00:00
version bool
2021-11-10 19:06:12 +00:00
}
const defaultAddress = ":8080"
2021-11-29 17:16:27 +00:00
var ErrVersion = errors . New ( "setting: version requested" )
2021-11-30 16:21:01 +00:00
2021-11-29 17:16:27 +00:00
var App = settings {
// hard-coded for the time being
Server : serverSettings {
ReadTimeout : 10 * time . Second ,
WriteTimeout : 10 * time . Second ,
} ,
}
func Setup ( args [ ] string ) ( output string , err error ) {
flags := flag . NewFlagSet ( "whatismyip" , flag . ContinueOnError )
var buf bytes . Buffer
2024-04-12 17:26:48 +00:00
var resolverConf string
2021-11-29 17:16:27 +00:00
flags . SetOutput ( & buf )
2021-11-10 19:06:12 +00:00
2021-11-29 17:16:27 +00:00
flags . StringVar ( & App . GeodbPath . City , "geoip2-city" , "" , "Path to GeoIP2 city database" )
flags . StringVar ( & App . GeodbPath . ASN , "geoip2-asn" , "" , "Path to GeoIP2 ASN database" )
2024-04-12 17:26:48 +00:00
flags . StringVar ( & App . TemplatePath , "template" , "" , "Path to the template file" )
flags . StringVar (
& resolverConf ,
"resolver" ,
"" ,
2024-05-09 18:01:40 +00:00
"Path to the resolver configuration. It actually enables the resolver for DNS client discovery." )
2021-11-29 17:16:27 +00:00
flags . StringVar (
& App . BindAddress ,
2021-11-18 18:26:53 +00:00
"bind" ,
defaultAddress ,
"Listening address (see https://pkg.go.dev/net?#Listen)" ,
)
2021-11-29 17:16:27 +00:00
flags . StringVar (
& App . TLSAddress ,
2021-11-18 18:26:53 +00:00
"tls-bind" ,
"" ,
"Listening address for TLS (see https://pkg.go.dev/net?#Listen)" ,
)
2021-11-29 17:16:27 +00:00
flags . StringVar ( & App . TLSCrtPath , "tls-crt" , "" , "When using TLS, path to certificate file" )
flags . StringVar ( & App . TLSKeyPath , "tls-key" , "" , "When using TLS, path to private key file" )
2022-05-02 16:00:36 +00:00
flags . StringVar (
& App . TrustedHeader ,
2021-11-18 18:26:53 +00:00
"trusted-header" ,
"" ,
2022-05-02 16:00:36 +00:00
"Trusted request header for remote IP (e.g. X-Real-IP). When using this feature if -trusted-port-header is not set the client port is shown as 'unknown'" ,
2021-11-18 18:26:53 +00:00
)
2022-05-02 16:00:36 +00:00
flags . StringVar (
& App . TrustedPortHeader ,
2022-05-01 17:47:27 +00:00
"trusted-port-header" ,
"" ,
2022-05-02 16:00:36 +00:00
"Trusted request header for remote client port (e.g. X-Real-Port). When this parameter is set -trusted-header becomes mandatory" ,
2022-05-01 17:47:27 +00:00
)
2021-11-29 17:16:27 +00:00
flags . BoolVar ( & App . version , "version" , false , "Output version information and exit" )
2022-04-02 16:10:48 +00:00
flags . BoolVar (
& App . EnableSecureHeaders ,
"enable-secure-headers" ,
false ,
"Add sane security-related headers to every response" ,
)
2023-03-18 19:38:18 +00:00
flags . BoolVar (
& App . EnableHTTP3 ,
"enable-http3" ,
false ,
"Enable HTTP/3 protocol. HTTP/3 requires --tls-bind set, as HTTP/3 starts as a TLS connection that then gets upgraded to UDP. The UDP port is the same as the one used for the TLS server." ,
)
2021-11-10 19:06:12 +00:00
2021-11-29 17:16:27 +00:00
err = flags . Parse ( args )
if err != nil {
return buf . String ( ) , err
2021-11-10 19:06:12 +00:00
}
2021-11-29 17:16:27 +00:00
if App . version {
return fmt . Sprintf ( "whatismyip version %s" , core . Version ) , ErrVersion
2021-11-10 19:06:12 +00:00
}
2022-05-01 17:47:27 +00:00
if App . TrustedPortHeader != "" && App . TrustedHeader == "" {
2023-03-16 18:59:04 +00:00
return "" , fmt . Errorf ( "truster-header is mandatory when truster-port-header is set" )
2022-05-01 17:47:27 +00:00
}
2021-11-29 17:16:27 +00:00
if App . GeodbPath . City == "" || App . GeodbPath . ASN == "" {
2023-03-16 18:59:04 +00:00
return "" , fmt . Errorf ( "geoip2-city and geoip2-asn parameters are mandatory" )
2021-11-10 19:06:12 +00:00
}
2021-11-29 17:16:27 +00:00
if ( App . TLSAddress != "" ) && ( App . TLSCrtPath == "" || App . TLSKeyPath == "" ) {
2023-03-16 18:59:04 +00:00
return "" , fmt . Errorf ( "in order to use TLS, the -tls-crt and -tls-key flags are mandatory" )
2021-11-10 19:06:12 +00:00
}
2023-03-18 19:38:18 +00:00
if App . EnableHTTP3 && App . TLSAddress == "" {
return "" , fmt . Errorf ( "in order to use HTTP3, the -tls-bind is mandatory" )
}
2021-11-29 17:16:27 +00:00
if App . TemplatePath != "" {
info , err := os . Stat ( App . TemplatePath )
if os . IsNotExist ( err ) {
2023-03-16 18:59:04 +00:00
return "" , fmt . Errorf ( "%s no such file or directory" , App . TemplatePath )
2021-11-29 17:16:27 +00:00
}
if info . IsDir ( ) {
2023-03-16 18:59:04 +00:00
return "" , fmt . Errorf ( "%s must be a file" , App . TemplatePath )
2021-11-29 17:16:27 +00:00
}
2021-11-10 19:06:12 +00:00
}
2024-04-12 17:26:48 +00:00
if resolverConf != "" {
var err error
App . Resolver , err = readYAML ( resolverConf )
if err != nil {
return "" , fmt . Errorf ( "error reading resolver configuration %s" , err )
}
}
2021-11-29 17:16:27 +00:00
return buf . String ( ) , nil
2021-11-10 19:06:12 +00:00
}
2024-04-12 17:26:48 +00:00
func readYAML ( path string ) ( resolver resolver , err error ) {
yamlFile , err := os . ReadFile ( path )
if err != nil {
return resolver , err
}
return resolver , yaml . Unmarshal ( yamlFile , & resolver )
}