incubator-nuttx/libs/libc/netdb/lib_dnsaddserver.c

405 lines
10 KiB
C

/****************************************************************************
* libs/libc/netdb/lib_dnsaddserver.c
*
* 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 <nuttx/config.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/dns.h>
#include "netdb/lib_dns.h"
#ifdef CONFIG_NETDB_DNSCLIENT
/****************************************************************************
* Public Data
****************************************************************************/
#ifndef CONFIG_NETDB_RESOLVCONF
/* The DNS server addresses */
union dns_addr_u g_dns_servers[CONFIG_NETDB_DNSSERVER_NAMESERVERS] =
{
#if defined(CONFIG_NETDB_DNSSERVER_IPv4)
{
.ipv4.sin_family = AF_INET,
.ipv4.sin_port = HTONS(DNS_DEFAULT_PORT),
.ipv4.sin_addr.s_addr = HTONL(CONFIG_NETDB_DNSSERVER_IPv4ADDR),
}
#elif defined(CONFIG_NETDB_DNSSERVER_IPv6)
{
.ipv6.sin6_family = AF_INET6,
.ipv6.sin6_port = HTONS(DNS_DEFAULT_PORT),
.ipv6.sin6_addr.in6_u.u6_addr16 =
{
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_1),
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_2),
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_3),
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_4),
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_5),
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_6),
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_7),
HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_8)
}
}
#endif
};
/* Number of currently configured nameservers */
#if defined(CONFIG_NETDB_DNSSERVER_IPv4) || defined(CONFIG_NETDB_DNSSERVER_IPv6)
uint8_t g_dns_nservers = 1;
#else
uint8_t g_dns_nservers;
#endif
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
static int dns_check_nameserver(FAR void *arg, FAR struct sockaddr *addr,
socklen_t addrlen)
{
FAR struct sockaddr *newaddr = (FAR struct sockaddr *)arg;
if (addr->sa_family == newaddr->sa_family)
{
if (memcmp(addr, newaddr, addrlen) == 0)
{
return -EEXIST;
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: dns_add_nameserver
*
* Description:
* Configure which DNS server to use for queries
*
****************************************************************************/
#ifdef CONFIG_NETDB_RESOLVCONF
int dns_add_nameserver(FAR const struct sockaddr *addr, socklen_t addrlen)
{
FAR FILE *stream;
char addrstr[DNS_MAX_ADDRSTR];
union dns_addr_u dns_addr;
FAR uint16_t *pport;
size_t copylen;
#if CONFIG_NETDB_DNSSERVER_NAMESERVERS > 1
char prev_addr[CONFIG_NETDB_DNSSERVER_NAMESERVERS - 1][DNS_MAX_LINE];
int prev_cnt;
int i;
#endif
int ret;
stream = fopen(CONFIG_NETDB_RESOLVCONF_PATH, "a+");
if (stream == NULL)
{
ret = -get_errno();
nerr("ERROR: Failed to open %s: %d\n",
CONFIG_NETDB_RESOLVCONF_PATH, ret);
DEBUGASSERT(ret < 0);
return ret;
}
dns_lock();
#ifdef CONFIG_NET_IPv4
/* Check for an IPv4 address */
if (addr->sa_family == AF_INET)
{
if (addrlen < sizeof(struct sockaddr_in))
{
ret = -EINVAL;
goto errout;
}
else
{
FAR struct sockaddr_in *in4 = (FAR struct sockaddr_in *)addr;
copylen = sizeof(struct sockaddr_in);
pport = &dns_addr.ipv4.sin_port;
if (inet_ntop(AF_INET, &in4->sin_addr, addrstr,
DNS_MAX_ADDRSTR) == NULL)
{
ret = -get_errno();
nerr("ERROR: inet_ntop failed: %d\n", ret);
DEBUGASSERT(ret < 0);
goto errout;
}
}
}
else
#endif
#ifdef CONFIG_NET_IPv6
/* Check for an IPv6 address */
if (addr->sa_family == AF_INET6)
{
if (addrlen < sizeof(struct sockaddr_in6))
{
ret = -EINVAL;
goto errout;
}
else
{
FAR struct sockaddr_in6 *in6 = (FAR struct sockaddr_in6 *)addr;
copylen = sizeof(struct sockaddr_in6);
pport = &dns_addr.ipv6.sin6_port;
if (inet_ntop(AF_INET6, &in6->sin6_addr, addrstr,
DNS_MAX_ADDRSTR) == NULL)
{
ret = -get_errno();
nerr("ERROR: inet_ntop failed: %d\n", ret);
DEBUGASSERT(ret < 0);
goto errout;
}
}
}
else
#endif
{
nerr("ERROR: Unsupported family: %d\n", addr->sa_family);
ret = -ENOSYS;
goto errout;
}
memcpy(&dns_addr.addr, addr, copylen);
/* A port number of zero means to use the default DNS server port number */
if (*pport == 0)
{
*pport = HTONS(DNS_DEFAULT_PORT);
}
ret = dns_foreach_nameserver(dns_check_nameserver, &dns_addr.addr);
if (ret < 0)
{
/* If the nameserver is existing, don't need to write it into the
* resolv.conf file.
*/
if (ret == -EEXIST)
{
ret = OK;
}
goto errout;
}
#if CONFIG_NETDB_DNSSERVER_NAMESERVERS > 1
fseek(stream, 0, SEEK_SET);
for (prev_cnt = 0; prev_cnt < CONFIG_NETDB_DNSSERVER_NAMESERVERS - 1;
prev_cnt++)
{
if (fgets(prev_addr[prev_cnt], DNS_MAX_LINE, stream) == NULL)
{
break;
}
}
#endif
fclose(stream);
stream = fopen(CONFIG_NETDB_RESOLVCONF_PATH, "w");
/* Write the new record to the head of the resolv.conf file. */
#ifdef CONFIG_NETDB_RESOLVCONF_NONSTDPORT
/* The OpenBSD version supports a [host]:port syntax. When a non-standard
* port is specified the host address must be enclosed in square brackets.
* For example:
*
* nameserver [10.0.0.1]:5353
* nameserver [::1]:5353
*/
ret = fprintf(stream, "%s [%s]:%u\n",
NETDB_DNS_KEYWORD, addrstr, NTOHS(*pport));
#else
ret = fprintf(stream, "%s %s\n", NETDB_DNS_KEYWORD, addrstr);
#endif
if (ret < 0)
{
ret = -get_errno();
nerr("ERROR: fprintf failed: %d\n", ret);
DEBUGASSERT(ret < 0);
goto errout;
}
#if CONFIG_NETDB_DNSSERVER_NAMESERVERS > 1
for (i = 0; i < prev_cnt; i++)
{
ret = fputs(prev_addr[i], stream);
if (ret < 0)
{
ret = -get_errno();
nerr("ERROR: fprintf failed: %d\n", ret);
DEBUGASSERT(ret < 0);
goto errout;
}
}
#endif
ret = OK;
errout:
dns_unlock();
fclose(stream);
if (ret == OK)
{
dns_notify_nameserver(&dns_addr.addr, addrlen);
}
return ret;
}
#else /* CONFIG_NETDB_RESOLVCONF */
int dns_add_nameserver(FAR const struct sockaddr *addr, socklen_t addrlen)
{
union dns_addr_u dns_addr;
FAR uint16_t *pport;
size_t copylen;
int nservers;
int idx;
int ret;
DEBUGASSERT(addr != NULL);
/* Get the index of the next free nameserver slot. */
dns_lock();
if (g_dns_nservers == CONFIG_NETDB_DNSSERVER_NAMESERVERS)
{
idx = 0;
nservers = g_dns_nservers;
}
else
{
idx = g_dns_nservers;
nservers = idx + 1;
}
#ifdef CONFIG_NET_IPv4
/* Check for an IPv4 address */
if (addr->sa_family == AF_INET)
{
/* Set up for the IPv4 address copy */
copylen = sizeof(struct sockaddr_in);
pport = &dns_addr.ipv4.sin_port;
}
else
#endif
#ifdef CONFIG_NET_IPv6
/* Check for an IPv6 address */
if (addr->sa_family == AF_INET6)
{
/* Set up for the IPv6 address copy */
copylen = sizeof(struct sockaddr_in6);
pport = &dns_addr.ipv6.sin6_port;
}
else
#endif
{
nerr("ERROR: Unsupported family: %d\n", addr->sa_family);
dns_unlock();
return -ENOSYS;
}
/* Copy the IP address */
if (addrlen < copylen)
{
nerr("ERROR: Invalid addrlen %ld for family %d\n",
(long)addrlen, addr->sa_family);
dns_unlock();
return -EINVAL;
}
memcpy(&dns_addr.addr, addr, copylen);
/* A port number of zero means to use the default DNS server port number */
if (*pport == 0)
{
*pport = HTONS(DNS_DEFAULT_PORT);
}
ret = dns_foreach_nameserver(dns_check_nameserver, &dns_addr.addr);
if (ret < 0)
{
/* If the nameserver is existing, we simply return the Ok. */
if (ret == -EEXIST)
{
ret = OK;
}
dns_unlock();
return ret;
}
memcpy(&g_dns_servers[idx].addr, &dns_addr.addr, copylen);
/* We now have a valid DNS address */
g_dns_nservers = nservers;
dns_unlock();
#if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0
dns_clear_answer();
#endif
dns_notify_nameserver(&dns_addr.addr, addrlen);
return OK;
}
#endif /* CONFIG_NETDB_RESOLVCONF */
#endif /* CONFIG_NETDB_DNSCLIENT */