IPv6 + IPv4 dual-stacking
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define TCP_IO_BUFFER_SIZE 1500
|
||||
#define TCP_FRAME_HEADER_SIZE 4U
|
||||
@@ -19,17 +20,9 @@ typedef enum {
|
||||
typedef struct tcp_connection_t tcp_connection_t;
|
||||
|
||||
struct tcp_connection_t {
|
||||
// TODO: We should make it so only ONE of this needs to be available.
|
||||
// Because of my temporary "I just need something that works" horseshit that I'm about to write, you'll need IPv4 and IPv6 is optional.
|
||||
// Note to self: Don't pull an IETF and some "NAT exists, we're fine" bullshit, because if we end up with our eqvivalent of Teredo or CGNAT, I'm gonna be fucking pissed.
|
||||
// And no, the solution isn't "eh, just bind to 0.0.0.0 and ignore it", because if we do that, we'll inevitably end up with a host that only has IPv6 and then we'll be fucked.
|
||||
// Honestly, I'm proud of whoever runs IPv6-only. Brave soul.
|
||||
int sockFd;
|
||||
struct sockaddr_in peerAddr;
|
||||
#ifdef USE_IPV6
|
||||
int sockFd6; // For IPv6 support
|
||||
struct sockaddr_in6 peerAddr6; // For IPv6 support
|
||||
#endif
|
||||
sa_family_t addrFamily;
|
||||
struct sockaddr_storage peerAddr;
|
||||
uint32_t connectionId;
|
||||
tcp_connection_role_t role;
|
||||
|
||||
@@ -55,7 +48,7 @@ struct tcp_connection_t {
|
||||
void* owner;
|
||||
};
|
||||
|
||||
int TcpConnection_Init(tcp_connection_t* conn, int sockFd, const struct sockaddr_in* peerAddr, tcp_connection_role_t role);
|
||||
int TcpConnection_Init(tcp_connection_t* conn, int sockFd, const struct sockaddr_storage* peerAddr, tcp_connection_role_t role);
|
||||
void TcpConnection_Destroy(tcp_connection_t* conn);
|
||||
|
||||
int TcpConnection_SetDataBuffer(tcp_connection_t* conn, const unsigned char* data, size_t len);
|
||||
@@ -63,7 +56,14 @@ int TcpConnection_SetDataBuffer(tcp_connection_t* conn, const unsigned char* dat
|
||||
void TcpConnection_ResetFramingState(tcp_connection_t* conn);
|
||||
int TcpConnection_FeedFramedData(tcp_connection_t* conn, const unsigned char* input, size_t inputLen);
|
||||
|
||||
// This just takes a socket ID, so it's independent from the v4/v6 stuff. It works for both.
|
||||
// Returns the peer's canonical IP string (strips ::ffff: IPv4-mapped prefix).
|
||||
// Writes at most bufLen bytes to buf. Returns buf on success, NULL on failure.
|
||||
const char* TcpConnection_GetPeerAddrStr(const tcp_connection_t* conn, char* buf, size_t bufLen);
|
||||
|
||||
// Returns non-zero if both connections have the same peer IP address.
|
||||
// Handles AF_INET vs AF_INET6 mismatches via IPv4-mapped normalisation.
|
||||
int TcpConnection_PeerAddrEqual(const tcp_connection_t* a, const tcp_connection_t* b);
|
||||
|
||||
int TcpConnection_SendRaw(int sockFd, const void* data, size_t len);
|
||||
int TcpConnection_SendFramed(tcp_connection_t* conn, const void* payload, size_t payloadLen);
|
||||
|
||||
|
||||
@@ -9,14 +9,9 @@
|
||||
#include <tcpd/tcpconnection.h>
|
||||
|
||||
typedef struct {
|
||||
int sockFd;
|
||||
struct sockaddr_in addr;
|
||||
#ifdef USE_IPV6
|
||||
int sockFd6; // IPv6 support
|
||||
struct sockaddr_in6 addr6; // IPv6 support
|
||||
#endif
|
||||
int sockFd; // IPv6 listening socket (-1 if IPv6 unavailable)
|
||||
int sockFdV4; // IPv4 listening socket (-1 on bind failure)
|
||||
int opt;
|
||||
int opt6; // IPv6 support
|
||||
int isRunning;
|
||||
void* owner;
|
||||
|
||||
@@ -32,7 +27,8 @@ typedef struct {
|
||||
tcp_connection_t** clientsArrPtr;
|
||||
pthread_mutex_t clientsMutex;
|
||||
|
||||
pthread_t svrThread;
|
||||
pthread_t svrThread; // IPv6 accept thread
|
||||
pthread_t svrThreadV4; // IPv4 accept thread
|
||||
} tcp_server_t;
|
||||
|
||||
struct tcpclient_thread_args {
|
||||
|
||||
@@ -255,7 +255,7 @@ net_node_t* Node_Create() {
|
||||
node->seenBlocks = DynSet_Create(32); // 32-byte canonical hashes
|
||||
TxMempool_Init();
|
||||
|
||||
TcpServer_Init(node->server, listenPort, "0.0.0.0");
|
||||
TcpServer_Init(node->server, listenPort, "::");
|
||||
|
||||
node->server->owner = node;
|
||||
node->server->on_connect = Node_Server_OnConnect;
|
||||
@@ -436,8 +436,8 @@ void Node_Server_OnConnect(tcp_connection_t* client) {
|
||||
if (echoPeersEnabled && node && client) {
|
||||
// Attempt to create an outbound connection back to the peer's IP on our configured port.
|
||||
// We avoid connecting if we already have an outbound to the same IP.
|
||||
char ipbuf[INET_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET, &client->peerAddr.sin_addr, ipbuf, sizeof(ipbuf))) {
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
if (TcpConnection_GetPeerAddrStr(client, ipbuf, sizeof(ipbuf))) {
|
||||
// Use the configured port as the target port for the peer's listening service.
|
||||
unsigned short targetPort = listenPort;
|
||||
|
||||
@@ -445,8 +445,7 @@ void Node_Server_OnConnect(tcp_connection_t* client) {
|
||||
pthread_mutex_lock(&node->outboundLock);
|
||||
for (size_t i = 0; i < MAX_CONS; ++i) {
|
||||
if (node->outboundClients[i].connection) {
|
||||
struct in_addr otherAddr = node->outboundClients[i].connection->peerAddr.sin_addr;
|
||||
if (otherAddr.s_addr == client->peerAddr.sin_addr.s_addr) {
|
||||
if (TcpConnection_PeerAddrEqual(node->outboundClients[i].connection, client)) {
|
||||
shouldConnect = 0;
|
||||
break;
|
||||
}
|
||||
@@ -930,11 +929,6 @@ void Node_BroadcastChainRange(net_node_t* node, size_t startHeightInclusive, tcp
|
||||
size_t chainSize = Chain_Size(currentChain);
|
||||
if (startHeightInclusive >= chainSize) return;
|
||||
|
||||
uint32_t sourceIp = 0;
|
||||
if (sourceConn) {
|
||||
sourceIp = sourceConn->peerAddr.sin_addr.s_addr;
|
||||
}
|
||||
|
||||
for (size_t h = startHeightInclusive; h < chainSize; ++h) {
|
||||
block_t* blk = NULL;
|
||||
if (!Chain_GetBlockCopy(currentChain, h, &blk) || !blk) {
|
||||
@@ -997,7 +991,7 @@ void Node_BroadcastChainRange(net_node_t* node, size_t startHeightInclusive, tcp
|
||||
tcp_connection_t* conn = node->outboundClients[i].connection;
|
||||
if (!conn) continue;
|
||||
if (conn == sourceConn) continue;
|
||||
if (sourceIp != 0 && conn->peerAddr.sin_addr.s_addr == sourceIp) continue;
|
||||
if (sourceConn && TcpConnection_PeerAddrEqual(conn, sourceConn)) continue;
|
||||
Node_SendPacket(node, conn, PACKET_TYPE_BROADCAST_BLOCK, payload, off);
|
||||
}
|
||||
pthread_mutex_unlock(&node->outboundLock);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <tcpd/tcpclient.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <numgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -82,18 +83,34 @@ int TcpClient_Connect(
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sockFd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockFd < 0) {
|
||||
// Detect address family from the IP string
|
||||
struct sockaddr_in6 addr6;
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr* pSockAddr;
|
||||
socklen_t sockAddrLen;
|
||||
int af;
|
||||
|
||||
memset(&addr6, 0, sizeof(addr6));
|
||||
memset(&addr4, 0, sizeof(addr4));
|
||||
|
||||
if (inet_pton(AF_INET6, peerIp, &addr6.sin6_addr) == 1) {
|
||||
af = AF_INET6;
|
||||
addr6.sin6_family = AF_INET6;
|
||||
addr6.sin6_port = htons(peerPort);
|
||||
pSockAddr = (struct sockaddr*)&addr6;
|
||||
sockAddrLen = sizeof(addr6);
|
||||
} else if (inet_pton(AF_INET, peerIp, &addr4.sin_addr) == 1) {
|
||||
af = AF_INET;
|
||||
addr4.sin_family = AF_INET;
|
||||
addr4.sin_port = htons(peerPort);
|
||||
pSockAddr = (struct sockaddr*)&addr4;
|
||||
sockAddrLen = sizeof(addr4);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in peerAddr;
|
||||
memset(&peerAddr, 0, sizeof(peerAddr));
|
||||
peerAddr.sin_family = AF_INET;
|
||||
peerAddr.sin_port = htons(peerPort);
|
||||
|
||||
if (inet_pton(AF_INET, peerIp, &peerAddr.sin_addr) <= 0) {
|
||||
close(sockFd);
|
||||
int sockFd = socket(af, SOCK_STREAM, 0);
|
||||
if (sockFd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -102,7 +119,7 @@ int TcpClient_Connect(
|
||||
if (flags == -1) flags = 0;
|
||||
fcntl(sockFd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
int rc = connect(sockFd, (struct sockaddr*)&peerAddr, sizeof(peerAddr));
|
||||
int rc = connect(sockFd, pSockAddr, sockAddrLen);
|
||||
if (rc < 0) {
|
||||
if (errno != EINPROGRESS) {
|
||||
close(sockFd);
|
||||
@@ -143,13 +160,18 @@ int TcpClient_Connect(
|
||||
// Restore blocking mode
|
||||
fcntl(sockFd, F_SETFL, flags & ~O_NONBLOCK);
|
||||
|
||||
// Pack the address into sockaddr_storage for TcpConnection_Init
|
||||
struct sockaddr_storage peerStorage;
|
||||
memset(&peerStorage, 0, sizeof(peerStorage));
|
||||
memcpy(&peerStorage, pSockAddr, sockAddrLen);
|
||||
|
||||
tcp_connection_t* conn = (tcp_connection_t*)malloc(sizeof(*conn));
|
||||
if (!conn) {
|
||||
close(sockFd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TcpConnection_Init(conn, sockFd, &peerAddr, TCP_CONNECTION_ROLE_OUTBOUND) != 0) {
|
||||
if (TcpConnection_Init(conn, sockFd, &peerStorage, TCP_CONNECTION_ROLE_OUTBOUND) != 0) {
|
||||
free(conn);
|
||||
close(sockFd);
|
||||
return -1;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int TcpConnection_Init(tcp_connection_t* conn, int sockFd, const struct sockaddr_in* peerAddr, tcp_connection_role_t role) {
|
||||
int TcpConnection_Init(tcp_connection_t* conn, int sockFd, const struct sockaddr_storage* peerAddr, tcp_connection_role_t role) {
|
||||
if (!conn || sockFd < 0 || !peerAddr) {
|
||||
return -1;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ int TcpConnection_Init(tcp_connection_t* conn, int sockFd, const struct sockaddr
|
||||
memset(conn, 0, sizeof(*conn));
|
||||
conn->sockFd = sockFd;
|
||||
conn->peerAddr = *peerAddr;
|
||||
conn->addrFamily = peerAddr->ss_family;
|
||||
conn->role = role;
|
||||
|
||||
if (pthread_mutex_init(&conn->sendLock, NULL) != 0) {
|
||||
@@ -214,26 +215,10 @@ int TcpConnection_SendFramed(tcp_connection_t* conn, const void* payload, size_t
|
||||
|
||||
pthread_mutex_lock(&conn->sendLock);
|
||||
|
||||
#ifdef USE_IPV6
|
||||
int sock;
|
||||
if (conn->sockFd6 >= 0) {
|
||||
// IPv6 is available, attempt to send on it. If it fails, we'll fall back to IPv4 if available.
|
||||
sock = conn->sockFd6;
|
||||
} else {
|
||||
// IPv4 fallback
|
||||
sock = conn->sockFd;
|
||||
}
|
||||
|
||||
int rc = TcpConnection_SendRaw(sock, &beLen, sizeof(beLen));
|
||||
if (rc == 0 && payloadLen > 0) {
|
||||
rc = TcpConnection_SendRaw(sock, payload, payloadLen);
|
||||
}
|
||||
#else
|
||||
int rc = TcpConnection_SendRaw(conn->sockFd, &beLen, sizeof(beLen));
|
||||
if (rc == 0 && payloadLen > 0) {
|
||||
rc = TcpConnection_SendRaw(conn->sockFd, payload, payloadLen);
|
||||
}
|
||||
#endif
|
||||
|
||||
pthread_mutex_unlock(&conn->sendLock);
|
||||
|
||||
@@ -251,11 +236,6 @@ void TcpConnection_RequestClose(tcp_connection_t* conn) {
|
||||
if (conn->sockFd >= 0) {
|
||||
shutdown(conn->sockFd, SHUT_RDWR);
|
||||
}
|
||||
#ifdef USE_IPV6
|
||||
if (conn->sockFd6 >= 0) {
|
||||
shutdown(conn->sockFd6, SHUT_RDWR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pthread_mutex_unlock(&conn->stateLock);
|
||||
}
|
||||
@@ -282,4 +262,58 @@ bool TcpConnection_IsDisconnectNotified(tcp_connection_t* conn) {
|
||||
return notified;
|
||||
}
|
||||
|
||||
static int extract_v4(const tcp_connection_t* conn, struct in_addr* v4out) {
|
||||
if (conn->addrFamily == AF_INET6) {
|
||||
const struct sockaddr_in6* a6 = (const struct sockaddr_in6*)&conn->peerAddr;
|
||||
if (IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr)) {
|
||||
memcpy(v4out, &a6->sin6_addr.s6_addr[12], sizeof(*v4out));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
*v4out = ((const struct sockaddr_in*)&conn->peerAddr)->sin_addr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* TcpConnection_GetPeerAddrStr(const tcp_connection_t* conn, char* buf, size_t bufLen) {
|
||||
if (!conn || !buf || bufLen == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (conn->addrFamily == AF_INET6) {
|
||||
const struct sockaddr_in6* a6 = (const struct sockaddr_in6*)&conn->peerAddr;
|
||||
if (IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr)) {
|
||||
struct in_addr v4;
|
||||
memcpy(&v4, &a6->sin6_addr.s6_addr[12], sizeof(v4));
|
||||
return inet_ntop(AF_INET, &v4, buf, (socklen_t)bufLen);
|
||||
}
|
||||
return inet_ntop(AF_INET6, &a6->sin6_addr, buf, (socklen_t)bufLen);
|
||||
}
|
||||
|
||||
const struct sockaddr_in* a4 = (const struct sockaddr_in*)&conn->peerAddr;
|
||||
return inet_ntop(AF_INET, &a4->sin_addr, buf, (socklen_t)bufLen);
|
||||
}
|
||||
|
||||
int TcpConnection_PeerAddrEqual(const tcp_connection_t* a, const tcp_connection_t* b) {
|
||||
if (!a || !b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct in_addr va, vb;
|
||||
int a_is_v4 = extract_v4(a, &va);
|
||||
int b_is_v4 = extract_v4(b, &vb);
|
||||
|
||||
if (a_is_v4 && b_is_v4) {
|
||||
return va.s_addr == vb.s_addr;
|
||||
}
|
||||
|
||||
if (!a_is_v4 && !b_is_v4) {
|
||||
const struct in6_addr* aa6 = &((const struct sockaddr_in6*)&a->peerAddr)->sin6_addr;
|
||||
const struct in6_addr* ab6 = &((const struct sockaddr_in6*)&b->peerAddr)->sin6_addr;
|
||||
return memcmp(aa6, ab6, sizeof(*aa6)) == 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <tcpd/tcpserver.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <numgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -10,6 +11,11 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct {
|
||||
tcp_server_t* serverPtr;
|
||||
int listenFd;
|
||||
} tcpaccept_thread_args_t;
|
||||
|
||||
static void TcpServer_RemoveClientByPtrUnlocked(tcp_server_t* svr, tcp_connection_t* cli) {
|
||||
if (!svr || !svr->clientsArrPtr || !cli) {
|
||||
return;
|
||||
@@ -70,15 +76,20 @@ static void* TcpServer_clientthreadprocess(void* ptr) {
|
||||
}
|
||||
|
||||
static void* TcpServer_threadprocess(void* ptr) {
|
||||
tcp_server_t* svr = (tcp_server_t*)ptr;
|
||||
if (!svr) {
|
||||
tcpaccept_thread_args_t* args = (tcpaccept_thread_args_t*)ptr;
|
||||
if (!args || !args->serverPtr) {
|
||||
free(args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tcp_server_t* svr = args->serverPtr;
|
||||
int listenFd = args->listenFd;
|
||||
free(args);
|
||||
|
||||
while (svr->isRunning) {
|
||||
struct sockaddr_in clientAddr;
|
||||
struct sockaddr_storage clientAddr;
|
||||
socklen_t clientSize = sizeof(clientAddr);
|
||||
int clientFd = accept(svr->sockFd, (struct sockaddr*)&clientAddr, &clientSize);
|
||||
int clientFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientSize);
|
||||
|
||||
if (clientFd < 0) {
|
||||
if (!svr->isRunning) {
|
||||
@@ -168,13 +179,12 @@ tcp_server_t* TcpServer_Create() {
|
||||
|
||||
memset(svr, 0, sizeof(*svr));
|
||||
svr->sockFd = -1;
|
||||
svr->sockFdV4 = -1;
|
||||
svr->svrThread = 0;
|
||||
svr->svrThreadV4 = 0;
|
||||
svr->isRunning = 0;
|
||||
svr->maxClients = 0;
|
||||
svr->clientsArrPtr = NULL;
|
||||
#ifdef USE_IPV6
|
||||
svr->sockFd6 = -1;
|
||||
#endif
|
||||
|
||||
if (pthread_mutex_init(&svr->clientsMutex, NULL) != 0) {
|
||||
free(svr);
|
||||
@@ -203,65 +213,68 @@ void TcpServer_Init(tcp_server_t* ptr, unsigned short port, const char* addr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ptr->sockFd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (ptr->sockFd < 0) {
|
||||
ptr->opt = 1;
|
||||
|
||||
// IPv6 (pure, not dual-stack — a dedicated IPv4 socket handles IPv4 clients)
|
||||
int fd6 = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (fd6 >= 0) {
|
||||
setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR, &ptr->opt, sizeof(ptr->opt));
|
||||
int v6only = 1;
|
||||
setsockopt(fd6, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only));
|
||||
|
||||
struct sockaddr_in6 a6;
|
||||
memset(&a6, 0, sizeof(a6));
|
||||
a6.sin6_family = AF_INET6;
|
||||
a6.sin6_port = htons(port);
|
||||
a6.sin6_addr = in6addr_any;
|
||||
|
||||
if (bind(fd6, (struct sockaddr*)&a6, sizeof(a6)) == 0) {
|
||||
ptr->sockFd = fd6;
|
||||
} else {
|
||||
close(fd6);
|
||||
}
|
||||
}
|
||||
|
||||
// IPv4 (always attempted regardless of IPv6 result)
|
||||
int fd4 = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd4 >= 0) {
|
||||
setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR, &ptr->opt, sizeof(ptr->opt));
|
||||
|
||||
struct sockaddr_in a4;
|
||||
memset(&a4, 0, sizeof(a4));
|
||||
a4.sin_family = AF_INET;
|
||||
a4.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, addr, &a4.sin_addr) <= 0) {
|
||||
a4.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
if (bind(fd4, (struct sockaddr*)&a4, sizeof(a4)) == 0) {
|
||||
ptr->sockFdV4 = fd4;
|
||||
} else {
|
||||
close(fd4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TcpServer_Start(tcp_server_t* ptr, int maxcons) {
|
||||
if (!ptr || (ptr->sockFd < 0 && ptr->sockFdV4 < 0) || maxcons <= 0 || ptr->isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
ptr->opt = 1;
|
||||
setsockopt(ptr->sockFd, SOL_SOCKET, SO_REUSEADDR, &ptr->opt, sizeof(int));
|
||||
|
||||
memset(&ptr->addr, 0, sizeof(ptr->addr));
|
||||
ptr->addr.sin_family = AF_INET;
|
||||
ptr->addr.sin_port = htons(port);
|
||||
inet_pton(AF_INET, addr, &ptr->addr.sin_addr);
|
||||
|
||||
if (bind(ptr->sockFd, (struct sockaddr*)&ptr->addr, sizeof(ptr->addr)) < 0) {
|
||||
if (ptr->sockFd >= 0 && listen(ptr->sockFd, maxcons) < 0) {
|
||||
close(ptr->sockFd);
|
||||
ptr->sockFd = -1;
|
||||
}
|
||||
|
||||
#ifdef USE_IPV6
|
||||
// IPv6 support
|
||||
ptr->sockFd6 = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (ptr->sockFd6 >= 0) {
|
||||
ptr->opt6 = 1;
|
||||
setsockopt(ptr->sockFd6, SOL_SOCKET, SO_REUSEADDR, &ptr->opt6, sizeof(int));
|
||||
memset(&ptr->addr6, 0, sizeof(ptr->addr6));
|
||||
ptr->addr6.sin6_family = AF_INET6;
|
||||
ptr->addr6.sin6_port = htons(port);
|
||||
inet_pton(AF_INET6, addr, &ptr->addr6.sin6_addr);
|
||||
if (bind(ptr->sockFd6, (struct sockaddr*)&ptr->addr6, sizeof(ptr->addr6)) < 0) {
|
||||
close(ptr->sockFd6);
|
||||
ptr->sockFd6 = -1;
|
||||
}
|
||||
} else {
|
||||
ptr->sockFd6 = -1; // IPv6 is optional, so if it isn't available, we just set it to -1
|
||||
if (ptr->sockFdV4 >= 0 && listen(ptr->sockFdV4, maxcons) < 0) {
|
||||
close(ptr->sockFdV4);
|
||||
ptr->sockFdV4 = -1;
|
||||
}
|
||||
#else
|
||||
// Safety for my future "I forgot the ifdef guard" self
|
||||
ptr->sockFd6 = -1; // IPv6 not supported in this build
|
||||
#endif
|
||||
}
|
||||
|
||||
void TcpServer_Start(tcp_server_t* ptr, int maxcons) {
|
||||
if (!ptr || ptr->sockFd < 0 || maxcons <= 0 || ptr->isRunning) {
|
||||
if (ptr->sockFd < 0 && ptr->sockFdV4 < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(ptr->sockFd, maxcons) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_IPV6
|
||||
if (ptr->sockFd6 >= 0) {
|
||||
if (listen(ptr->sockFd6, maxcons) < 0) {
|
||||
close(ptr->sockFd6);
|
||||
ptr->sockFd6 = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&ptr->clientsMutex);
|
||||
|
||||
ptr->maxClients = (size_t)maxcons;
|
||||
@@ -279,7 +292,35 @@ void TcpServer_Start(tcp_server_t* ptr, int maxcons) {
|
||||
ptr->isRunning = 1;
|
||||
pthread_mutex_unlock(&ptr->clientsMutex);
|
||||
|
||||
if (pthread_create(&ptr->svrThread, NULL, TcpServer_threadprocess, ptr) != 0) {
|
||||
int anyThreadStarted = 0;
|
||||
|
||||
if (ptr->sockFd >= 0) {
|
||||
tcpaccept_thread_args_t* args = (tcpaccept_thread_args_t*)malloc(sizeof(*args));
|
||||
if (args) {
|
||||
args->serverPtr = ptr;
|
||||
args->listenFd = ptr->sockFd;
|
||||
if (pthread_create(&ptr->svrThread, NULL, TcpServer_threadprocess, args) == 0) {
|
||||
anyThreadStarted = 1;
|
||||
} else {
|
||||
free(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr->sockFdV4 >= 0) {
|
||||
tcpaccept_thread_args_t* args = (tcpaccept_thread_args_t*)malloc(sizeof(*args));
|
||||
if (args) {
|
||||
args->serverPtr = ptr;
|
||||
args->listenFd = ptr->sockFdV4;
|
||||
if (pthread_create(&ptr->svrThreadV4, NULL, TcpServer_threadprocess, args) == 0) {
|
||||
anyThreadStarted = 1;
|
||||
} else {
|
||||
free(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyThreadStarted) {
|
||||
pthread_mutex_lock(&ptr->clientsMutex);
|
||||
ptr->isRunning = 0;
|
||||
free(ptr->clientsArrPtr);
|
||||
@@ -302,19 +343,22 @@ void TcpServer_Stop(tcp_server_t* ptr) {
|
||||
ptr->sockFd = -1;
|
||||
}
|
||||
|
||||
#ifdef USE_IPV6
|
||||
if (ptr->sockFd6 >= 0) {
|
||||
shutdown(ptr->sockFd6, SHUT_RDWR);
|
||||
close(ptr->sockFd6);
|
||||
ptr->sockFd6 = -1;
|
||||
if (ptr->sockFdV4 >= 0) {
|
||||
shutdown(ptr->sockFdV4, SHUT_RDWR);
|
||||
close(ptr->sockFdV4);
|
||||
ptr->sockFdV4 = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ptr->svrThread != 0 && !pthread_equal(ptr->svrThread, pthread_self())) {
|
||||
pthread_join(ptr->svrThread, NULL);
|
||||
}
|
||||
ptr->svrThread = 0;
|
||||
|
||||
if (ptr->svrThreadV4 != 0 && !pthread_equal(ptr->svrThreadV4, pthread_self())) {
|
||||
pthread_join(ptr->svrThreadV4, NULL);
|
||||
}
|
||||
ptr->svrThreadV4 = 0;
|
||||
|
||||
pthread_mutex_lock(&ptr->clientsMutex);
|
||||
size_t maxClients = ptr->maxClients;
|
||||
tcp_connection_t** local = ptr->clientsArrPtr;
|
||||
@@ -381,12 +425,6 @@ void TcpServer_KillClient(tcp_server_t* ptr, tcp_connection_t* cli) {
|
||||
so_linger.l_linger = 0;
|
||||
setsockopt(cli->sockFd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
|
||||
|
||||
#ifdef USE_IPV6
|
||||
if (cli->sockFd6 >= 0) {
|
||||
setsockopt(cli->sockFd6, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
|
||||
}
|
||||
#endif
|
||||
|
||||
TcpServer_Disconnect(ptr, cli);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user