92 lines
4.2 KiB
C
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);
|
|
}
|