From b4abfb076fcfbcea0d27ff5d5a4f2e9ddb7c0c47 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Mon, 26 Jan 2026 18:28:24 +0100 Subject: [PATCH] implementing server --- .gitignore | 2 +- project.te | 2 +- src/main.cpp | 213 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 175 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index d163863..567609b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -build/ \ No newline at end of file +build/ diff --git a/project.te b/project.te index 2fe956b..cb0e151 100644 --- a/project.te +++ b/project.te @@ -1,2 +1,2 @@ :OpenCode -:Set BinaryUnderDebug "C:/handmade_http/build/server.exe" \ No newline at end of file +:Set BinaryUnderDebug "C:/dev/practice_sockets/build/server.exe" diff --git a/src/main.cpp b/src/main.cpp index 23f9b55..c08341a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,8 @@ +/* +- [ ] Setup so that both client and server are in one program? + - [ ] I would like to make it nicely visible in debugger, I don't want to switch etc. + +*/ #define WIN32_LEAN_AND_MEAN #define WIN32_NO_MIN_MAX #define NOMINMAX @@ -6,11 +11,34 @@ #include #include #include -#include "basic.cpp" - #pragma comment(lib, "ws2_32") -char *win32_alloc_error_string(DWORD errorCode) { +#define panicf(...) (fprintf(stderr, "%s:%d: ", __FILE__, __LINE__), fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n"), exit(1)) +#define debugf(...) (printf("%s:%d: ", __FILE__, __LINE__), printf(__VA_ARGS__), printf("\n")) +#define assert(x) ((!(x)) && (panicf("%s:%d: assertion failed " #x, __FILE__, __LINE__), 0)) + +template +struct DEFER_ExitScope { + T lambda; + DEFER_ExitScope(T lambda) : lambda(lambda) {} + ~DEFER_ExitScope() { lambda(); } + DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){}; + + private: + DEFER_ExitScope &operator=(const DEFER_ExitScope &); +}; + +class DEFER_ExitScopeHelp { + public: + template + DEFER_ExitScope operator+(T t) { return t; } +}; + +#define DEFER_CONCAT_INTERNAL(x, y) x##y +#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y) +#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]() + +char *serialize_error_code(DWORD errorCode) { char* message = NULL; size_t size = FormatMessageA( @@ -33,76 +61,181 @@ char *win32_alloc_error_string(DWORD errorCode) { return message; // Remember to LocalFree() this later! } -void serialize_addrinfo(struct addrinfo *in) { - for (struct addrinfo *p = in; p != NULL; p = p->ai_next) { - const char *ipkind = ""; - void *addr = NULL; +char* strf(const char* format, ...) { + va_list args; + va_start(args, format); + va_list args_copy; + va_copy(args_copy, args); + int size = vsnprintf(NULL, 0, format, args_copy) + 1; + va_end(args_copy); + char* buffer = (char *)malloc(size); + assert(buffer); + vsnprintf(buffer, size, format, args); + va_end(args); + return buffer; +} + +#define lengthof(x) (sizeof((x)) / sizeof((x)[0])) + +struct EnumValue { + int value; + const char *name; +}; +#define ENUM_VALUE(x) {x, #x} +const char *serialize(EnumValue *table, int table_size, int value) { + for (int i = 0; i < table_size; i += 1) { + EnumValue *it = table + i; + if (it->value == value) return it->name; + } + return ""; +} + +EnumValue ai_flag_table[] = { + ENUM_VALUE(AI_PASSIVE), + ENUM_VALUE(AI_CANONNAME), + ENUM_VALUE(AI_NUMERICHOST), +}; + +char *serialize_ai_flags(int n) { + char *result = strf("%d", n); + for (int i = 0; i < lengthof(ai_flag_table); i += 1) { + auto *it = ai_flag_table + i; + if (n & it->value) result = strf("%s %s", result, it->name); + } + return result; +} + +struct { + const char *name; + int value; + const char *desc; +} ai_family_table[] = { + {"AF_UNSPEC", 0, "The address family is unspecified"}, + {"AF_INET", 2, "The Internet Protocol version 4 (IPv4) address family."}, + {"AF_NETBIOS", 17, "The NetBIOS address family. This address family is only supported if a Windows Sockets provider for NetBIOS is installed."}, + {"AF_INET6", 23, "The Internet Protocol version 6 (IPv6) address family."}, + {"AF_IRDA", 26, "The Infrared Data Association (IrDA) address family. This address family is only supported if the computer has an infrared port and driver installed."}, + {"AF_BTH", 32, "The Bluetooth address family. This address family is only supported if a Bluetooth adapter is installed on Windows Server 2003 or later."}, +}; + +const char *serialize_ai_family(int n) { + for (int i = 0; i < lengthof(ai_family_table); i += 1) { + auto *it = ai_family_table + i; + if (it->value == n) { + return it->name; + } + } + return ""; +} + + +EnumValue ai_socktype_table[] = { + ENUM_VALUE(SOCK_STREAM), + ENUM_VALUE(SOCK_DGRAM), + ENUM_VALUE(SOCK_RAW), + ENUM_VALUE(SOCK_RDM), + ENUM_VALUE(SOCK_SEQPACKET), +}; + +EnumValue ai_protocol_table[] = { + ENUM_VALUE(IPPROTO_TCP), + ENUM_VALUE(IPPROTO_UDP), + ENUM_VALUE(IPPROTO_PGM), +}; + +void serialize_addrinfo(struct addrinfo* in) { + for (struct addrinfo* p = in; p != NULL; p = p->ai_next) { + printf("addrinfo %p {\n", p); + printf(" ai_flags = %s(%d)\n", serialize_ai_flags(p->ai_flags), p->ai_flags); + printf(" ai_family = %s(%d)\n", serialize_ai_family(p->ai_family), p->ai_family); + printf(" ai_socktype = %s(%d)\n", serialize(ai_socktype_table, lengthof(ai_socktype_table), p->ai_socktype), p->ai_socktype); + printf(" ai_protocol = %s(%d)\n", serialize(ai_protocol_table, lengthof(ai_protocol_table), p->ai_protocol), p->ai_protocol); + printf(" ai_addrlen = %zu\n", p->ai_addrlen); + printf(" ai_canonname = \"%s\"\n", p->ai_canonname ? p->ai_canonname : ""); + + const char* ipkind = ""; + void* addr = NULL; if (p->ai_family == AF_INET) { - sockaddr_in *ipv4 = (sockaddr_in *)p->ai_addr; + sockaddr_in* ipv4 = (sockaddr_in*)p->ai_addr; addr = &(ipv4->sin_addr); ipkind = "IPv4"; - } else { - sockaddr_in6 *ipv6 = (sockaddr_in6 *)p->ai_addr; + } + else { + sockaddr_in6* ipv6 = (sockaddr_in6*)p->ai_addr; addr = &(ipv6->sin6_addr); ipkind = "IPv6"; } char ipstr[INET6_ADDRSTRLEN]; inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); - printf(" %s: %s\n", ipkind, ipstr); + printf(" ai_addr = %s(%s)\n", ipkind, ipstr); + printf("}\n"); } } + +#define PORT "8000" + int main() { WSADATA wsaData; int err_wsa_startup = WSAStartup(MAKEWORD(2, 2), &wsaData); if (err_wsa_startup != 0) { - panicf("WSAStartup failed, error code: %d\n", err_wsa_startup); + panicf("WSAStartup failed, error code: %d", err_wsa_startup); } defer { WSACleanup(); }; if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { - panicf("Version 2.2 of Winsock not available.\n"); + panicf("Version 2.2 of Winsock not available"); } struct addrinfo *servinfo = {}; struct addrinfo hints = {}; hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; + hints.ai_socktype = SOCK_STREAM; // tcp hints.ai_flags = AI_PASSIVE; - INT err = getaddrinfo(NULL, "8000", &hints, &servinfo); + // hints.ai_protocol = IPROTO_TCP; // tcp + INT err = getaddrinfo(NULL, PORT, &hints, &servinfo); if (err != 0) { panicf("getaddrinfo failed, error code: %s\n", gai_strerrorA(err)); } defer { freeaddrinfo(servinfo); }; serialize_addrinfo(servinfo); + SOCKET socket_fd = -1; + for (addrinfo *it = servinfo; it; it = it->ai_next) { + socket_fd = socket(it->ai_family, it->ai_socktype, it->ai_protocol); + if (socket_fd == -1) { + debugf("skipping socket call %s", serialize_error_code(WSAGetLastError())); + continue; + } - printf("exiting...\n"); + int yes = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(int)) == -1) { + panicf("failed to setsockopt %s", serialize_error_code(WSAGetLastError())); + } + + if (bind(socket_fd, it->ai_addr, (int)it->ai_addrlen) == -1) { + panicf("bind failed %s", serialize_error_code(WSAGetLastError())); + closesocket(socket_fd); + socket_fd = -1; + continue; + } + + break; + } + + if (socket_fd == -1) { + panicf("failed to bind"); + } + + int max_pending_connections = 10; + if (listen(socket_fd, max_pending_connections) == -1) { + panicf("listen failed %s", serialize_error_code(WSAGetLastError())); + } + + debugf("waiting for connections..."); + + debugf("exiting..."); return 0; } - -#if 0 -int getaddrinfo(const char *node, // e.g. "www.example.com" or IP - const char *service, // e.g. "http" or port number - const struct addrinfo *hints, - struct addrinfo **res); - -struct addrinfo { - int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc. - int ai_family; // AF_INET, AF_INET6, AF_UNSPEC - int ai_socktype; // SOCK_STREAM, SOCK_DGRAM - int ai_protocol; // use 0 for "any" - size_t ai_addrlen; // size of ai_addr in bytes - struct sockaddr *ai_addr; // struct sockaddr_in or _in6 - char *ai_canonname; // full canonical hostname - - struct addrinfo *ai_next; // linked list, next node -}; - -struct sockaddr { - unsigned short sa_family; // address family, AF_xxx - char sa_data[14]; // 14 bytes of protocol address -}; -#endif \ No newline at end of file