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
|
package qna
import (
"context"
"errors"
"fmt"
"net"
"net/url"
)
type Question struct {
Domain string // domain name to visit via HTTPS
}
type Answer struct {
Domain string // domain name of the visited HTTPS site
HTTP string // value set in the Onion-Location HTTP header (if any)
HTML string // value set in the Onion-Location HTML attribute (if any)
ReqErr error // nil if HTTP GET request could be constructed
DoErr error // nil if HTTP GET request could be executed
}
func (a Answer) String() string {
if a.ReqErr != nil {
return fmt.Sprintf("%s: %v", a.ReqErr)
}
if a.DoErr != nil {
return fmt.Sprintf("%s: %v", a.DoErr)
}
if a.HTTP == "" && a.HTML == "" {
return fmt.Sprintf("%s: connected but found no Onion-Location")
}
return fmt.Sprintf("%s header=%s attribute=%s", a.Domain, a.HTTP, a.HTML)
}
type Progress struct {
NumReqErr int // error before sending requests
NumDoErr int // error while sending request
NumOK int // got response
NumOnion int // found onion in response
// More details about deadline errors while sending request
NumDeadline int
// More details about DNS errors while sending request
NumDNSNotFound int
NumDNSTimeout int
NumDNSOther int
}
func (p Progress) String() string {
str := fmt.Sprintf(" Processed: %d\n", p.NumAnswer())
str += fmt.Sprintf(" Success: %d (Onion-Location:%d)\n", p.NumOK, p.NumOnion)
str += fmt.Sprintf(" Failure: %d (Req:%d Do:%d)\n", p.NumError(), p.NumReqErr, p.NumDoErr)
str += fmt.Sprintf(" CTX: %d (Deadline exceeded)\n", p.NumDeadline)
str += fmt.Sprintf(" DNS: %d (NotFound:%d Timeout:%d Other:%d)",
p.NumDNSErr(), p.NumDNSNotFound, p.NumDNSTimeout, p.NumDNSOther)
return str
}
func (p *Progress) NumAnswer() int {
return p.NumReqErr + p.NumDoErr + p.NumOK
}
func (p *Progress) NumError() int {
return p.NumReqErr + p.NumDoErr
}
func (p *Progress) NumDNSErr() int {
return p.NumDNSNotFound + p.NumDNSTimeout + p.NumDNSOther
}
func (p *Progress) AddAnswer(a Answer) {
if a.ReqErr != nil {
p.NumReqErr += 1
return
}
if a.DoErr != nil {
p.NumDoErr += 1
if isDeadlineError(a.DoErr) {
p.NumDeadline += 1
}
if err := dnsError(a.DoErr); err != nil {
if err.IsTimeout {
p.NumDNSTimeout += 1
} else if err.IsNotFound {
p.NumDNSNotFound += 1
} else {
p.NumDNSOther += 1
}
}
return
}
p.NumOK += 1
if a.HTTP != "" || a.HTML != "" {
p.NumOnion += 1
}
}
func dnsError(err error) *net.DNSError {
if err == nil {
return nil
}
urlErr, ok := err.(*url.Error)
if !ok {
return nil
}
err = urlErr.Err
opErr, ok := err.(*net.OpError)
if !ok {
return nil
}
err = opErr.Err
dnsErr, ok := err.(*net.DNSError)
if !ok {
return nil
}
return dnsErr
}
func isDeadlineError(err error) bool {
urlErr, ok := err.(*url.Error)
return ok && errors.Is(urlErr.Err, context.DeadlineExceeded)
}
|