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