Files
djhackersdev_bemanitools/src/main/util/net.c
T

490 lines
12 KiB
C

#define LOG_MODULE "util-net"
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "util/log.h"
#include "util/mem.h"
#include "util/net.h"
#include "util/str.h"
#include <inttypes.h>
static bool net_parse_url(const char *str, struct net_addr *addr)
{
char host[NET_MAX_URL_HOSTNAME_LEN + 1];
char path[NET_MAX_URL_PATH_LEN + 1];
uint32_t ipv4[4];
uint32_t port;
int toks;
addr->type = NET_ADDR_TYPE_URL;
memset(host, 0, sizeof(host));
memset(path, 0, sizeof(path));
if (!strncmp(str, "http://", strlen("http://"))) {
addr->url.is_https = false;
str += strlen("http://");
} else if (!strncmp(str, "https://", strlen("https://"))) {
addr->url.is_https = true;
str += strlen("https://");
} else {
return false;
}
/* Try url with IPV4 address */
if (sscanf(str, "%d.%d.%d.%d", &ipv4[0], &ipv4[1], &ipv4[2], &ipv4[3]) ==
4) {
addr->url.type = NET_ADDR_TYPE_IPV4;
addr->url.ipv4.addr = (ipv4[0] & 0xFF) | ((ipv4[1] & 0xFF) << 8) |
((ipv4[2] & 0xFF) << 16) | ((ipv4[3] & 0xFF) << 24);
addr->url.ipv4.port = NET_INVALID_PORT;
memset(addr->url.path, 0, sizeof(addr->url.path));
/* Port and trailing path are optional, check with and without */
toks = sscanf(
str,
"%d.%d.%d.%d:%d/%s",
&ipv4[0],
&ipv4[1],
&ipv4[2],
&ipv4[3],
&port,
path);
if (toks > 4) {
if (toks >= 5) {
addr->url.ipv4.port = (uint16_t) port;
}
if (toks == 6) {
strcpy(addr->url.path, path);
}
} else {
/* No port, but may have trailing path */
toks = sscanf(
str,
"%d.%d.%d.%d/%s",
&ipv4[0],
&ipv4[1],
&ipv4[2],
&ipv4[3],
path);
if (toks > 4) {
strcpy(addr->url.path, path);
}
}
} else {
/* URL with hostname */
addr->url.type = NET_ADDR_TYPE_HOSTNAME;
memset(addr->url.hostname.host, 0, sizeof(addr->url.hostname.host));
addr->url.hostname.port = NET_INVALID_PORT;
memset(addr->url.path, 0, sizeof(addr->url.path));
toks = sscanf(str, "%[^:]:%d/%s", host, &port, path);
if (toks == 0) {
return false;
}
if (toks >= 2) {
strcpy(addr->url.hostname.host, host);
addr->url.hostname.port = port;
if (toks == 3) {
strcpy(addr->url.path, path);
}
} else {
/* No port, but may have trailing path */
toks = sscanf(str, "%[^/]/%s", host, path);
strcpy(addr->url.hostname.host, host);
if (toks > 1) {
strcpy(addr->url.path, path);
}
}
}
return true;
}
static bool net_parse_ipv4_or_hostname(const char *str, struct net_addr *addr)
{
char host[NET_MAX_URL_HOSTNAME_LEN + 1];
uint32_t ipv4[4];
uint32_t port;
int toks;
memset(host, 0, sizeof(host));
toks = sscanf(
str, "%d.%d.%d.%d:%d", &ipv4[0], &ipv4[1], &ipv4[2], &ipv4[3], &port);
if (toks >= 4) {
addr->type = NET_ADDR_TYPE_IPV4;
addr->ipv4.addr = (ipv4[0] & 0xFF) | ((ipv4[1] & 0xFF) << 8) |
((ipv4[2] & 0xFF) << 16) | ((ipv4[3] & 0xFF) << 24);
addr->ipv4.port = NET_INVALID_PORT;
if (toks == 5) {
addr->ipv4.port = port;
}
} else if (toks == 0) {
addr->type = NET_ADDR_TYPE_HOSTNAME;
addr->hostname.port = NET_INVALID_PORT;
toks = sscanf(str, "%[^:]:%d", host, &port);
if (toks == 0) {
return false;
}
strcpy(addr->hostname.host, host);
if (toks > 1) {
addr->hostname.port = port;
}
} else {
return false;
}
return true;
}
static char *net_ipv4_to_str(const struct net_addr_ipv4 *addr)
{
size_t len;
uint8_t *addr_ipv4;
char *str;
addr_ipv4 = (uint8_t *) &addr->addr;
if (addr->port != NET_INVALID_PORT) {
len = snprintf(
NULL,
0,
"%d.%d.%d.%d:%d",
addr_ipv4[0],
addr_ipv4[1],
addr_ipv4[2],
addr_ipv4[3],
addr->port);
str = xmalloc(len + 1);
sprintf(
str,
"%d.%d.%d.%d:%d",
addr_ipv4[0],
addr_ipv4[1],
addr_ipv4[2],
addr_ipv4[3],
addr->port);
} else {
len = snprintf(
NULL,
0,
"%d.%d.%d.%d",
addr_ipv4[0],
addr_ipv4[1],
addr_ipv4[2],
addr_ipv4[3]);
str = xmalloc(len + 1);
sprintf(
str,
"%d.%d.%d.%d",
addr_ipv4[0],
addr_ipv4[1],
addr_ipv4[2],
addr_ipv4[3]);
}
return str;
}
static char *net_hostname_to_str(const struct net_addr_hostname *addr)
{
size_t len;
char *str;
if (addr->port != NET_INVALID_PORT) {
len = snprintf(NULL, 0, "%s:%d", addr->host, addr->port);
str = xmalloc(len + 1);
sprintf(str, "%s:%d", addr->host, addr->port);
} else {
str = str_dup(addr->host);
}
return str;
}
static char *net_url_to_str(const struct net_addr_url *addr)
{
size_t len;
size_t str_len;
char *tmp;
char *str;
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
tmp = net_ipv4_to_str(&addr->ipv4);
break;
case NET_ADDR_TYPE_HOSTNAME:
tmp = net_hostname_to_str(&addr->hostname);
break;
case NET_ADDR_TYPE_INVALID:
case NET_ADDR_TYPE_URL:
/* URL is not a valid type here */
default:
log_assert(false);
return NULL;
}
str_len = strlen(addr->path);
len = snprintf(
NULL,
0,
"http%s://%s%s%s",
addr->is_https ? "s" : "",
tmp,
str_len > 0 ? "/" : "",
str_len > 0 ? addr->path : "");
str = xmalloc(len + 1);
sprintf(
str,
"http%s://%s%s%s",
addr->is_https ? "s" : "",
tmp,
str_len > 0 ? "/" : "",
str_len > 0 ? addr->path : "");
free(tmp);
return str;
}
static bool net_resolve_hostname_str(const char *host, uint32_t *ipv4)
{
struct WSAData data;
struct addrinfo hints;
struct addrinfo *res;
int err;
log_assert(host);
log_assert(ipv4);
/* Resolve and cache */
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
/* Using gethostbyname seems to crash on certain setups...use
getaddrinfo (which is not deprecated) */
WSAStartup(MAKEWORD(2, 0), &data);
err = getaddrinfo(host, NULL, &hints, &res);
if (err != 0) {
*ipv4 = NET_INVALID_ADDR;
return false;
}
*ipv4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr.S_un.S_addr;
freeaddrinfo(res);
WSACleanup();
return true;
}
static bool net_resolve_hostname_ipv4(
const struct net_addr_ipv4 *addr, struct net_addr_ipv4 *res_addr)
{
res_addr->addr = addr->addr;
res_addr->port = addr->port;
return true;
}
static bool net_resolve_hostname_host(
const struct net_addr_hostname *addr, struct net_addr_ipv4 *res_addr)
{
res_addr->port = addr->port;
return net_resolve_hostname_str(addr->host, &res_addr->addr);
}
static bool net_resolve_hostname_url(
const struct net_addr_url *addr, struct net_addr_ipv4 *res_addr)
{
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
return net_resolve_hostname_ipv4(&addr->ipv4, res_addr);
case NET_ADDR_TYPE_HOSTNAME:
return net_resolve_hostname_host(&addr->hostname, res_addr);
case NET_ADDR_TYPE_INVALID:
case NET_ADDR_TYPE_URL:
/* Not a valid type here */
default:
log_assert(false);
return false;
}
}
bool net_str_parse(const char *str, struct net_addr *addr)
{
log_assert(str);
log_assert(addr);
if (!strncmp(str, "http", strlen("http"))) {
return net_parse_url(str, addr);
} else {
return net_parse_ipv4_or_hostname(str, addr);
}
}
char *net_addr_to_str(const struct net_addr *addr)
{
log_assert(addr);
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
return net_ipv4_to_str(&addr->ipv4);
case NET_ADDR_TYPE_HOSTNAME:
return net_hostname_to_str(&addr->hostname);
case NET_ADDR_TYPE_URL:
return net_url_to_str(&addr->url);
case NET_ADDR_TYPE_INVALID:
default:
log_assert(false);
return NULL;
}
}
bool net_resolve_hostname(const char *hostname, struct net_addr_ipv4 *res_addr)
{
return net_resolve_hostname_str(hostname, &res_addr->addr);
}
bool net_resolve_hostname_net_addr(
const struct net_addr *addr, struct net_addr_ipv4 *res_addr)
{
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
return net_resolve_hostname_ipv4(&addr->ipv4, res_addr);
case NET_ADDR_TYPE_HOSTNAME:
return net_resolve_hostname_host(&addr->hostname, res_addr);
case NET_ADDR_TYPE_URL:
return net_resolve_hostname_url(&addr->url, res_addr);
case NET_ADDR_TYPE_INVALID:
default:
log_assert(false);
return false;
}
}
bool net_check_remote_connection(
const struct net_addr *addr, uint32_t timeout_ms)
{
struct net_addr_ipv4 target;
if (!net_resolve_hostname_net_addr(addr, &target)) {
return false;
}
return net_check_remote_connection_ipv4(&target, timeout_ms);
}
bool net_check_remote_connection_ipv4(
const struct net_addr_ipv4 *addr, uint32_t timeout_ms)
{
struct WSAData data;
SOCKET s;
u_long block;
fd_set set_w;
fd_set set_e;
struct timeval timeout;
int err;
bool success;
struct sockaddr_in sockaddr;
WSAStartup(MAKEWORD(2, 0), &data);
err = 0;
success = true;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET) {
log_assert(false);
return false;
}
/* Need socket in non-blocking mode to timeout */
block = 1;
if (ioctlsocket(s, FIONBIO, &block) == SOCKET_ERROR) {
log_assert(false);
closesocket(s);
return false;
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = addr->addr;
sockaddr.sin_port = htons(addr->port);
if (connect(s, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) ==
SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
/* connection failed */;
success = false;
} else {
FD_ZERO(&set_w);
FD_SET(s, &set_w);
FD_ZERO(&set_e);
FD_SET(s, &set_e);
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
err = select(0, NULL, &set_w, &set_e, &timeout);
if (err <= 0) {
/* select failed or timeout */
success = false;
} else {
if (FD_ISSET(s, &set_e)) {
/* Connection failed */
success = false;
}
}
}
} else {
success = false;
}
closesocket(s);
WSACleanup();
return success;
}