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 }