Files
banban/ql-application/threadx/interface/custom_app/net_diag_probe.c
T

92 lines
4.2 KiB
C

/* Use the same PDP 1 socket path as the working WSS client. No raw ICMP
* or optional Ping API; select + SO_ERROR distinguish timeout/refusal/success. */
#include <string.h>
#include <stdio.h>
#include "ql_rtos.h"
#include "ql_data_call.h"
#include "sockets.h"
#include "netdb.h"
#include "hal_api.h"
#include "net_diag_probe.h"
#include "net_diag.h"
static unsigned long probe_ms(void) { return (unsigned long)hal_os_up_time_ms(); }
void net_diag_probe(const char *host, unsigned short port, net_probe_result *r)
{
struct addrinfo hints, *addresses = NULL, *address;
struct sockaddr_in local;
struct ql_data_call_info data;
char service[8];
unsigned long started;
int fd = -1, nonblocking = 1, ret, connect_error;
socklen_t error_size;
memset(r, 0, sizeof(*r)); r->outcome = PROBE_LOCAL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM;
snprintf(service, sizeof(service), "%u", (unsigned int)port);
if (!net_diag_can_probe()) { r->outcome = PROBE_CANCELLED; return; }
started = probe_ms();
ret = getaddrinfo_with_pcid(host, service, &hints, &addresses, 1);
r->dns_ms = probe_ms() - started; r->dns_result = ret;
if (ret != 0 || !addresses) {
r->outcome = PROBE_DNS; r->error = ret ? ret : -1;
if (addresses) freeaddrinfo(addresses);
return;
}
/* One address per round: avoid multiplying timeout/load by DNS answer count. */
for (address = addresses; address; address = address->ai_next)
if (address->ai_family == AF_INET && address->ai_addr &&
address->ai_addrlen >= sizeof(struct sockaddr_in)) break;
if (!address) { r->outcome = PROBE_DNS; r->error = -1; goto done; }
inet_ntop(AF_INET, &((struct sockaddr_in *)address->ai_addr)->sin_addr, r->address, sizeof(r->address));
if (!net_diag_can_probe()) { r->outcome = PROBE_CANCELLED; goto done; }
memset(&data, 0, sizeof(data));
ret = ql_get_data_call_info(1, 0, &data);
if (ret != 0 || data.v4.state != 1) { r->error = ret ? ret : -1; goto done; }
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) { r->error = ql_get_soc_errno(); goto done; }
if (ioctl(fd, FIONBIO, &nonblocking) != 0) { r->error = ql_get_soc_errno(); goto done; }
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET; local.sin_port = htons(ql_soc_generate_port());
local.sin_addr = data.v4.addr.ip;
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) != 0) { r->error = ql_get_soc_errno(); goto done; }
started = probe_ms();
ret = connect(fd, address->ai_addr, address->ai_addrlen);
r->tcp_started = 1;
if (ret == 0) { r->outcome = PROBE_OK; goto connected; }
connect_error = ql_get_soc_errno();
if (connect_error != EINPROGRESS && connect_error != EWOULDBLOCK && connect_error != EALREADY) {
r->outcome = PROBE_CONNECT; r->error = connect_error; goto connected;
}
/* Short slices allow OTA/sleep/shutdown to cancel within one select slice. */
r->outcome = PROBE_TIMEOUT;
while ((unsigned long)(probe_ms() - started) < 3000UL) {
fd_set writable, exceptional;
struct timeval wait;
int error = 0;
if (!net_diag_can_probe()) { r->outcome = PROBE_CANCELLED; break; }
FD_ZERO(&writable); FD_ZERO(&exceptional);
FD_SET(fd, &writable); FD_SET(fd, &exceptional);
{
unsigned long remaining = 3000UL - (unsigned long)(probe_ms() - started);
if (remaining > 3000UL) break;
wait.tv_sec = 0; wait.tv_usec = (remaining < 200UL ? remaining : 200UL) * 1000UL;
}
ret = select(fd + 1, NULL, &writable, &exceptional, &wait);
if (ret < 0) { r->outcome = PROBE_LOCAL; r->error = ql_get_soc_errno(); break; }
if (ret == 0) continue;
if (!FD_ISSET(fd, &writable) && !FD_ISSET(fd, &exceptional)) continue;
error_size = sizeof(error);
ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &error_size);
if (ret != 0) { r->outcome = PROBE_LOCAL; r->error = ql_get_soc_errno(); }
else { r->error = error; r->outcome = error == 0 ? PROBE_OK : PROBE_CONNECT; }
break;
}
connected:
r->tcp_ms = probe_ms() - started;
done:
if (fd >= 0) close(fd);
freeaddrinfo(addresses);
}