diff options
Diffstat (limited to 'internal/qna')
-rw-r--r-- | internal/qna/qna.go | 151 |
1 files changed, 104 insertions, 47 deletions
diff --git a/internal/qna/qna.go b/internal/qna/qna.go index d883016..3ad942c 100644 --- a/internal/qna/qna.go +++ b/internal/qna/qna.go @@ -2,10 +2,13 @@ package qna import ( "context" + "crypto/tls" "errors" "fmt" "net" "net/url" + "os" + "strings" ) type Question struct { @@ -35,93 +38,147 @@ func (a Answer) String() string { } 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 + NumOK int + NumOnion int + + NumMakeReqErr int + NumDNSNotFoundErr int + NumDNSTimeoutErr int + NumDNSOtherErr int + NumConnErr int + NumTLSCertErr int + NumTLSOtherErr int + Num3xxErr int + NumDeadlineErr int + NumOtherErr int } func (p Progress) String() string { - str := fmt.Sprintf(" Processed: %d\n", p.NumAnswer()) + str := fmt.Sprintf(" Processed: %d\n", p.NumProcess()) 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) + str += fmt.Sprintf(" Failure: %d (See breakdown below)\n", p.NumError()) + str += fmt.Sprintf(" Req: %d (Before sending request)\n", p.NumMakeReqErr) + str += fmt.Sprintf(" DNS: %d (NotFound:%d Timeout:%d Other:%d)\n", + p.NumDNSErr(), p.NumDNSNotFoundErr, p.NumDNSTimeoutErr, p.NumDNSOtherErr) + str += fmt.Sprintf(" TCP: %d (Connection error)\n", p.NumConnErr) + str += fmt.Sprintf(" TLS: %d (Cert:%d Other:%d)\n", p.NumTLSErr(), p.NumTLSCertErr, p.NumTLSOtherErr) + str += fmt.Sprintf(" 3xx: %d (Too many redirects)\n", p.Num3xxErr) + str += fmt.Sprintf(" CTX: %d (Deadline exceeded)\n", p.NumDeadlineErr) + str += fmt.Sprintf(" ???: %d (Other errors)", p.NumOtherErr) return str } -func (p *Progress) NumAnswer() int { - return p.NumReqErr + p.NumDoErr + p.NumOK +func (p *Progress) NumDNSErr() int { + return p.NumDNSNotFoundErr + p.NumDNSTimeoutErr + p.NumDNSOtherErr +} + +func (p *Progress) NumTLSErr() int { + return p.NumTLSCertErr + p.NumTLSOtherErr } func (p *Progress) NumError() int { - return p.NumReqErr + p.NumDoErr + return p.NumMakeReqErr + p.NumDNSErr() + p.NumConnErr + p.NumTLSErr() + p.Num3xxErr + p.NumDeadlineErr + p.NumOtherErr } -func (p *Progress) NumDNSErr() int { - return p.NumDNSNotFound + p.NumDNSTimeout + p.NumDNSOther +func (p *Progress) NumProcess() int { + return p.NumOK + p.NumError() } func (p *Progress) AddAnswer(a Answer) { - if a.ReqErr != nil { - p.NumReqErr += 1 + if err := a.ReqErr; err != nil { + p.NumMakeReqErr++ 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 + if err := a.DoErr; err != nil { + if e := dnsError(err); e != nil { + if e.IsTimeout { + p.NumDNSTimeoutErr++ + } else if e.IsNotFound { + p.NumDNSNotFoundErr++ } else { - p.NumDNSOther += 1 + p.NumDNSOtherErr++ } + } else if isConnError(err) { + p.NumConnErr++ + } else if isTLSError(err) { + if isTLSCertError(err) { + p.NumTLSCertErr++ + } else { + p.NumTLSOtherErr++ + } + } else if is3xxErr(err) { + p.Num3xxErr++ + } else if isDeadlineError(err) { + p.NumDeadlineErr++ + } else { + p.NumOtherErr++ } return } - - p.NumOK += 1 + p.NumOK++ if a.HTTP != "" || a.HTML != "" { - p.NumOnion += 1 + p.NumOnion++ } } func dnsError(err error) *net.DNSError { - if err == nil { + urlErr, ok := err.(*url.Error) + if !ok { + return nil + } + opErr, ok := urlErr.Err.(*net.OpError) + if !ok { + return nil + } + dnsErr, ok := opErr.Err.(*net.DNSError) + if !ok { return nil } + return dnsErr +} +func isConnError(err error) bool { urlErr, ok := err.(*url.Error) if !ok { - return nil + return false + } + if urlErr.Err.Error() == "EOF" { + return true } + opErr, ok := urlErr.Err.(*net.OpError) + if !ok { + return false + } + syscallErr, ok := opErr.Err.(*os.SyscallError) + if !ok { + return false + } + return syscallErr.Syscall == "connect" || syscallErr.Syscall == "read" +} - err = urlErr.Err - opErr, ok := err.(*net.OpError) +func isTLSError(err error) bool { + urlErr, ok := err.(*url.Error) if !ok { - return nil + return false + } + if urlErr.Error() == "http: server gave HTTP response to HTTPS client" { + return true } + return strings.Contains(urlErr.Error(), "tls: ") +} - err = opErr.Err - dnsErr, ok := err.(*net.DNSError) +func isTLSCertError(err error) bool { + urlErr, ok := err.(*url.Error) if !ok { - return nil + return false } + _, ok = urlErr.Err.(*tls.CertificateVerificationError) + return ok +} - return dnsErr +func is3xxErr(err error) bool { + urlErr, ok := err.(*url.Error) + return ok && urlErr.Err.Error() == "stopped after 10 redirects" } func isDeadlineError(err error) bool { |