1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "lookup.h"
17 #include "stdio_impl.h"
18 #include <ctype.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <netinet/in.h>
23
24 #include "hilog_adapter.h"
25
26 #if 1
27 #define SIGCHAIN_LOG_DOMAIN 0xD003F00
28 #define SIGCHAIN_LOG_TAG "ressolvconf"
29 #define GETADDRINFO_PRINT_DEBUG(...) ((void)HiLogAdapterPrint(LOG_CORE, LOG_INFO, \
30 SIGCHAIN_LOG_DOMAIN, SIGCHAIN_LOG_TAG, __VA_ARGS__))
31 #else
32 #define GETADDRINFO_PRINT_DEBUG(...)
33 #endif
34
35 #define DNS_RESOLV_CONF_PATH "/etc/resolv.conf"
36
37 #if OHOS_DNS_PROXY_BY_NETSYS | OHOS_FWMARK_CLIENT_BY_NETSYS
38 #include "atomic.h"
39
40 #include <dlfcn.h>
41
open_dns_lib(void)42 static void *open_dns_lib(void)
43 {
44 static void *dns_lib_handle = NULL;
45 if (dns_lib_handle != NULL) {
46 a_barrier();
47 return dns_lib_handle;
48 }
49
50 void *lib = dlopen(DNS_SO_PATH, RTLD_LAZY);
51 if (lib == NULL) {
52 DNS_CONFIG_PRINT("%s: dlopen %s failed: %s",
53 __func__, DNS_SO_PATH, dlerror());
54 return NULL;
55 }
56
57 void *old_lib = a_cas_p(&dns_lib_handle, NULL, lib);
58 if (old_lib == NULL) {
59 DNS_CONFIG_PRINT("%s: %s loaded", __func__, DNS_SO_PATH);
60 return lib;
61 } else {
62 /* Another thread has already loaded the library,
63 * dlclose is invoked to make refcount correct */
64 DNS_CONFIG_PRINT("%s: %s has been loaded by another thread",
65 __func__, DNS_SO_PATH);
66 if (dlclose(lib)) {
67 DNS_CONFIG_PRINT("%s: dlclose %s failed: %s",
68 __func__, DNS_SO_PATH, dlerror());
69 }
70 return old_lib;
71 }
72 }
73
load_from_dns_lib(const char * symbol)74 static void *load_from_dns_lib(const char *symbol)
75 {
76 void *lib_handle = open_dns_lib();
77 if (lib_handle == NULL) {
78 return NULL;
79 }
80
81 void *sym_addr = dlsym(lib_handle, symbol);
82 if (sym_addr == NULL) {
83 DNS_CONFIG_PRINT("%s: loading symbol %s with dlsym failed: %s",
84 __func__, symbol, dlerror());
85 }
86 return sym_addr;
87 }
88
resolve_dns_sym(void ** holder,const char * symbol)89 void resolve_dns_sym(void **holder, const char *symbol)
90 {
91 if (*holder != NULL) {
92 a_barrier();
93 return;
94 }
95
96 void *ptr = load_from_dns_lib(symbol);
97 if (ptr == NULL) {
98 return;
99 }
100
101 void *old_ptr = a_cas_p(holder, NULL, ptr);
102 if (old_ptr != NULL) {
103 DNS_CONFIG_PRINT("%s: %s has been found by another thread",
104 __func__, symbol);
105 } else {
106 DNS_CONFIG_PRINT("%s: %s found", __func__, symbol);
107 }
108 }
109
load_config_getter(void)110 static GetConfig load_config_getter(void)
111 {
112 static GetConfig config_getter = NULL;
113 resolve_dns_sym((void **) &config_getter, OHOS_GET_CONFIG_FUNC_NAME);
114 return config_getter;
115 }
116
117 #endif
118
__get_resolv_conf(struct resolvconf * conf,char * search,size_t search_sz)119 int __get_resolv_conf(struct resolvconf *conf, char *search, size_t search_sz)
120 {
121 return get_resolv_conf_ext(conf, search, search_sz, 0);
122 }
123
get_resolv_conf_ext(struct resolvconf * conf,char * search,size_t search_sz,int netid)124 int get_resolv_conf_ext(struct resolvconf *conf, char *search, size_t search_sz, int netid)
125 {
126 char line[256];
127 unsigned char _buf[256];
128 FILE *f, _f;
129 int nns = 0;
130
131 conf->ndots = 1;
132 conf->timeout = 5;
133 conf->attempts = 2;
134 if (search) *search = 0;
135
136 #if OHOS_DNS_PROXY_BY_NETSYS
137 GetConfig func = load_config_getter();
138 if (!func) {
139 DNS_CONFIG_PRINT("%s: loading %s failed, use %s as a fallback",
140 __func__, OHOS_GET_CONFIG_FUNC_NAME, DNS_RESOLV_CONF_PATH);
141 goto etc_resolv_conf;
142 }
143
144 struct resolv_config config = {0};
145 int ret = func(netid, &config);
146 if (ret < 0) {
147 DNS_CONFIG_PRINT("__get_resolv_conf OHOS_GET_CONFIG_FUNC_NAME err %d\n", ret);
148 return EAI_NONAME;
149 }
150 int32_t timeout_second = config.timeout_ms / 1000;
151
152 netsys_conf:
153 if (timeout_second > 0) {
154 if (timeout_second >= 60) {
155 conf->timeout = 60;
156 } else {
157 conf->timeout = timeout_second;
158 }
159 }
160 if (config.retry_count > 0) {
161 if (config.retry_count >= 10) {
162 conf->attempts = 10;
163 } else {
164 conf->attempts = config.retry_count;
165 }
166 }
167 for (int i = 0; i < MAX_SERVER_NUM; ++i) {
168 if (config.nameservers[i] == NULL || config.nameservers[i][0] == 0 || nns >= MAXNS) {
169 continue;
170 }
171 if (__lookup_ipliteral(conf->ns + nns, config.nameservers[i], AF_UNSPEC) > 0) {
172 nns++;
173 }
174 }
175
176 if (nns != 0) {
177 goto get_conf_ok;
178 }
179
180 etc_resolv_conf:
181 #endif
182 f = __fopen_rb_ca(DNS_RESOLV_CONF_PATH, &_f, _buf, sizeof _buf);
183 if (!f) switch (errno) {
184 case ENOENT:
185 case ENOTDIR:
186 case EACCES:
187 goto no_resolv_conf;
188 default:
189 return -1;
190 }
191
192 while (fgets(line, sizeof line, f)) {
193 char *p, *z;
194 if (!strchr(line, '\n') && !feof(f)) {
195 /* Ignore lines that get truncated rather than
196 * potentially misinterpreting them. */
197 int c;
198 do c = getc(f);
199 while (c != '\n' && c != EOF);
200 continue;
201 }
202 if (!strncmp(line, "options", 7) && isspace(line[7])) {
203 p = strstr(line, "ndots:");
204 if (p && isdigit(p[6])) {
205 p += 6;
206 unsigned long x = strtoul(p, &z, 10);
207 if (z != p) conf->ndots = x > 15 ? 15 : x;
208 }
209 p = strstr(line, "attempts:");
210 if (p && isdigit(p[9])) {
211 p += 9;
212 unsigned long x = strtoul(p, &z, 10);
213 if (z != p) conf->attempts = x > 10 ? 10 : x;
214 }
215 p = strstr(line, "timeout:");
216 if (p && (isdigit(p[8]) || p[8]=='.')) {
217 p += 8;
218 unsigned long x = strtoul(p, &z, 10);
219 if (z != p) conf->timeout = x > 60 ? 60 : x;
220 }
221 continue;
222 }
223 if (!strncmp(line, "nameserver", 10) && isspace(line[10])) {
224 if (nns >= MAXNS) continue;
225 for (p=line+11; isspace(*p); p++);
226 for (z=p; *z && !isspace(*z); z++);
227 *z=0;
228 if (__lookup_ipliteral(conf->ns+nns, p, AF_UNSPEC) > 0)
229 nns++;
230 continue;
231 }
232
233 if (!search) continue;
234 if ((strncmp(line, "domain", 6) && strncmp(line, "search", 6))
235 || !isspace(line[6]))
236 continue;
237 for (p=line+7; isspace(*p); p++);
238 size_t l = strlen(p);
239 /* This can never happen anyway with chosen buffer sizes. */
240 if (l >= search_sz) continue;
241 memcpy(search, p, l+1);
242 }
243
244 __fclose_ca(f);
245
246 no_resolv_conf:
247 if (!nns) {
248 __lookup_ipliteral(conf->ns, "127.0.0.1", AF_UNSPEC);
249 nns = 1;
250 }
251
252 get_conf_ok:
253 conf->nns = nns;
254
255 return 0;
256 }
257
res_bind_socket(int fd,int netid)258 int res_bind_socket(int fd, int netid)
259 {
260 int ret = -1;
261 void* libhandler;
262
263 GETADDRINFO_PRINT_DEBUG("res_bind_socket netid:%{public}d \n", netid);
264
265 #ifdef OHOS_FWMARK_CLIENT_BY_NETSYS
266 libhandler = dlopen(FWMARKCLIENT_SO_PATH, RTLD_LAZY);
267 if (libhandler == NULL) {
268 GETADDRINFO_PRINT_DEBUG("dns_get_addr_info_from_netsys_cache dlopen err %s\n", dlerror());
269 return -1;
270 }
271
272 BindSocket_Ext func = dlsym(libhandler, OHOS_BIND_SOCKET_FUNC_NAME);
273 if (func == NULL) {
274 GETADDRINFO_PRINT_DEBUG("dns_get_addr_info_from_netsys_cache dlsym err %s\n", dlerror());
275 dlclose(libhandler);
276 return -1;
277 } else {
278 ret = func(fd, netid);
279 GETADDRINFO_PRINT_DEBUG("res_bind_socket ret %{public}d \n", ret);
280 dlclose(libhandler);
281 }
282 #endif
283 return ret;
284 }
285
286