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
parent 9523753b87
commit 9967803c31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1657 additions and 230 deletions

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2021-09-28 18:13:53.557509 +0200 CEST m=+0.459759459 // 2021-09-29 10:21:32.800846 +0200 CEST m=+0.427651209
// https://curl.haxx.se/ca/cacert.pem // https://curl.haxx.se/ca/cacert.pem
package netxlite package netxlite

View File

@ -1,25 +1,13 @@
// Code generated by go generate; DO NOT EDIT. // 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 package netxlite
//go:generate go run ./internal/generrno/ //go:generate go run ./internal/generrno/
import (
"errors"
"syscall"
)
// This enumeration lists the failures defined at // This enumeration lists the failures defined at
// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md // https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md
const ( const (
//
// System errors
//
FailureConnectionRefused = "connection_refused"
FailureConnectionReset = "connection_reset"
FailureHostUnreachable = "host_unreachable"
FailureTimedOut = "timed_out"
FailureAddressFamilyNotSupported = "address_family_not_supported" FailureAddressFamilyNotSupported = "address_family_not_supported"
FailureAddressInUse = "address_in_use" FailureAddressInUse = "address_in_use"
FailureAddressNotAvailable = "address_not_available" FailureAddressNotAvailable = "address_not_available"
@ -27,10 +15,24 @@ const (
FailureBadAddress = "bad_address" FailureBadAddress = "bad_address"
FailureBadFileDescriptor = "bad_file_descriptor" FailureBadFileDescriptor = "bad_file_descriptor"
FailureConnectionAborted = "connection_aborted" FailureConnectionAborted = "connection_aborted"
FailureConnectionAlreadyClosed = "connection_already_closed"
FailureConnectionAlreadyInProgress = "connection_already_in_progress" 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" FailureDestinationAddressRequired = "destination_address_required"
FailureEOFError = "eof_error"
FailureGenericTimeoutError = "generic_timeout_error"
FailureHostUnreachable = "host_unreachable"
FailureInterrupted = "interrupted" FailureInterrupted = "interrupted"
FailureInvalidArgument = "invalid_argument" FailureInvalidArgument = "invalid_argument"
FailureJSONParseError = "json_parse_error"
FailureMessageSize = "message_size" FailureMessageSize = "message_size"
FailureNetworkDown = "network_down" FailureNetworkDown = "network_down"
FailureNetworkReset = "network_reset" FailureNetworkReset = "network_reset"
@ -42,34 +44,18 @@ const (
FailureOperationWouldBlock = "operation_would_block" FailureOperationWouldBlock = "operation_would_block"
FailurePermissionDenied = "permission_denied" FailurePermissionDenied = "permission_denied"
FailureProtocolNotSupported = "protocol_not_supported" FailureProtocolNotSupported = "protocol_not_supported"
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" FailureQUICIncompatibleVersion = "quic_incompatible_version"
FailureSSLFailedHandshake = "ssl_failed_handshake" FailureSSLFailedHandshake = "ssl_failed_handshake"
FailureSSLInvalidCertificate = "ssl_invalid_certificate"
FailureSSLInvalidHostname = "ssl_invalid_hostname" FailureSSLInvalidHostname = "ssl_invalid_hostname"
FailureSSLUnknownAuthority = "ssl_unknown_authority" FailureSSLUnknownAuthority = "ssl_unknown_authority"
FailureSSLInvalidCertificate = "ssl_invalid_certificate" FailureTimedOut = "timed_out"
FailureJSONParseError = "json_parse_error" FailureWrongProtocolType = "wrong_protocol_type"
FailureConnectionAlreadyClosed = "connection_already_closed"
) )
// failureMap lists all failures so we can match them // failureMap lists all failures so we can match them
// when they are wrapped by quic.TransportError. // when they are wrapped by quic.TransportError.
var failuresMap = map[string]string{ 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_family_not_supported": "address_family_not_supported",
"address_in_use": "address_in_use", "address_in_use": "address_in_use",
"address_not_available": "address_not_available", "address_not_available": "address_not_available",
@ -77,10 +63,24 @@ var failuresMap = map[string]string{
"bad_address": "bad_address", "bad_address": "bad_address",
"bad_file_descriptor": "bad_file_descriptor", "bad_file_descriptor": "bad_file_descriptor",
"connection_aborted": "connection_aborted", "connection_aborted": "connection_aborted",
"connection_already_closed": "connection_already_closed",
"connection_already_in_progress": "connection_already_in_progress", "connection_already_in_progress": "connection_already_in_progress",
"connection_refused": "connection_refused",
"connection_reset": "connection_reset",
"destination_address_required": "destination_address_required", "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", "interrupted": "interrupted",
"invalid_argument": "invalid_argument", "invalid_argument": "invalid_argument",
"json_parse_error": "json_parse_error",
"message_size": "message_size", "message_size": "message_size",
"network_down": "network_down", "network_down": "network_down",
"network_reset": "network_reset", "network_reset": "network_reset",
@ -92,86 +92,11 @@ var failuresMap = map[string]string{
"operation_would_block": "operation_would_block", "operation_would_block": "operation_would_block",
"permission_denied": "permission_denied", "permission_denied": "permission_denied",
"protocol_not_supported": "protocol_not_supported", "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", "quic_incompatible_version": "quic_incompatible_version",
"ssl_failed_handshake": "ssl_failed_handshake", "ssl_failed_handshake": "ssl_failed_handshake",
"ssl_invalid_certificate": "ssl_invalid_certificate",
"ssl_invalid_hostname": "ssl_invalid_hostname", "ssl_invalid_hostname": "ssl_invalid_hostname",
"ssl_unknown_authority": "ssl_unknown_authority", "ssl_unknown_authority": "ssl_unknown_authority",
"ssl_invalid_certificate": "ssl_invalid_certificate", "timed_out": "timed_out",
"json_parse_error": "json_parse_error", "wrong_protocol_type": "wrong_protocol_type",
"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 ""
} }

View File

@ -1,10 +1,17 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:53.909532 +0200 CEST m=+0.001205084 // Generated: 2021-09-29 10:33:56.065909 +0200 CEST m=+0.000565085
package netxlite package netxlite
import "golang.org/x/sys/unix" import (
"errors"
"syscall"
"golang.org/x/sys/unix"
)
// This enumeration provides a canonical name for
// every system-call error we support on this systems.
const ( const (
ECONNREFUSED = unix.ECONNREFUSED ECONNREFUSED = unix.ECONNREFUSED
ECONNRESET = unix.ECONNRESET ECONNRESET = unix.ECONNRESET
@ -34,3 +41,70 @@ const (
EPROTONOSUPPORT = unix.EPROTONOSUPPORT EPROTONOSUPPORT = unix.EPROTONOSUPPORT
EPROTOTYPE = unix.EPROTOTYPE EPROTOTYPE = unix.EPROTOTYPE
) )
// 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 unix.ECONNREFUSED:
return FailureConnectionRefused
case unix.ECONNRESET:
return FailureConnectionReset
case unix.EHOSTUNREACH:
return FailureHostUnreachable
case unix.ETIMEDOUT:
return FailureTimedOut
case unix.EAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case unix.EADDRINUSE:
return FailureAddressInUse
case unix.EADDRNOTAVAIL:
return FailureAddressNotAvailable
case unix.EISCONN:
return FailureAlreadyConnected
case unix.EFAULT:
return FailureBadAddress
case unix.EBADF:
return FailureBadFileDescriptor
case unix.ECONNABORTED:
return FailureConnectionAborted
case unix.EALREADY:
return FailureConnectionAlreadyInProgress
case unix.EDESTADDRREQ:
return FailureDestinationAddressRequired
case unix.EINTR:
return FailureInterrupted
case unix.EINVAL:
return FailureInvalidArgument
case unix.EMSGSIZE:
return FailureMessageSize
case unix.ENETDOWN:
return FailureNetworkDown
case unix.ENETRESET:
return FailureNetworkReset
case unix.ENETUNREACH:
return FailureNetworkUnreachable
case unix.ENOBUFS:
return FailureNoBufferSpace
case unix.ENOPROTOOPT:
return FailureNoProtocolOption
case unix.ENOTSOCK:
return FailureNotASocket
case unix.ENOTCONN:
return FailureNotConnected
case unix.EWOULDBLOCK:
return FailureOperationWouldBlock
case unix.EACCES:
return FailurePermissionDenied
case unix.EPROTONOSUPPORT:
return FailureProtocolNotSupported
case unix.EPROTOTYPE:
return FailureWrongProtocolType
}
return ""
}

View File

@ -0,0 +1,188 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-29 10:33:56.171735 +0200 CEST m=+0.106392751
package netxlite
import (
"io"
"syscall"
"testing"
"golang.org/x/sys/unix"
)
func TestClassifySyscallError(t *testing.T) {
t.Run("for a non-syscall error", func(t *testing.T) {
if v := classifySyscallError(io.EOF); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
t.Run("for ECONNREFUSED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNREFUSED); v != FailureConnectionRefused {
t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v)
}
})
t.Run("for ECONNRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNRESET); v != FailureConnectionReset {
t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v)
}
})
t.Run("for EHOSTUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.EHOSTUNREACH); v != FailureHostUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v)
}
})
t.Run("for ETIMEDOUT", func(t *testing.T) {
if v := classifySyscallError(unix.ETIMEDOUT); v != FailureTimedOut {
t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v)
}
})
t.Run("for EAFNOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EAFNOSUPPORT); v != FailureAddressFamilyNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v)
}
})
t.Run("for EADDRINUSE", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRINUSE); v != FailureAddressInUse {
t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v)
}
})
t.Run("for EADDRNOTAVAIL", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRNOTAVAIL); v != FailureAddressNotAvailable {
t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v)
}
})
t.Run("for EISCONN", func(t *testing.T) {
if v := classifySyscallError(unix.EISCONN); v != FailureAlreadyConnected {
t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v)
}
})
t.Run("for EFAULT", func(t *testing.T) {
if v := classifySyscallError(unix.EFAULT); v != FailureBadAddress {
t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v)
}
})
t.Run("for EBADF", func(t *testing.T) {
if v := classifySyscallError(unix.EBADF); v != FailureBadFileDescriptor {
t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v)
}
})
t.Run("for ECONNABORTED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNABORTED); v != FailureConnectionAborted {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v)
}
})
t.Run("for EALREADY", func(t *testing.T) {
if v := classifySyscallError(unix.EALREADY); v != FailureConnectionAlreadyInProgress {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v)
}
})
t.Run("for EDESTADDRREQ", func(t *testing.T) {
if v := classifySyscallError(unix.EDESTADDRREQ); v != FailureDestinationAddressRequired {
t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v)
}
})
t.Run("for EINTR", func(t *testing.T) {
if v := classifySyscallError(unix.EINTR); v != FailureInterrupted {
t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v)
}
})
t.Run("for EINVAL", func(t *testing.T) {
if v := classifySyscallError(unix.EINVAL); v != FailureInvalidArgument {
t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v)
}
})
t.Run("for EMSGSIZE", func(t *testing.T) {
if v := classifySyscallError(unix.EMSGSIZE); v != FailureMessageSize {
t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v)
}
})
t.Run("for ENETDOWN", func(t *testing.T) {
if v := classifySyscallError(unix.ENETDOWN); v != FailureNetworkDown {
t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v)
}
})
t.Run("for ENETRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ENETRESET); v != FailureNetworkReset {
t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v)
}
})
t.Run("for ENETUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.ENETUNREACH); v != FailureNetworkUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v)
}
})
t.Run("for ENOBUFS", func(t *testing.T) {
if v := classifySyscallError(unix.ENOBUFS); v != FailureNoBufferSpace {
t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v)
}
})
t.Run("for ENOPROTOOPT", func(t *testing.T) {
if v := classifySyscallError(unix.ENOPROTOOPT); v != FailureNoProtocolOption {
t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v)
}
})
t.Run("for ENOTSOCK", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTSOCK); v != FailureNotASocket {
t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v)
}
})
t.Run("for ENOTCONN", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTCONN); v != FailureNotConnected {
t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v)
}
})
t.Run("for EWOULDBLOCK", func(t *testing.T) {
if v := classifySyscallError(unix.EWOULDBLOCK); v != FailureOperationWouldBlock {
t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v)
}
})
t.Run("for EACCES", func(t *testing.T) {
if v := classifySyscallError(unix.EACCES); v != FailurePermissionDenied {
t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v)
}
})
t.Run("for EPROTONOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTONOSUPPORT); v != FailureProtocolNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v)
}
})
t.Run("for EPROTOTYPE", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTOTYPE); v != FailureWrongProtocolType {
t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v)
}
})
t.Run("for the zero errno value", func(t *testing.T) {
if v := classifySyscallError(syscall.Errno(0)); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
}

View File

@ -1,10 +1,17 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:54.015321 +0200 CEST m=+0.106996042 // Generated: 2021-09-29 10:33:56.208597 +0200 CEST m=+0.143256126
package netxlite package netxlite
import "golang.org/x/sys/unix" import (
"errors"
"syscall"
"golang.org/x/sys/unix"
)
// This enumeration provides a canonical name for
// every system-call error we support on this systems.
const ( const (
ECONNREFUSED = unix.ECONNREFUSED ECONNREFUSED = unix.ECONNREFUSED
ECONNRESET = unix.ECONNRESET ECONNRESET = unix.ECONNRESET
@ -34,3 +41,70 @@ const (
EPROTONOSUPPORT = unix.EPROTONOSUPPORT EPROTONOSUPPORT = unix.EPROTONOSUPPORT
EPROTOTYPE = unix.EPROTOTYPE EPROTOTYPE = unix.EPROTOTYPE
) )
// 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 unix.ECONNREFUSED:
return FailureConnectionRefused
case unix.ECONNRESET:
return FailureConnectionReset
case unix.EHOSTUNREACH:
return FailureHostUnreachable
case unix.ETIMEDOUT:
return FailureTimedOut
case unix.EAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case unix.EADDRINUSE:
return FailureAddressInUse
case unix.EADDRNOTAVAIL:
return FailureAddressNotAvailable
case unix.EISCONN:
return FailureAlreadyConnected
case unix.EFAULT:
return FailureBadAddress
case unix.EBADF:
return FailureBadFileDescriptor
case unix.ECONNABORTED:
return FailureConnectionAborted
case unix.EALREADY:
return FailureConnectionAlreadyInProgress
case unix.EDESTADDRREQ:
return FailureDestinationAddressRequired
case unix.EINTR:
return FailureInterrupted
case unix.EINVAL:
return FailureInvalidArgument
case unix.EMSGSIZE:
return FailureMessageSize
case unix.ENETDOWN:
return FailureNetworkDown
case unix.ENETRESET:
return FailureNetworkReset
case unix.ENETUNREACH:
return FailureNetworkUnreachable
case unix.ENOBUFS:
return FailureNoBufferSpace
case unix.ENOPROTOOPT:
return FailureNoProtocolOption
case unix.ENOTSOCK:
return FailureNotASocket
case unix.ENOTCONN:
return FailureNotConnected
case unix.EWOULDBLOCK:
return FailureOperationWouldBlock
case unix.EACCES:
return FailurePermissionDenied
case unix.EPROTONOSUPPORT:
return FailureProtocolNotSupported
case unix.EPROTOTYPE:
return FailureWrongProtocolType
}
return ""
}

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:54.431042 +0200 CEST m=+0.522721376 // Generated: 2021-09-29 10:33:56.27181 +0200 CEST m=+0.206469960
package netxlite package netxlite
@ -7,6 +7,8 @@ import (
"io" "io"
"syscall" "syscall"
"testing" "testing"
"golang.org/x/sys/unix"
) )
func TestClassifySyscallError(t *testing.T) { func TestClassifySyscallError(t *testing.T) {
@ -17,163 +19,163 @@ func TestClassifySyscallError(t *testing.T) {
}) })
t.Run("for ECONNREFUSED", func(t *testing.T) { t.Run("for ECONNREFUSED", func(t *testing.T) {
if v := classifySyscallError(ECONNREFUSED); v != FailureConnectionRefused { if v := classifySyscallError(unix.ECONNREFUSED); v != FailureConnectionRefused {
t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v) t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v)
} }
}) })
t.Run("for ECONNRESET", func(t *testing.T) { t.Run("for ECONNRESET", func(t *testing.T) {
if v := classifySyscallError(ECONNRESET); v != FailureConnectionReset { if v := classifySyscallError(unix.ECONNRESET); v != FailureConnectionReset {
t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v) t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v)
} }
}) })
t.Run("for EHOSTUNREACH", func(t *testing.T) { t.Run("for EHOSTUNREACH", func(t *testing.T) {
if v := classifySyscallError(EHOSTUNREACH); v != FailureHostUnreachable { if v := classifySyscallError(unix.EHOSTUNREACH); v != FailureHostUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v) t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v)
} }
}) })
t.Run("for ETIMEDOUT", func(t *testing.T) { t.Run("for ETIMEDOUT", func(t *testing.T) {
if v := classifySyscallError(ETIMEDOUT); v != FailureTimedOut { if v := classifySyscallError(unix.ETIMEDOUT); v != FailureTimedOut {
t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v) t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v)
} }
}) })
t.Run("for EAFNOSUPPORT", func(t *testing.T) { t.Run("for EAFNOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(EAFNOSUPPORT); v != FailureAddressFamilyNotSupported { if v := classifySyscallError(unix.EAFNOSUPPORT); v != FailureAddressFamilyNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v) t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v)
} }
}) })
t.Run("for EADDRINUSE", func(t *testing.T) { t.Run("for EADDRINUSE", func(t *testing.T) {
if v := classifySyscallError(EADDRINUSE); v != FailureAddressInUse { if v := classifySyscallError(unix.EADDRINUSE); v != FailureAddressInUse {
t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v) t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v)
} }
}) })
t.Run("for EADDRNOTAVAIL", func(t *testing.T) { t.Run("for EADDRNOTAVAIL", func(t *testing.T) {
if v := classifySyscallError(EADDRNOTAVAIL); v != FailureAddressNotAvailable { if v := classifySyscallError(unix.EADDRNOTAVAIL); v != FailureAddressNotAvailable {
t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v) t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v)
} }
}) })
t.Run("for EISCONN", func(t *testing.T) { t.Run("for EISCONN", func(t *testing.T) {
if v := classifySyscallError(EISCONN); v != FailureAlreadyConnected { if v := classifySyscallError(unix.EISCONN); v != FailureAlreadyConnected {
t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v) t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v)
} }
}) })
t.Run("for EFAULT", func(t *testing.T) { t.Run("for EFAULT", func(t *testing.T) {
if v := classifySyscallError(EFAULT); v != FailureBadAddress { if v := classifySyscallError(unix.EFAULT); v != FailureBadAddress {
t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v) t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v)
} }
}) })
t.Run("for EBADF", func(t *testing.T) { t.Run("for EBADF", func(t *testing.T) {
if v := classifySyscallError(EBADF); v != FailureBadFileDescriptor { if v := classifySyscallError(unix.EBADF); v != FailureBadFileDescriptor {
t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v) t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v)
} }
}) })
t.Run("for ECONNABORTED", func(t *testing.T) { t.Run("for ECONNABORTED", func(t *testing.T) {
if v := classifySyscallError(ECONNABORTED); v != FailureConnectionAborted { if v := classifySyscallError(unix.ECONNABORTED); v != FailureConnectionAborted {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v) t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v)
} }
}) })
t.Run("for EALREADY", func(t *testing.T) { t.Run("for EALREADY", func(t *testing.T) {
if v := classifySyscallError(EALREADY); v != FailureConnectionAlreadyInProgress { if v := classifySyscallError(unix.EALREADY); v != FailureConnectionAlreadyInProgress {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v) t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v)
} }
}) })
t.Run("for EDESTADDRREQ", func(t *testing.T) { t.Run("for EDESTADDRREQ", func(t *testing.T) {
if v := classifySyscallError(EDESTADDRREQ); v != FailureDestinationAddressRequired { if v := classifySyscallError(unix.EDESTADDRREQ); v != FailureDestinationAddressRequired {
t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v) t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v)
} }
}) })
t.Run("for EINTR", func(t *testing.T) { t.Run("for EINTR", func(t *testing.T) {
if v := classifySyscallError(EINTR); v != FailureInterrupted { if v := classifySyscallError(unix.EINTR); v != FailureInterrupted {
t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v) t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v)
} }
}) })
t.Run("for EINVAL", func(t *testing.T) { t.Run("for EINVAL", func(t *testing.T) {
if v := classifySyscallError(EINVAL); v != FailureInvalidArgument { if v := classifySyscallError(unix.EINVAL); v != FailureInvalidArgument {
t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v) t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v)
} }
}) })
t.Run("for EMSGSIZE", func(t *testing.T) { t.Run("for EMSGSIZE", func(t *testing.T) {
if v := classifySyscallError(EMSGSIZE); v != FailureMessageSize { if v := classifySyscallError(unix.EMSGSIZE); v != FailureMessageSize {
t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v) t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v)
} }
}) })
t.Run("for ENETDOWN", func(t *testing.T) { t.Run("for ENETDOWN", func(t *testing.T) {
if v := classifySyscallError(ENETDOWN); v != FailureNetworkDown { if v := classifySyscallError(unix.ENETDOWN); v != FailureNetworkDown {
t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v) t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v)
} }
}) })
t.Run("for ENETRESET", func(t *testing.T) { t.Run("for ENETRESET", func(t *testing.T) {
if v := classifySyscallError(ENETRESET); v != FailureNetworkReset { if v := classifySyscallError(unix.ENETRESET); v != FailureNetworkReset {
t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v) t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v)
} }
}) })
t.Run("for ENETUNREACH", func(t *testing.T) { t.Run("for ENETUNREACH", func(t *testing.T) {
if v := classifySyscallError(ENETUNREACH); v != FailureNetworkUnreachable { if v := classifySyscallError(unix.ENETUNREACH); v != FailureNetworkUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v) t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v)
} }
}) })
t.Run("for ENOBUFS", func(t *testing.T) { t.Run("for ENOBUFS", func(t *testing.T) {
if v := classifySyscallError(ENOBUFS); v != FailureNoBufferSpace { if v := classifySyscallError(unix.ENOBUFS); v != FailureNoBufferSpace {
t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v) t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v)
} }
}) })
t.Run("for ENOPROTOOPT", func(t *testing.T) { t.Run("for ENOPROTOOPT", func(t *testing.T) {
if v := classifySyscallError(ENOPROTOOPT); v != FailureNoProtocolOption { if v := classifySyscallError(unix.ENOPROTOOPT); v != FailureNoProtocolOption {
t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v) t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v)
} }
}) })
t.Run("for ENOTSOCK", func(t *testing.T) { t.Run("for ENOTSOCK", func(t *testing.T) {
if v := classifySyscallError(ENOTSOCK); v != FailureNotASocket { if v := classifySyscallError(unix.ENOTSOCK); v != FailureNotASocket {
t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v) t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v)
} }
}) })
t.Run("for ENOTCONN", func(t *testing.T) { t.Run("for ENOTCONN", func(t *testing.T) {
if v := classifySyscallError(ENOTCONN); v != FailureNotConnected { if v := classifySyscallError(unix.ENOTCONN); v != FailureNotConnected {
t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v) t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v)
} }
}) })
t.Run("for EWOULDBLOCK", func(t *testing.T) { t.Run("for EWOULDBLOCK", func(t *testing.T) {
if v := classifySyscallError(EWOULDBLOCK); v != FailureOperationWouldBlock { if v := classifySyscallError(unix.EWOULDBLOCK); v != FailureOperationWouldBlock {
t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v) t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v)
} }
}) })
t.Run("for EACCES", func(t *testing.T) { t.Run("for EACCES", func(t *testing.T) {
if v := classifySyscallError(EACCES); v != FailurePermissionDenied { if v := classifySyscallError(unix.EACCES); v != FailurePermissionDenied {
t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v) t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v)
} }
}) })
t.Run("for EPROTONOSUPPORT", func(t *testing.T) { t.Run("for EPROTONOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(EPROTONOSUPPORT); v != FailureProtocolNotSupported { if v := classifySyscallError(unix.EPROTONOSUPPORT); v != FailureProtocolNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v) t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v)
} }
}) })
t.Run("for EPROTOTYPE", func(t *testing.T) { t.Run("for EPROTOTYPE", func(t *testing.T) {
if v := classifySyscallError(EPROTOTYPE); v != FailureWrongProtocolType { if v := classifySyscallError(unix.EPROTOTYPE); v != FailureWrongProtocolType {
t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v) t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v)
} }
}) })

