diff --git a/TODO.txt b/TODO.txt index 19cac69..40b8423 100644 --- a/TODO.txt +++ b/TODO.txt @@ -14,6 +14,9 @@ Check if Block FullVerify is actually verifying fully (not missing any condition A loophole in the reorg penalty system could potentially exist where someone broadcasts blocks one-at-a-time. Determine a solution to this. +IPv6 support for the P2P node. Come on guys, it's 2026. RFC 2460 was in 1998. It's about time. + Like if someone is behind NAT, fine, workable. CGNAT? Lmao good luck. + TO TEST: Implement Horizen's "Reorg Penalty" system to make it harder for the young chain to be attacked by a powerful miner. diff --git a/include/tcpd/tcpconnection.h b/include/tcpd/tcpconnection.h index 9d82189..51f62b5 100644 --- a/include/tcpd/tcpconnection.h +++ b/include/tcpd/tcpconnection.h @@ -19,8 +19,17 @@ 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 uint32_t connectionId; tcp_connection_role_t role; @@ -54,6 +63,7 @@ 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. int TcpConnection_SendRaw(int sockFd, const void* data, size_t len); int TcpConnection_SendFramed(tcp_connection_t* conn, const void* payload, size_t payloadLen); diff --git a/include/tcpd/tcpserver.h b/include/tcpd/tcpserver.h index 119fedd..b772f35 100644 --- a/include/tcpd/tcpserver.h +++ b/include/tcpd/tcpserver.h @@ -11,7 +11,12 @@ typedef struct { int sockFd; struct sockaddr_in addr; +#ifdef USE_IPV6 + int sockFd6; // IPv6 support + struct sockaddr_in6 addr6; // IPv6 support +#endif int opt; + int opt6; // IPv6 support int isRunning; void* owner; diff --git a/src/tcpd/tcpconnection.c b/src/tcpd/tcpconnection.c index c6cbfca..1f8a36d 100644 --- a/src/tcpd/tcpconnection.c +++ b/src/tcpd/tcpconnection.c @@ -214,10 +214,26 @@ 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); @@ -235,6 +251,11 @@ 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); } diff --git a/src/tcpd/tcpserver.c b/src/tcpd/tcpserver.c index 304ce9e..8d1f882 100644 --- a/src/tcpd/tcpserver.c +++ b/src/tcpd/tcpserver.c @@ -172,6 +172,9 @@ tcp_server_t* TcpServer_Create() { 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); @@ -217,6 +220,28 @@ void TcpServer_Init(tcp_server_t* ptr, unsigned short port, const char* addr) { 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 + } +#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) { @@ -228,6 +253,15 @@ void TcpServer_Start(tcp_server_t* ptr, int maxcons) { 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; @@ -268,6 +302,14 @@ 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; + } +#endif + if (ptr->svrThread != 0 && !pthread_equal(ptr->svrThread, pthread_self())) { pthread_join(ptr->svrThread, NULL); } @@ -339,6 +381,12 @@ 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); }