213 lines
5.4 KiB
C
213 lines
5.4 KiB
C
/**
|
|
* Copyright (C) 2011-2012 Juho Vähä-Herttua
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
*==================================================================
|
|
* modified by fduncanh 2022
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "compat.h"
|
|
|
|
int
|
|
netutils_init()
|
|
{
|
|
#ifdef WIN32
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
int ret;
|
|
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
ret = WSAStartup(wVersionRequested, &wsaData);
|
|
if (ret) {
|
|
return -1;
|
|
}
|
|
|
|
if (LOBYTE(wsaData.wVersion) != 2 ||
|
|
HIBYTE(wsaData.wVersion) != 2) {
|
|
/* Version mismatch, requested version not found */
|
|
return -1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
netutils_cleanup()
|
|
{
|
|
#ifdef WIN32
|
|
WSACleanup();
|
|
#endif
|
|
}
|
|
|
|
unsigned char *
|
|
netutils_get_address(void *sockaddr, int *length, unsigned int *zone_id)
|
|
{
|
|
unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
|
|
struct sockaddr *address = sockaddr;
|
|
|
|
assert(address);
|
|
assert(length);
|
|
assert(zone_id);
|
|
if (address->sa_family == AF_INET) {
|
|
struct sockaddr_in *sin;
|
|
*zone_id = 0;
|
|
sin = (struct sockaddr_in *)address;
|
|
*length = sizeof(sin->sin_addr.s_addr);
|
|
return (unsigned char *)&sin->sin_addr.s_addr;
|
|
} else if (address->sa_family == AF_INET6) {
|
|
struct sockaddr_in6 *sin6;
|
|
|
|
sin6 = (struct sockaddr_in6 *)address;
|
|
if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
|
|
/* Actually an embedded IPv4 address */
|
|
*zone_id = 0;
|
|
*length = sizeof(sin6->sin6_addr.s6_addr)-12;
|
|
return (sin6->sin6_addr.s6_addr+12);
|
|
}
|
|
*zone_id = (unsigned int) sin6->sin6_scope_id;
|
|
*length = sizeof(sin6->sin6_addr.s6_addr);
|
|
return sin6->sin6_addr.s6_addr;
|
|
}
|
|
|
|
*length = 0;
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
|
|
{
|
|
int family = use_ipv6 ? AF_INET6 : AF_INET;
|
|
int type = use_udp ? SOCK_DGRAM : SOCK_STREAM;
|
|
int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP;
|
|
|
|
struct sockaddr_storage saddr;
|
|
socklen_t socklen;
|
|
int server_fd;
|
|
int ret;
|
|
#ifndef _WIN32
|
|
int reuseaddr = 1;
|
|
#else
|
|
const char reuseaddr = 1;
|
|
#endif
|
|
|
|
assert(port);
|
|
|
|
server_fd = socket(family, type, proto);
|
|
if (server_fd == -1) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof (reuseaddr));
|
|
if (ret == -1) {
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(&saddr, 0, sizeof(saddr));
|
|
if (use_ipv6) {
|
|
struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
|
|
|
|
/* Initialize sockaddr for bind */
|
|
sin6ptr->sin6_family = family;
|
|
sin6ptr->sin6_addr = in6addr_any;
|
|
sin6ptr->sin6_port = htons(*port);
|
|
|
|
#ifndef _WIN32
|
|
int v6only = 1;
|
|
/* Make sure we only listen to IPv6 addresses */
|
|
setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
(char *) &v6only, sizeof(v6only));
|
|
#endif
|
|
|
|
socklen = sizeof(*sin6ptr);
|
|
ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
|
|
if (ret == -1) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
|
|
if (ret == -1) {
|
|
goto cleanup;
|
|
}
|
|
*port = ntohs(sin6ptr->sin6_port);
|
|
} else {
|
|
struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
|
|
|
|
/* Initialize sockaddr for bind */
|
|
sinptr->sin_family = family;
|
|
sinptr->sin_addr.s_addr = INADDR_ANY;
|
|
sinptr->sin_port = htons(*port);
|
|
|
|
socklen = sizeof(*sinptr);
|
|
ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
|
|
if (ret == -1) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
|
|
if (ret == -1) {
|
|
goto cleanup;
|
|
}
|
|
*port = ntohs(sinptr->sin_port);
|
|
}
|
|
return server_fd;
|
|
|
|
cleanup:
|
|
ret = SOCKET_GET_ERROR();
|
|
if (server_fd != -1) {
|
|
closesocket(server_fd);
|
|
}
|
|
SOCKET_SET_ERROR(ret);
|
|
return -1;
|
|
}
|
|
|
|
// Src is the ip address
|
|
int
|
|
netutils_parse_address(int family, const char *src, void *dst, int dstlen)
|
|
{
|
|
struct addrinfo *result;
|
|
struct addrinfo *ptr;
|
|
struct addrinfo hints;
|
|
int length;
|
|
int ret;
|
|
|
|
if (family != AF_INET && family != AF_INET6) {
|
|
return -1;
|
|
}
|
|
if (!src || !dst) {
|
|
return -1;
|
|
}
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = family;
|
|
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
|
|
|
|
ret = getaddrinfo(src, NULL, &hints, &result);
|
|
if (ret != 0) {
|
|
return -1;
|
|
}
|
|
|
|
length = -1;
|
|
for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) {
|
|
if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
|
|
memcpy(dst, ptr->ai_addr, ptr->ai_addrlen);
|
|
length = ptr->ai_addrlen;
|
|
break;
|
|
}
|
|
}
|
|
freeaddrinfo(result);
|
|
return length;
|
|
}
|