View File

@ -1,10 +1,17 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:54.08291 +0200 CEST m=+0.174585667 // Generated: 2021-09-29 10:33:56.310236 +0200 CEST m=+0.244897126
package netxlite package netxlite
import "golang.org/x/sys/unix" import (
"errors"
"syscall"
"golang.org/x/sys/unix"
)
// This enumeration provides a canonical name for
// every system-call error we support on this systems.
const ( const (
ECONNREFUSED = unix.ECONNREFUSED ECONNREFUSED = unix.ECONNREFUSED
ECONNRESET = unix.ECONNRESET ECONNRESET = unix.ECONNRESET
@ -34,3 +41,70 @@ const (
EPROTONOSUPPORT = unix.EPROTONOSUPPORT EPROTONOSUPPORT = unix.EPROTONOSUPPORT
EPROTOTYPE = unix.EPROTOTYPE EPROTOTYPE = unix.EPROTOTYPE
) )
// 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 unix.ECONNREFUSED:
return FailureConnectionRefused
case unix.ECONNRESET:
return FailureConnectionReset
case unix.EHOSTUNREACH:
return FailureHostUnreachable
case unix.ETIMEDOUT:
return FailureTimedOut
case unix.EAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case unix.EADDRINUSE:
return FailureAddressInUse
case unix.EADDRNOTAVAIL:
return FailureAddressNotAvailable
case unix.EISCONN:
return FailureAlreadyConnected
case unix.EFAULT:
return FailureBadAddress
case unix.EBADF:
return FailureBadFileDescriptor
case unix.ECONNABORTED:
return FailureConnectionAborted
case unix.EALREADY:
return FailureConnectionAlreadyInProgress
case unix.EDESTADDRREQ:
return FailureDestinationAddressRequired
case unix.EINTR:
return FailureInterrupted
case unix.EINVAL:
return FailureInvalidArgument
case unix.EMSGSIZE:
return FailureMessageSize
case unix.ENETDOWN:
return FailureNetworkDown
case unix.ENETRESET:
return FailureNetworkReset
case unix.ENETUNREACH:
return FailureNetworkUnreachable
case unix.ENOBUFS:
return FailureNoBufferSpace
case unix.ENOPROTOOPT:
return FailureNoProtocolOption
case unix.ENOTSOCK:
return FailureNotASocket
case unix.ENOTCONN:
return FailureNotConnected
case unix.EWOULDBLOCK:
return FailureOperationWouldBlock
case unix.EACCES:
return FailurePermissionDenied
case unix.EPROTONOSUPPORT:
return FailureProtocolNotSupported
case unix.EPROTOTYPE:
return FailureWrongProtocolType
}
return ""
}

