• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024 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 <netdb.h>
17 #include <stdlib.h>
18 #include <sys/socket.h>
19 #include <pthread.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <stdint.h>
25 #include <poll.h>
26 #include <time.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <pthread.h>
30 #include "syscall.h"
31 #include "functionalext.h"
32 
33 struct address {
34 	int family;
35 	unsigned scopeid;
36 	uint8_t addr[16];
37 	int sortkey;
38 };
39 
40 #define MAXNS 3
41 #define TCP_FASTOPEN_CONNECT 30
42 
43 struct resolvconf {
44 	struct address ns[MAXNS];
45 	unsigned nns, attempts, ndots;
46 	unsigned timeout;
47 };
48 
cleanup(void * p)49 static void cleanup(void *p)
50 {
51 	struct pollfd *pfd = p;
52 	for (int i=0; pfd[i].fd >= -1; i++)
53 		if (pfd[i].fd >= 0) close(pfd[i].fd);
54 }
55 
mtime()56 static unsigned long mtime()
57 {
58 	struct timespec ts;
59 	if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0 && errno == ENOSYS)
60 		clock_gettime(CLOCK_REALTIME, &ts);
61 	return (unsigned long)ts.tv_sec * 1000
62 		+ ts.tv_nsec / 1000000;
63 }
64 
start_tcp(struct pollfd * pfd,int family,const void * sa,socklen_t sl,const unsigned char * q,int ql,int netid)65 static int start_tcp(struct pollfd *pfd, int family, const void *sa,
66 	socklen_t sl, const unsigned char *q, int ql, int netid)
67 {
68 	struct msghdr mh = {
69 		.msg_name = (void *)sa,
70 		.msg_namelen = sl,
71 		.msg_iovlen = 2,
72 		.msg_iov = (struct iovec [2]){
73 			{ .iov_base = (uint8_t[]){ ql>>8, ql }, .iov_len = 2 },
74 			{ .iov_base = (void *)q, .iov_len = ql } }
75 	};
76 	int r;
77 	int fd = socket(family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
78 	pfd->fd = fd;
79 	pfd->events = POLLOUT;
80 	if (!setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
81 	    &(int){1}, sizeof(int))) {
82 		r = sendmsg(fd, &mh, MSG_FASTOPEN|MSG_NOSIGNAL);
83 		if (r == ql+2) pfd->events = POLLIN;
84 		if (r >= 0) return r;
85 		if (errno == EINPROGRESS) return 0;
86 	}
87 	r = connect(fd, sa, sl);
88 	if (!r || errno == EINPROGRESS) return 0;
89 	close(fd);
90 	pfd->fd = -1;
91 	return -1;
92 }
93 
step_mh(struct msghdr * mh,size_t n)94 static void step_mh(struct msghdr *mh, size_t n)
95 {
96 	/* Adjust iovec in msghdr to skip first n bytes. */
97 	while (mh->msg_iovlen && n >= mh->msg_iov->iov_len) {
98 		n -= mh->msg_iov->iov_len;
99 		mh->msg_iov++;
100 		mh->msg_iovlen--;
101 	}
102 	if (!mh->msg_iovlen) return;
103 	mh->msg_iov->iov_base = (char *)mh->msg_iov->iov_base + n;
104 	mh->msg_iov->iov_len -= n;
105 }
106 
res_msend_rc_ext(int netid,int nqueries,const unsigned char * const * queries,const int * qlens,unsigned char * const * answers,int * alens,int asize,const struct resolvconf * conf,int newcheck)107 int res_msend_rc_ext(int netid, int nqueries, const unsigned char *const *queries,
108 	const int *qlens, unsigned char *const *answers, int *alens, int asize,
109 	const struct resolvconf *conf, int newcheck)
110 {
111 	int fd;
112 	int timeout, attempts, retry_interval, servfail_retry;
113 	union {
114 		struct sockaddr_in sin;
115 		struct sockaddr_in6 sin6;
116 	} sa = {0}, ns[MAXNS] = {{0}};
117 	socklen_t sl = sizeof sa.sin;
118 	int nns = 0;
119 	int family = AF_INET;
120 	int rlen;
121 	int next;
122 	int i, j;
123 	int cs;
124 	struct pollfd pfd[nqueries+2];
125 	int qpos[nqueries], apos[nqueries];
126 	unsigned char alen_buf[nqueries][2];
127 	int r;
128 	unsigned long t0, t1, t2;
129 
130 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
131 
132 	timeout = 1000*conf->timeout;
133 	attempts = conf->attempts;
134 
135 	for (nns=0; nns<conf->nns; nns++) {
136 		const struct address *iplit = &conf->ns[nns];
137 		if (iplit->family == AF_INET) {
138 			memcpy(&ns[nns].sin.sin_addr, iplit->addr, 4);
139 			ns[nns].sin.sin_port = htons(53);
140 			ns[nns].sin.sin_family = AF_INET;
141 		} else {
142 			sl = sizeof sa.sin6;
143 			memcpy(&ns[nns].sin6.sin6_addr, iplit->addr, 16);
144 			ns[nns].sin6.sin6_port = htons(40000);
145 			ns[nns].sin6.sin6_scope_id = iplit->scopeid;
146 			ns[nns].sin6.sin6_family = family = AF_INET6;
147 		}
148 	}
149 
150 	/* Get local address and open/bind a socket */
151 	fd = socket(family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
152 
153 	/* Handle case where system lacks IPv6 support */
154 	if (fd < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) {
155 		for (i=0; i<nns && conf->ns[nns].family == AF_INET6; i++);
156 		if (i==nns) {
157 			pthread_setcancelstate(cs, 0);
158 			return -1;
159 		}
160 		fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
161 		family = AF_INET;
162 		sl = sizeof sa.sin;
163 	}
164 
165 	/* Convert any IPv4 addresses in a mixed environment to v4-mapped */
166 	if (fd >= 0 && family == AF_INET6) {
167 		setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof(int));
168 		for (i=0; i<nns; i++) {
169 			if (ns[i].sin.sin_family != AF_INET) continue;
170 			memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
171 				&ns[i].sin.sin_addr, 4);
172 			memcpy(ns[i].sin6.sin6_addr.s6_addr,
173 				"\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
174 			ns[i].sin6.sin6_family = AF_INET6;
175 			ns[i].sin6.sin6_flowinfo = 0;
176 			ns[i].sin6.sin6_scope_id = 0;
177 		}
178 	}
179 
180 	sa.sin.sin_family = family;
181 	if (fd < 0 || bind(fd, (void *)&sa, sl) < 0) {
182 		if (fd >= 0) close(fd);
183 		pthread_setcancelstate(cs, 0);
184 		return -1;
185 	}
186 
187 	/* Past this point, there are no errors. Each individual query will
188 	 * yield either no reply (indicated by zero length) or an answer
189 	 * packet which is up to the caller to interpret. */
190 
191 	for (i=0; i<nqueries; i++) pfd[i].fd = -1;
192 	pfd[nqueries].fd = fd;
193 	pfd[nqueries].events = POLLIN;
194 	pfd[nqueries+1].fd = -2;
195 
196 	pthread_cleanup_push(cleanup, pfd);
197 	pthread_setcancelstate(cs, 0);
198 
199 	memset(alens, 0, sizeof *alens * nqueries);
200 
201 	retry_interval = timeout / attempts;
202 	next = 0;
203 	t0 = t2 = mtime();
204 	t1 = t2 - retry_interval;
205 
206 	for (; t2-t0 < timeout; t2=mtime()) {
207 		/* This is the loop exit condition: that all queries
208 		 * have an accepted answer. */
209 		for (i=0; i<nqueries && alens[i]>0; i++);
210 		if (i==nqueries) break;
211 
212 		if (t2-t1 >= retry_interval) {
213 			/* Query all configured namservers in parallel */
214 			for (i=0; i<nqueries; i++) {
215 				if (!alens[i]) {
216 					for (j=0; j<nns; j++) {
217 						if (sendto(fd, queries[i], qlens[i], MSG_NOSIGNAL, (void *)&ns[j], sl) == -1) {
218 						}
219 					}
220 				}
221 			}
222 			t1 = t2;
223 			servfail_retry = 2 * nqueries;
224 		}
225 
226 		/* Wait for a response, or until time to retry */
227 		if (poll(pfd, nqueries+1, t1+retry_interval-t2) <= 0) continue;
228 
229 		while (next < nqueries) {
230 			struct msghdr mh = {
231 				.msg_name = (void *)&sa,
232 				.msg_namelen = sl,
233 				.msg_iovlen = 1,
234 				.msg_iov = (struct iovec []){
235 					{ .iov_base = (void *)answers[next],
236 					  .iov_len = asize }
237 				}
238 			};
239 			rlen = recvmsg(fd, &mh, 0);
240 			if (rlen < 0) {
241 				break;
242 			}
243 
244 			/* Ignore non-identifiable packets */
245 			if (rlen < 4) continue;
246 
247 			/* Ignore replies from addresses we didn't send to */
248             if (newcheck) {
249                 switch (sa.sin.sin_family) {
250                     case AF_INET:
251                         for (j = 0; j < nns; j++) {
252                             if (ns[j].sin.sin_family == AF_INET && ns[j].sin.sin_port == sa.sin.sin_port &&
253                             (ns[j].sin.sin_addr.s_addr == INADDR_ANY ||
254                             ns[j].sin.sin_addr.s_addr == sa.sin.sin_addr.s_addr)) {
255                                 break;
256                             }
257                         }
258                         break;
259                     case AF_INET6:
260                         for (j = 0; j < nns; j++) {
261                             if (ns[j].sin6.sin6_family == AF_INET6 &&
262                             ns[j].sin6.sin6_port == sa.sin6.sin6_port &&
263                             (ns[j].sin6.sin6_scope_id == 0 || ns[j].sin6.sin6_scope_id == sa.sin6.sin6_scope_id) &&
264                             (IN6_IS_ADDR_UNSPECIFIED(&ns[j].sin6.sin6_addr) ||
265                             IN6_ARE_ADDR_EQUAL(&ns[j].sin6.sin6_addr, &sa.sin6.sin6_addr))) {
266                                 break;
267                             }
268                         }
269                         break;
270                     default:
271                         j = nns;
272                         break;
273                 }
274             } else {
275                 for (j=0; j<nns && memcmp(ns+j, &sa, sl); j++);
276             }
277 			if (j==nns) {
278 				memset(answers[next], 0, asize);
279 				continue;
280 			}
281 
282 			/* Find which query this answer goes with, if any */
283 			for (i=next; i<nqueries && (
284 				answers[next][0] != queries[i][0] ||
285 				answers[next][1] != queries[i][1] ); i++);
286 			if (i==nqueries) continue;
287 			if (alens[i]) continue;
288 
289 			/* Only accept positive or negative responses;
290 			 * retry immediately on server failure, and ignore
291 			 * all other codes such as refusal. */
292 			switch (answers[next][3] & 15) {
293 				case 0:
294 				case 3:
295 					break;
296 				case 2:
297 					if (servfail_retry && servfail_retry--)
298 						sendto(fd, queries[i],
299 							qlens[i], MSG_NOSIGNAL,
300 							(void *)&ns[j], sl);
301 				default:
302 					continue;
303 			}
304 
305 			/* Store answer in the right slot, or update next
306 			 * available temp slot if it's already in place. */
307 			alens[i] = rlen;
308 			if (i == next)
309 				for (; next<nqueries && alens[next]; next++);
310 			else
311 				memcpy(answers[i], answers[next], rlen);
312 
313 			/* Ignore further UDP if all slots full or TCP-mode */
314 			if (next == nqueries) pfd[nqueries].events = 0;
315 
316 			/* If answer is truncated (TC bit), fallback to TCP */
317 			if ((answers[i][2] & 2) || (mh.msg_flags & MSG_TRUNC)) {
318 				alens[i] = -1;
319 				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
320 				r = start_tcp(pfd+i, family, ns+j, sl, queries[i], qlens[i], netid);
321 				pthread_setcancelstate(cs, 0);
322 				if (r >= 0) {
323 					qpos[i] = r;
324 					apos[i] = 0;
325 				}
326 				continue;
327 			}
328 		}
329 
330 		for (i=0; i<nqueries; i++) if (pfd[i].revents & POLLOUT) {
331 			struct msghdr mh = {
332 				.msg_iovlen = 2,
333 				.msg_iov = (struct iovec [2]){
334 					{ .iov_base = (uint8_t[]){ qlens[i]>>8, qlens[i] }, .iov_len = 2 },
335 					{ .iov_base = (void *)queries[i], .iov_len = qlens[i] } }
336 			};
337 			step_mh(&mh, qpos[i]);
338 			r = sendmsg(pfd[i].fd, &mh, MSG_NOSIGNAL);
339 			if (r < 0) goto out;
340 			qpos[i] += r;
341 			if (qpos[i] == qlens[i]+2)
342 				pfd[i].events = POLLIN;
343 		}
344 
345 		for (i=0; i<nqueries; i++) if (pfd[i].revents & POLLIN) {
346 			struct msghdr mh = {
347 				.msg_iovlen = 2,
348 				.msg_iov = (struct iovec [2]){
349 					{ .iov_base = alen_buf[i], .iov_len = 2 },
350 					{ .iov_base = answers[i], .iov_len = asize } }
351 			};
352 			step_mh(&mh, apos[i]);
353 			r = recvmsg(pfd[i].fd, &mh, 0);
354 			if (r <= 0) goto out;
355 			apos[i] += r;
356 			if (apos[i] < 2) continue;
357 			int alen = alen_buf[i][0]*256 + alen_buf[i][1];
358 			if (alen < 13) goto out;
359 			if (apos[i] < alen+2 && apos[i] < asize+2)
360 				continue;
361 			int rcode = answers[i][3] & 15;
362 			if (rcode != 0 && rcode != 3)
363 				goto out;
364 
365 			/* Storing the length here commits the accepted answer.
366 			 * Immediately close TCP socket so as not to consume
367 			 * resources we no longer need. */
368 			alens[i] = alen;
369 			close(pfd[i].fd);
370 			pfd[i].fd = -1;
371 		}
372 	}
373 out:
374 	pthread_cleanup_pop(1);
375 
376 	/* Disregard any incomplete TCP results */
377 	for (i=0; i<nqueries; i++) if (alens[i]<0) alens[i] = 0;
378 
379 	return 0;
380 }
381 
382 /**
383  * 该用例未在编译构建文件内开启,需手动开启,添加到test_src_functionalext_supplement_network.gni内
384  * 该用例需要两台测试设备,两台设备连接同一热点网络,本文件产物作为client端
385  * client端设备同时需执行ip addr flush dev p2p0, 关闭p2p0的ipv6网络,否则当前模拟场景因为p2p0存在,
386  * 无法在scopeid为0的情况下,正常发送给server端(内核将默认的0优先匹配给了p2p0)
387  */
388 
main(void)389 int main(void)
390 {
391     unsigned char qbuf[2][280], abuf[2][4800];
392 	const unsigned char *qp[2] = { qbuf[0], qbuf[1] };
393 	unsigned char *ap[2] = { abuf[0], abuf[1] };
394 	int qlens[2], alens[2];
395     int nqueries = 1;
396 	// timeout: 查询超时时间,attempts: 失败重试次数, nns: nameserver数量
397     struct resolvconf conf = {
398         .nns = 1,
399         .attempts = 2,
400         .ndots = 1,
401         .timeout = 5};
402     conf.ns[0].family = AF_INET6;
403 	// scopeid设置为0,在本地网络下(fe80::***), 内核会查询对应网卡的ifindex并返回
404 	// 当前使用wlan0,在client端设置scopeid为0的情况下,内核返回的wlan0的ifindex为41或42(设备差异)
405     conf.ns[0].scopeid = 0;
406 	// server设备wlan0网卡的ipv6地址,因设备与网络不同而不同,需要测试人员替换
407 	// wlan0网卡的ipv6地址查询方式: ifconfig -a
408     char* dst = "fe80::****:****:****:****";
409     if ((inet_pton(AF_INET6, dst, conf.ns[0].addr) != 1)) {
410         t_error("%s invalid dns convert for ipv6\n", __func__);
411         return t_status;
412     }
413 	// client端发送query内容,非正式的dns请求格式
414     char buffer[] = "test.check.platform.com";
415 	// server端预期answer回复
416     unsigned char target[4800] = "test.check.platform.com A 111.111.11.11";
417     strcpy((char*)&qbuf[0], buffer);
418     qlens[0] = sizeof(buffer);
419 	// 使用新的nameserver check逻辑,放宽scopeid严格相等的校验,校验通过
420     if (res_msend_rc_ext(0, nqueries, qp, qlens, ap, alens, sizeof *abuf, &conf, 1) < 0) {
421         t_error("%s invalid dns query\n", __func__);
422     }
423 	if (strcmp((char*)&abuf[0], (char*)&target)) {
424 		t_error("%s invalid answer %s, target: %s\n", __func__, abuf[0], target);
425 	}
426 	alens[0] = 0;
427 	memset(abuf[0], 0, sizeof *abuf);
428 	// 使用原本的nameserver check逻辑,scopeid校验失败
429     if (res_msend_rc_ext(0, nqueries, qp, qlens, ap, alens, sizeof *abuf, &conf, 0) < 0) {
430         t_error("%s invalid dns query\n", __func__);
431     }
432 	if (!strcmp((char*)&abuf[0], (char*)&target)) {
433 		t_error("%s invalid answer %s, target: %s\n", __func__, abuf[0], target);
434 	}
435 
436     return t_status;
437 }