blob: e5e96cabcb6907e6e73c530f91713ac54f200c71 [file]
/*
* Copyright (C) 2025 metraTec GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT simcom_sim7080
#include <zephyr/logging/log.h>
#include <zephyr/net/offloaded_netdev.h>
LOG_MODULE_REGISTER(modem_simcom_sim7080_dns, CONFIG_MODEM_LOG_LEVEL);
#include <zephyr/drivers/modem/simcom-sim7080.h>
#include "sim7080.h"
static struct zsock_addrinfo dns_result;
static struct net_sockaddr dns_result_addr;
static char dns_result_canonname[DNS_MAX_NAME_SIZE + 1];
/*
* Parses the dns response from the modem.
*
* Response on success:
* +CDNSGIP: 1,<domain name>,<IPv4>[,<IPv6>]
*
* Response on failure:
* +CDNSGIP: 0,<err>
*/
MODEM_CMD_DEFINE(on_cmd_cdnsgip)
{
int state;
char ips[256];
size_t out_len;
int ret = -1;
state = atoi(argv[0]);
if (state == 0) {
LOG_ERR("DNS lookup failed with error %s", argv[1]);
goto exit;
}
/* Offset to skip the leading " */
out_len = net_buf_linearize(ips, sizeof(ips) - 1, data->rx_buf, 1, len);
ips[out_len] = '\0';
/* find trailing " */
char *ipv4 = strstr(ips, "\"");
if (!ipv4) {
LOG_ERR("Malformed DNS response!!");
goto exit;
}
*ipv4 = '\0';
net_addr_pton(dns_result.ai_family, ips,
&((struct net_sockaddr_in *)&dns_result_addr)->sin_addr);
ret = 0;
exit:
k_sem_give(&mdata.sem_dns);
return ret;
}
/*
* Perform a dns lookup.
*/
static int offload_getaddrinfo(const char *node, const char *service,
const struct zsock_addrinfo *hints, struct zsock_addrinfo **res)
{
struct modem_cmd cmd[] = { MODEM_CMD("+CDNSGIP: ", on_cmd_cdnsgip, 2U, ",") };
char sendbuf[sizeof("AT+CDNSGIP=\"\",##,#####") + 128];
uint32_t port = 0;
int ret;
/* Modem is not attached to the network. */
if (sim7080_get_state() != SIM7080_STATE_NETWORKING) {
LOG_ERR("Modem currently not attached to the network!");
return DNS_EAI_AGAIN;
}
/* init result */
(void)memset(&dns_result, 0, sizeof(dns_result));
(void)memset(&dns_result_addr, 0, sizeof(dns_result_addr));
/* Currently only support IPv4. */
dns_result.ai_family = NET_AF_INET;
dns_result_addr.sa_family = NET_AF_INET;
dns_result.ai_addr = &dns_result_addr;
dns_result.ai_addrlen = sizeof(dns_result_addr);
dns_result.ai_canonname = dns_result_canonname;
dns_result_canonname[0] = '\0';
if (service) {
port = atoi(service);
if (port < 1 || port > USHRT_MAX) {
return DNS_EAI_SERVICE;
}
}
if (port > 0U) {
if (dns_result.ai_family == NET_AF_INET) {
net_sin(&dns_result_addr)->sin_port = htons(port);
}
}
/* Check if node is an IP address */
if (net_addr_pton(dns_result.ai_family, node,
&((struct net_sockaddr_in *)&dns_result_addr)->sin_addr) == 0) {
*res = &dns_result;
return 0;
}
/* user flagged node as numeric host, but we failed net_addr_pton */
if (hints && hints->ai_flags & AI_NUMERICHOST) {
return DNS_EAI_NONAME;
}
ret = snprintk(sendbuf, sizeof(sendbuf), "AT+CDNSGIP=\"%s\",%u,%u", node,
mdata.dns.recount, mdata.dns.timeout);
if (ret < 0) {
LOG_ERR("Formatting dns query failed");
return ret;
}
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmd, ARRAY_SIZE(cmd), sendbuf,
&mdata.sem_dns, MDM_DNS_TIMEOUT);
if (ret < 0) {
return ret;
}
*res = (struct zsock_addrinfo *)&dns_result;
return 0;
}
/*
* Free addrinfo structure.
*/
static void offload_freeaddrinfo(struct zsock_addrinfo *res)
{
/* No need to free static memory. */
ARG_UNUSED(res);
}
/*
* DNS vtable.
*/
const struct socket_dns_offload offload_dns_ops = {
.getaddrinfo = offload_getaddrinfo,
.freeaddrinfo = offload_freeaddrinfo,
};
int mdm_sim7080_dns_set_lookup_params(uint8_t recount, uint16_t timeout)
{
if (recount > SIM7080_DNS_MAX_RECOUNT || timeout > SIM7080_DNS_MAX_TIMEOUT_MS) {
return -EINVAL;
}
mdata.dns.recount = recount;
mdata.dns.timeout = timeout;
return 0;
}
void mdm_sim7080_dns_get_lookup_params(uint8_t *recount, uint16_t *timeout)
{
if (recount) {
*recount = mdata.dns.recount;
}
if (timeout) {
*timeout = mdata.dns.timeout;
}
}