View File

@ -0,0 +1,188 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-29 10:33:56.373611 +0200 CEST m=+0.308273168
package netxlite
import (
"io"
"syscall"
"testing"
"golang.org/x/sys/unix"
)
func TestClassifySyscallError(t *testing.T) {
t.Run("for a non-syscall error", func(t *testing.T) {
if v := classifySyscallError(io.EOF); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
t.Run("for ECONNREFUSED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNREFUSED); v != FailureConnectionRefused {
t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v)
}
})
t.Run("for ECONNRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNRESET); v != FailureConnectionReset {
t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v)
}
})
t.Run("for EHOSTUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.EHOSTUNREACH); v != FailureHostUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v)
}
})
t.Run("for ETIMEDOUT", func(t *testing.T) {
if v := classifySyscallError(unix.ETIMEDOUT); v != FailureTimedOut {
t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v)
}
})
t.Run("for EAFNOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EAFNOSUPPORT); v != FailureAddressFamilyNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v)
}
})
t.Run("for EADDRINUSE", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRINUSE); v != FailureAddressInUse {
t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v)
}
})
t.Run("for EADDRNOTAVAIL", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRNOTAVAIL); v != FailureAddressNotAvailable {
t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v)
}
})
t.Run("for EISCONN", func(t *testing.T) {
if v := classifySyscallError(unix.EISCONN); v != FailureAlreadyConnected {
t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v)
}
})
t.Run("for EFAULT", func(t *testing.T) {
if v := classifySyscallError(unix.EFAULT); v != FailureBadAddress {
t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v)
}
})
t.Run("for EBADF", func(t *testing.T) {
if v := classifySyscallError(unix.EBADF); v != FailureBadFileDescriptor {
t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v)
}
})
t.Run("for ECONNABORTED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNABORTED); v != FailureConnectionAborted {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v)
}
})
t.Run("for EALREADY", func(t *testing.T) {
if v := classifySyscallError(unix.EALREADY); v != FailureConnectionAlreadyInProgress {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v)
}
})
t.Run("for EDESTADDRREQ", func(t *testing.T) {
if v := classifySyscallError(unix.EDESTADDRREQ); v != FailureDestinationAddressRequired {
t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v)
}
})
t.Run("for EINTR", func(t *testing.T) {
if v := classifySyscallError(unix.EINTR); v != FailureInterrupted {
t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v)
}
})
t.Run("for EINVAL", func(t *testing.T) {
if v := classifySyscallError(unix.EINVAL); v != FailureInvalidArgument {
t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v)
}
})
t.Run("for EMSGSIZE", func(t *testing.T) {
if v := classifySyscallError(unix.EMSGSIZE); v != FailureMessageSize {
t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v)
}
})
t.Run("for ENETDOWN", func(t *testing.T) {
if v := classifySyscallError(unix.ENETDOWN); v != FailureNetworkDown {
t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v)
}
})
t.Run("for ENETRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ENETRESET); v != FailureNetworkReset {
t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v)
}
})
t.Run("for ENETUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.ENETUNREACH); v != FailureNetworkUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v)
}
})
t.Run("for ENOBUFS", func(t *testing.T) {
if v := classifySyscallError(unix.ENOBUFS); v != FailureNoBufferSpace {
t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v)
}
})
t.Run("for ENOPROTOOPT", func(t *testing.T) {
if v := classifySyscallError(unix.ENOPROTOOPT); v != FailureNoProtocolOption {
t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v)
}
})
t.Run("for ENOTSOCK", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTSOCK); v != FailureNotASocket {
t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v)
}
})
t.Run("for ENOTCONN", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTCONN); v != FailureNotConnected {
t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v)
}
})
t.Run("for EWOULDBLOCK", func(t *testing.T) {
if v := classifySyscallError(unix.EWOULDBLOCK); v != FailureOperationWouldBlock {
t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v)
}
})
t.Run("for EACCES", func(t *testing.T) {
if v := classifySyscallError(unix.EACCES); v != FailurePermissionDenied {
t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v)
}
})
t.Run("for EPROTONOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTONOSUPPORT); v != FailureProtocolNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v)
}
})
t.Run("for EPROTOTYPE", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTOTYPE); v != FailureWrongProtocolType {
t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v)
}
})
t.Run("for the zero errno value", func(t *testing.T) {
if v := classifySyscallError(syscall.Errno(0)); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
}

View File

@ -1,10 +1,17 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:54.151936 +0200 CEST m=+0.243612834 // Generated: 2021-09-29 10:33:56.410358 +0200 CEST m=+0.345021835
package netxlite package netxlite
import "golang.org/x/sys/unix" import (
"errors"
"syscall"
"golang.org/x/sys/unix"
)
// This enumeration provides a canonical name for
// every system-call error we support on this systems.
const ( const (
ECONNREFUSED = unix.ECONNREFUSED ECONNREFUSED = unix.ECONNREFUSED
ECONNRESET = unix.ECONNRESET ECONNRESET = unix.ECONNRESET
@ -34,3 +41,70 @@ const (
EPROTONOSUPPORT = unix.EPROTONOSUPPORT EPROTONOSUPPORT = unix.EPROTONOSUPPORT
EPROTOTYPE = unix.EPROTOTYPE EPROTOTYPE = unix.EPROTOTYPE
) )
// 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 unix.ECONNREFUSED:
return FailureConnectionRefused
case unix.ECONNRESET:
return FailureConnectionReset
case unix.EHOSTUNREACH:
return FailureHostUnreachable
case unix.ETIMEDOUT:
return FailureTimedOut
case unix.EAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case unix.EADDRINUSE:
return FailureAddressInUse
case unix.EADDRNOTAVAIL:
return FailureAddressNotAvailable
case unix.EISCONN:
return FailureAlreadyConnected
case unix.EFAULT:
return FailureBadAddress
case unix.EBADF:
return FailureBadFileDescriptor
case unix.ECONNABORTED:
return FailureConnectionAborted
case unix.EALREADY:
return FailureConnectionAlreadyInProgress
case unix.EDESTADDRREQ:
return FailureDestinationAddressRequired
case unix.EINTR:
return FailureInterrupted
case unix.EINVAL:
return FailureInvalidArgument
case unix.EMSGSIZE:
return FailureMessageSize
case unix.ENETDOWN:
return FailureNetworkDown
case unix.ENETRESET:
return FailureNetworkReset
case unix.ENETUNREACH:
return FailureNetworkUnreachable
case unix.ENOBUFS:
return FailureNoBufferSpace
case unix.ENOPROTOOPT:
return FailureNoProtocolOption
case unix.ENOTSOCK:
return FailureNotASocket
case unix.ENOTCONN:
return FailureNotConnected
case unix.EWOULDBLOCK:
return FailureOperationWouldBlock
case unix.EACCES:
return FailurePermissionDenied
case unix.EPROTONOSUPPORT:
return FailureProtocolNotSupported
case unix.EPROTOTYPE:
return FailureWrongProtocolType
}
return ""
}

