/
options.go
187 lines (165 loc) · 4.96 KB
/
options.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
package main
import (
"flag"
"fmt"
"net"
"os"
"strconv"
"time"
"github.com/shuque/dane"
)
// OptionsStruct - options
type OptionsStruct struct {
TLSversion string
CAfile string
useV4 bool
useV6 bool
ipAddress net.IP
DANE bool
PKIX bool
SNI string
DaneEEname bool
noverify bool
SMTPAnyMode bool
appname string
sname string
timeout time.Duration
resolver *dane.Resolver
rport int
printchain bool
}
// Options -
var Options OptionsStruct
// Defaults
var (
defaultDNSTimeout = 3
defaultDNSRetries = 3
defaultTCPTimeout = 4
defaultResolverPort = 53
)
// Globals
var debug = false
// parseArgs parses command line arguments.
func parseArgs(args []string) (hostname string, port int) {
var err error
var mode string
help := flag.Bool("h", false, "Print this help string")
flag.BoolVar(&debug, "d", false, "Debug mode")
flag.StringVar(&Options.TLSversion, "version", "", "TLS version to use")
flag.StringVar(&Options.CAfile, "cafile", "", "PKIX Root CA file in PEM format")
flag.BoolVar(&Options.useV6, "6", false, "use IPv6 only")
flag.BoolVar(&Options.useV4, "4", false, "use IPv4 only")
flag.StringVar(&mode, "m", "", "Mode: dane or pkix")
flag.StringVar(&Options.SNI, "sni", "", "SNI name to send and verify")
flag.StringVar(&Options.appname, "s", "", "STARTTLS app (smtp,imap,pop3)")
flag.StringVar(&Options.sname, "n", "", "Service name")
tmpResolver := flag.String("r", "", "Resolver IP address")
flag.IntVar(&Options.rport, "rp", defaultResolverPort, "Resolver port number")
tmpTimeout := flag.Int("t", defaultDNSTimeout, "query timeout in seconds")
flag.BoolVar(&Options.DaneEEname, "dane-ee-name", false, "DANE EE name")
flag.BoolVar(&Options.SMTPAnyMode, "smtp-any-mode", false, "SMTP any mode")
flag.BoolVar(&Options.noverify, "noverify", false, "noverify")
flag.BoolVar(&Options.printchain, "printchain", false, "printchain")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `
%s, version %s (built with go dane v%s)
Usage: %s [Options] <host> [<port>]
If port is omitted, the default port 443 is used. If hostname is an
IP address string, then a name must be specified via the SNI option.
Options:
-h Print this help string
-d Debug mode - print additional info
-m mode Mode: "dane" or "pkix"
-cafile FILE PKIX Root CA file in PEM format
-sni name Specify SNI name to send and verify
-s starttls STARTTLS application (smtp, imap, pop3)
-n name Service name (if different from hostname)
-version VER TLS version to use (e.g. "1.3")
-4 Use IPv4 transport only
-6 Use IPv6 transport only
-r ip DNS Resolver IP address
-rp port DNS Resolver port (default %d)
-t N DNS query timeout value in seconds (default %d)
-dane-ee-name Do hostname check even for DANE-EE mode
-smtp-any-mode Allow STARTTLS SMTP for any DANE usage mode
-noverify Don't perform server certificate verification
-printchain Print details of full certificate chain
`, Progname, Version, dane.Version, Progname, defaultResolverPort, defaultDNSTimeout)
}
flag.Parse()
if *help {
flag.Usage()
os.Exit(1)
}
if *tmpResolver != "" {
resolverIP := net.ParseIP(*tmpResolver)
if resolverIP == nil {
fmt.Printf("Can't parse resolver IP address: %s\n", *tmpResolver)
flag.Usage()
os.Exit(3)
}
resolvers := []*dane.Server{dane.NewServer("", resolverIP, Options.rport)}
Options.resolver = dane.NewResolver(resolvers)
} else {
Options.resolver, err = dane.GetResolver("")
if err != nil {
fmt.Printf("Error obtaining resolver address: %s", err.Error())
os.Exit(3)
}
}
Options.timeout = time.Second * time.Duration(*tmpTimeout)
Options.resolver.Timeout = Options.timeout
Options.resolver.Retries = defaultDNSRetries
if Options.useV4 && Options.useV6 {
fmt.Printf("Cannot specify both -4 and -6. Choose one.\n")
flag.Usage()
os.Exit(3)
}
if !(Options.useV4 || Options.useV6) {
Options.useV4 = true
Options.useV6 = true
}
Options.resolver.IPv6 = Options.useV6
Options.resolver.IPv4 = Options.useV4
switch mode {
case "":
Options.DANE = true
Options.PKIX = true
case "dane":
Options.DANE = true
case "pkix":
Options.PKIX = true
default:
fmt.Printf("Invalid mode specified: %s\n", mode)
flag.Usage()
os.Exit(3)
}
if flag.NArg() == 1 {
hostname = flag.Args()[0]
port = 443
} else if flag.NArg() == 2 {
hostname = flag.Args()[0]
port, err = strconv.Atoi(flag.Args()[1])
if err != nil {
fmt.Printf("Invalid port: %s\n", flag.Args()[1])
flag.Usage()
os.Exit(3)
}
} else {
fmt.Printf("Invalid arguments\n")
flag.Usage()
os.Exit(3)
}
Options.ipAddress = net.ParseIP(hostname)
if Options.ipAddress != nil {
if Options.SNI == "" {
fmt.Printf("SNI must be specified with an IP address argument\n")
os.Exit(2)
}
} else {
if Options.SNI == "" {
Options.SNI = hostname
}
}
return hostname, port
}