From dbcef6da88abc79d0e29c8c76c20303a8c95fdce Mon Sep 17 00:00:00 2001 From: Jacob Nilsson Date: Sat, 31 Jan 2026 04:28:58 +0100 Subject: [PATCH 01/12] implement skeleton for c bindings this commit implements a skeleton for c bindings, to be expanded on later. it also includes c bindings for sock_addr --- .gitignore | 3 +- .idea/copilot.data.migration.agent.xml | 6 ++ CMakeLists.txt | 75 +++++++++++++- include/netkit-c/definitions.h | 33 ++++++ include/netkit-c/export.h | 11 ++ include/netkit-c/sock/sock_addr.h | 23 +++++ include/netkit-c/sock/sock_addr_types.h | 26 +++++ include/netkit-c/sock/sock_types.h | 31 ++++++ include/netkit/sock/sock_addr_type.hpp | 2 +- src/c/netkit.cpp | 127 ++++++++++++++++++++++++ tests/c/test_sock_addr.c | 24 +++++ 11 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 .idea/copilot.data.migration.agent.xml create mode 100644 include/netkit-c/definitions.h create mode 100644 include/netkit-c/export.h create mode 100644 include/netkit-c/sock/sock_addr.h create mode 100644 include/netkit-c/sock/sock_addr_types.h create mode 100644 include/netkit-c/sock/sock_types.h create mode 100644 src/c/netkit.cpp create mode 100644 tests/c/test_sock_addr.c diff --git a/.gitignore b/.gitignore index 8084896..8ffe7e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -cmake-* \ No newline at end of file +cmake-* +.idea \ No newline at end of file diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml new file mode 100644 index 0000000..4ea72a9 --- /dev/null +++ b/.idea/copilot.data.migration.agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bf52651..b483a61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,10 @@ cmake_minimum_required(VERSION 3.30) -project(netkit LANGUAGES CXX) +project(netkit LANGUAGES C CXX) option(NETKIT_ENABLE_OPENSSL "Enable OpenSSL support in netkit" ON) -option(NETKIT_ENABLE_TESTS "Enable building tests" ON) +option(NETKIT_ENABLE_TESTS "Enable building tests" OFF) +option(NETKIT_ENABLE_C_BINDINGS "Enable experimental C bindings for netkit" ON) +option(NETKIT_ENABLE_C_TESTS "Enable C building tests" ON) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -38,6 +40,8 @@ add_library(netkit SHARED set_target_properties(netkit PROPERTIES EXPORT_NAME netkit POSITION_INDEPENDENT_CODE ON + VERSION 0.1.0 + SOVERSION 1 ) target_compile_definitions(netkit @@ -135,7 +139,7 @@ if (NETKIT_ENABLE_TESTS) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.2 + GIT_TAG v3.12.0 ) FetchContent_MakeAvailable(Catch2) @@ -153,3 +157,68 @@ if (NETKIT_ENABLE_TESTS) include(Catch) catch_discover_tests(netkit-tests) endif() + +if (NETKIT_ENABLE_C_BINDINGS) + message(STATUS "C bindings enabled") + add_library(netkit-c SHARED + src/c/netkit.cpp + ) + + set_target_properties(netkit-c PROPERTIES + EXPORT_NAME netkit-c + POSITION_INDEPENDENT_CODE ON + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + VERSION 0.1.0 + SOVERSION 1 + ) + + target_compile_definitions(netkit-c + PRIVATE NETKIT_C_BUILD_DLL + ) + + target_link_libraries(netkit-c + PUBLIC netkit + ) + + target_include_directories(netkit-c + PUBLIC + $ + $ + ) + + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/netkit-c/ + DESTINATION include/netkit-c + FILES_MATCHING + PATTERN "*.h" + ) + + install(TARGETS netkit-c + EXPORT netkitTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + ) + + if (NETKIT_ENABLE_C_TESTS) + message(STATUS "C API tests enabled") + + add_executable(netkit-c-tests + tests/c/test_sock_addr.c + ) + + add_dependencies(netkit-c-tests + netkit-c + ) + + target_link_libraries(netkit-c-tests + PRIVATE netkit-c + ) + + add_test(NAME netkit-c-tests COMMAND netkit-c-tests) + else() + message(STATUS "C API tests disabled") + endif() +else() + message(STATUS "C bindings disabled") +endif() \ No newline at end of file diff --git a/include/netkit-c/definitions.h b/include/netkit-c/definitions.h new file mode 100644 index 0000000..0a01772 --- /dev/null +++ b/include/netkit-c/definitions.h @@ -0,0 +1,33 @@ +#ifndef NETKIT_SOCK_ADDR_H +#define NETKIT_SOCK_ADDR_H + +#if defined(__APPLE__) +#define NETKIT_MACOS 1 +#endif +#if defined(__unix__) || defined(__unix) || defined(__APPLE__) && defined(__MACH__) +#define NETKIT_UNIX 1 +#endif +#ifdef _WIN32 +#define NETKIT_WINDOWS 1 +#endif + +#ifndef NETKIT_FALLBACK_IPV4_DNS_1 +#define NETKIT_FALLBACK_IPV4_DNS_1 "8.8.8.8" +#endif +#ifndef NETKIT_FALLBACK_IPV4_DNS_2 +#define NETKIT_FALLBACK_IPV4_DNS_2 "8.8.4.4" +#endif +#ifndef NETKIT_FALLBACK_IPV6_DNS_1 +#define NETKIT_FALLBACK_IPV6_DNS_1 "2001:4860:4860::8888" +#endif +#ifndef NETKIT_FALLBACK_IPV6_DNS_2 +#define NETKIT_FALLBACK_IPV6_DNS_2 "2001:4860:4860::8844" +#endif +#ifndef NETKIT_LOCALHOST_IPV4 +#define NETKIT_LOCALHOST_IPV4 "127.0.0.1" +#endif +#ifndef NETKIT_LOCALHOST_IPV6 +#define NETKIT_LOCALHOST_IPV6 "::1" +#endif + +#endif \ No newline at end of file diff --git a/include/netkit-c/export.h b/include/netkit-c/export.h new file mode 100644 index 0000000..04fa34c --- /dev/null +++ b/include/netkit-c/export.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef _WIN32 +#ifdef NETKIT_C_BUILD_DLL +#define NETKIT_C_API __declspec(dllexport) +#else +#define NETKIT_C_API __declspec(dllimport) +#endif +#else +#define NETKIT_C_API +#endif \ No newline at end of file diff --git a/include/netkit-c/sock/sock_addr.h b/include/netkit-c/sock/sock_addr.h new file mode 100644 index 0000000..771eca2 --- /dev/null +++ b/include/netkit-c/sock/sock_addr.h @@ -0,0 +1,23 @@ +#ifndef NETKIT_SOCK_ADDR_H +#define NETKIT_SOCK_ADDR_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef struct netkit_sock_addr netkit_sock_addr_t; + +NETKIT_C_API netkit_sock_addr_t* netkit_sock_addr_create(const char* hostname, int port, netkit_sock_addr_type_t type); +NETKIT_C_API netkit_sock_addr_t* netkit_sock_addr_create_unix(const char* file_path); +NETKIT_C_API void netkit_sock_addr_destroy(netkit_sock_addr_t* addr); +NETKIT_C_API int netkit_sock_addr_get_port(netkit_sock_addr_t* addr); +NETKIT_C_API netkit_sock_addr_type_t netkit_sock_addr_get_type(netkit_sock_addr_t* addr); +NETKIT_C_API netkit_sock_addr_status_t netkit_sock_addr_get_hostname(netkit_sock_addr_t* addr, char* hostname, size_t len, size_t* out_len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/netkit-c/sock/sock_addr_types.h b/include/netkit-c/sock/sock_addr_types.h new file mode 100644 index 0000000..179f24f --- /dev/null +++ b/include/netkit-c/sock/sock_addr_types.h @@ -0,0 +1,26 @@ +#ifndef NETKIT_SOCK_ADDR_TYPES_H +#define NETKIT_SOCK_ADDR_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum netkit_sock_addr_status { + NETKIT_SOCK_ADDR_STATUS_SUCCESS, + NETKIT_SOCK_ADDR_STATUS_FAILED, +} netkit_sock_addr_status_t; + +typedef enum netkit_sock_addr_type { + NETKIT_SOCK_ADDR_NONE = 0, + NETKIT_SOCK_ADDR_IPV4 = 1, + NETKIT_SOCK_ADDR_IPV6 = 2, + NETKIT_SOCK_ADDR_HOSTNAME_IPV4 = 3, + NETKIT_SOCK_ADDR_HOSTNAME_IPV6 = 4, + NETKIT_SOCK_ADDR_HOSTNAME = 5, + NETKIT_SOCK_ADDR_FILENAME = 6 +} netkit_sock_addr_type_t; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/netkit-c/sock/sock_types.h b/include/netkit-c/sock/sock_types.h new file mode 100644 index 0000000..f2c32d1 --- /dev/null +++ b/include/netkit-c/sock/sock_types.h @@ -0,0 +1,31 @@ +#ifndef NETKIT_SOCK_TYPES_H +#define NETKIT_SOCK_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define NETKIT_SOCK_OPT_HAS(mask, opt) (((mask) & (opt)) != 0) + +typedef enum netkit_sock_type { + NETKIT_SOCK_NONE, + NETKIT_SOCK_TCP, + NETKIT_SOCK_UDP, + NETKIT_SOCK_UNIX +} netkit_sock_type_t; + +typedef enum netkit_sock_opt { + NETKIT_SOCK_OPT_NONE = 1 << 0, + NETKIT_SOCK_OPT_REUSE_ADDR = 1 << 1, + NETKIT_SOCK_OPT_NO_REUSE_ADDR = 1 << 2, + NETKIT_SOCK_OPT_NO_DELAY = 1 << 3, + NETKIT_SOCK_OPT_KEEP_ALIVE = 1 << 4, + NETKIT_SOCK_OPT_NO_KEEP_ALIVE = 1 << 5, + NETKIT_SOCK_OPT_NON_BLOCKING = 1 << 6, + NETKIT_SOCK_OPT_BLOCKING = 1 << 7 +} netkit_sock_opt_t; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/netkit/sock/sock_addr_type.hpp b/include/netkit/sock/sock_addr_type.hpp index c5933b7..b354c29 100644 --- a/include/netkit/sock/sock_addr_type.hpp +++ b/include/netkit/sock/sock_addr_type.hpp @@ -4,7 +4,7 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file query_builder.hpp + * @file sock_addr_type.hpp * @license MIT * @note Part of the Netkit library. * @brief Provides common socket types, options, and related enums and structs. diff --git a/src/c/netkit.cpp b/src/c/netkit.cpp new file mode 100644 index 0000000..25a2888 --- /dev/null +++ b/src/c/netkit.cpp @@ -0,0 +1,127 @@ +#include +#include +#include +#include + +struct netkit_sock_addr { + std::shared_ptr impl; + netkit_sock_addr_type_t impl_type = NETKIT_SOCK_ADDR_NONE; + + explicit netkit_sock_addr() = default; + explicit netkit_sock_addr(const char* file_path) : impl_type(NETKIT_SOCK_ADDR_FILENAME) { + impl = std::make_shared(file_path); + } + + netkit_sock_addr(const char* hostname, int port, netkit_sock_addr_type_t type) : impl_type(type) { + netkit::sock::sock_addr_type t; + + switch (type) { + case NETKIT_SOCK_ADDR_FILENAME: + t = netkit::sock::sock_addr_type::filename; + break; + case NETKIT_SOCK_ADDR_HOSTNAME: + t = netkit::sock::sock_addr_type::hostname; + break; + case NETKIT_SOCK_ADDR_HOSTNAME_IPV4: + t = netkit::sock::sock_addr_type::hostname_ipv4; + break; + case NETKIT_SOCK_ADDR_HOSTNAME_IPV6: + t = netkit::sock::sock_addr_type::hostname_ipv6; + break; + case NETKIT_SOCK_ADDR_IPV6: + t = netkit::sock::sock_addr_type::ipv6; + break; + case NETKIT_SOCK_ADDR_IPV4: + t = netkit::sock::sock_addr_type::ipv4; + break; + default: + t = netkit::sock::sock_addr_type::hostname; + break; + } + + impl = std::make_shared(hostname, port, t); + } +}; + +extern "C" NETKIT_C_API netkit_sock_addr_t* netkit_sock_addr_create_unix(const char* file_path) { + netkit_sock_addr_t* ret; + + try { + ret = new netkit_sock_addr{file_path}; + } catch (std::exception&) { + return nullptr; + } catch (...) { + return nullptr; + } + + return ret; +} + +extern "C" NETKIT_C_API netkit_sock_addr_t* netkit_sock_addr_create(const char* hostname, int port, netkit_sock_addr_type_t type) { + netkit_sock_addr_t* ret; + + try { + ret = new netkit_sock_addr{hostname, port, type}; + } catch (std::exception&) { + return nullptr; + } catch (...) { + return nullptr; + } + + return ret; +} + +extern "C" NETKIT_C_API void netkit_sock_addr_destroy(netkit_sock_addr_t* addr) { + if (!addr) { + return; + } + + delete addr; +} + +extern "C" NETKIT_C_API int netkit_sock_addr_get_port(netkit_sock_addr_t* addr) { + if (!addr) { + return -1; + } + + try { + return addr->impl->get_port(); + } catch (std::exception&) { + return -1; + } catch (...) { + return -1; + } +} + +extern "C" NETKIT_C_API netkit_sock_addr_type_t netkit_sock_addr_get_type(netkit_sock_addr_t* addr) { + if (!addr) { + return NETKIT_SOCK_ADDR_NONE; + } + + return addr->impl_type; +} + +extern "C" NETKIT_C_API netkit_sock_addr_status_t netkit_sock_addr_get_hostname(netkit_sock_addr_t* addr, char* hostname, size_t len, size_t* out_len) { + if (!addr) { + return NETKIT_SOCK_ADDR_STATUS_FAILED; + } + + try { + const auto& host = addr->impl->get_hostname(); + + if (out_len) { + *out_len = host.size() + 1; + } + + if (hostname) { + if (len == 0) { + return NETKIT_SOCK_ADDR_STATUS_FAILED; + } + std::snprintf(hostname, len, "%s", host.c_str()); + } + + return NETKIT_SOCK_ADDR_STATUS_SUCCESS; + } catch (...) { + return NETKIT_SOCK_ADDR_STATUS_FAILED; + } +} \ No newline at end of file diff --git a/tests/c/test_sock_addr.c b/tests/c/test_sock_addr.c new file mode 100644 index 0000000..de84b02 --- /dev/null +++ b/tests/c/test_sock_addr.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +int main(void) { + netkit_sock_addr_t* addr = netkit_sock_addr_create("google.com", 80, NETKIT_SOCK_ADDR_HOSTNAME); + + assert(addr != NULL); + + size_t len; + netkit_sock_addr_get_hostname(addr, NULL, 0, &len); + + char* hostname = malloc(len); + netkit_sock_addr_get_hostname(addr, hostname, len, &len); + + assert(!strncmp(hostname, "google.com", len)); + assert(80 == netkit_sock_addr_get_port(addr)); + assert(NETKIT_SOCK_ADDR_HOSTNAME == netkit_sock_addr_get_type(addr)); + + free(hostname); + + netkit_sock_addr_destroy(addr); +} \ No newline at end of file From 1ec930e7d111ef89a34ac51119624e253724fe3b Mon Sep 17 00:00:00 2001 From: Jacob Nilsson Date: Sat, 31 Jan 2026 04:34:09 +0100 Subject: [PATCH 02/12] include stdint.h in sock_types.h fixes compilation error on unix platforms --- include/netkit-c/sock/sock_types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/netkit-c/sock/sock_types.h b/include/netkit-c/sock/sock_types.h index f2c32d1..b0451ac 100644 --- a/include/netkit-c/sock/sock_types.h +++ b/include/netkit-c/sock/sock_types.h @@ -5,6 +5,8 @@ extern "C" { #endif +#include + #define NETKIT_SOCK_OPT_HAS(mask, opt) (((mask) & (opt)) != 0) typedef enum netkit_sock_type { From cc150e36a46903f14e817e06cacd20329b5c445e Mon Sep 17 00:00:00 2001 From: Jacob Nilsson Date: Sat, 31 Jan 2026 04:38:19 +0100 Subject: [PATCH 03/12] include stddef.h in sock_addr.h should solve compilation errors on unix platforms --- include/netkit-c/sock/sock_addr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/netkit-c/sock/sock_addr.h b/include/netkit-c/sock/sock_addr.h index 771eca2..25ba5c1 100644 --- a/include/netkit-c/sock/sock_addr.h +++ b/include/netkit-c/sock/sock_addr.h @@ -6,7 +6,7 @@ extern "C" { #include #include -#include +#include typedef struct netkit_sock_addr netkit_sock_addr_t; From 361812b7ef3104d5fb6291475b2b84bf4356aea3 Mon Sep 17 00:00:00 2001 From: Jacob Nilsson Date: Sat, 31 Jan 2026 19:40:17 +0100 Subject: [PATCH 04/12] c-bindings: implement sync_sock this commit extends the c bindings to sync_sock. it also includes some example code. note that the c++ examples have been moved. --- CMakeLists.txt | 3 +- README.md | 2 +- examples/{http_client => c/socket}/.gitignore | 0 examples/c/socket/CMakeLists.txt | 15 + examples/c/socket/main.c | 67 ++++ examples/{ => cpp}/file_server/.gitignore | 0 examples/{ => cpp}/file_server/CMakeLists.txt | 0 examples/{ => cpp}/file_server/main.cpp | 2 +- .../http_client}/.gitignore | 0 examples/{ => cpp}/http_client/CMakeLists.txt | 0 examples/{ => cpp}/http_client/main.cpp | 2 +- .../http_server}/.gitignore | 0 examples/{ => cpp}/http_server/CMakeLists.txt | 0 examples/{ => cpp}/http_server/main.cpp | 2 +- .../http_server_param}/.gitignore | 0 .../http_server_param/CMakeLists.txt | 0 .../{ => cpp}/http_server_param/css/index.css | 0 .../http_server_param/files/bliss.png | Bin .../{ => cpp}/http_server_param/index.html | 0 examples/{ => cpp}/http_server_param/main.cpp | 2 +- .../network_interface}/.gitignore | 0 .../network_interface/CMakeLists.txt | 0 examples/{ => cpp}/network_interface/main.cpp | 2 +- .../{socket_ssl => cpp/socket}/.gitignore | 0 examples/{ => cpp}/socket/CMakeLists.txt | 0 examples/{ => cpp}/socket/main.cpp | 2 +- examples/cpp/socket_ssl/.gitignore | 1 + examples/{ => cpp}/socket_ssl/CMakeLists.txt | 0 examples/{ => cpp}/socket_ssl/main.cpp | 4 +- include/netkit-c/sock/sock_types.h | 20 ++ include/netkit-c/sock/sync_sock.h | 36 ++ include/netkit/sock/sock_addr.hpp | 5 + src/c/{netkit.cpp => sock/sock_addr.cpp} | 11 +- src/c/sock/sync_sock.cpp | 327 ++++++++++++++++++ src/sock/sock_addr.cpp | 4 + tests/c/test_sock_addr.c | 56 ++- 36 files changed, 548 insertions(+), 15 deletions(-) rename examples/{http_client => c/socket}/.gitignore (100%) create mode 100644 examples/c/socket/CMakeLists.txt create mode 100644 examples/c/socket/main.c rename examples/{ => cpp}/file_server/.gitignore (100%) rename examples/{ => cpp}/file_server/CMakeLists.txt (100%) rename examples/{ => cpp}/file_server/main.cpp (99%) rename examples/{http_server => cpp/http_client}/.gitignore (100%) rename examples/{ => cpp}/http_client/CMakeLists.txt (100%) rename examples/{ => cpp}/http_client/main.cpp (98%) rename examples/{http_server_param => cpp/http_server}/.gitignore (100%) rename examples/{ => cpp}/http_server/CMakeLists.txt (100%) rename examples/{ => cpp}/http_server/main.cpp (98%) rename examples/{network_interface => cpp/http_server_param}/.gitignore (100%) rename examples/{ => cpp}/http_server_param/CMakeLists.txt (100%) rename examples/{ => cpp}/http_server_param/css/index.css (100%) rename examples/{ => cpp}/http_server_param/files/bliss.png (100%) rename examples/{ => cpp}/http_server_param/index.html (100%) rename examples/{ => cpp}/http_server_param/main.cpp (99%) rename examples/{socket => cpp/network_interface}/.gitignore (100%) rename examples/{ => cpp}/network_interface/CMakeLists.txt (100%) rename examples/{ => cpp}/network_interface/main.cpp (99%) rename examples/{socket_ssl => cpp/socket}/.gitignore (100%) rename examples/{ => cpp}/socket/CMakeLists.txt (100%) rename examples/{ => cpp}/socket/main.cpp (98%) create mode 100644 examples/cpp/socket_ssl/.gitignore rename examples/{ => cpp}/socket_ssl/CMakeLists.txt (100%) rename examples/{ => cpp}/socket_ssl/main.cpp (94%) create mode 100644 include/netkit-c/sock/sync_sock.h rename src/c/{netkit.cpp => sock/sock_addr.cpp} (93%) create mode 100644 src/c/sock/sync_sock.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b483a61..3bc081e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,8 @@ endif() if (NETKIT_ENABLE_C_BINDINGS) message(STATUS "C bindings enabled") add_library(netkit-c SHARED - src/c/netkit.cpp + src/c/sock/sock_addr.cpp + src/c/sock/sync_sock.cpp ) set_target_properties(netkit-c PROPERTIES diff --git a/README.md b/README.md index 4c3d5a1..d364402 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ find_package(netkit) add_executable( MY_TARGET - main.cpp + main.c ) target_link_libraries(netkit-example PRIVATE netkit::netkit diff --git a/examples/http_client/.gitignore b/examples/c/socket/.gitignore similarity index 100% rename from examples/http_client/.gitignore rename to examples/c/socket/.gitignore diff --git a/examples/c/socket/CMakeLists.txt b/examples/c/socket/CMakeLists.txt new file mode 100644 index 0000000..bfe40e1 --- /dev/null +++ b/examples/c/socket/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.11) + +project(netkit-example LANGUAGES C) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(netkit) + +add_executable( + netkit-example + main.c +) +target_link_libraries(netkit-example PRIVATE netkit::netkit-c) diff --git a/examples/c/socket/main.c b/examples/c/socket/main.c new file mode 100644 index 0000000..0d746eb --- /dev/null +++ b/examples/c/socket/main.c @@ -0,0 +1,67 @@ +/** netkit + * C++23 cross-platform networking toolkit library providing safe Unix-style sockets and protocol abstractions. + * + * Copyright (c) 2025-2026 Jacob Nilsson + * Licensed under the MIT License. + * + * @file main.c + * @license MIT + * @note Example code using the Netkit library. + * @brief A lower-level example demonstrating the usage of sync_sock to make a simple HTTP request. + */ +#include +#include +#include +#include +#include +#include + +int main(void) { + netkit_sock_addr_t* addr = netkit_sock_addr_create("google.com", 80, NETKIT_SOCK_ADDR_HOSTNAME); + if (!addr) { + return 1; + } + + netkit_sync_sock_t* sock = netkit_sync_sock_create(addr, NETKIT_SOCK_TCP, NETKIT_SOCK_OPT_NONE); + if (!sock) { + fprintf(stderr, "%s\n", "Failed to create socket."); + return 1; + } + + if (netkit_sync_sock_connect(sock) != NETKIT_SOCK_STATUS_SUCCESS) { + fprintf(stderr, "%s\n", "Failed to connect."); + } + + char* buf = "GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n"; + + if (!netkit_sync_sock_send(sock, buf, strlen(buf))) { + fprintf(stderr, "%s\n", "Failed to send."); + } + + netkit_recv_result_t* result = netkit_recv_result_create(); + if (!result) { + fprintf(stderr, "%s\n", "Failed to create recv_result"); + return 1; + } + + netkit_recv_status_t status = netkit_sync_sock_recv(sock, result, 5, "", 0); + if (status == NETKIT_RECV_ERROR) { + fprintf(stderr, "Error.\n"); + return 1; + } else if (status == NETKIT_RECV_TIMEOUT) { + fprintf(stderr, "Timeout.\n"); + return 1; + } + netkit_sync_sock_close(sock); + + if (result->data) { + fprintf(stdout, "%s\n", result->data); + } else { + fprintf(stderr, "%s\n", "result->data == NULL"); + return 1; + } + + netkit_recv_result_destroy(result); + netkit_sock_addr_destroy(addr); + netkit_sync_sock_destroy(sock); +} \ No newline at end of file diff --git a/examples/file_server/.gitignore b/examples/cpp/file_server/.gitignore similarity index 100% rename from examples/file_server/.gitignore rename to examples/cpp/file_server/.gitignore diff --git a/examples/file_server/CMakeLists.txt b/examples/cpp/file_server/CMakeLists.txt similarity index 100% rename from examples/file_server/CMakeLists.txt rename to examples/cpp/file_server/CMakeLists.txt diff --git a/examples/file_server/main.cpp b/examples/cpp/file_server/main.cpp similarity index 99% rename from examples/file_server/main.cpp rename to examples/cpp/file_server/main.cpp index 57eeea8..de4ba8f 100644 --- a/examples/file_server/main.cpp +++ b/examples/cpp/file_server/main.cpp @@ -4,7 +4,7 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file main.cpp + * @file main.c * @license MIT * @note Example code using the Netkit library. * @brief A simple HTTP file server that lists files in a directory and serves them over HTTP. diff --git a/examples/http_server/.gitignore b/examples/cpp/http_client/.gitignore similarity index 100% rename from examples/http_server/.gitignore rename to examples/cpp/http_client/.gitignore diff --git a/examples/http_client/CMakeLists.txt b/examples/cpp/http_client/CMakeLists.txt similarity index 100% rename from examples/http_client/CMakeLists.txt rename to examples/cpp/http_client/CMakeLists.txt diff --git a/examples/http_client/main.cpp b/examples/cpp/http_client/main.cpp similarity index 98% rename from examples/http_client/main.cpp rename to examples/cpp/http_client/main.cpp index 0581cd7..f6de090 100644 --- a/examples/http_client/main.cpp +++ b/examples/cpp/http_client/main.cpp @@ -4,7 +4,7 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file main.cpp + * @file main.c * @license MIT * @note Example code using the Netkit library. * @note If netkit was built with OpenSSL support, HTTPS requests will be made. diff --git a/examples/http_server_param/.gitignore b/examples/cpp/http_server/.gitignore similarity index 100% rename from examples/http_server_param/.gitignore rename to examples/cpp/http_server/.gitignore diff --git a/examples/http_server/CMakeLists.txt b/examples/cpp/http_server/CMakeLists.txt similarity index 100% rename from examples/http_server/CMakeLists.txt rename to examples/cpp/http_server/CMakeLists.txt diff --git a/examples/http_server/main.cpp b/examples/cpp/http_server/main.cpp similarity index 98% rename from examples/http_server/main.cpp rename to examples/cpp/http_server/main.cpp index dde64f7..00c6470 100644 --- a/examples/http_server/main.cpp +++ b/examples/cpp/http_server/main.cpp @@ -4,7 +4,7 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file main.cpp + * @file main.c * @license MIT * @note Example code using the Netkit library. * @brief A simple HTTP server that responds with "Hello, World!" to any request. diff --git a/examples/network_interface/.gitignore b/examples/cpp/http_server_param/.gitignore similarity index 100% rename from examples/network_interface/.gitignore rename to examples/cpp/http_server_param/.gitignore diff --git a/examples/http_server_param/CMakeLists.txt b/examples/cpp/http_server_param/CMakeLists.txt similarity index 100% rename from examples/http_server_param/CMakeLists.txt rename to examples/cpp/http_server_param/CMakeLists.txt diff --git a/examples/http_server_param/css/index.css b/examples/cpp/http_server_param/css/index.css similarity index 100% rename from examples/http_server_param/css/index.css rename to examples/cpp/http_server_param/css/index.css diff --git a/examples/http_server_param/files/bliss.png b/examples/cpp/http_server_param/files/bliss.png similarity index 100% rename from examples/http_server_param/files/bliss.png rename to examples/cpp/http_server_param/files/bliss.png diff --git a/examples/http_server_param/index.html b/examples/cpp/http_server_param/index.html similarity index 100% rename from examples/http_server_param/index.html rename to examples/cpp/http_server_param/index.html diff --git a/examples/http_server_param/main.cpp b/examples/cpp/http_server_param/main.cpp similarity index 99% rename from examples/http_server_param/main.cpp rename to examples/cpp/http_server_param/main.cpp index 9eaa1c1..dfdedda 100644 --- a/examples/http_server_param/main.cpp +++ b/examples/cpp/http_server_param/main.cpp @@ -4,7 +4,7 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file main.cpp + * @file main.c * @license MIT * @note Example code using the Netkit library. * @brief Slightly more advanced/useful HTTP file server that serves files from a specified index file and directory. diff --git a/examples/socket/.gitignore b/examples/cpp/network_interface/.gitignore similarity index 100% rename from examples/socket/.gitignore rename to examples/cpp/network_interface/.gitignore diff --git a/examples/network_interface/CMakeLists.txt b/examples/cpp/network_interface/CMakeLists.txt similarity index 100% rename from examples/network_interface/CMakeLists.txt rename to examples/cpp/network_interface/CMakeLists.txt diff --git a/examples/network_interface/main.cpp b/examples/cpp/network_interface/main.cpp similarity index 99% rename from examples/network_interface/main.cpp rename to examples/cpp/network_interface/main.cpp index dd9c1c6..660c4ee 100644 --- a/examples/network_interface/main.cpp +++ b/examples/cpp/network_interface/main.cpp @@ -4,7 +4,7 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file main.cpp + * @file main.c * @license MIT * @note Example code using the Netkit library. * @brief A clone of the 'ifconfig' command that lists network interfaces and their addresses. diff --git a/examples/socket_ssl/.gitignore b/examples/cpp/socket/.gitignore similarity index 100% rename from examples/socket_ssl/.gitignore rename to examples/cpp/socket/.gitignore diff --git a/examples/socket/CMakeLists.txt b/examples/cpp/socket/CMakeLists.txt similarity index 100% rename from examples/socket/CMakeLists.txt rename to examples/cpp/socket/CMakeLists.txt diff --git a/examples/socket/main.cpp b/examples/cpp/socket/main.cpp similarity index 98% rename from examples/socket/main.cpp rename to examples/cpp/socket/main.cpp index cf60dfb..6599c92 100644 --- a/examples/socket/main.cpp +++ b/examples/cpp/socket/main.cpp @@ -4,7 +4,7 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file main.cpp + * @file main.c * @license MIT * @note Example code using the Netkit library. * @note See examples/socket_ssl for a TLS/SSL version of this example. diff --git a/examples/cpp/socket_ssl/.gitignore b/examples/cpp/socket_ssl/.gitignore new file mode 100644 index 0000000..3c34c2f --- /dev/null +++ b/examples/cpp/socket_ssl/.gitignore @@ -0,0 +1 @@ +*build* \ No newline at end of file diff --git a/examples/socket_ssl/CMakeLists.txt b/examples/cpp/socket_ssl/CMakeLists.txt similarity index 100% rename from examples/socket_ssl/CMakeLists.txt rename to examples/cpp/socket_ssl/CMakeLists.txt diff --git a/examples/socket_ssl/main.cpp b/examples/cpp/socket_ssl/main.cpp similarity index 94% rename from examples/socket_ssl/main.cpp rename to examples/cpp/socket_ssl/main.cpp index dd771b9..d512f28 100644 --- a/examples/socket_ssl/main.cpp +++ b/examples/cpp/socket_ssl/main.cpp @@ -4,11 +4,11 @@ * Copyright (c) 2025-2026 Jacob Nilsson * Licensed under the MIT License. * - * @file main.cpp + * @file main.c * @license MIT * @note Example code using the Netkit library. * @note Only functional if Netkit was built with OpenSSL support. - * @note See examples/socket/main.cpp for a non-SSL/TLS version. + * @note See examples/socket/main.c for a non-SSL/TLS version. * @brief A lower-level example demonstrating the usage of sync_sock to make a simple HTTP request, with SSL/TLS. */ #include diff --git a/include/netkit-c/sock/sock_types.h b/include/netkit-c/sock/sock_types.h index b0451ac..9928806 100644 --- a/include/netkit-c/sock/sock_types.h +++ b/include/netkit-c/sock/sock_types.h @@ -5,9 +5,12 @@ extern "C" { #endif +#include #include +#ifndef NETKIT_SOCK_OPT_HAS #define NETKIT_SOCK_OPT_HAS(mask, opt) (((mask) & (opt)) != 0) +#endif typedef enum netkit_sock_type { NETKIT_SOCK_NONE, @@ -16,6 +19,11 @@ typedef enum netkit_sock_type { NETKIT_SOCK_UNIX } netkit_sock_type_t; +typedef enum netkit_sock_status { + NETKIT_SOCK_STATUS_FAILED = 1, + NETKIT_SOCK_STATUS_SUCCESS = 0, +} netkit_sock_status_t; + typedef enum netkit_sock_opt { NETKIT_SOCK_OPT_NONE = 1 << 0, NETKIT_SOCK_OPT_REUSE_ADDR = 1 << 1, @@ -27,6 +35,18 @@ typedef enum netkit_sock_opt { NETKIT_SOCK_OPT_BLOCKING = 1 << 7 } netkit_sock_opt_t; +typedef enum netkit_recv_status { + NETKIT_RECV_SUCCESS, + NETKIT_RECV_TIMEOUT, + NETKIT_RECV_CLOSED, + NETKIT_RECV_ERROR, +} netkit_recv_status_t; + +typedef struct netkit_recv_result { + char* data; + size_t size; +} netkit_recv_result_t; + #ifdef __cplusplus } #endif diff --git a/include/netkit-c/sock/sync_sock.h b/include/netkit-c/sock/sync_sock.h new file mode 100644 index 0000000..30efd7e --- /dev/null +++ b/include/netkit-c/sock/sync_sock.h @@ -0,0 +1,36 @@ +#ifndef NETKIT_SYNC_SOCK_H +#define NETKIT_SYNC_SOCK_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +typedef struct netkit_sync_sock netkit_sync_sock_t; + +NETKIT_C_API netkit_sync_sock_t* netkit_sync_sock_create(netkit_sock_addr_t* addr, netkit_sock_type_t type, netkit_sock_opt_t opts); +NETKIT_C_API netkit_sock_status_t netkit_sync_sock_destroy(netkit_sync_sock_t* sock); +NETKIT_C_API netkit_sock_status_t netkit_sync_sock_connect(netkit_sync_sock_t* sock); +NETKIT_C_API netkit_sock_status_t netkit_sync_sock_bind(netkit_sync_sock_t* sock); +NETKIT_C_API netkit_sock_status_t netkit_sync_sock_unbind(netkit_sync_sock_t* sock); +NETKIT_C_API netkit_sock_status_t netkit_sync_sock_listen(netkit_sync_sock_t* sock); +NETKIT_C_API netkit_sock_status_t netkit_sync_sock_listen_n(netkit_sync_sock_t* sock, int backlog); +NETKIT_C_API netkit_sync_sock_t* netkit_sync_sock_accept(netkit_sync_sock_t* sock); +NETKIT_C_API int netkit_sync_sock_send(netkit_sync_sock_t* sock, void* buf, size_t len); +NETKIT_C_API void netkit_sync_sock_overflow_bytes(netkit_sync_sock_t* sock, char* buf, size_t len, size_t* out_len); +NETKIT_C_API void netkit_sync_sock_clear_overflow_bytes(netkit_sync_sock_t* sock); + +NETKIT_C_API netkit_recv_result_t* netkit_recv_result_create(void); +NETKIT_C_API void netkit_recv_result_destroy(netkit_recv_result_t* recv_result); + +NETKIT_C_API netkit_recv_status_t netkit_sync_sock_recv(netkit_sync_sock_t* sock, netkit_recv_result_t* out, int timeout_seconds, char* match, size_t eof); +NETKIT_C_API void netkit_sync_sock_close(netkit_sync_sock_t* sock); +NETKIT_C_API netkit_sock_addr_t* netkit_sync_sock_get_peer(netkit_sync_sock_t* sock); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/netkit/sock/sock_addr.hpp b/include/netkit/sock/sock_addr.hpp index 8a5f87d..8e6adc3 100644 --- a/include/netkit/sock/sock_addr.hpp +++ b/include/netkit/sock/sock_addr.hpp @@ -76,6 +76,11 @@ namespace netkit::sock { * @return The stored port. */ [[nodiscard]] int get_port() const; + /** + * @brief Get the stored type. + * @return The stored type. + */ + [[nodiscard]] sock_addr_type get_type() const; ~sock_addr() = default; }; } diff --git a/src/c/netkit.cpp b/src/c/sock/sock_addr.cpp similarity index 93% rename from src/c/netkit.cpp rename to src/c/sock/sock_addr.cpp index 25a2888..3851b42 100644 --- a/src/c/netkit.cpp +++ b/src/c/sock/sock_addr.cpp @@ -1,15 +1,17 @@ +#include + #include #include + #include -#include struct netkit_sock_addr { - std::shared_ptr impl; + std::unique_ptr impl; netkit_sock_addr_type_t impl_type = NETKIT_SOCK_ADDR_NONE; explicit netkit_sock_addr() = default; explicit netkit_sock_addr(const char* file_path) : impl_type(NETKIT_SOCK_ADDR_FILENAME) { - impl = std::make_shared(file_path); + impl = std::make_unique(file_path); } netkit_sock_addr(const char* hostname, int port, netkit_sock_addr_type_t type) : impl_type(type) { @@ -39,7 +41,7 @@ struct netkit_sock_addr { break; } - impl = std::make_shared(hostname, port, t); + impl = std::make_unique(hostname, port, t); } }; @@ -117,6 +119,7 @@ extern "C" NETKIT_C_API netkit_sock_addr_status_t netkit_sock_addr_get_hostname( if (len == 0) { return NETKIT_SOCK_ADDR_STATUS_FAILED; } + std::snprintf(hostname, len, "%s", host.c_str()); } diff --git a/src/c/sock/sync_sock.cpp b/src/c/sock/sync_sock.cpp new file mode 100644 index 0000000..0e42232 --- /dev/null +++ b/src/c/sock/sync_sock.cpp @@ -0,0 +1,327 @@ +#include +#include +#include +#include +#include +#include + +struct netkit_sync_sock { + std::unique_ptr impl; + explicit netkit_sync_sock() = default; + static netkit::sock::sock_type get_type(netkit_sock_type_t type) { + switch (type) { + case NETKIT_SOCK_UDP: + return netkit::sock::sock_type::udp; + case NETKIT_SOCK_UNIX: + return netkit::sock::sock_type::unix; + default: + return netkit::sock::sock_type::tcp; + } + } + static netkit::sock::sock_addr_type get_addr_type(netkit_sock_addr_type_t type) { + switch (type) { + case NETKIT_SOCK_ADDR_FILENAME: + return netkit::sock::sock_addr_type::filename; + case NETKIT_SOCK_ADDR_IPV4: + return netkit::sock::sock_addr_type::ipv4; + case NETKIT_SOCK_ADDR_IPV6: + return netkit::sock::sock_addr_type::ipv6; + case NETKIT_SOCK_ADDR_HOSTNAME_IPV4: + return netkit::sock::sock_addr_type::hostname_ipv4; + case NETKIT_SOCK_ADDR_HOSTNAME_IPV6: + return netkit::sock::sock_addr_type::hostname_ipv6; + default: + return netkit::sock::sock_addr_type::hostname; + } + } + static netkit_sock_addr_type_t get_addr_type(netkit::sock::sock_addr_type type) { + switch (type) { + case netkit::sock::sock_addr_type::filename: + return NETKIT_SOCK_ADDR_FILENAME; + case netkit::sock::sock_addr_type::ipv4: + return NETKIT_SOCK_ADDR_IPV4; + case netkit::sock::sock_addr_type::ipv6: + return NETKIT_SOCK_ADDR_IPV6; + case netkit::sock::sock_addr_type::hostname_ipv4: + return NETKIT_SOCK_ADDR_HOSTNAME_IPV4; + case netkit::sock::sock_addr_type::hostname_ipv6: + return NETKIT_SOCK_ADDR_HOSTNAME_IPV6; + default: + return NETKIT_SOCK_ADDR_HOSTNAME; + } + } + static netkit::sock::sock_opt get_opt(netkit_sock_opt_t type) { + netkit::sock::sock_opt opt{}; + + if (NETKIT_SOCK_OPT_HAS(type, NETKIT_SOCK_OPT_NONE)) { + return netkit::sock::sock_opt::no_reuse_addr | netkit::sock::sock_opt::no_delay | netkit::sock::sock_opt::blocking; + } + + if (NETKIT_SOCK_OPT_HAS(type, NETKIT_SOCK_OPT_BLOCKING)) { + opt = opt | netkit::sock::sock_opt::blocking; + } else if (NETKIT_SOCK_OPT_HAS(type, NETKIT_SOCK_OPT_NON_BLOCKING)) { + opt = opt | netkit::sock::sock_opt::no_blocking; + } + if (NETKIT_SOCK_OPT_HAS(type, NETKIT_SOCK_OPT_KEEP_ALIVE)) { + opt = opt | netkit::sock::sock_opt::keep_alive; + } else if (NETKIT_SOCK_OPT_HAS(type, NETKIT_SOCK_OPT_NO_KEEP_ALIVE)) { + opt = opt | netkit::sock::sock_opt::no_keep_alive; + } + if (NETKIT_SOCK_OPT_HAS(type, NETKIT_SOCK_OPT_REUSE_ADDR)) { + opt = opt | netkit::sock::sock_opt::reuse_addr; + } else if (NETKIT_SOCK_OPT_HAS(type, NETKIT_SOCK_OPT_NO_REUSE_ADDR)) { + opt = opt | netkit::sock::sock_opt::no_reuse_addr; + } + + return opt; + } + static netkit::sock::sock_addr get_sock_addr(netkit_sock_addr_t* addr) { + if (!addr) { + throw std::invalid_argument("invalid sock_addr"); + } + + size_t len = 0; + netkit_sock_addr_get_hostname(addr, nullptr, 0, &len); + + if (len == 0) { + throw std::runtime_error("hostname length is 0"); + } + + std::string hostname(len, '\0'); + + netkit_sock_addr_get_hostname(addr, hostname.data(), len, nullptr); + + if (!hostname.empty() && hostname.back() == '\0') { + hostname.pop_back(); + } + + return { + hostname, + netkit_sock_addr_get_port(addr), + get_addr_type(netkit_sock_addr_get_type(addr)) + }; + } + netkit_sync_sock(netkit_sock_addr_t* addr, netkit_sock_type_t type, netkit_sock_opt_t opts) { + if (!addr) { + throw std::invalid_argument("netkit_sock_addr_t* addr is null"); + } + + impl = std::make_unique(get_sock_addr(addr), get_type(type), get_opt(opts)); + } +}; + +netkit_sync_sock_t* from_cstyle(std::unique_ptr ptr) { + auto* sync_sock = new netkit_sync_sock(); + + sync_sock->impl = std::move(ptr); + + return sync_sock; +} + +extern "C" NETKIT_C_API netkit_sync_sock_t* netkit_sync_sock_create(netkit_sock_addr_t* addr, netkit_sock_type_t type, netkit_sock_opt_t opts) { + if (!addr) { + return nullptr; + } + + try { + return new netkit_sync_sock(addr, type, opts); + } catch (...) { + return nullptr; + } +} + +extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_destroy(netkit_sync_sock_t* sock) { + if (!sock) { + return NETKIT_SOCK_STATUS_FAILED; + } + + try { + delete sock; + return NETKIT_SOCK_STATUS_SUCCESS; + } catch (...) { + } + + return NETKIT_SOCK_STATUS_FAILED; +} + +extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_connect(netkit_sync_sock_t* sock) { + if (!sock) { + return NETKIT_SOCK_STATUS_FAILED; + } + + try { + sock->impl->connect(); + } catch (...) { + return NETKIT_SOCK_STATUS_FAILED; + } + + return NETKIT_SOCK_STATUS_SUCCESS; +} + +extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_bind(netkit_sync_sock_t* sock) { + if (!sock) { + return NETKIT_SOCK_STATUS_FAILED; + } + + try { + sock->impl->bind(); + } catch (...) { + return NETKIT_SOCK_STATUS_FAILED; + } + + return NETKIT_SOCK_STATUS_SUCCESS; +} +extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_unbind(netkit_sync_sock_t* sock) { + if (!sock) { + return NETKIT_SOCK_STATUS_FAILED; + } + + try { + sock->impl->unbind(); + } catch (...) { + return NETKIT_SOCK_STATUS_FAILED; + } + + return NETKIT_SOCK_STATUS_SUCCESS; +} +extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_listen(netkit_sync_sock_t* sock) { + if (!sock) { + return NETKIT_SOCK_STATUS_FAILED; + } + + try { + sock->impl->listen(); + } catch (...) { + return NETKIT_SOCK_STATUS_FAILED; + } + + return NETKIT_SOCK_STATUS_SUCCESS; +} + +extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_listen_n(netkit_sync_sock_t* sock, int backlog) { + if (!sock) { + return NETKIT_SOCK_STATUS_FAILED; + } + + try { + sock->impl->listen(backlog); + } catch (...) { + return NETKIT_SOCK_STATUS_FAILED; + } + + return NETKIT_SOCK_STATUS_SUCCESS; +} +extern "C" NETKIT_C_API netkit_sync_sock_t* netkit_sync_sock_accept(netkit_sync_sock_t* sock) { + if (!sock) { + return nullptr; + } + + try { + return from_cstyle(sock->impl->accept()); + } catch (...) { + return nullptr; + } +} +extern "C" NETKIT_C_API int netkit_sync_sock_send(netkit_sync_sock_t* sock, void* buf, size_t len) { + if (!sock) { + return -1; + } + + try { + return sock->impl->send(buf, len); + } catch (...) { + return -1; + } +} +extern "C" NETKIT_C_API void netkit_sync_sock_overflow_bytes(netkit_sync_sock_t* sock, char* buf, size_t len, size_t* out_len) { + if (!sock) { + return; + } + + try { + const auto& bytes = sock->impl->overflow_bytes(); + + if (out_len) { + *out_len = bytes.size() + 1; + } + + std::snprintf(buf, len, "%s", bytes.c_str()); + } catch (...) {} +} +extern "C" NETKIT_C_API void netkit_sync_sock_clear_overflow_bytes(netkit_sync_sock_t* sock) { + if (!sock) { + return; + } + + try { + sock->impl->clear_overflow_bytes(); + } catch (...) {} +} + +extern "C" NETKIT_C_API netkit_recv_result_t* netkit_recv_result_create(void) { + return new netkit_recv_result_t{nullptr, 0}; +} +extern "C" NETKIT_C_API void netkit_recv_result_destroy(netkit_recv_result_t* recv_result) { + if (!recv_result) { + return; + } + delete[] recv_result->data; + recv_result->data = nullptr; + delete recv_result; +} + +extern "C" NETKIT_C_API netkit_recv_status_t netkit_sync_sock_recv(netkit_sync_sock_t* sock, netkit_recv_result_t* out, int timeout_seconds, char* match, size_t eof) { + if (!sock || !out) { + return NETKIT_RECV_ERROR; + } + + try { + auto ret = sock->impl->recv(timeout_seconds, match ? match : "", eof); + + delete[] out->data; + + out->size = ret.data.size(); + out->data = new char[out->size + 1]; + + std::memcpy(out->data, ret.data.data(), out->size); + + out->data[out->size] = '\0'; + + switch (ret.status) { + case netkit::sock::sock_recv_status::success: + return NETKIT_RECV_SUCCESS; + case netkit::sock::sock_recv_status::timeout: + return NETKIT_RECV_TIMEOUT; + case netkit::sock::sock_recv_status::closed: + return NETKIT_RECV_CLOSED; + default: + return NETKIT_RECV_ERROR; + } + } catch (const std::exception& e) { + return NETKIT_RECV_ERROR; + } catch (...) { + return NETKIT_RECV_ERROR; + } +} + +extern "C" NETKIT_C_API void netkit_sync_sock_close(netkit_sync_sock_t* sock) { + if (!sock) { + return; + } + + try { + sock->impl->close(); + } catch (...) {} +} +extern "C" NETKIT_C_API netkit_sock_addr_t* netkit_sync_sock_get_peer(netkit_sync_sock_t* sock) { + if (!sock) { + return nullptr; + } + + try { + auto impl_peer = sock->impl->get_peer(); + return netkit_sock_addr_create(impl_peer.get_hostname().c_str(), impl_peer.get_port(), netkit_sync_sock::get_addr_type(impl_peer.get_type())); + } catch (...) { + return nullptr; + } +} \ No newline at end of file diff --git a/src/sock/sock_addr.cpp b/src/sock/sock_addr.cpp index 99e76e6..cf560bb 100644 --- a/src/sock/sock_addr.cpp +++ b/src/sock/sock_addr.cpp @@ -158,4 +158,8 @@ int netkit::sock::sock_addr::get_port() const { } return port; +} + +netkit::sock::sock_addr_type netkit::sock::sock_addr::get_type() const { + return type; } \ No newline at end of file diff --git a/tests/c/test_sock_addr.c b/tests/c/test_sock_addr.c index de84b02..b23d24f 100644 --- a/tests/c/test_sock_addr.c +++ b/tests/c/test_sock_addr.c @@ -1,8 +1,61 @@ #include #include +#include +#include #include #include +int main(void) { + netkit_sock_addr_t* addr = netkit_sock_addr_create("google.com", 80, NETKIT_SOCK_ADDR_HOSTNAME); + if (!addr) { + return 1; + } + + netkit_sync_sock_t* sock = netkit_sync_sock_create(addr, NETKIT_SOCK_TCP, NETKIT_SOCK_OPT_NONE); + if (!sock) { + fprintf(stderr, "%s\n", "Failed to create socket."); + return 1; + } + + if (netkit_sync_sock_connect(sock) != NETKIT_SOCK_STATUS_SUCCESS) { + fprintf(stderr, "%s\n", "Failed to connect."); + } + + char* buf = "GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n"; + + if (!netkit_sync_sock_send(sock, buf, strlen(buf))) { + fprintf(stderr, "%s\n", "Failed to send."); + } + + netkit_recv_result_t* result = netkit_recv_result_create(); + if (!result) { + fprintf(stderr, "%s\n", "Failed to create recv_result"); + return 1; + } + + netkit_recv_status_t status = netkit_sync_sock_recv(sock, result, 5, "", 0); + if (status == NETKIT_RECV_ERROR) { + fprintf(stderr, "Error.\n"); + return 1; + } else if (status == NETKIT_RECV_TIMEOUT) { + fprintf(stderr, "Timeout.\n"); + return 1; + } + netkit_sync_sock_close(sock); + + if (result->data) { + fprintf(stdout, "%s\n", result->data); + } else { + fprintf(stderr, "%s\n", "result->data == NULL"); + return 1; + } + + netkit_recv_result_destroy(result); + netkit_sock_addr_destroy(addr); + netkit_sync_sock_destroy(sock); +} + +/* int main(void) { netkit_sock_addr_t* addr = netkit_sock_addr_create("google.com", 80, NETKIT_SOCK_ADDR_HOSTNAME); @@ -21,4 +74,5 @@ int main(void) { free(hostname); netkit_sock_addr_destroy(addr); -} \ No newline at end of file +} +*/ \ No newline at end of file From ba458de1f73eec432ca74dabb418fb138d1ef410 Mon Sep 17 00:00:00 2001 From: Jacob Nilsson Date: Sun, 1 Feb 2026 03:53:21 +0100 Subject: [PATCH 05/12] some naming cleanup --- .idea/codeStyles/Project.xml | 105 ++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/editor.xml | 4 +- .idea/inspectionProfiles/Project_Default.xml | 6 + CMakeLists.txt | 4 +- examples/c/socket/main.c | 10 +- include/netkit-c/definitions.h | 4 +- include/netkit-c/export.h | 5 +- include/netkit-c/sock/sock_addr_types.h | 18 +-- include/netkit-c/sock/sock_types.h | 42 ++--- include/netkit/dns/sync_resolver.hpp | 36 ++--- .../netkit/sock/{sock_addr.hpp => addr.hpp} | 18 +-- .../{sock_addr_type.hpp => addr_type.hpp} | 24 +-- include/netkit/sock/basic_sync_sock.hpp | 21 ++- include/netkit/sock/openssl/ssl_sync_sock.hpp | 10 +- include/netkit/sock/sock_peer.hpp | 4 +- include/netkit/sock/sync_sock.hpp | 36 ++--- src/c/sock/{sock_addr.cpp => addr.cpp} | 52 +++--- src/c/sock/sync_sock.cpp | 150 +++++++++--------- src/crypto/windows/certs.cpp | 4 +- src/http/sync_client.cpp | 12 +- src/sock/{sock_addr.cpp => addr.cpp} | 56 +++---- src/sock/openssl/ssl_sync_sock_openssl.cpp | 20 +-- src/sock/sock_peer.cpp | 13 +- src/sock/sync_sock.cpp | 111 +++++++------ tests/c/test_sock_addr.c | 59 +------ 26 files changed, 446 insertions(+), 383 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml rename include/netkit/sock/{sock_addr.hpp => addr.hpp} (85%) rename include/netkit/sock/{sock_addr_type.hpp => addr_type.hpp} (81%) rename src/c/sock/{sock_addr.cpp => addr.cpp} (62%) rename src/sock/{sock_addr.cpp => addr.cpp} (72%) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..81469bd --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,105 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml index 963c96f..270a895 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -139,7 +139,7 @@