View File

@ -0,0 +1,188 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-29 10:33:56.491959 +0200 CEST m=+0.426624626
package netxlite
import (
"io"
"syscall"
"testing"
"golang.org/x/sys/unix"
)
func TestClassifySyscallError(t *testing.T) {
t.Run("for a non-syscall error", func(t *testing.T) {
if v := classifySyscallError(io.EOF); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
t.Run("for ECONNREFUSED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNREFUSED); v != FailureConnectionRefused {
t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v)
}
})
t.Run("for ECONNRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNRESET); v != FailureConnectionReset {
t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v)
}
})
t.Run("for EHOSTUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.EHOSTUNREACH); v != FailureHostUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v)
}
})
t.Run("for ETIMEDOUT", func(t *testing.T) {
if v := classifySyscallError(unix.ETIMEDOUT); v != FailureTimedOut {
t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v)
}
})
t.Run("for EAFNOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EAFNOSUPPORT); v != FailureAddressFamilyNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v)
}
})
t.Run("for EADDRINUSE", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRINUSE); v != FailureAddressInUse {
t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v)
}
})
t.Run("for EADDRNOTAVAIL", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRNOTAVAIL); v != FailureAddressNotAvailable {
t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v)
}
})
t.Run("for EISCONN", func(t *testing.T) {
if v := classifySyscallError(unix.EISCONN); v != FailureAlreadyConnected {
t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v)
}
})
t.Run("for EFAULT", func(t *testing.T) {
if v := classifySyscallError(unix.EFAULT); v != FailureBadAddress {
t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v)
}
})
t.Run("for EBADF", func(t *testing.T) {
if v := classifySyscallError(unix.EBADF); v != FailureBadFileDescriptor {
t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v)
}
})
t.Run("for ECONNABORTED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNABORTED); v != FailureConnectionAborted {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v)
}
})
t.Run("for EALREADY", func(t *testing.T) {
if v := classifySyscallError(unix.EALREADY); v != FailureConnectionAlreadyInProgress {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v)
}
})
t.Run("for EDESTADDRREQ", func(t *testing.T) {
if v := classifySyscallError(unix.EDESTADDRREQ); v != FailureDestinationAddressRequired {
t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v)
}
})
t.Run("for EINTR", func(t *testing.T) {
if v := classifySyscallError(unix.EINTR); v != FailureInterrupted {
t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v)
}
})
t.Run("for EINVAL", func(t *testing.T) {
if v := classifySyscallError(unix.EINVAL); v != FailureInvalidArgument {
t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v)
}
})
t.Run("for EMSGSIZE", func(t *testing.T) {
if v := classifySyscallError(unix.EMSGSIZE); v != FailureMessageSize {
t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v)
}
})
t.Run("for ENETDOWN", func(t *testing.T) {
if v := classifySyscallError(unix.ENETDOWN); v != FailureNetworkDown {
t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v)
}
})
t.Run("for ENETRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ENETRESET); v != FailureNetworkReset {
t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v)
}
})
t.Run("for ENETUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.ENETUNREACH); v != FailureNetworkUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v)
}
})
t.Run("for ENOBUFS", func(t *testing.T) {
if v := classifySyscallError(unix.ENOBUFS); v != FailureNoBufferSpace {
t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v)
}
})
t.Run("for ENOPROTOOPT", func(t *testing.T) {
if v := classifySyscallError(unix.ENOPROTOOPT); v != FailureNoProtocolOption {
t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v)
}
})
t.Run("for ENOTSOCK", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTSOCK); v != FailureNotASocket {
t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v)
}
})
t.Run("for ENOTCONN", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTCONN); v != FailureNotConnected {
t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v)
}
})
t.Run("for EWOULDBLOCK", func(t *testing.T) {
if v := classifySyscallError(unix.EWOULDBLOCK); v != FailureOperationWouldBlock {
t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v)
}
})
t.Run("for EACCES", func(t *testing.T) {
if v := classifySyscallError(unix.EACCES); v != FailurePermissionDenied {
t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v)
}
})
t.Run("for EPROTONOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTONOSUPPORT); v != FailureProtocolNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v)
}
})
t.Run("for EPROTOTYPE", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTOTYPE); v != FailureWrongProtocolType {
t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v)
}
})
t.Run("for the zero errno value", func(t *testing.T) {
if v := classifySyscallError(syscall.Errno(0)); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
}

View File

@ -1,10 +1,17 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:54.230156 +0200 CEST m=+0.321833417 // Generated: 2021-09-29 10:33:56.529823 +0200 CEST m=+0.464489585
package netxlite package netxlite
import "golang.org/x/sys/unix" import (
"errors"
"syscall"
"golang.org/x/sys/unix"
)
// This enumeration provides a canonical name for
// every system-call error we support on this systems.
const ( const (
ECONNREFUSED = unix.ECONNREFUSED ECONNREFUSED = unix.ECONNREFUSED
ECONNRESET = unix.ECONNRESET ECONNRESET = unix.ECONNRESET
@ -34,3 +41,70 @@ const (
EPROTONOSUPPORT = unix.EPROTONOSUPPORT EPROTONOSUPPORT = unix.EPROTONOSUPPORT
EPROTOTYPE = unix.EPROTOTYPE EPROTOTYPE = unix.EPROTOTYPE
) )
// 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 unix.ECONNREFUSED:
return FailureConnectionRefused
case unix.ECONNRESET:
return FailureConnectionReset
case unix.EHOSTUNREACH:
return FailureHostUnreachable
case unix.ETIMEDOUT:
return FailureTimedOut
case unix.EAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case unix.EADDRINUSE:
return FailureAddressInUse
case unix.EADDRNOTAVAIL:
return FailureAddressNotAvailable
case unix.EISCONN:
return FailureAlreadyConnected
case unix.EFAULT:
return FailureBadAddress
case unix.EBADF:
return FailureBadFileDescriptor
case unix.ECONNABORTED:
return FailureConnectionAborted
case unix.EALREADY:
return FailureConnectionAlreadyInProgress
case unix.EDESTADDRREQ:
return FailureDestinationAddressRequired
case unix.EINTR:
return FailureInterrupted
case unix.EINVAL:
return FailureInvalidArgument
case unix.EMSGSIZE:
return FailureMessageSize
case unix.ENETDOWN:
return FailureNetworkDown
case unix.ENETRESET:
return FailureNetworkReset
case unix.ENETUNREACH:
return FailureNetworkUnreachable
case unix.ENOBUFS:
return FailureNoBufferSpace
case unix.ENOPROTOOPT:
return FailureNoProtocolOption
case unix.ENOTSOCK:
return FailureNotASocket
case unix.ENOTCONN:
return FailureNotConnected
case unix.EWOULDBLOCK:
return FailureOperationWouldBlock
case unix.EACCES:
return FailurePermissionDenied
case unix.EPROTONOSUPPORT:
return FailureProtocolNotSupported
case unix.EPROTOTYPE:
return FailureWrongProtocolType
}
return ""
}

