forked from ooni/probe-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
getaddrinfo_linux.go
161 lines (151 loc) · 6.27 KB
/
getaddrinfo_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo && linux
package netxlite
/*
// Both glibc and musl expose the EAI_NODATA error if we
// ask them to expose it through this define. See below for
// more details on how each of the supported libcs hides
// (or does not hide) the EAI_NODATA define.
#cgo CFLAGS: -D_GNU_SOURCE
#include <netdb.h>
*/
import "C"
import (
"runtime"
"syscall"
)
// Implementation note: the original Go codebase separated linux and android
// but we want them to be in the same file, so we can implement tests for both
// operating system and increase our confidence that the behavior will be the
// one we'd like to see on Android systems.
var getaddrinfoAIFlags = getaddrinfoGetPlatformSpecificAIFlags(runtime.GOOS)
// This function returns the platforms-specific AI flags that go1.17.6
// used to set when we merged resolver's code into ooni/probe-cli
//
// SPDX-License-Identifier: BSD-3-Clause.
func getaddrinfoGetPlatformSpecificAIFlags(goos string) C.int {
switch goos {
case "android":
return C.AI_CANONNAME
default:
// NOTE(rsc): In theory there are approximately balanced
// arguments for and against including AI_ADDRCONFIG
// in the flags (it includes IPv4 results only on IPv4 systems,
// and similarly for IPv6), but in practice setting it causes
// getaddrinfo to return the wrong canonical name on Linux.
// So definitely leave it out.
return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
}
}
// Making constants available to Go code so we can run tests (it seems
// it's not possible to import C directly in tests, sadly).
const (
aiCanonname = C.AI_CANONNAME
aiV4Mapped = C.AI_V4MAPPED
aiAll = C.AI_ALL
eaiSystem = C.EAI_SYSTEM
eaiNoName = C.EAI_NONAME
eaiBadFlags = C.EAI_BADFLAGS
eaiNoData = C.EAI_NODATA
)
// toError is the function that converts the return value from
// the getaddrinfo function into a proper Go error.
//
// This function is adapted from cgoLookupIPCNAME
// https://github.com/golang/go/blob/go1.17.6/src/net/cgo_unix.go#L145
//
// SPDX-License-Identifier: BSD-3-Clause.
func (state *getaddrinfoState) toError(code int64, err error, goos string) error {
switch code {
case C.EAI_SYSTEM:
if err == nil {
// err should not be nil, but sometimes getaddrinfo returns
// code == C.EAI_SYSTEM with err == nil on Linux.
// The report claims that it happens when we have too many
// open files, so use syscall.EMFILE (too many open files in system).
// Most system calls would return ENFILE (too many open files),
// so at the least EMFILE should be easy to recognize if this
// comes up again. golang.org/issue/6232.
err = syscall.EMFILE
}
return newErrGetaddrinfo(code, err)
case C.EAI_NONAME:
return newErrGetaddrinfo(code, ErrOODNSNoSuchHost)
case C.EAI_NODATA:
return state.toErrorNODATA(err, goos)
default:
return newErrGetaddrinfo(code, ErrOODNSMisbehaving)
}
}
// toErrorNODATA maps the EAI_NODATA value to the proper return value
// depending on the underlying operating system.
//
// As of 2022-05-28, this is the status of the major C libraries whose
// getaddrinfo return value we may end up processing here:
//
// 1. musl libc (statically linked Linux builds for official OONI
// Probe packages we build): EAI_NODATA is defined in netdb.h in a
// section guarded by _GNU_SOURCE and _BSD_SOURCE and the code
// does not otherwise ever use this definition.
//
// 2. GNU libc (which is what you would get if you compile OONI Probe
// for yourself in a GNU/Linux system): the codebase defines EAI_NODATA
// inside netdb.h protected by __USE_GNU, which is defined to 1 in
// include/features.h if the user defines _GNU_SOURCE. Additionally,
// the getaddrinfo implementation returns EAI_NODATA when a name
// exists but there's no associated address for such a name. There
// was a bug, fixed in glibc 2.27, were EAI_NONAME was returned
// when EAI_NODATA would actually have been more proper:
//
// https://sourceware.org/bugzilla/show_bug.cgi?id=21922
//
// 3. Android libc: EAI_NODATA is defined in netdb.h and is not
// protected by any feature flag. The getaddrinfo function (as
// of 4ebdeebef74) calls android_getaddrinfofornet, which in turns
// calls android_getaddrinfofornetcontext. This function will
// eventually call android_getaddrinfo_proxy. If this function
// returns any status code different from EAI_SYSTEM, then bionic
// will return its return value. Otherwise, the code ends up
// calling explore_fqdn, which in turn calls nsdispatch, which
// is what NetBSD is still doing today.
//
// So, android_getaddrinfo_proxy was introduced a long time
// ago on October 28, 2010 by this commit:
//
// https://github.com/aosp-mirror/platform_bionic/commit/a1dbf0b453801620565e5911f354f82706b0200d
//
// Then a subsequent commit changed android_getaddrinfo_proxy
// to basically default to EAI_NODATA on proxy errors:
//
// https://github.com/aosp-mirror/platform_bionic/commit/c63e59039d28c352e3053bb81319e960c392dbd4
//
// As of today and 4ebdeebef74, android_getaddrinfo_proxy returns
// one of the following possible return codes:
//
// a) 0 on success;
//
// b) EAI_SYSTEM if it cannot speak to the proxy (which causes the code
// to fall through to the original NetBSD implementation);
//
// c) EAI_NODATA in all the other cases.
//
// The above discussion about Android provides us with a theory that explains the
// https://github.com/ooni/probe/issues/2029 issue. That said, we are still missing
// some bits, e.g., why some Android 6 phones did not experience this problem.
//
// We originally proposed to handle the EAI_NODATA error on Android like it was a
// EAI_NONAME error. However, this mapping seems very inaccurate. Any error inside
// the DNS proxy could cause EAI_NODATA (_unless_ we're "lucky" for some reason
// and the original NetBSD code runs). Therefore, the sanest choice is to introduce
// a new OONI error describing this error condition `android_dns_cache_no_data`
// and handle this error as a special case when checking for NXDOMAIN.
func (state *getaddrinfoState) toErrorNODATA(err error, goos string) error {
switch goos {
case "android":
return newErrGetaddrinfo(C.EAI_NODATA, ErrAndroidDNSCacheNoData)
default:
return newErrGetaddrinfo(C.EAI_NODATA, ErrOODNSNoAnswer)
}
}