This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 74cdbe771b49002db2cdaf67e023cb9db3efb80b Author: Marco Casaroli <[email protected]> AuthorDate: Sat Oct 17 20:25:56 2026 +0200 libs/libc/netdb: Add res_query() and res_search(). The DNS client resolves host addresses only. Programs that need other records, for example MX, SRV or TXT records, use res_query() to get the raw answer from the name server. NuttX did not have res_query(). This commit adds these functions to <resolv.h>: - res_mkquery() builds a query message with one question. - res_send() sends a message over UDP to the name servers of the DNS client and returns the answer. It uses the same name server list, socket timeouts and retries as the DNS client. It skips a server that fails or refuses the query. It does not retry over TCP: a large answer comes back with the TC bit set. - res_query() builds the query, sends it and sets h_errno from the response code. - res_search() is the same as res_query(), because NuttX has no search domain list. res_send(), res_query() and res_search() need CONFIG_NETDB_DNSCLIENT, as the DNS client does. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- include/resolv.h | 18 ++++ libs/libc/netdb/CMakeLists.txt | 3 +- libs/libc/netdb/Make.defs | 4 +- libs/libc/netdb/lib_resmkquery.c | 106 ++++++++++++++++++++++ libs/libc/netdb/lib_resquery.c | 118 ++++++++++++++++++++++++ libs/libc/netdb/lib_ressend.c | 191 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 437 insertions(+), 3 deletions(-) diff --git a/include/resolv.h b/include/resolv.h index 2acbf6271f4..216dfd068de 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -29,6 +29,8 @@ #include <stddef.h> +#include <arpa/nameser.h> + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -52,6 +54,22 @@ int dn_comp(FAR const char *src, FAR unsigned char *dst, int space, int dn_expand(FAR const unsigned char *base, FAR const unsigned char *end, FAR const unsigned char *src, FAR char *dest, int space); +/* DNS queries */ + +int res_mkquery(int op, FAR const char *dname, int rclass, int type, + FAR const unsigned char *data, int datalen, + FAR const unsigned char *newrr, + FAR unsigned char *buf, int buflen); + +int res_send(FAR const unsigned char *msg, int msglen, + FAR unsigned char *answer, int anslen); + +int res_query(FAR const char *dname, int rclass, int type, + FAR unsigned char *answer, int anslen); + +int res_search(FAR const char *dname, int rclass, int type, + FAR unsigned char *answer, int anslen); + #if defined(__cplusplus) } #endif diff --git a/libs/libc/netdb/CMakeLists.txt b/libs/libc/netdb/CMakeLists.txt index cf063f00797..20c2ad208f4 100644 --- a/libs/libc/netdb/CMakeLists.txt +++ b/libs/libc/netdb/CMakeLists.txt @@ -46,6 +46,7 @@ if(CONFIG_LIBC_NETDB) lib_getnameinfo.c lib_rexec.c lib_dn.c + lib_resmkquery.c lib_proto.c lib_protor.c) @@ -62,7 +63,7 @@ if(CONFIG_LIBC_NETDB) list(APPEND SRCS lib_dnsaddserver.c lib_dnsdelserver.c lib_dnsdefaultserver.c) list(APPEND SRCS lib_dnsforeach.c lib_dnsnotify.c) - list(APPEND SRCS lib_dnsqueryfamily.c) + list(APPEND SRCS lib_dnsqueryfamily.c lib_ressend.c lib_resquery.c) if(NOT CONFIG_NETDB_DNSCLIENT_ENTRIES EQUAL 0) list(APPEND SRCS lib_dnscache.c) diff --git a/libs/libc/netdb/Make.defs b/libs/libc/netdb/Make.defs index 95aac44cc73..a335bf52aab 100644 --- a/libs/libc/netdb/Make.defs +++ b/libs/libc/netdb/Make.defs @@ -31,7 +31,7 @@ CSRCS += lib_gethostbyaddr.c lib_gethostbyaddrr.c CSRCS += lib_getservbyname.c lib_getservbynamer.c CSRCS += lib_getservbyport.c lib_getservbyportr.c CSRCS += lib_gaistrerror.c lib_freeaddrinfo.c lib_getaddrinfo.c -CSRCS += lib_getnameinfo.c lib_rexec.c lib_dn.c +CSRCS += lib_getnameinfo.c lib_rexec.c lib_dn.c lib_resmkquery.c CSRCS += lib_proto.c lib_protor.c # Add host file support @@ -46,7 +46,7 @@ ifeq ($(CONFIG_NETDB_DNSCLIENT),y) CSRCS += lib_dnsinit.c lib_dnsbind.c lib_dnsquery.c CSRCS += lib_dnsaddserver.c lib_dnsdelserver.c lib_dnsdefaultserver.c CSRCS += lib_dnsforeach.c lib_dnsnotify.c -CSRCS += lib_dnsqueryfamily.c +CSRCS += lib_dnsqueryfamily.c lib_ressend.c lib_resquery.c ifneq ($(CONFIG_NETDB_DNSCLIENT_ENTRIES),0) CSRCS += lib_dnscache.c diff --git a/libs/libc/netdb/lib_resmkquery.c b/libs/libc/netdb/lib_resmkquery.c new file mode 100644 index 00000000000..3bdb4a3067c --- /dev/null +++ b/libs/libc/netdb/lib_resmkquery.c @@ -0,0 +1,106 @@ +/**************************************************************************** + * libs/libc/netdb/lib_resmkquery.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <resolv.h> + +#include <arpa/nameser.h> + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: res_mkquery + * + * Description: + * Build a DNS query message with one question for the domain name dname + * of the given class and type. The data and newrr arguments are not + * used. + * + * Input Parameters: + * op - The operation code, normally QUERY. + * dname - The domain name to query. + * rclass - The class of the query, for example C_IN. + * type - The type of the query, for example T_MX. + * data - Not used. + * datalen - Not used. + * newrr - Not used. + * buf - The buffer that receives the message. + * buflen - The size of buf. + * + * Returned Value: + * The size of the message on success, or -1 on failure. + * + ****************************************************************************/ + +int res_mkquery(int op, FAR const char *dname, int rclass, int type, + FAR const unsigned char *data, int datalen, + FAR const unsigned char *newrr, + FAR unsigned char *buf, int buflen) +{ + FAR unsigned char *cp; + uint16_t id; + int n; + + UNUSED(data); + UNUSED(datalen); + UNUSED(newrr); + + if (op < 0 || op > 15 || rclass < 0 || rclass > 0xffff || + type < 0 || type > 0xffff || buf == NULL || + buflen < NS_HFIXEDSZ + 1 + NS_QFIXEDSZ) + { + return -1; + } + + /* Encode the domain name after the header */ + + n = dn_comp(dname, buf + NS_HFIXEDSZ, + buflen - NS_HFIXEDSZ - NS_QFIXEDSZ, NULL, NULL); + if (n < 0) + { + return -1; + } + + /* The header: a random ID, recursion desired and one question */ + + memset(buf, 0, NS_HFIXEDSZ); + id = arc4random(); + buf[0] = id >> 8; + buf[1] = id; + buf[2] = (op << 3) | 0x01; + buf[5] = 1; + + /* The type and class of the question */ + + cp = buf + NS_HFIXEDSZ + n; + NS_PUT16(type, cp); + NS_PUT16(rclass, cp); + + return cp - buf; +} diff --git a/libs/libc/netdb/lib_resquery.c b/libs/libc/netdb/lib_resquery.c new file mode 100644 index 00000000000..e8b63ce7595 --- /dev/null +++ b/libs/libc/netdb/lib_resquery.c @@ -0,0 +1,118 @@ +/**************************************************************************** + * libs/libc/netdb/lib_resquery.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <netdb.h> +#include <resolv.h> + +#include <arpa/nameser.h> + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: res_query + * + * Description: + * Query the name servers for records of the given class and type for + * the domain name dname, and copy the raw answer into the buffer + * answer. + * + * Input Parameters: + * dname - The domain name to query. + * rclass - The class of the query, for example C_IN. + * type - The type of the query, for example T_SRV. + * answer - The buffer that receives the answer message. + * anslen - The size of answer. + * + * Returned Value: + * The size of the answer on success. On failure, -1 is returned and + * h_errno is set to HOST_NOT_FOUND, NO_DATA, NO_RECOVERY or TRY_AGAIN. + * + ****************************************************************************/ + +int res_query(FAR const char *dname, int rclass, int type, + FAR unsigned char *answer, int anslen) +{ + unsigned char query[NS_HFIXEDSZ + NS_MAXCDNAME + NS_QFIXEDSZ]; + int ret; + + ret = res_mkquery(QUERY, dname, rclass, type, NULL, 0, NULL, + query, sizeof(query)); + if (ret < 0) + { + h_errno = NO_RECOVERY; + return -1; + } + + ret = res_send(query, ret, answer, anslen); + if (ret < NS_HFIXEDSZ) + { + h_errno = TRY_AGAIN; + return -1; + } + + switch (answer[3] & 0x0f) + { + case ns_r_noerror: + if (answer[6] == 0 && answer[7] == 0) + { + h_errno = NO_DATA; + return -1; + } + + return ret; + + case ns_r_nxdomain: + h_errno = HOST_NOT_FOUND; + break; + + case ns_r_servfail: + h_errno = TRY_AGAIN; + break; + + default: + h_errno = NO_RECOVERY; + break; + } + + return -1; +} + +/**************************************************************************** + * Name: res_search + * + * Description: + * Same as res_query(). NuttX has no search domain list, so the name is + * queried as given. + * + ****************************************************************************/ + +int res_search(FAR const char *dname, int rclass, int type, + FAR unsigned char *answer, int anslen) +{ + return res_query(dname, rclass, type, answer, anslen); +} diff --git a/libs/libc/netdb/lib_ressend.c b/libs/libc/netdb/lib_ressend.c new file mode 100644 index 00000000000..432fc277f93 --- /dev/null +++ b/libs/libc/netdb/lib_ressend.c @@ -0,0 +1,191 @@ +/**************************************************************************** + * libs/libc/netdb/lib_ressend.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <resolv.h> + +#include <arpa/nameser.h> +#include <sys/socket.h> + +#include "netdb/lib_dns.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct res_send_s +{ + FAR const unsigned char *msg; /* The query message */ + int msglen; /* The size of the query */ + FAR unsigned char *answer; /* The buffer for the answer */ + int anslen; /* The size of the answer buffer */ + int result; /* Size of the answer or negated errno */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: res_send_callback + * + * Description: + * Send the query to one name server and wait for the answer. + * + * Returned Value: + * One (1) if an answer was received, zero (0) to try the next name + * server. + * + ****************************************************************************/ + +static int res_send_callback(FAR void *arg, FAR struct sockaddr *addr, + socklen_t addrlen) +{ + FAR struct res_send_s *rs = arg; + ssize_t nrecv; + int retries; + int rcode; + int sd; + + for (retries = 0; retries < CONFIG_NETDB_DNSCLIENT_RETRIES; retries++) + { + sd = dns_bind(addr->sa_family, false, retries); + if (sd < 0) + { + rs->result = sd; + return 0; + } + + if (connect(sd, addr, addrlen) < 0 || + send(sd, rs->msg, rs->msglen, 0) < 0) + { + rs->result = -get_errno(); + close(sd); + return 0; + } + + /* Wait for the answer with the ID of the query. Ignore other + * messages. A timeout sends the query again. + */ + + for (; ; ) + { + nrecv = recv(sd, rs->answer, rs->anslen, 0); + if (nrecv < 0) + { + rs->result = -get_errno(); + break; + } + + if (nrecv >= NS_HFIXEDSZ && + (rs->answer[2] & 0x80) != 0 && + memcmp(rs->answer, rs->msg, 2) == 0) + { + break; + } + } + + close(sd); + + if (nrecv >= 0) + { + rs->result = nrecv; + + /* After a server failure or a refusal, try the next server */ + + rcode = rs->answer[3] & 0x0f; + return rcode == ns_r_servfail || rcode == ns_r_refused ? 0 : 1; + } + + if (rs->result != -EAGAIN) + { + break; + } + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: res_send + * + * Description: + * Send the DNS message msg to the configured name servers over UDP and + * copy the answer into the buffer answer. A server that fails or + * refuses the query is skipped. An answer that does not fit the buffer + * is truncated. + * + * Input Parameters: + * msg - The query message, for example from res_mkquery(). + * msglen - The size of the query message. + * answer - The buffer that receives the answer. + * anslen - The size of answer. + * + * Returned Value: + * The size of the answer on success. On failure, -1 is returned and + * errno is set. + * + ****************************************************************************/ + +int res_send(FAR const unsigned char *msg, int msglen, + FAR unsigned char *answer, int anslen) +{ + struct res_send_s rs; + int ret; + + if (msg == NULL || msglen < NS_HFIXEDSZ || answer == NULL || + anslen < NS_HFIXEDSZ) + { + set_errno(EINVAL); + return -1; + } + + rs.msg = msg; + rs.msglen = msglen; + rs.answer = answer; + rs.anslen = anslen; + rs.result = -EADDRNOTAVAIL; + + ret = dns_foreach_nameserver(res_send_callback, &rs); + if (ret < 0) + { + rs.result = ret; + } + + if (rs.result < 0) + { + set_errno(-rs.result); + return -1; + } + + return rs.result; +}