View File

@ -0,0 +1,188 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-29 10:33:56.592275 +0200 CEST m=+0.526942210
package netxlite
import (
"io"
"syscall"
"testing"
"golang.org/x/sys/unix"
)
func TestClassifySyscallError(t *testing.T) {
t.Run("for a non-syscall error", func(t *testing.T) {
if v := classifySyscallError(io.EOF); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
t.Run("for ECONNREFUSED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNREFUSED); v != FailureConnectionRefused {
t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v)
}
})
t.Run("for ECONNRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNRESET); v != FailureConnectionReset {
t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v)
}
})
t.Run("for EHOSTUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.EHOSTUNREACH); v != FailureHostUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v)
}
})
t.Run("for ETIMEDOUT", func(t *testing.T) {
if v := classifySyscallError(unix.ETIMEDOUT); v != FailureTimedOut {
t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v)
}
})
t.Run("for EAFNOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EAFNOSUPPORT); v != FailureAddressFamilyNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v)
}
})
t.Run("for EADDRINUSE", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRINUSE); v != FailureAddressInUse {
t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v)
}
})
t.Run("for EADDRNOTAVAIL", func(t *testing.T) {
if v := classifySyscallError(unix.EADDRNOTAVAIL); v != FailureAddressNotAvailable {
t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v)
}
})
t.Run("for EISCONN", func(t *testing.T) {
if v := classifySyscallError(unix.EISCONN); v != FailureAlreadyConnected {
t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v)
}
})
t.Run("for EFAULT", func(t *testing.T) {
if v := classifySyscallError(unix.EFAULT); v != FailureBadAddress {
t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v)
}
})
t.Run("for EBADF", func(t *testing.T) {
if v := classifySyscallError(unix.EBADF); v != FailureBadFileDescriptor {
t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v)
}
})
t.Run("for ECONNABORTED", func(t *testing.T) {
if v := classifySyscallError(unix.ECONNABORTED); v != FailureConnectionAborted {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v)
}
})
t.Run("for EALREADY", func(t *testing.T) {
if v := classifySyscallError(unix.EALREADY); v != FailureConnectionAlreadyInProgress {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v)
}
})
t.Run("for EDESTADDRREQ", func(t *testing.T) {
if v := classifySyscallError(unix.EDESTADDRREQ); v != FailureDestinationAddressRequired {
t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v)
}
})
t.Run("for EINTR", func(t *testing.T) {
if v := classifySyscallError(unix.EINTR); v != FailureInterrupted {
t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v)
}
})
t.Run("for EINVAL", func(t *testing.T) {
if v := classifySyscallError(unix.EINVAL); v != FailureInvalidArgument {
t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v)
}
})
t.Run("for EMSGSIZE", func(t *testing.T) {
if v := classifySyscallError(unix.EMSGSIZE); v != FailureMessageSize {
t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v)
}
})
t.Run("for ENETDOWN", func(t *testing.T) {
if v := classifySyscallError(unix.ENETDOWN); v != FailureNetworkDown {
t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v)
}
})
t.Run("for ENETRESET", func(t *testing.T) {
if v := classifySyscallError(unix.ENETRESET); v != FailureNetworkReset {
t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v)
}
})
t.Run("for ENETUNREACH", func(t *testing.T) {
if v := classifySyscallError(unix.ENETUNREACH); v != FailureNetworkUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v)
}
})
t.Run("for ENOBUFS", func(t *testing.T) {
if v := classifySyscallError(unix.ENOBUFS); v != FailureNoBufferSpace {
t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v)
}
})
t.Run("for ENOPROTOOPT", func(t *testing.T) {
if v := classifySyscallError(unix.ENOPROTOOPT); v != FailureNoProtocolOption {
t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v)
}
})
t.Run("for ENOTSOCK", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTSOCK); v != FailureNotASocket {
t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v)
}
})
t.Run("for ENOTCONN", func(t *testing.T) {
if v := classifySyscallError(unix.ENOTCONN); v != FailureNotConnected {
t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v)
}
})
t.Run("for EWOULDBLOCK", func(t *testing.T) {
if v := classifySyscallError(unix.EWOULDBLOCK); v != FailureOperationWouldBlock {
t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v)
}
})
t.Run("for EACCES", func(t *testing.T) {
if v := classifySyscallError(unix.EACCES); v != FailurePermissionDenied {
t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v)
}
})
t.Run("for EPROTONOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTONOSUPPORT); v != FailureProtocolNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v)
}
})
t.Run("for EPROTOTYPE", func(t *testing.T) {
if v := classifySyscallError(unix.EPROTOTYPE); v != FailureWrongProtocolType {
t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v)
}
})
t.Run("for the zero errno value", func(t *testing.T) {
if v := classifySyscallError(syscall.Errno(0)); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
}

View File

@ -1,10 +1,17 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-28 18:13:54.317744 +0200 CEST m=+0.409422292 // Generated: 2021-09-29 10:33:56.628771 +0200 CEST m=+0.563439293
package netxlite package netxlite
import "golang.org/x/sys/windows" import (
"errors"
"syscall"
"golang.org/x/sys/windows"
)
// This enumeration provides a canonical name for
// every system-call error we support on this systems.
const ( const (
ECONNREFUSED = windows.WSAECONNREFUSED ECONNREFUSED = windows.WSAECONNREFUSED
ECONNRESET = windows.WSAECONNRESET ECONNRESET = windows.WSAECONNRESET
@ -33,4 +40,80 @@ const (
EACCES = windows.WSAEACCES EACCES = windows.WSAEACCES
EPROTONOSUPPORT = windows.WSAEPROTONOSUPPORT EPROTONOSUPPORT = windows.WSAEPROTONOSUPPORT
EPROTOTYPE = windows.WSAEPROTOTYPE EPROTOTYPE = windows.WSAEPROTOTYPE
WSANO_DATA = windows.WSANO_DATA
WSANO_RECOVERY = windows.WSANO_RECOVERY
WSATRY_AGAIN = windows.WSATRY_AGAIN
) )
// 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 windows.WSAECONNREFUSED:
return FailureConnectionRefused
case windows.WSAECONNRESET:
return FailureConnectionReset
case windows.WSAEHOSTUNREACH:
return FailureHostUnreachable
case windows.WSAETIMEDOUT:
return FailureTimedOut
case windows.WSAEAFNOSUPPORT:
return FailureAddressFamilyNotSupported
case windows.WSAEADDRINUSE:
return FailureAddressInUse
case windows.WSAEADDRNOTAVAIL:
return FailureAddressNotAvailable
case windows.WSAEISCONN:
return FailureAlreadyConnected
case windows.WSAEFAULT:
return FailureBadAddress
case windows.WSAEBADF:
return FailureBadFileDescriptor
case windows.WSAECONNABORTED:
return FailureConnectionAborted
case windows.WSAEALREADY:
return FailureConnectionAlreadyInProgress
case windows.WSAEDESTADDRREQ:
return FailureDestinationAddressRequired
case windows.WSAEINTR:
return FailureInterrupted
case windows.WSAEINVAL:
return FailureInvalidArgument
case windows.WSAEMSGSIZE:
return FailureMessageSize
case windows.WSAENETDOWN:
return FailureNetworkDown
case windows.WSAENETRESET:
return FailureNetworkReset
case windows.WSAENETUNREACH:
return FailureNetworkUnreachable
case windows.WSAENOBUFS:
return FailureNoBufferSpace
case windows.WSAENOPROTOOPT:
return FailureNoProtocolOption
case windows.WSAENOTSOCK:
return FailureNotASocket
case windows.WSAENOTCONN:
return FailureNotConnected
case windows.WSAEWOULDBLOCK:
return FailureOperationWouldBlock
case windows.WSAEACCES:
return FailurePermissionDenied
case windows.WSAEPROTONOSUPPORT:
return FailureProtocolNotSupported
case windows.WSAEPROTOTYPE:
return FailureWrongProtocolType
case windows.WSANO_DATA:
return FailureDNSNoAnswer
case windows.WSANO_RECOVERY:
return FailureDNSNonRecoverableFailure
case windows.WSATRY_AGAIN:
return FailureDNSTemporaryFailure
}
return ""
}

