fix(netxlite): map additional GetAddrInfoW errors (#521)

On Windows, GetAddrInfoW is a syscall and the Go resolver does
not attempt to map errors beyond WSA_HOST_NOT_FOUND, which becomes
"no such host", which we map to "dns_nxdomain_error".

See https://github.com/golang/go/blob/go1.17.1/src/net/lookup_windows.go#L16.

To map more GetAddrInfoW errors, thus, we need to enhance our
error classifier to have system specific errors.

Then, we need to filter for the WSA errors that are most likely
to pop up and map them to OONI failures. Those are three:

- WSANO_DATA which we have from our own UDP resolver as well
and which we can map to `dns_no_answer`

- WSANO_RECOVERY which we don't have but existed for MK so
we will use `dns_non_recoverable_failure`, which was an MK error

- WSATRY_AGAIN which likewise we map to the error that MK
used to emit, so `dns_temporary_failure`

This diff should address https://github.com/ooni/probe/issues/1467.
This commit is contained in:
Simone Basso 2021-09-29 11:21:28 +02:00 committed by GitHub
commit 9967803c31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1648 additions and 221 deletions

View file

@ -1,25 +1,13 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:54.361886 +0200 CEST m=+0.453564501
// Generated: 2021-09-29 10:33:56.711301 +0200 CEST m=+0.645971001
package netxlite
//go:generate go run ./internal/generrno/
import (
"errors"
"syscall"
)
// This enumeration lists the failures defined at
// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md
const (
//
// System errors
//
FailureConnectionRefused = "connection_refused"
FailureConnectionReset = "connection_reset"
FailureHostUnreachable = "host_unreachable"
FailureTimedOut = "timed_out"
FailureAddressFamilyNotSupported = "address_family_not_supported"
FailureAddressInUse = "address_in_use"
FailureAddressNotAvailable = "address_not_available"
@ -27,10 +15,24 @@ const (
FailureBadAddress = "bad_address"
FailureBadFileDescriptor = "bad_file_descriptor"
FailureConnectionAborted = "connection_aborted"
FailureConnectionAlreadyClosed = "connection_already_closed"
FailureConnectionAlreadyInProgress = "connection_already_in_progress"
FailureConnectionRefused = "connection_refused"
FailureConnectionReset = "connection_reset"
FailureDNSBogonError = "dns_bogon_error"
FailureDNSNXDOMAINError = "dns_nxdomain_error"
FailureDNSNoAnswer = "dns_no_answer"
FailureDNSNonRecoverableFailure = "dns_non_recoverable_failure"
FailureDNSRefusedError = "dns_refused_error"
FailureDNSServerMisbehaving = "dns_server_misbehaving"
FailureDNSTemporaryFailure = "dns_temporary_failure"
FailureDestinationAddressRequired = "destination_address_required"
FailureEOFError = "eof_error"
FailureGenericTimeoutError = "generic_timeout_error"
FailureHostUnreachable = "host_unreachable"
FailureInterrupted = "interrupted"
FailureInvalidArgument = "invalid_argument"
FailureJSONParseError = "json_parse_error"
FailureMessageSize = "message_size"
FailureNetworkDown = "network_down"
FailureNetworkReset = "network_reset"
@ -42,34 +44,18 @@ const (
FailureOperationWouldBlock = "operation_would_block"
FailurePermissionDenied = "permission_denied"
FailureProtocolNotSupported = "protocol_not_supported"
FailureQUICIncompatibleVersion = "quic_incompatible_version"
FailureSSLFailedHandshake = "ssl_failed_handshake"
FailureSSLInvalidCertificate = "ssl_invalid_certificate"
FailureSSLInvalidHostname = "ssl_invalid_hostname"
FailureSSLUnknownAuthority = "ssl_unknown_authority"
FailureTimedOut = "timed_out"
FailureWrongProtocolType = "wrong_protocol_type"
//
// Library errors
//
FailureDNSBogonError = "dns_bogon_error"
FailureDNSNXDOMAINError = "dns_nxdomain_error"
FailureDNSRefusedError = "dns_refused_error"
FailureDNSServerMisbehaving = "dns_server_misbehaving"
FailureDNSNoAnswer = "dns_no_answer"
FailureEOFError = "eof_error"
FailureGenericTimeoutError = "generic_timeout_error"
FailureQUICIncompatibleVersion = "quic_incompatible_version"
FailureSSLFailedHandshake = "ssl_failed_handshake"
FailureSSLInvalidHostname = "ssl_invalid_hostname"
FailureSSLUnknownAuthority = "ssl_unknown_authority"
FailureSSLInvalidCertificate = "ssl_invalid_certificate"
FailureJSONParseError = "json_parse_error"
FailureConnectionAlreadyClosed = "connection_already_closed"
)
// failureMap lists all failures so we can match them
// when they are wrapped by quic.TransportError.
var failuresMap = map[string]string{
"connection_refused": "connection_refused",
"connection_reset": "connection_reset",
"host_unreachable": "host_unreachable",
"timed_out": "timed_out",
"address_family_not_supported": "address_family_not_supported",
"address_in_use": "address_in_use",
"address_not_available": "address_not_available",
@ -77,10 +63,24 @@ var failuresMap = map[string]string{
"bad_address": "bad_address",
"bad_file_descriptor": "bad_file_descriptor",
"connection_aborted": "connection_aborted",
"connection_already_closed": "connection_already_closed",
"connection_already_in_progress": "connection_already_in_progress",
"connection_refused": "connection_refused",
"connection_reset": "connection_reset",
"destination_address_required": "destination_address_required",
"dns_bogon_error": "dns_bogon_error",
"dns_no_answer": "dns_no_answer",
"dns_non_recoverable_failure": "dns_non_recoverable_failure",
"dns_nxdomain_error": "dns_nxdomain_error",
"dns_refused_error": "dns_refused_error",
"dns_server_misbehaving": "dns_server_misbehaving",
"dns_temporary_failure": "dns_temporary_failure",
"eof_error": "eof_error",
"generic_timeout_error": "generic_timeout_error",
"host_unreachable": "host_unreachable",
"interrupted": "interrupted",
"invalid_argument": "invalid_argument",
"json_parse_error": "json_parse_error",
"message_size": "message_size",
"network_down": "network_down",
"network_reset": "network_reset",
@ -92,86 +92,11 @@ var failuresMap = map[string]string{
"operation_would_block": "operation_would_block",
"permission_denied": "permission_denied",
"protocol_not_supported": "protocol_not_supported",
"wrong_protocol_type": "wrong_protocol_type",
"dns_bogon_error": "dns_bogon_error",
"dns_nxdomain_error": "dns_nxdomain_error",
"dns_refused_error": "dns_refused_error",
"dns_server_misbehaving": "dns_server_misbehaving",
"dns_no_answer": "dns_no_answer",
"eof_error": "eof_error",
"generic_timeout_error": "generic_timeout_error",
"quic_incompatible_version": "quic_incompatible_version",
"ssl_failed_handshake": "ssl_failed_handshake",
"ssl_invalid_certificate": "ssl_invalid_certificate",
"ssl_invalid_hostname": "ssl_invalid_hostname",
"ssl_unknown_authority": "ssl_unknown_authority",
"ssl_invalid_certificate": "ssl_invalid_certificate",
"json_parse_error": "json_parse_error",
"connection_already_closed": "connection_already_closed",
}
// classifySyscallError converts a syscall error to the
// proper OONI error. Returns the OONI error string
// on success, an empty string otherwise.
func classifySyscallError(err error) string {
var errno syscall.Errno
if !errors.As(err, &errno) {
return ""
}
switch errno {
case ECONNREFUSED:
return FailureConnectionRefused
case ECONNRESET:
return FailureConnectionReset
case EHOSTUNREACH:
return FailureHostUnreachable
case ETIMEDOUT:
return FailureTimedOut
case EAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case EADDRINUSE:
return FailureAddressInUse
case EADDRNOTAVAIL:
return FailureAddressNotAvailable
case EISCONN:
return FailureAlreadyConnected
case EFAULT:
return FailureBadAddress
case EBADF:
return FailureBadFileDescriptor
case ECONNABORTED:
return FailureConnectionAborted
case EALREADY:
return FailureConnectionAlreadyInProgress
case EDESTADDRREQ:
return FailureDestinationAddressRequired
case EINTR:
return FailureInterrupted
case EINVAL:
return FailureInvalidArgument
case EMSGSIZE:
return FailureMessageSize
case ENETDOWN:
return FailureNetworkDown
case ENETRESET:
return FailureNetworkReset
case ENETUNREACH:
return FailureNetworkUnreachable
case ENOBUFS:
return FailureNoBufferSpace
case ENOPROTOOPT:
return FailureNoProtocolOption
case ENOTSOCK:
return FailureNotASocket
case ENOTCONN:
return FailureNotConnected
case EWOULDBLOCK:
return FailureOperationWouldBlock
case EACCES:
return FailurePermissionDenied
case EPROTONOSUPPORT:
return FailureProtocolNotSupported
case EPROTOTYPE:
return FailureWrongProtocolType
}
return ""
"timed_out": "timed_out",
"wrong_protocol_type": "wrong_protocol_type",
}