View File

@ -0,0 +1,206 @@
// Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-29 10:33:56.675065 +0200 CEST m=+0.609734418
package netxlite
import (
"io"
"syscall"
"testing"
"golang.org/x/sys/windows"
)
func TestClassifySyscallError(t *testing.T) {
t.Run("for a non-syscall error", func(t *testing.T) {
if v := classifySyscallError(io.EOF); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
t.Run("for WSAECONNREFUSED", func(t *testing.T) {
if v := classifySyscallError(windows.WSAECONNREFUSED); v != FailureConnectionRefused {
t.Fatalf("expected '%s', got '%s'", FailureConnectionRefused, v)
}
})
t.Run("for WSAECONNRESET", func(t *testing.T) {
if v := classifySyscallError(windows.WSAECONNRESET); v != FailureConnectionReset {
t.Fatalf("expected '%s', got '%s'", FailureConnectionReset, v)
}
})
t.Run("for WSAEHOSTUNREACH", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEHOSTUNREACH); v != FailureHostUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureHostUnreachable, v)
}
})
t.Run("for WSAETIMEDOUT", func(t *testing.T) {
if v := classifySyscallError(windows.WSAETIMEDOUT); v != FailureTimedOut {
t.Fatalf("expected '%s', got '%s'", FailureTimedOut, v)
}
})
t.Run("for WSAEAFNOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEAFNOSUPPORT); v != FailureAddressFamilyNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureAddressFamilyNotSupported, v)
}
})
t.Run("for WSAEADDRINUSE", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEADDRINUSE); v != FailureAddressInUse {
t.Fatalf("expected '%s', got '%s'", FailureAddressInUse, v)
}
})
t.Run("for WSAEADDRNOTAVAIL", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEADDRNOTAVAIL); v != FailureAddressNotAvailable {
t.Fatalf("expected '%s', got '%s'", FailureAddressNotAvailable, v)
}
})
t.Run("for WSAEISCONN", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEISCONN); v != FailureAlreadyConnected {
t.Fatalf("expected '%s', got '%s'", FailureAlreadyConnected, v)
}
})
t.Run("for WSAEFAULT", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEFAULT); v != FailureBadAddress {
t.Fatalf("expected '%s', got '%s'", FailureBadAddress, v)
}
})
t.Run("for WSAEBADF", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEBADF); v != FailureBadFileDescriptor {
t.Fatalf("expected '%s', got '%s'", FailureBadFileDescriptor, v)
}
})
t.Run("for WSAECONNABORTED", func(t *testing.T) {
if v := classifySyscallError(windows.WSAECONNABORTED); v != FailureConnectionAborted {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAborted, v)
}
})
t.Run("for WSAEALREADY", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEALREADY); v != FailureConnectionAlreadyInProgress {
t.Fatalf("expected '%s', got '%s'", FailureConnectionAlreadyInProgress, v)
}
})
t.Run("for WSAEDESTADDRREQ", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEDESTADDRREQ); v != FailureDestinationAddressRequired {
t.Fatalf("expected '%s', got '%s'", FailureDestinationAddressRequired, v)
}
})
t.Run("for WSAEINTR", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEINTR); v != FailureInterrupted {
t.Fatalf("expected '%s', got '%s'", FailureInterrupted, v)
}
})
t.Run("for WSAEINVAL", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEINVAL); v != FailureInvalidArgument {
t.Fatalf("expected '%s', got '%s'", FailureInvalidArgument, v)
}
})
t.Run("for WSAEMSGSIZE", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEMSGSIZE); v != FailureMessageSize {
t.Fatalf("expected '%s', got '%s'", FailureMessageSize, v)
}
})
t.Run("for WSAENETDOWN", func(t *testing.T) {
if v := classifySyscallError(windows.WSAENETDOWN); v != FailureNetworkDown {
t.Fatalf("expected '%s', got '%s'", FailureNetworkDown, v)
}
})
t.Run("for WSAENETRESET", func(t *testing.T) {
if v := classifySyscallError(windows.WSAENETRESET); v != FailureNetworkReset {
t.Fatalf("expected '%s', got '%s'", FailureNetworkReset, v)
}
})
t.Run("for WSAENETUNREACH", func(t *testing.T) {
if v := classifySyscallError(windows.WSAENETUNREACH); v != FailureNetworkUnreachable {
t.Fatalf("expected '%s', got '%s'", FailureNetworkUnreachable, v)
}
})
t.Run("for WSAENOBUFS", func(t *testing.T) {
if v := classifySyscallError(windows.WSAENOBUFS); v != FailureNoBufferSpace {
t.Fatalf("expected '%s', got '%s'", FailureNoBufferSpace, v)
}
})
t.Run("for WSAENOPROTOOPT", func(t *testing.T) {
if v := classifySyscallError(windows.WSAENOPROTOOPT); v != FailureNoProtocolOption {
t.Fatalf("expected '%s', got '%s'", FailureNoProtocolOption, v)
}
})
t.Run("for WSAENOTSOCK", func(t *testing.T) {
if v := classifySyscallError(windows.WSAENOTSOCK); v != FailureNotASocket {
t.Fatalf("expected '%s', got '%s'", FailureNotASocket, v)
}
})
t.Run("for WSAENOTCONN", func(t *testing.T) {
if v := classifySyscallError(windows.WSAENOTCONN); v != FailureNotConnected {
t.Fatalf("expected '%s', got '%s'", FailureNotConnected, v)
}
})
t.Run("for WSAEWOULDBLOCK", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEWOULDBLOCK); v != FailureOperationWouldBlock {
t.Fatalf("expected '%s', got '%s'", FailureOperationWouldBlock, v)
}
})
t.Run("for WSAEACCES", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEACCES); v != FailurePermissionDenied {
t.Fatalf("expected '%s', got '%s'", FailurePermissionDenied, v)
}
})
t.Run("for WSAEPROTONOSUPPORT", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEPROTONOSUPPORT); v != FailureProtocolNotSupported {
t.Fatalf("expected '%s', got '%s'", FailureProtocolNotSupported, v)
}
})
t.Run("for WSAEPROTOTYPE", func(t *testing.T) {
if v := classifySyscallError(windows.WSAEPROTOTYPE); v != FailureWrongProtocolType {
t.Fatalf("expected '%s', got '%s'", FailureWrongProtocolType, v)
}
})
t.Run("for WSANO_DATA", func(t *testing.T) {
if v := classifySyscallError(windows.WSANO_DATA); v != FailureDNSNoAnswer {
t.Fatalf("expected '%s', got '%s'", FailureDNSNoAnswer, v)
}
})
t.Run("for WSANO_RECOVERY", func(t *testing.T) {
if v := classifySyscallError(windows.WSANO_RECOVERY); v != FailureDNSNonRecoverableFailure {
t.Fatalf("expected '%s', got '%s'", FailureDNSNonRecoverableFailure, v)
}
})
t.Run("for WSATRY_AGAIN", func(t *testing.T) {
if v := classifySyscallError(windows.WSATRY_AGAIN); v != FailureDNSTemporaryFailure {
t.Fatalf("expected '%s', got '%s'", FailureDNSTemporaryFailure, v)
}
})
t.Run("for the zero errno value", func(t *testing.T) {
if v := classifySyscallError(syscall.Errno(0)); v != "" {
t.Fatalf("expected empty string, got '%s'", v)
}
})
}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"sort"
"time" "time"
"github.com/iancoleman/strcase" "github.com/iancoleman/strcase"
@ -17,15 +18,53 @@ type ErrorSpec struct {
// failure is the error name according to OONI (e.g., FailureConnectionRefused). // failure is the error name according to OONI (e.g., FailureConnectionRefused).
failure string failure string
// system specifies for which system this error is valid. If
// this value is empty then the spec is valid for all systems.
system string
}
// IsForSystem returns true when the spec's system matches the
// given system or when the spec's system is "".
func (es *ErrorSpec) IsForSystem(system string) bool {
return es.system == system || es.system == ""
} }
// AsErrnoName returns the name of the corresponding errno, if this // AsErrnoName returns the name of the corresponding errno, if this
// is a system error, or panics otherwise. // is a system error, or panics otherwise.
func (es *ErrorSpec) AsErrnoName() string { func (es *ErrorSpec) AsErrnoName(system string) string {
if !es.IsSystemError() { if !es.IsSystemError() {
panic("not a system error") panic("not a system error")
} }
s := es.errno
if system == "windows" {
s = "WSA" + s
}
return s
}
// AsCanonicalErrnoName attempts to canonicalize the errno name
// using the following algorithm:
//
// - if the error is present on all systems, use the unix name;
//
// - otherwise, use the system-name name for the error.
//
// So, for example, we will get:
//
// - EWOULDBLOCK because it's present on both Unix and Windows;
//
// - WSANO_DATA because it's Windows only.
func (es *ErrorSpec) AsCanonicalErrnoName() string {
if !es.IsSystemError() {
panic("not a system error")
}
switch es.system {
case "windows":
return es.AsErrnoName(es.system)
default:
return es.errno return es.errno
}
} }
// AsFailureVar returns the name of the failure var. // AsFailureVar returns the name of the failure var.
@ -41,7 +80,13 @@ func (es *ErrorSpec) AsFailureString() string {
// NewSystemError constructs a new ErrorSpec representing a system // NewSystemError constructs a new ErrorSpec representing a system
// error, i.e., an error returned by a system call. // error, i.e., an error returned by a system call.
func NewSystemError(errno, failure string) *ErrorSpec { func NewSystemError(errno, failure string) *ErrorSpec {
return &ErrorSpec{errno: errno, failure: failure} return &ErrorSpec{errno: errno, failure: failure, system: ""}
}
// NewWindowsError constructs a new ErrorSpec representing a
// Windows-only system error, i.e., an error returned by a system call.
func NewWindowsError(errno, failure string) *ErrorSpec {
return &ErrorSpec{errno: errno, failure: failure, system: "windows"}
} }
// NewLibraryError constructs a new ErrorSpec representing a library // NewLibraryError constructs a new ErrorSpec representing a library
@ -87,6 +132,24 @@ var Specs = []*ErrorSpec{
NewSystemError("EPROTONOSUPPORT", "protocol_not_supported"), NewSystemError("EPROTONOSUPPORT", "protocol_not_supported"),
NewSystemError("EPROTOTYPE", "wrong_protocol_type"), NewSystemError("EPROTOTYPE", "wrong_protocol_type"),
// Windows-only system errors.
//
// Why do we have these extra errors here? Because on Windows
// GetAddrInfoW is a system call while it's a library call
// on Unix. Because of that, the Go stdlib treats Windows and
// Unix differently and allows more syscall errors to slip
// through when we're performing DNS resolutions.
//
// Because MK handled _some_ getaddrinfo return codes, I've
// marked names compatible with MK using [*].
//
// Implementation note: we need to specify acronyms we
// want to be upper case in uppercase here. For example,
// we must write "DNS" rather than writing "dns".
NewWindowsError("NO_DATA", "DNS_no_answer"), // [ ] WSANO_DATA
NewWindowsError("NO_RECOVERY", "DNS_non_recoverable_failure"), // [*] WSANO_RECOVERY
NewWindowsError("TRY_AGAIN", "DNS_temporary_failure"), // [*] WSATRY_AGAIN
// Implementation note: we need to specify acronyms we // Implementation note: we need to specify acronyms we
// want to be upper case in uppercase here. For example, // want to be upper case in uppercase here. For example,
// we must write "DNS" rather than writing "dns". // we must write "DNS" rather than writing "dns".
@ -106,6 +169,19 @@ var Specs = []*ErrorSpec{
NewLibraryError("connection_already_closed"), NewLibraryError("connection_already_closed"),
} }
// mapSystemToLibrary maps the operating system name to the name
// of the related golang.org/x/sys/$name library.
func mapSystemToLibrary(system string) string {
switch system {
case "android", "darwin", "freebsd", "ios", "linux":
return "unix"
case "windows":
return "windows"
default:
panic(fmt.Sprintf("unsupported system: %s", system))
}
}
func fileCreate(filename string) *os.File { func fileCreate(filename string) *os.File {
filep, err := os.Create(filename) filep, err := os.Create(filename)
if err != nil { if err != nil {
@ -137,74 +213,31 @@ func gofmt(filename string) {
} }
} }
func writeSystemSpecificFile(kind, library, prefix string) { func writeSystemSpecificFile(system string) {
filename := "errno_" + kind + ".go" filename := "errno_" + system + ".go"
filep := fileCreate(filename) filep := fileCreate(filename)
library := mapSystemToLibrary(system)
fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n") fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n")
filePrintf(filep, "// Generated: %+v\n\n", time.Now()) filePrintf(filep, "// Generated: %+v\n\n", time.Now())
fileWrite(filep, "package netxlite\n\n") fileWrite(filep, "package netxlite\n\n")
filePrintf(filep, "import \"golang.org/x/sys/%s\"\n\n", library)
fileWrite(filep, "const (\n")
for _, spec := range Specs {
if !spec.IsSystemError() {
continue
}
filePrintf(filep, "\t%s = %s.%s%s\n",
spec.AsErrnoName(), library, prefix, spec.AsErrnoName())
}
fileWrite(filep, ")\n\n")
fileClose(filep)
gofmt(filename)
}
func writeGenericFile() {
filename := "errno.go"
filep := fileCreate(filename)
fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n")
filePrintf(filep, "// Generated: %+v\n\n", time.Now())
fileWrite(filep, "package netxlite\n\n")
fileWrite(filep, "//go:generate go run ./internal/generrno/\n\n")
fileWrite(filep, "import (\n") fileWrite(filep, "import (\n")
fileWrite(filep, "\t\"errors\"\n") fileWrite(filep, "\t\"errors\"\n")
fileWrite(filep, "\t\"syscall\"\n") fileWrite(filep, "\t\"syscall\"\n")
fileWrite(filep, ")\n\n")
fileWrite(filep, "// This enumeration lists the failures defined at\n")
fileWrite(filep, "// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md\n")
fileWrite(filep, "const (\n")
fileWrite(filep, "//\n")
fileWrite(filep, "// System errors\n")
fileWrite(filep, "//\n")
for _, spec := range Specs {
if !spec.IsSystemError() {
continue
}
filePrintf(filep, "\t%s = \"%s\"\n",
spec.AsFailureVar(),
spec.AsFailureString())
}
fileWrite(filep, "\n") fileWrite(filep, "\n")
fileWrite(filep, "//\n") filePrintf(filep, "\t\"golang.org/x/sys/%s\"\n", library)
fileWrite(filep, "// Library errors\n")
fileWrite(filep, "//\n")
for _, spec := range Specs {
if spec.IsSystemError() {
continue
}
filePrintf(filep, "\t%s = \"%s\"\n",
spec.AsFailureVar(),
spec.AsFailureString())
}
fileWrite(filep, ")\n\n") fileWrite(filep, ")\n\n")
fileWrite(filep, "// failureMap lists all failures so we can match them\n") fileWrite(filep, "// This enumeration provides a canonical name for\n")
fileWrite(filep, "// when they are wrapped by quic.TransportError.\n") fileWrite(filep, "// every system-call error we support on this systems.\n")
fileWrite(filep, "var failuresMap = map[string]string{\n") fileWrite(filep, "const (\n")
for _, spec := range Specs { for _, spec := range Specs {
filePrintf(filep, "\t\"%s\": \"%s\",\n", if !spec.IsSystemError() || !spec.IsForSystem(system) {
spec.AsFailureString(), spec.AsFailureString()) continue
} }
fileWrite(filep, "}\n\n") filePrintf(filep, "\t%s = %s.%s\n",
spec.AsCanonicalErrnoName(), library, spec.AsErrnoName(system))
}
fileWrite(filep, ")\n\n")
fileWrite(filep, "// classifySyscallError converts a syscall error to the\n") fileWrite(filep, "// classifySyscallError converts a syscall error to the\n")
fileWrite(filep, "// proper OONI error. Returns the OONI error string\n") fileWrite(filep, "// proper OONI error. Returns the OONI error string\n")
@ -216,10 +249,10 @@ func writeGenericFile() {
fileWrite(filep, "\t}\n") fileWrite(filep, "\t}\n")
fileWrite(filep, "\tswitch errno {\n") fileWrite(filep, "\tswitch errno {\n")
for _, spec := range Specs { for _, spec := range Specs {
if !spec.IsSystemError() { if !spec.IsSystemError() || !spec.IsForSystem(library) {
continue continue
} }
filePrintf(filep, "\tcase %s:\n", spec.AsErrnoName()) filePrintf(filep, "\tcase %s.%s:\n", library, spec.AsErrnoName(system))
filePrintf(filep, "\t\treturn %s\n", spec.AsFailureVar()) filePrintf(filep, "\t\treturn %s\n", spec.AsFailureVar())
} }
fileWrite(filep, "\t}\n") fileWrite(filep, "\t}\n")
@ -230,9 +263,56 @@ func writeGenericFile() {
gofmt(filename) gofmt(filename)
} }
func writeGenericTestFile() { func writeGenericFile() {
filename := "errno_test.go" filename := "errno.go"
filep := fileCreate(filename) filep := fileCreate(filename)
fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n")
filePrintf(filep, "// Generated: %+v\n\n", time.Now())
fileWrite(filep, "package netxlite\n\n")
fileWrite(filep, "//go:generate go run ./internal/generrno/\n\n")
fileWrite(filep, "// This enumeration lists the failures defined at\n")
fileWrite(filep, "// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md\n")
fileWrite(filep, "const (\n")
names := make(map[string]string)
for _, spec := range Specs {
names[spec.AsFailureVar()] = spec.AsFailureString()
}
var nameskeys []string
for key := range names {
nameskeys = append(nameskeys, key)
}
sort.Strings(nameskeys)
for _, key := range nameskeys {
filePrintf(filep, "\t%s = \"%s\"\n", key, names[key])
}
fileWrite(filep, ")\n\n")
fileWrite(filep, "// failureMap lists all failures so we can match them\n")
fileWrite(filep, "// when they are wrapped by quic.TransportError.\n")
fileWrite(filep, "var failuresMap = map[string]string{\n")
failures := make(map[string]string)
for _, spec := range Specs {
failures[spec.AsFailureString()] = spec.AsFailureString()
}
var failureskey []string
for key := range failures {
failureskey = append(failureskey, key)
}
sort.Strings(failureskey)
for _, key := range failureskey {
filePrintf(filep, "\t\"%s\": \"%s\",\n", key, failures[key])
}
fileWrite(filep, "}\n\n")
fileClose(filep)
gofmt(filename)
}
func writeSystemSpecificTestFile(system string) {
filename := fmt.Sprintf("errno_%s_test.go", system)
filep := fileCreate(filename)
library := mapSystemToLibrary(system)
fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n") fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n")
filePrintf(filep, "// Generated: %+v\n\n", time.Now()) filePrintf(filep, "// Generated: %+v\n\n", time.Now())
@ -241,6 +321,8 @@ func writeGenericTestFile() {
fileWrite(filep, "\t\"io\"\n") fileWrite(filep, "\t\"io\"\n")
fileWrite(filep, "\t\"syscall\"\n") fileWrite(filep, "\t\"syscall\"\n")
fileWrite(filep, "\t\"testing\"\n") fileWrite(filep, "\t\"testing\"\n")
fileWrite(filep, "\n")
filePrintf(filep, "\t\"golang.org/x/sys/%s\"\n", library)
fileWrite(filep, ")\n\n") fileWrite(filep, ")\n\n")
fileWrite(filep, "func TestClassifySyscallError(t *testing.T) {\n") fileWrite(filep, "func TestClassifySyscallError(t *testing.T) {\n")
@ -251,13 +333,13 @@ func writeGenericTestFile() {
fileWrite(filep, "\t})\n\n") fileWrite(filep, "\t})\n\n")
for _, spec := range Specs { for _, spec := range Specs {
if !spec.IsSystemError() { if !spec.IsSystemError() || !spec.IsForSystem(library) {
continue continue
} }
filePrintf(filep, "\tt.Run(\"for %s\", func (t *testing.T) {\n", filePrintf(filep, "\tt.Run(\"for %s\", func (t *testing.T) {\n",
spec.AsErrnoName()) spec.AsErrnoName(system))
filePrintf(filep, "\t\tif v := classifySyscallError(%s); v != %s {\n", filePrintf(filep, "\t\tif v := classifySyscallError(%s.%s); v != %s {\n",
spec.AsErrnoName(), spec.AsFailureVar()) library, spec.AsErrnoName(system), spec.AsFailureVar())
filePrintf(filep, "\t\t\tt.Fatalf(\"expected '%%s', got '%%s'\", %s, v)\n", filePrintf(filep, "\t\t\tt.Fatalf(\"expected '%%s', got '%%s'\", %s, v)\n",
spec.AsFailureVar()) spec.AsFailureVar())
fileWrite(filep, "\t\t}\n") fileWrite(filep, "\t\t}\n")
@ -275,13 +357,20 @@ func writeGenericTestFile() {
gofmt(filename) gofmt(filename)
} }
func main() { // SupportedSystems contains the list of supported systems.
writeSystemSpecificFile("android", "unix", "") var SupportedSystems = []string{
writeSystemSpecificFile("darwin", "unix", "") "android",
writeSystemSpecificFile("freebsd", "unix", "") "darwin",
writeSystemSpecificFile("ios", "unix", "") "freebsd",
writeSystemSpecificFile("linux", "unix", "") "ios",
writeSystemSpecificFile("windows", "windows", "WSA") "linux",
writeGenericFile() "windows",
writeGenericTestFile() }
func main() {
for _, system := range SupportedSystems {
writeSystemSpecificFile(system)
writeSystemSpecificTestFile(system)
}
writeGenericFile()
} }