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/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/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/.idea/editor.xml b/.idea/editor.xml
index 963c96f..270a895 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -139,7 +139,7 @@
-
+
@@ -244,7 +244,7 @@
-
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..9443f2f
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bf52651..d4954f0 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_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)
@@ -23,7 +25,7 @@ add_library(netkit SHARED
src/network/ip_list.cpp
src/dns/record_type.cpp
src/dns/nameserver_list.cpp
- src/sock/sock_addr.cpp
+ src/sock/addr.cpp
src/dns/response_parser.cpp
src/dns/query_builder.cpp
src/sock/sock_peer.cpp
@@ -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
@@ -100,7 +104,7 @@ include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/netkitConfigVersion.cmake"
- VERSION 1.0.0
+ VERSION 0.1.0
COMPATIBILITY SameMajorVersion
)
@@ -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,73 @@ 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/sock/addr.cpp
+ src/c/sock/sync_sock.cpp
+ src/c/sock/openssl/ssl_sync_sock.cpp
+ src/c/network/utility.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
+ tests/c/test_network_utility.c
+ tests/c/test_main.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/README.md b/README.md
index 4c3d5a1..67e897b 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ C++23 cross-platform networking toolkit library providing safe Unix-style socket
## Features
-- Binding, connecting, sending, receiving and closing synchronous sockets
+- Binding, connecting, sending, receiving and closing synchronous TCP/UDP sockets
- HTTP/1.0 and HTTP/1.1 body parser, including headers and body.
- IPv4 and IPv6 support
- TCP and UDP support
@@ -15,7 +15,7 @@ C++23 cross-platform networking toolkit library providing safe Unix-style socket
- Network interface enumeration
- Exceptions for errors
- Inheritable classes for easy extension
-- C++23
+- Designed for C++23, with optional C bindings for use in non-C++ projects and interoperability with other programming languages entirely
- Support for Windows, Linux, macOS and other Unix-compatible systems.
- Permissive MIT license, allowing use in both open source and proprietary software.
@@ -24,7 +24,6 @@ Still missing:
- Asynchronous I/O
- Schannel support for Windows
- WebSocket abstraction
-- C bindings
## Dependencies
@@ -61,7 +60,9 @@ target_link_libraries(netkit-example PRIVATE
...
```
-See `examples/` for further examples of how to use the library.
+If you wish to use the C bindings, link with `netkit::netkit-c` instead.
+
+See `examples/c/` and `examples/cpp/` for further examples of how to use the library.
## License
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..6901236
--- /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 REQUIRED)
+
+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..ec8e4fc
--- /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, SOCK_ADDR_HOSTNAME);
+ if (!addr) {
+ return 1;
+ }
+
+ netkit_sync_sock_t* sock = netkit_sync_sock_create(addr, SOCK_TCP, SOCK_OPT_NONE);
+ if (!sock) {
+ fprintf(stderr, "%s\n", "Failed to create socket.");
+ return 1;
+ }
+
+ if (netkit_sync_sock_connect(sock) != 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 == RECV_ERROR) {
+ fprintf(stderr, "Error.\n");
+ return 1;
+ } else if (status == 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/http_server/.gitignore b/examples/c/socket_ssl/.gitignore
similarity index 100%
rename from examples/http_server/.gitignore
rename to examples/c/socket_ssl/.gitignore
diff --git a/examples/c/socket_ssl/CMakeLists.txt b/examples/c/socket_ssl/CMakeLists.txt
new file mode 100644
index 0000000..bfe40e1
--- /dev/null
+++ b/examples/c/socket_ssl/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_ssl/main.c b/examples/c/socket_ssl/main.c
new file mode 100644
index 0000000..e929811
--- /dev/null
+++ b/examples/c/socket_ssl/main.c
@@ -0,0 +1,98 @@
+/** 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 ssl_sync_sock to make a simple HTTP request with TLS/SSL support.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_TRIES 3
+
+int main(void) {
+ netkit_sock_addr_t* addr = netkit_sock_addr_create("www.google.com", 443, SOCK_ADDR_HOSTNAME);
+ if (!addr) {
+ return 1;
+ }
+
+ netkit_sync_sock_t* sock = netkit_sync_sock_create(addr, SOCK_TCP, SOCK_OPT_NONE);
+ if (!sock) {
+ fprintf(stderr, "%s\n", "Failed to create socket.");
+ return 1;
+ }
+
+ if (netkit_sync_sock_connect(sock) != SOCK_STATUS_SUCCESS) {
+ fprintf(stderr, "%s\n", "Failed to connect.");
+ return 1;
+ }
+
+ netkit_ssl_sync_sock_t* ssl_sock = netkit_ssl_sync_sock_create(
+ sock,
+ NETKIT_SSL_SYNC_SOCK_MODE_CLIENT,
+ NETKIT_SSL_SYNC_SOCK_VERSION_TLS_1_2,
+ NETKIT_SSL_SYNC_SOCK_VERIFICATION_PEER,
+ NULL,
+ NULL
+ );
+ if (!ssl_sock) {
+ fprintf(stderr, "%s\n", "Failed to create SSL sync sock.");
+ return 1;
+ }
+
+ if (netkit_ssl_sync_sock_perform_handshake(ssl_sock)) {
+ fprintf(stderr, "%s\n", "Failed to perform handshake.");
+ return 1;
+ }
+
+ char* buf = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n";
+
+ if (!netkit_ssl_sync_sock_send(ssl_sock, buf, strlen(buf))) {
+ fprintf(stderr, "%s\n", "Failed to send.");
+ return 1;
+ }
+
+ netkit_recv_result_t* result = netkit_recv_result_create();
+ if (!result) {
+ fprintf(stderr, "%s\n", "Failed to create recv_result");
+ return 1;
+ }
+
+ int tries = 0;
+
+ while (tries++ < MAX_TRIES) {
+ netkit_recv_status_t status = netkit_ssl_sync_sock_recv(ssl_sock, result, -1, NULL, 0);
+ if (status == RECV_TIMEOUT) {
+ fprintf(stderr, "%s\n", "Failed to receive: timeout.");
+ return 1;
+ } else if (status == RECV_ERROR) {
+ fprintf(stderr, "%s\n", "Failed to receive: error.");
+ return 1;
+ }
+ }
+
+ netkit_ssl_sync_sock_close(ssl_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_ssl_sync_sock_destroy(ssl_sock);
+ netkit_sync_sock_destroy(sock);
+
+ return 0;
+}
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_param/.gitignore b/examples/cpp/http_client/.gitignore
similarity index 100%
rename from examples/http_server_param/.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/network_interface/.gitignore b/examples/cpp/http_server/.gitignore
similarity index 100%
rename from examples/network_interface/.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/socket/.gitignore b/examples/cpp/http_server_param/.gitignore
similarity index 100%
rename from examples/socket/.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_ssl/.gitignore b/examples/cpp/network_interface/.gitignore
similarity index 100%
rename from examples/socket_ssl/.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/cpp/socket/.gitignore b/examples/cpp/socket/.gitignore
new file mode 100644
index 0000000..3c34c2f
--- /dev/null
+++ b/examples/cpp/socket/.gitignore
@@ -0,0 +1 @@
+*build*
\ No newline at end of file
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 84%
rename from examples/socket_ssl/main.cpp
rename to examples/cpp/socket_ssl/main.cpp
index dd771b9..0736ca1 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
@@ -22,9 +22,9 @@ int main() {
addr, netkit::sock::sock_type::tcp);
netkit::sock::ssl_sync_sock sock((std::move(_sock)),
- netkit::sock::ssl_sync_sock::mode::client,
- netkit::sock::ssl_sync_sock::version::TLS_1_2,
- netkit::sock::ssl_sync_sock::verification::peer
+ netkit::sock::mode::client,
+ netkit::sock::version::TLS_1_2,
+ netkit::sock::verification::peer
);
sock.connect();
diff --git a/include/netkit-c/definitions.h b/include/netkit-c/definitions.h
new file mode 100644
index 0000000..83fb32a
--- /dev/null
+++ b/include/netkit-c/definitions.h
@@ -0,0 +1,33 @@
+#ifndef NETKIT_DEFINITIONS_H
+#define NETKIT_DEFINITIONS_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..b122825
--- /dev/null
+++ b/include/netkit-c/export.h
@@ -0,0 +1,14 @@
+#ifndef NETKIT_EXPORT_H
+#define NETKIT_EXPORT_H
+
+#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 __attribute__((visibility("default")))
+#endif
+
+#endif
\ No newline at end of file
diff --git a/include/netkit-c/network/utility.h b/include/netkit-c/network/utility.h
new file mode 100644
index 0000000..6bd7eda
--- /dev/null
+++ b/include/netkit-c/network/utility.h
@@ -0,0 +1,19 @@
+#ifndef NETKIT_UTILITY_H
+#define NETKIT_UTILITY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+NETKIT_C_API int netkit_network_is_ipv4(const char* ip, size_t len);
+NETKIT_C_API int netkit_network_is_ipv6(const char* ip, size_t len);
+NETKIT_C_API int netkit_network_is_valid_port(int port);
+NETKIT_C_API int netkit_network_usable_ipv6_address_exists();
+
+#ifdef __cplusplus
+}
+#endif
+#endif
\ No newline at end of file
diff --git a/include/netkit-c/sock/openssl/ssl_sync_sock.h b/include/netkit-c/sock/openssl/ssl_sync_sock.h
new file mode 100644
index 0000000..bfeaf55
--- /dev/null
+++ b/include/netkit-c/sock/openssl/ssl_sync_sock.h
@@ -0,0 +1,65 @@
+#ifndef NETKIT_SSL_SYNC_SOCK_H
+#define NETKIT_SSL_SYNC_SOCK_H
+#include "netkit-c/sock/sync_sock.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#ifdef NETKIT_OPENSSL
+
+#include
+#include
+#include
+#include
+#include
+
+typedef enum netkit_ssl_sync_sock_mode {
+ NETKIT_SSL_SYNC_SOCK_MODE_CLIENT,
+ NETKIT_SSL_SYNC_SOCK_MODE_SERVER,
+} netkit_ssl_sync_sock_mode_t;
+
+typedef enum netkit_ssl_sync_sock_version {
+ NETKIT_SSL_SYNC_SOCK_VERSION_SSL_2,
+ NETKIT_SSL_SYNC_SOCK_VERSION_SSL_3,
+ NETKIT_SSL_SYNC_SOCK_VERSION_TLS_1_1,
+ NETKIT_SSL_SYNC_SOCK_VERSION_TLS_1_2,
+ NETKIT_SSL_SYNC_SOCK_VERSION_TLS_1_3,
+} netkit_ssl_sync_sock_version_t;
+
+typedef enum netkit_ssl_sync_sock_verification {
+ NETKIT_SSL_SYNC_SOCK_VERIFICATION_NONE,
+ NETKIT_SSL_SYNC_SOCK_VERIFICATION_PEER,
+} netkit_ssl_sync_sock_verification_t;
+
+typedef struct netkit_ssl_sync_sock netkit_ssl_sync_sock_t;
+
+NETKIT_C_API netkit_ssl_sync_sock_t* netkit_ssl_sync_sock_create(netkit_sync_sock_t* sock,
+ netkit_ssl_sync_sock_mode_t mode,
+ netkit_ssl_sync_sock_version_t version,
+ netkit_ssl_sync_sock_verification_t verification,
+ const char* cert_path,
+ const char* key_path);
+NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_destroy(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_connect(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_bind(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_unbind(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_listen(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_listen_n(netkit_ssl_sync_sock_t* sock, int backlog);
+NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_perform_handshake(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API netkit_ssl_sync_sock_t* netkit_ssl_sync_sock_accept(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API int netkit_ssl_sync_sock_send(netkit_ssl_sync_sock_t* sock, void* buf, size_t len);
+NETKIT_C_API void netkit_ssl_sync_sock_overflow_bytes(netkit_ssl_sync_sock_t* sock, char* buf, size_t len, size_t* out_len);
+NETKIT_C_API void netkit_ssl_sync_sock_clear_overflow_bytes(netkit_ssl_sync_sock_t* sock);
+
+NETKIT_C_API netkit_recv_status_t netkit_ssl_sync_sock_recv(netkit_ssl_sync_sock_t* sock, netkit_recv_result_t* out, int timeout_seconds, const char* match, size_t eof);
+NETKIT_C_API void netkit_ssl_sync_sock_close(netkit_ssl_sync_sock_t* sock);
+NETKIT_C_API netkit_sock_addr_t* netkit_ssl_sync_sock_get_peer(netkit_ssl_sync_sock_t* sock);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/include/netkit-c/sock/sock_addr.h b/include/netkit-c/sock/sock_addr.h
new file mode 100644
index 0000000..25ba5c1
--- /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..7ab750c
--- /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 {
+ SOCK_ADDR_STATUS_SUCCESS,
+ SOCK_ADDR_STATUS_FAILED,
+} netkit_sock_addr_status_t;
+
+typedef enum netkit_sock_addr_type {
+ SOCK_ADDR_NONE = 0,
+ SOCK_ADDR_IPV4 = 1,
+ SOCK_ADDR_IPV6 = 2,
+ SOCK_ADDR_HOSTNAME_IPV4 = 3,
+ SOCK_ADDR_HOSTNAME_IPV6 = 4,
+ SOCK_ADDR_HOSTNAME = 5,
+ 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..7569f48
--- /dev/null
+++ b/include/netkit-c/sock/sock_types.h
@@ -0,0 +1,55 @@
+#ifndef NETKIT_SOCK_TYPES_H
+#define NETKIT_SOCK_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+#ifndef SOCK_OPT_HAS
+#define SOCK_OPT_HAS(mask, opt) (((mask) & (opt)) != 0)
+#endif
+#ifndef SOCK_OPT
+
+typedef enum netkit_sock_type {
+ SOCK_NONE,
+ SOCK_TCP,
+ SOCK_UDP,
+ SOCK_UNIX
+} netkit_sock_type_t;
+
+typedef enum netkit_sock_status {
+ SOCK_STATUS_FAILED = 1,
+ SOCK_STATUS_SUCCESS = 0,
+} netkit_sock_status_t;
+
+typedef enum netkit_sock_opt {
+ SOCK_OPT_NONE = 1 << 0,
+ SOCK_OPT_REUSE_ADDR = 1 << 1,
+ SOCK_OPT_NO_REUSE_ADDR = 1 << 2,
+ SOCK_OPT_NO_DELAY = 1 << 3,
+ SOCK_OPT_KEEP_ALIVE = 1 << 4,
+ SOCK_OPT_NO_KEEP_ALIVE = 1 << 5,
+ SOCK_OPT_NON_BLOCKING = 1 << 6,
+ SOCK_OPT_BLOCKING = 1 << 7
+} netkit_sock_opt_t;
+
+typedef enum netkit_recv_status {
+ RECV_SUCCESS,
+ RECV_TIMEOUT,
+ RECV_CLOSED,
+ RECV_ERROR,
+} netkit_recv_status_t;
+
+typedef struct netkit_recv_result {
+ char* data;
+ size_t size;
+} netkit_recv_result_t;
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+#endif
\ No newline at end of file
diff --git a/include/netkit-c/sock/sync_sock.h b/include/netkit-c/sock/sync_sock.h
new file mode 100644
index 0000000..a4a4114
--- /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, const 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/dns/record_type.hpp b/include/netkit/dns/record_type.hpp
index 598b7af..746bc17 100644
--- a/include/netkit/dns/record_type.hpp
+++ b/include/netkit/dns/record_type.hpp
@@ -11,15 +11,14 @@
*/
#pragma once
-#include
+#include
+#include
#include
#include
-#include
-
-#include
+#include
namespace netkit::dns {
- using ip_list = netkit::sock::ip_list;
+ using ip_list = netkit::network::ip_list;
enum class record_type {
A = 1,
diff --git a/include/netkit/dns/sync_resolver.hpp b/include/netkit/dns/sync_resolver.hpp
index 1c77355..6fcdd9c 100644
--- a/include/netkit/dns/sync_resolver.hpp
+++ b/include/netkit/dns/sync_resolver.hpp
@@ -12,14 +12,14 @@
#pragma once
#include
-#include
+#include
#include
+#include
#include
-#include
-#include
-#include
-#include
#include
+#include
+#include
+#include
#ifdef NETKIT_UNIX
#include
@@ -83,13 +83,13 @@ namespace netkit::dns {
std::vector all_records;
auto send_udp = [&query](const std::string &server,
- netkit::sock::sock_addr_type family) -> std::optional > {
- netkit::sock::sock_addr addr(server, 53, family);
+ netkit::sock::addr_type family) -> std::optional > {
+ netkit::sock::addr addr(server, 53, family);
netkit::sock::sync_sock sock(
addr,
- netkit::sock::sock_type::udp,
- netkit::sock::sock_opt::blocking |
- netkit::sock::sock_opt::no_delay
+ netkit::sock::type::udp,
+ netkit::sock::opt::blocking |
+ netkit::sock::opt::no_delay
);
sock.connect();
@@ -108,13 +108,13 @@ namespace netkit::dns {
};
- auto send_tcp = [&](const std::string& server, netkit::sock::sock_addr_type family) -> std::optional> {
- netkit::sock::sock_addr addr(server, 53, family);
+ auto send_tcp = [&](const std::string& server, netkit::sock::addr_type family) -> std::optional> {
+ netkit::sock::addr addr(server, 53, family);
netkit::sock::sync_sock sock(
addr,
- netkit::sock::sock_type::tcp,
- netkit::sock::sock_opt::blocking |
- netkit::sock::sock_opt::no_delay
+ netkit::sock::type::tcp,
+ netkit::sock::opt::blocking |
+ netkit::sock::opt::no_delay
);
sock.connect();
@@ -148,7 +148,7 @@ namespace netkit::dns {
return std::vector(resp.begin(), resp.end());
};
- auto try_server = [&](const std::string& server, netkit::sock::sock_addr_type family) -> bool {
+ auto try_server = [&](const std::string& server, netkit::sock::addr_type family) -> bool {
auto udp_resp = send_udp(server, family);
std::optional> final_resp;
@@ -174,7 +174,7 @@ namespace netkit::dns {
if (network::usable_ipv6_address_exists() && list.contains_ipv6()) {
for (const auto& s : list.get_ipv6()) {
- if (try_server(s, netkit::sock::sock_addr_type::ipv6)) {
+ if (try_server(s, netkit::sock::addr_type::ipv6)) {
success = true;
break;
}
@@ -183,7 +183,7 @@ namespace netkit::dns {
if (!success && list.contains_ipv4()) {
for (const auto& s : list.get_ipv4()) {
- if (try_server(s, netkit::sock::sock_addr_type::ipv4)) {
+ if (try_server(s, netkit::sock::addr_type::ipv4)) {
success = true;
break;
}
diff --git a/include/netkit/http/request_handler.hpp b/include/netkit/http/request_handler.hpp
index 08ef75f..95688df 100644
--- a/include/netkit/http/request_handler.hpp
+++ b/include/netkit/http/request_handler.hpp
@@ -205,8 +205,8 @@ namespace netkit::http::server {
while (chunked.find("0\r\n\r\n") == std::string::npos) {
auto res = client_sock->recv(5, "", 0); // no eof
- if (res.status == sock::sock_recv_status::closed) break;
- if (res.status == sock::sock_recv_status::timeout) throw socket_error("recv timeout");
+ if (res.status == sock::recv_status::closed) break;
+ if (res.status == sock::recv_status::timeout) throw socket_error("recv timeout");
if (res.data.empty()) continue;
chunked += res.data;
}
@@ -221,8 +221,8 @@ namespace netkit::http::server {
while (body.size() < content_length) {
auto res = client_sock->recv(30, "", 0);
- if (res.status == sock::sock_recv_status::closed) break;
- if (res.status == sock::sock_recv_status::timeout) throw socket_error("recv timeout");
+ if (res.status == sock::recv_status::closed) break;
+ if (res.status == sock::recv_status::timeout) throw socket_error("recv timeout");
if (res.data.empty()) continue;
body += res.data;
}
diff --git a/include/netkit/http/sync_server.hpp b/include/netkit/http/sync_server.hpp
index 52450b9..f5e9ada 100644
--- a/include/netkit/http/sync_server.hpp
+++ b/include/netkit/http/sync_server.hpp
@@ -13,7 +13,7 @@
#pragma once
#include
-#include
+#include
namespace netkit::http::server {
/**
@@ -38,9 +38,9 @@ namespace netkit::http::server {
throw parsing_error("invalid port");
}
- sock::sock_addr addr = {"localhost", settings.port, netkit::sock::sock_addr_type::hostname};
- this->sock = std::make_unique(addr, netkit::sock::sock_type::tcp,
- netkit::sock::sock_opt::reuse_addr|netkit::sock::sock_opt::no_delay|netkit::sock::sock_opt::blocking);
+ sock::addr addr = {"localhost", settings.port, netkit::sock::addr_type::hostname};
+ this->sock = std::make_unique(addr, netkit::sock::type::tcp,
+ netkit::sock::opt::reuse_addr|netkit::sock::opt::no_delay|netkit::sock::opt::blocking);
try {
sock->bind();
diff --git a/include/netkit/netkit.hpp b/include/netkit/netkit.hpp
index 147a30d..7b4d714 100644
--- a/include/netkit/netkit.hpp
+++ b/include/netkit/netkit.hpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
// DNS headers
#include
@@ -32,12 +33,11 @@
#include
// Socket headers
-#include
-#include
+#include
+#include
#include
#include
#include
-#include
// HTTP headers
#include
diff --git a/include/netkit/sock/ip_list.hpp b/include/netkit/network/ip_list.hpp
similarity index 97%
rename from include/netkit/sock/ip_list.hpp
rename to include/netkit/network/ip_list.hpp
index b7b0ed9..4957948 100644
--- a/include/netkit/sock/ip_list.hpp
+++ b/include/netkit/network/ip_list.hpp
@@ -18,7 +18,7 @@ namespace netkit::dns {
class dns_resolver; // forward declaration
}
-namespace netkit::sock {
+namespace netkit::network {
class NETKIT_API ip_list final {
protected:
diff --git a/include/netkit/sock/sock_addr.hpp b/include/netkit/sock/addr.hpp
similarity index 84%
rename from include/netkit/sock/sock_addr.hpp
rename to include/netkit/sock/addr.hpp
index 8a5f87d..50583e1 100644
--- a/include/netkit/sock/sock_addr.hpp
+++ b/include/netkit/sock/addr.hpp
@@ -14,19 +14,19 @@
#pragma once
#include
+#include
#include
-#include
namespace netkit::sock {
- class NETKIT_API sock_addr final {
+ class NETKIT_API addr final {
std::filesystem::path path{};
std::string hostname{};
std::string ip{};
int port{};
- sock_addr_type type{sock_addr_type::hostname};
- friend sock_addr get_peer(sock_fd_t);
+ addr_type type{addr_type::hostname};
+ friend addr get_peer(fd_t);
- sock_addr() = default;
+ addr() = default;
public:
/**
* @brief Constructs a sock_addr object.
@@ -34,13 +34,13 @@ namespace netkit::sock {
* @param port The port to use.
* @param t The address type (ipv4, ipv6, hostname_ipv4, hostname_ipv6).
*/
- sock_addr(const std::string& hostname, int port, sock_addr_type t);
+ addr(const std::string& hostname, int port, addr_type t);
/**
* @brief Constructs a sock_addr object for a file path.
* @param path The file path to use.
* @throws parsing_error if the path does not exist.
*/
- explicit sock_addr(const std::filesystem::path& path);
+ explicit addr(std::filesystem::path path);
/**
* @brief Check whether the address is IPv4 or IPv6.
* @return True if the address is IPv4, false if it is IPv6 or invalid.
@@ -76,6 +76,11 @@ namespace netkit::sock {
* @return The stored port.
*/
[[nodiscard]] int get_port() const;
- ~sock_addr() = default;
+ /**
+ * @brief Get the stored type.
+ * @return The stored type.
+ */
+ [[nodiscard]] addr_type get_type() const;
+ ~addr() = default;
};
}
diff --git a/include/netkit/sock/sock_addr_type.hpp b/include/netkit/sock/addr_type.hpp
similarity index 78%
rename from include/netkit/sock/sock_addr_type.hpp
rename to include/netkit/sock/addr_type.hpp
index c5933b7..455c8c2 100644
--- a/include/netkit/sock/sock_addr_type.hpp
+++ b/include/netkit/sock/addr_type.hpp
@@ -4,15 +4,15 @@
* Copyright (c) 2025-2026 Jacob Nilsson
* Licensed under the MIT License.
*
- * @file query_builder.hpp
+ * @file addr_type.hpp
* @license MIT
* @note Part of the Netkit library.
* @brief Provides common socket types, options, and related enums and structs.
*/
#pragma once
+#include
#include
-#include
#ifdef NETKIT_WINDOWS
#include
@@ -24,12 +24,12 @@ namespace netkit::sock {
* @note This is a typedef for int, but can be changed to a different type if needed.
*/
#ifdef NETKIT_WINDOWS
- using sock_fd_t = SOCKET;
+ using fd_t = SOCKET;
#elifdef NETKIT_UNIX
- using sock_fd_t = int;
+ using fd_t = int;
#endif
- enum class sock_addr_type {
+ enum class addr_type {
ipv4 = 0, /* IPv4 address */
ipv6 = 1, /* IPv6 address */
hostname_ipv4 = 2, /* Hostname; resolve to IPv4 address */
@@ -41,7 +41,7 @@ namespace netkit::sock {
/**
* @brief Socket types.
*/
- enum class sock_type {
+ enum class type {
tcp, /* TCP socket */
udp, /* UDP socket */
unix, /* UNIX domain socket */
@@ -50,7 +50,7 @@ namespace netkit::sock {
* @brief Socket options.
* @note These options can be used with the sync_sock class to set socket options.
*/
- enum class sock_opt {
+ enum class opt {
reuse_addr = 1 << 0, /* Reuse address option */
no_reuse_addr = 1 << 1, /* Do not reuse address option */
no_delay = 1 << 2, /* Disable Nagle's algorithm (TCP_NODELAY) */
@@ -64,7 +64,7 @@ namespace netkit::sock {
* @brief Socket receive status.
* @note This enum is used to indicate the status of a socket receive operation.
*/
- enum class sock_recv_status {
+ enum class recv_status {
success,
timeout,
closed,
@@ -75,18 +75,18 @@ namespace netkit::sock {
* @brief Result of a socket receive operation.
* @note This struct contains the result data and the status of the receive operation.
*/
- struct sock_recv_result {
+ struct recv_result {
std::string data{};
- sock_recv_status status{sock_recv_status::success};
+ recv_status status{recv_status::success};
};
- inline sock_opt operator|(sock_opt lhs, sock_opt rhs) {
- using T = std::underlying_type_t;
- return static_cast(static_cast(lhs) | static_cast(rhs));
+ inline opt operator|(opt lhs, opt rhs) {
+ using T = std::underlying_type_t;
+ return static_cast(static_cast(lhs) | static_cast(rhs));
}
- inline bool operator&(sock_opt lhs, sock_opt rhs) {
- using T = std::underlying_type_t;
+ inline bool operator&(opt lhs, opt rhs) {
+ using T = std::underlying_type_t;
return static_cast(lhs) & static_cast(rhs);
}
}
\ No newline at end of file
diff --git a/include/netkit/sock/basic_sync_sock.hpp b/include/netkit/sock/basic_sync_sock.hpp
index 88c189f..34fec54 100644
--- a/include/netkit/sock/basic_sync_sock.hpp
+++ b/include/netkit/sock/basic_sync_sock.hpp
@@ -12,9 +12,8 @@
#pragma once
#include
-
-#include
-#include
+#include
+#include
namespace netkit::sock {
/**
@@ -34,16 +33,16 @@ namespace netkit::sock {
virtual std::unique_ptr accept() = 0;
virtual int send(const void* buf, size_t len) = 0;
virtual void send(const std::string& buf) = 0;
- [[nodiscard]] virtual sock_recv_result recv(int timeout_seconds) = 0;
- [[nodiscard]] virtual sock_recv_result recv(int timeout_seconds, const std::string& match) = 0;
- [[nodiscard]] virtual sock_recv_result recv(int timeout_seconds, const std::string& match, size_t eof) = 0;
- [[nodiscard]] virtual sock_recv_result recv(int timeout_seconds, size_t eof) = 0;
- [[nodiscard]] virtual sock_recv_result primitive_recv() = 0;
+ [[nodiscard]] virtual recv_result recv(int timeout_seconds) = 0;
+ [[nodiscard]] virtual recv_result recv(int timeout_seconds, const std::string& match) = 0;
+ [[nodiscard]] virtual recv_result recv(int timeout_seconds, const std::string& match, size_t eof) = 0;
+ [[nodiscard]] virtual recv_result recv(int timeout_seconds, size_t eof) = 0;
+ [[nodiscard]] virtual recv_result primitive_recv() = 0;
[[nodiscard]] virtual std::string overflow_bytes() const = 0;
- virtual sock_addr& get_addr() = 0;
- [[nodiscard]] virtual const sock_addr& get_addr() const = 0;
+ virtual addr& get_addr() = 0;
+ [[nodiscard]] virtual const addr& get_addr() const = 0;
virtual void clear_overflow_bytes() const = 0;
virtual void close() = 0;
- [[nodiscard]] virtual sock_addr get_peer() const = 0;
+ [[nodiscard]] virtual addr get_peer() const = 0;
};
}
\ No newline at end of file
diff --git a/include/netkit/sock/openssl/ssl_sync_sock.hpp b/include/netkit/sock/openssl/ssl_sync_sock.hpp
index ac94d96..17f86f9 100644
--- a/include/netkit/sock/openssl/ssl_sync_sock.hpp
+++ b/include/netkit/sock/openssl/ssl_sync_sock.hpp
@@ -15,6 +15,7 @@
#include
#include
+#include
#ifdef NETKIT_OPENSSL
#include
@@ -26,25 +27,26 @@
namespace netkit::sock {
#ifdef NETKIT_OPENSSL
- class NETKIT_API ssl_sync_sock {
- public:
- enum class mode {
- client, server
- };
+ enum class mode {
+ client,
+ server
+ };
- enum class version {
- SSL2 = SSL2_VERSION,
- SSL3 = SSL3_VERSION,
- TLS_1_1 = TLS1_1_VERSION,
- TLS_1_2 = TLS1_2_VERSION,
- TLS_1_3 = TLS1_3_VERSION,
- };
+ enum class version {
+ SSL_2 = SSL2_VERSION,
+ SSL_3 = SSL3_VERSION,
+ TLS_1_1 = TLS1_1_VERSION,
+ TLS_1_2 = TLS1_2_VERSION,
+ TLS_1_3 = TLS1_3_VERSION,
+ };
- enum class verification {
- peer = SSL_VERIFY_PEER,
- none = SSL_VERIFY_NONE,
- };
+ enum class verification {
+ peer = SSL_VERIFY_PEER,
+ none = SSL_VERIFY_NONE,
+ };
+ class NETKIT_API ssl_sync_sock {
+ public:
explicit ssl_sync_sock(std::unique_ptr underlying,
mode ssl_mode, version ssl_version = version::TLS_1_2,
verification ssl_verification = verification::peer,
@@ -60,14 +62,15 @@ namespace netkit::sock {
std::unique_ptr accept();
int send(const void* buf, size_t len) const;
void send(const std::string& buf) const;
- sock_recv_result recv(int timeout_seconds) const;
- sock_recv_result recv(int timeout_seconds, const std::string& match) const;
- sock_recv_result recv(int timeout_seconds, const std::string& match, size_t eof) const;
- sock_recv_result recv(int timeout_seconds, size_t eof) const;
+ recv_result recv(int timeout_seconds) const;
+ recv_result recv(int timeout_seconds, const std::string& match) const;
+ recv_result recv(int timeout_seconds, const std::string& match, size_t eof) const;
+ recv_result recv(int timeout_seconds, size_t eof) const;
std::string overflow_bytes() const;
void clear_overflow_bytes() const;
void close();
void perform_handshake();
+ [[nodiscard]] netkit::sock::addr get_peer() const;
private:
mutable std::string overflow_;
mutable std::mutex state_mtx_;
@@ -96,7 +99,7 @@ namespace netkit::sock {
void drain_write_bio() const;
void feed_read_bio_blocking() const;
void ensure_ready() const;
- sock_recv_result recv_internal(int, const std::string* match, size_t eof) const;
+ recv_result recv_internal(int, const std::string* match, size_t eof) const;
static void throw_ssl_error(const std::string& msg);
};
#endif
diff --git a/include/netkit/sock/sock_peer.hpp b/include/netkit/sock/sock_peer.hpp
index 3235b8e..579565d 100644
--- a/include/netkit/sock/sock_peer.hpp
+++ b/include/netkit/sock/sock_peer.hpp
@@ -11,8 +11,8 @@
*/
#pragma once
-#include
+#include
namespace netkit::sock {
- sock_addr get_peer(sock_fd_t sockfd);
+ addr get_peer(fd_t sockfd);
}
\ No newline at end of file
diff --git a/include/netkit/sock/sync_sock.hpp b/include/netkit/sock/sync_sock.hpp
index 1e260b1..c29c5e9 100644
--- a/include/netkit/sock/sync_sock.hpp
+++ b/include/netkit/sock/sync_sock.hpp
@@ -22,18 +22,18 @@
#include
#endif
-#include
-#include
+#include
+#include
#include
namespace netkit::sock {
class NETKIT_API sync_sock : public basic_sync_sock {
- sock_addr addr;
- sock_type type{};
+ addr addr_;
+ type type_{};
#ifdef NETKIT_WINDOWS
- sock_fd_t sockfd{INVALID_SOCKET};
+ fd_t sockfd{INVALID_SOCKET};
#else
- sock_fd_t sockfd{-1};
+ fd_t sockfd{-1};
#endif
sockaddr_storage sa_storage{};
bool bound{false};
@@ -42,7 +42,7 @@ namespace netkit::sock {
[[nodiscard]] const sockaddr* get_sa() const;
[[nodiscard]] socklen_t get_sa_len() const;
void prep_sa();
- void set_sock_opts(sock_opt opts) const;
+ void set_sock_opts(opt opts) const;
public:
/**
* @brief Constructs a sync_sock object.
@@ -51,7 +51,7 @@ namespace netkit::sock {
* @param opts The socket options (reuse_addr, no_reuse_addr).
*/
#ifdef NETKIT_UNIX
- sync_sock(const sock_addr& addr, sock_type t, sock_opt opts = sock_opt::no_reuse_addr|sock_opt::no_delay|sock_opt::blocking);
+ sync_sock(const sock::addr& addr, sock::type t, opt opts = opt::no_reuse_addr|opt::no_delay|opt::blocking);
/**
* @brief Constructs a sync_sock object from an existing file descriptor.
* @param existing_fd The existing file descriptor.
@@ -59,14 +59,14 @@ namespace netkit::sock {
* @param t The socket type (tcp, udp, unix).
* @param opts The socket options (reuse_addr, no_reuse_addr).
*/
- sync_sock(int existing_fd, const sock_addr& peer, sock_type t, sock_opt opts = sock_opt::no_reuse_addr|sock_opt::no_delay|sock_opt::blocking);
+ sync_sock(int existing_fd, const sock::addr& peer, sock::type t, opt opts = opt::no_reuse_addr|opt::no_delay|opt::blocking);
#endif
#ifdef NETKIT_WINDOWS
- sync_sock(const sock_addr& addr, sock_type t, sock_opt opts = sock_opt::no_reuse_addr|sock_opt::no_delay|sock_opt::blocking);
+ sync_sock(const sock::addr& addr, sock::type t, opt opts = opt::no_reuse_addr|opt::no_delay|opt::blocking);
#endif
~sync_sock() override;
- sock_addr& get_addr() override;
- [[nodiscard]] const sock_addr& get_addr() const override;
+ sock::addr& get_addr() override;
+ [[nodiscard]] const sock::addr& get_addr() const override;
void connect() override;
/**
* @brief Bind the socket to the address.
@@ -123,32 +123,32 @@ namespace netkit::sock {
* @param eof The number of bytes to read before considering the match complete.
* @return The received data as a sock_recv_result object.
*/
- [[nodiscard]] sock_recv_result recv(int timeout_seconds, const std::string& match, size_t eof) override;
- [[nodiscard]] sock_recv_result primitive_recv() override;
+ [[nodiscard]] recv_result recv(int timeout_seconds, const std::string& match, size_t eof) override;
+ [[nodiscard]] recv_result primitive_recv() override;
/* @brief Receive data from the server.
* @param timeout_seconds The timeout in seconds (-1 means wait indefinitely).
* @return The received data as a sock_recv_result
*/
- [[nodiscard]] sock_recv_result recv(int timeout_seconds) override;
+ [[nodiscard]] recv_result recv(int timeout_seconds) override;
/**
* @brief Receive data from the server until a specific match is found.
* @param timeout_seconds The timeout in seconds (-1 means wait indefinitely).
* @param match The substring to look for in received data.
* @return The received data as a sock_recv_result object.
*/
- [[nodiscard]] sock_recv_result recv(int timeout_seconds, const std::string& match) override;
+ [[nodiscard]] recv_result recv(int timeout_seconds, const std::string& match) override;
/**
* @brief Receive data from the server until a specific match is found or a certain amount of data is received.
* @param timeout_seconds The timeout in seconds (-1 means wait indefinitely).
* @param eof The number of bytes to read before considering the match complete.
* @return The received data as a sock_recv_result object.
*/
- [[nodiscard]] sock_recv_result recv(int timeout_seconds, size_t eof) override;
+ [[nodiscard]] recv_result recv(int timeout_seconds, size_t eof) override;
/**
* @brief Close the socket.
*/
void close() override;
- [[nodiscard]] sock_addr get_peer() const override;
+ [[nodiscard]] sock::addr get_peer() const override;
};
}
\ No newline at end of file
diff --git a/src/c/network/utility.cpp b/src/c/network/utility.cpp
new file mode 100644
index 0000000..cef584b
--- /dev/null
+++ b/src/c/network/utility.cpp
@@ -0,0 +1,54 @@
+#include
+#include
+#include
+#include
+
+extern "C" NETKIT_C_API int netkit_network_is_ipv4(const char* ip, size_t len) {
+ if (!ip || len == 0) {
+ return 0;
+ }
+
+ try {
+ std::string ip_str{ip, len};
+ return netkit::network::is_ipv4(ip_str) ? 1 : 0;
+ } catch (std::exception&) {
+ return 0;
+ } catch (...) {
+ return 0;
+ }
+}
+
+extern "C" NETKIT_C_API int netkit_network_is_ipv6(const char* ip, size_t len) {
+ if (!ip || len == 0) {
+ return 0;
+ }
+
+ try {
+ std::string ip_str{ip, len};
+ return netkit::network::is_ipv6(ip_str) ? 1 : 0;
+ } catch (std::exception&) {
+ return 0;
+ } catch (...) {
+ return 0;
+ }
+}
+
+extern "C" NETKIT_C_API int netkit_network_is_valid_port(int port) {
+ try {
+ return netkit::network::is_valid_port(port) ? 1 : 0;
+ } catch (std::exception&) {
+ return 0;
+ } catch (...) {
+ return 0;
+ }
+}
+
+extern "C" NETKIT_C_API int netkit_network_usable_ipv6_address_exists() {
+ try {
+ return netkit::network::usable_ipv6_address_exists() ? 1 : 0;
+ } catch (std::exception&) {
+ return 0;
+ } catch (...) {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/c/sock/addr.cpp b/src/c/sock/addr.cpp
new file mode 100644
index 0000000..0482566
--- /dev/null
+++ b/src/c/sock/addr.cpp
@@ -0,0 +1,128 @@
+#include
+#include
+#include
+#include
+
+struct netkit_sock_addr {
+ std::unique_ptr impl;
+ netkit_sock_addr_type_t impl_type = SOCK_ADDR_NONE;
+
+ explicit netkit_sock_addr() = default;
+ explicit netkit_sock_addr(const char* file_path) : impl_type(SOCK_ADDR_FILENAME) {
+ impl = std::make_unique(file_path);
+ }
+
+ netkit_sock_addr(const char* hostname, int port, netkit_sock_addr_type_t type) : impl_type(type) {
+ netkit::sock::addr_type t;
+
+ switch (type) {
+ case SOCK_ADDR_FILENAME:
+ t = netkit::sock::addr_type::filename;
+ break;
+ case SOCK_ADDR_HOSTNAME:
+ t = netkit::sock::addr_type::hostname;
+ break;
+ case SOCK_ADDR_HOSTNAME_IPV4:
+ t = netkit::sock::addr_type::hostname_ipv4;
+ break;
+ case SOCK_ADDR_HOSTNAME_IPV6:
+ t = netkit::sock::addr_type::hostname_ipv6;
+ break;
+ case SOCK_ADDR_IPV6:
+ t = netkit::sock::addr_type::ipv6;
+ break;
+ case SOCK_ADDR_IPV4:
+ t = netkit::sock::addr_type::ipv4;
+ break;
+ default:
+ t = netkit::sock::addr_type::hostname;
+ break;
+ }
+
+ impl = std::make_unique(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 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 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 SOCK_ADDR_STATUS_FAILED;
+ }
+
+ std::snprintf(hostname, len, "%s", host.c_str());
+ }
+
+ return SOCK_ADDR_STATUS_SUCCESS;
+ } catch (...) {
+ return SOCK_ADDR_STATUS_FAILED;
+ }
+}
\ No newline at end of file
diff --git a/src/c/sock/openssl/ssl_sync_sock.cpp b/src/c/sock/openssl/ssl_sync_sock.cpp
new file mode 100644
index 0000000..9bb2f32
--- /dev/null
+++ b/src/c/sock/openssl/ssl_sync_sock.cpp
@@ -0,0 +1,327 @@
+#ifdef NETKIT_OPENSSL
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct netkit_sync_sock {
+ std::unique_ptr impl;
+};
+
+struct netkit_ssl_sync_sock {
+ std::unique_ptr impl;
+ netkit_ssl_sync_sock() = default;
+
+ static netkit::sock::mode to_mode(const netkit_ssl_sync_sock_mode_t mode) {
+ switch (mode) {
+ case NETKIT_SSL_SYNC_SOCK_MODE_CLIENT: return netkit::sock::mode::client;
+ case NETKIT_SSL_SYNC_SOCK_MODE_SERVER: return netkit::sock::mode::server;
+ }
+
+ throw std::invalid_argument("invalid mode");
+ }
+
+ static netkit::sock::version to_version(const netkit_ssl_sync_sock_version_t version) {
+ switch (version) {
+ case NETKIT_SSL_SYNC_SOCK_VERSION_SSL_2: return netkit::sock::version::SSL_2;
+ case NETKIT_SSL_SYNC_SOCK_VERSION_SSL_3: return netkit::sock::version::SSL_3;
+ case NETKIT_SSL_SYNC_SOCK_VERSION_TLS_1_1: return netkit::sock::version::TLS_1_1;
+ case NETKIT_SSL_SYNC_SOCK_VERSION_TLS_1_2: return netkit::sock::version::TLS_1_2;
+ case NETKIT_SSL_SYNC_SOCK_VERSION_TLS_1_3: return netkit::sock::version::TLS_1_3;
+ }
+
+ throw std::invalid_argument("invalid version");
+ }
+
+ static netkit::sock::verification to_verification(const netkit_ssl_sync_sock_verification_t verification) {
+ switch (verification) {
+ case NETKIT_SSL_SYNC_SOCK_VERIFICATION_NONE: return netkit::sock::verification::none;
+ case NETKIT_SSL_SYNC_SOCK_VERIFICATION_PEER: return netkit::sock::verification::peer;
+ }
+
+ throw std::invalid_argument("invalid verification");
+ }
+ static netkit::sock::addr_type get_addr_type(netkit_sock_addr_type_t type) {
+ switch (type) {
+ case SOCK_ADDR_FILENAME:
+ return netkit::sock::addr_type::filename;
+ case SOCK_ADDR_IPV4:
+ return netkit::sock::addr_type::ipv4;
+ case SOCK_ADDR_IPV6:
+ return netkit::sock::addr_type::ipv6;
+ case SOCK_ADDR_HOSTNAME_IPV4:
+ return netkit::sock::addr_type::hostname_ipv4;
+ case SOCK_ADDR_HOSTNAME_IPV6:
+ return netkit::sock::addr_type::hostname_ipv6;
+ default:
+ return netkit::sock::addr_type::hostname;
+ }
+ }
+ static netkit_sock_addr_type_t get_addr_type(netkit::sock::addr_type type) {
+ switch (type) {
+ case netkit::sock::addr_type::filename:
+ return SOCK_ADDR_FILENAME;
+ case netkit::sock::addr_type::ipv4:
+ return SOCK_ADDR_IPV4;
+ case netkit::sock::addr_type::ipv6:
+ return SOCK_ADDR_IPV6;
+ case netkit::sock::addr_type::hostname_ipv4:
+ return SOCK_ADDR_HOSTNAME_IPV4;
+ case netkit::sock::addr_type::hostname_ipv6:
+ return SOCK_ADDR_HOSTNAME_IPV6;
+ default:
+ return SOCK_ADDR_HOSTNAME;
+ }
+ }
+
+ netkit_ssl_sync_sock(netkit_sync_sock_t* sock,
+ netkit_ssl_sync_sock_mode_t mode,
+ netkit_ssl_sync_sock_version_t version,
+ netkit_ssl_sync_sock_verification_t verification,
+ const char* cert_path,
+ const char* key_path) {
+
+ if (!sock->impl) {
+ throw std::invalid_argument("invalid sock");
+ }
+
+ this->impl = std::make_unique(
+ std::move(sock->impl),
+ to_mode(mode),
+ to_version(version),
+ to_verification(verification),
+ cert_path == nullptr ? "" : cert_path,
+ key_path == nullptr ? "" : key_path
+ );
+ }
+};
+
+netkit_ssl_sync_sock_t* from_cstyle(std::unique_ptr ptr) {
+ auto* sync_sock = new netkit_ssl_sync_sock();
+
+ sync_sock->impl = std::move(ptr);
+
+ return sync_sock;
+}
+
+extern "C" NETKIT_C_API netkit_ssl_sync_sock_t* netkit_ssl_sync_sock_create(netkit_sync_sock_t* sock,
+ netkit_ssl_sync_sock_mode_t mode,
+ netkit_ssl_sync_sock_version_t version,
+ netkit_ssl_sync_sock_verification_t verification,
+ const char* cert_path,
+ const char* key_path) {
+
+ if (!sock) {
+ return nullptr;
+ }
+
+ try {
+ return new netkit_ssl_sync_sock(sock, mode, version, verification, cert_path, key_path);
+ } catch (...) {
+ return nullptr;
+ }
+}
+
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_destroy(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ delete sock;
+ return SOCK_STATUS_SUCCESS;
+ } catch (...) {
+ }
+
+ return SOCK_STATUS_FAILED;
+}
+
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_connect(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->connect();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_bind(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->bind();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_unbind(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->unbind();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_listen(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->listen();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_listen_n(netkit_ssl_sync_sock_t* sock, int backlog) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->listen(backlog);
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+extern "C" NETKIT_C_API netkit_ssl_sync_sock_t* netkit_ssl_sync_sock_accept(netkit_ssl_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_ssl_sync_sock_send(netkit_ssl_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_ssl_sync_sock_overflow_bytes(netkit_ssl_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_ssl_sync_sock_clear_overflow_bytes(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return;
+ }
+
+ try {
+ sock->impl->clear_overflow_bytes();
+ } catch (...) {}
+}
+
+extern "C" NETKIT_C_API netkit_recv_status_t netkit_ssl_sync_sock_recv(netkit_ssl_sync_sock_t* sock, netkit_recv_result_t* out, int timeout_seconds, const char* match, size_t eof) {
+ if (!sock || !out) {
+ return 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::recv_status::success:
+ return RECV_SUCCESS;
+ case netkit::sock::recv_status::timeout:
+ return RECV_TIMEOUT;
+ case netkit::sock::recv_status::closed:
+ return RECV_CLOSED;
+ default:
+ return RECV_ERROR;
+ }
+ } catch (const std::exception&) {
+ return RECV_ERROR;
+ } catch (...) {
+ return RECV_ERROR;
+ }
+}
+
+extern "C" NETKIT_C_API void netkit_ssl_sync_sock_close(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return;
+ }
+
+ try {
+ sock->impl->close();
+ } catch (...) {}
+}
+extern "C" NETKIT_C_API netkit_sock_addr_t* netkit_ssl_sync_sock_get_peer(netkit_ssl_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_ssl_sync_sock::get_addr_type(impl_peer.get_type()));
+ } catch (...) {
+ return nullptr;
+ }
+}
+
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_ssl_sync_sock_perform_handshake(netkit_ssl_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->perform_handshake();
+ return SOCK_STATUS_SUCCESS;
+ } catch (std::exception& e) {
+ std::cerr << e.what() << std::endl;
+ return SOCK_STATUS_FAILED;
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/c/sock/sync_sock.cpp b/src/c/sock/sync_sock.cpp
new file mode 100644
index 0000000..ef858da
--- /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::type get_type(netkit_sock_type_t type) {
+ switch (type) {
+ case SOCK_UDP:
+ return netkit::sock::type::udp;
+ case SOCK_UNIX:
+ return netkit::sock::type::unix;
+ default:
+ return netkit::sock::type::tcp;
+ }
+ }
+ static netkit::sock::addr_type get_addr_type(netkit_sock_addr_type_t type) {
+ switch (type) {
+ case SOCK_ADDR_FILENAME:
+ return netkit::sock::addr_type::filename;
+ case SOCK_ADDR_IPV4:
+ return netkit::sock::addr_type::ipv4;
+ case SOCK_ADDR_IPV6:
+ return netkit::sock::addr_type::ipv6;
+ case SOCK_ADDR_HOSTNAME_IPV4:
+ return netkit::sock::addr_type::hostname_ipv4;
+ case SOCK_ADDR_HOSTNAME_IPV6:
+ return netkit::sock::addr_type::hostname_ipv6;
+ default:
+ return netkit::sock::addr_type::hostname;
+ }
+ }
+ static netkit_sock_addr_type_t get_addr_type(netkit::sock::addr_type type) {
+ switch (type) {
+ case netkit::sock::addr_type::filename:
+ return SOCK_ADDR_FILENAME;
+ case netkit::sock::addr_type::ipv4:
+ return SOCK_ADDR_IPV4;
+ case netkit::sock::addr_type::ipv6:
+ return SOCK_ADDR_IPV6;
+ case netkit::sock::addr_type::hostname_ipv4:
+ return SOCK_ADDR_HOSTNAME_IPV4;
+ case netkit::sock::addr_type::hostname_ipv6:
+ return SOCK_ADDR_HOSTNAME_IPV6;
+ default:
+ return SOCK_ADDR_HOSTNAME;
+ }
+ }
+ static netkit::sock::opt get_opt(netkit_sock_opt_t type) {
+ netkit::sock::opt opt{};
+
+ if (SOCK_OPT_HAS(type, SOCK_OPT_NONE)) {
+ return netkit::sock::opt::no_reuse_addr | netkit::sock::opt::no_delay | netkit::sock::opt::blocking;
+ }
+
+ if (SOCK_OPT_HAS(type, SOCK_OPT_BLOCKING)) {
+ opt = opt | netkit::sock::opt::blocking;
+ } else if (SOCK_OPT_HAS(type, SOCK_OPT_NON_BLOCKING)) {
+ opt = opt | netkit::sock::opt::no_blocking;
+ }
+ if (SOCK_OPT_HAS(type, SOCK_OPT_KEEP_ALIVE)) {
+ opt = opt | netkit::sock::opt::keep_alive;
+ } else if (SOCK_OPT_HAS(type, SOCK_OPT_NO_KEEP_ALIVE)) {
+ opt = opt | netkit::sock::opt::no_keep_alive;
+ }
+ if (SOCK_OPT_HAS(type, SOCK_OPT_REUSE_ADDR)) {
+ opt = opt | netkit::sock::opt::reuse_addr;
+ } else if (SOCK_OPT_HAS(type, SOCK_OPT_NO_REUSE_ADDR)) {
+ opt = opt | netkit::sock::opt::no_reuse_addr;
+ }
+
+ return opt;
+ }
+ static netkit::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 SOCK_STATUS_FAILED;
+ }
+
+ try {
+ delete sock;
+ return SOCK_STATUS_SUCCESS;
+ } catch (...) {
+ }
+
+ return SOCK_STATUS_FAILED;
+}
+
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_connect(netkit_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->connect();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_bind(netkit_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->bind();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_unbind(netkit_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->unbind();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return SOCK_STATUS_SUCCESS;
+}
+extern "C" NETKIT_C_API netkit_sock_status_t netkit_sync_sock_listen(netkit_sync_sock_t* sock) {
+ if (!sock) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->listen();
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return 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 SOCK_STATUS_FAILED;
+ }
+
+ try {
+ sock->impl->listen(backlog);
+ } catch (...) {
+ return SOCK_STATUS_FAILED;
+ }
+
+ return 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, const char* match, size_t eof) {
+ if (!sock || !out) {
+ return 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::recv_status::success:
+ return RECV_SUCCESS;
+ case netkit::sock::recv_status::timeout:
+ return RECV_TIMEOUT;
+ case netkit::sock::recv_status::closed:
+ return RECV_CLOSED;
+ default:
+ return RECV_ERROR;
+ }
+ } catch (const std::exception& e) {
+ return RECV_ERROR;
+ } catch (...) {
+ return 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/crypto/windows/certs.cpp b/src/crypto/windows/certs.cpp
index bd88c84..72ed973 100644
--- a/src/crypto/windows/certs.cpp
+++ b/src/crypto/windows/certs.cpp
@@ -18,7 +18,7 @@
#include
-bool netkit::crypto::windows::is_outdated(const std::wstring &path) {
+bool netkit::crypto::windows::is_outdated(const std::wstring& path) {
WIN32_FILE_ATTRIBUTE_DATA data;
if (!GetFileAttributesExW(path.c_str(),
@@ -43,7 +43,7 @@ bool netkit::crypto::windows::is_outdated(const std::wstring &path) {
return age > max_age;
}
-bool netkit::crypto::windows::export_certs(const std::wstring &path) {
+bool netkit::crypto::windows::export_certs(const std::wstring& path) {
FILE* f = _wfopen(path.c_str(), L"wb");
if (!f) return false;
diff --git a/src/http/sync_client.cpp b/src/http/sync_client.cpp
index 40cc397..7abf020 100644
--- a/src/http/sync_client.cpp
+++ b/src/http/sync_client.cpp
@@ -16,21 +16,21 @@
#include
std::string netkit::http::client::sync_client::make_request(const std::string& request) const {
- sock::sock_addr addr(hostname, port, sock::sock_addr_type::hostname);
+ sock::addr addr(hostname, port, sock::addr_type::hostname);
std::optional sock{std::nullopt};
#ifdef NETKIT_OPENSSL
if (port == 443) {
- auto tcp_sock = std::make_unique(addr, netkit::sock::sock_type::tcp);
+ auto tcp_sock = std::make_unique(addr, netkit::sock::type::tcp);
sock.emplace(std::in_place_type,
std::move(tcp_sock),
- netkit::sock::ssl_sync_sock::mode::client);
+ netkit::sock::mode::client);
} else {
- sock.emplace(netkit::sock::sync_sock(addr, netkit::sock::sock_type::tcp));
+ sock.emplace(netkit::sock::sync_sock(addr, netkit::sock::type::tcp));
std::get(*sock).connect();
}
#else
- sock.emplace(sock::sync_sock(addr, sock::sock_type::tcp));
+ sock.emplace(sock::sync_sock(addr, sock::type::tcp));
std::get(*sock).connect();
#endif
@@ -42,10 +42,10 @@ std::string netkit::http::client::sync_client::make_request(const std::string& r
std::string s_headers;
while (true) {
auto result = std::visit([&](auto& sckt) { return sckt.recv(timeout, "\r\n\r\n", 0); }, s);
- if (result.status == sock::sock_recv_status::timeout) {
+ if (result.status == sock::recv_status::timeout) {
throw std::runtime_error("timeout while reading headers");
}
- if (result.status == sock::sock_recv_status::closed) {
+ if (result.status == sock::recv_status::closed) {
throw std::runtime_error("connection closed during headers");
}
if (result.data.empty()) {
diff --git a/src/network/ip_list.cpp b/src/network/ip_list.cpp
index 535e747..9580d92 100644
--- a/src/network/ip_list.cpp
+++ b/src/network/ip_list.cpp
@@ -9,10 +9,10 @@
* @note Part of the Netkit library.
* @brief Implementation of the IP address list class.
*/
-#include
+#include
#include
-netkit::sock::ip_list::ip_list(const std::string& any, const std::string& second) {
+netkit::network::ip_list::ip_list(const std::string& any, const std::string& second) {
if (netkit::network::is_ipv4(any) && !any.empty()) {
this->v4 = any;
} else if (netkit::network::is_ipv6(any) && !any.empty()) {
@@ -25,22 +25,22 @@ netkit::sock::ip_list::ip_list(const std::string& any, const std::string& second
}
}
-bool netkit::sock::ip_list::contains_ipv4() const {
+bool netkit::network::ip_list::contains_ipv4() const {
return !this->v4.empty();
}
-bool netkit::sock::ip_list::contains_ipv6() const {
+bool netkit::network::ip_list::contains_ipv6() const {
return !this->v6.empty();
}
-std::string netkit::sock::ip_list::get_ipv4() const {
+std::string netkit::network::ip_list::get_ipv4() const {
return this->v4;
}
-std::string netkit::sock::ip_list::get_ipv6() const {
+std::string netkit::network::ip_list::get_ipv6() const {
return this->v6;
}
-std::string netkit::sock::ip_list::get_ip() const {
+std::string netkit::network::ip_list::get_ip() const {
return this->v6.empty() ? this->v4 : this->v6;
}
\ No newline at end of file
diff --git a/src/sock/sock_addr.cpp b/src/sock/addr.cpp
similarity index 68%
rename from src/sock/sock_addr.cpp
rename to src/sock/addr.cpp
index 99e76e6..8b699a1 100644
--- a/src/sock/sock_addr.cpp
+++ b/src/sock/addr.cpp
@@ -4,22 +4,23 @@
* Copyright (c) 2025-2026 Jacob Nilsson
* Licensed under the MIT License.
*
- * @file sock_addr.cpp
+ * @file addr.cpp
* @license MIT
* @note Part of the Netkit library.
* @brief Implementation of the sock_addr class.
*/
#include
-#include
+#include
+#include
#include
#include
-#include
#include
-#include
#include
+#include
+#include
/* solely for use internally */
-[[nodiscard]] static netkit::sock::ip_list get_a_aaaa_from_hostname(const std::string& hostname) {
+[[nodiscard]] static netkit::network::ip_list get_a_aaaa_from_hostname(const std::string& hostname) {
if (hostname == "localhost") {
return {NETKIT_LOCALHOST_IPV4, NETKIT_LOCALHOST_IPV6};
}
@@ -59,7 +60,7 @@
return {v4, v6};
}
-netkit::sock::sock_addr::sock_addr(const std::string& hostname, int port, sock_addr_type t) :
+netkit::sock::addr::addr(const std::string& hostname, int port, addr_type t) :
hostname(hostname), port(port), type(t) {
const auto resolve_host = [](const std::string& h, bool t) -> std::string {
@@ -72,9 +73,9 @@ netkit::sock::sock_addr::sock_addr(const std::string& hostname, int port, sock_a
}
};
- if (type == sock_addr_type::hostname) {
+ if (type == addr_type::hostname) {
ip = resolve_host(hostname, true);
- type = netkit::sock::sock_addr_type::ipv6;
+ type = netkit::sock::addr_type::ipv6;
if (!netkit::network::usable_ipv6_address_exists()) {
ip.clear();
@@ -82,15 +83,15 @@ netkit::sock::sock_addr::sock_addr(const std::string& hostname, int port, sock_a
if (ip.empty()) {
ip = resolve_host(hostname, false);
- type = netkit::sock::sock_addr_type::ipv4;
+ type = netkit::sock::addr_type::ipv4;
}
- } else if (type == sock_addr_type::hostname_ipv4) {
+ } else if (type == addr_type::hostname_ipv4) {
ip = resolve_host(hostname, false);
- type = netkit::sock::sock_addr_type::ipv4;
- } else if (type == sock_addr_type::hostname_ipv6) {
+ type = netkit::sock::addr_type::ipv4;
+ } else if (type == addr_type::hostname_ipv6) {
ip = resolve_host(hostname, true);
- type = netkit::sock::sock_addr_type::ipv6;
- } else if (type == sock_addr_type::ipv4 || type == sock_addr_type::ipv6) {
+ type = netkit::sock::addr_type::ipv6;
+ } else if (type == addr_type::ipv4 || type == addr_type::ipv6) {
ip = hostname;
} else {
throw ip_error("sock_addr(): invalid address type");
@@ -109,53 +110,53 @@ netkit::sock::sock_addr::sock_addr(const std::string& hostname, int port, sock_a
}
}
-netkit::sock::sock_addr::sock_addr(const std::filesystem::path& path) : path(path), type(sock_addr_type::filename) {
- if (!std::filesystem::exists(path)) {
- throw parsing_error("sock_addr(): path does not exist");
- }
-}
+netkit::sock::addr::addr(std::filesystem::path path) : path(std::move(path)), type(addr_type::filename) {}
-bool netkit::sock::sock_addr::is_ipv4() const noexcept {
- return type == sock_addr_type::ipv4;
+bool netkit::sock::addr::is_ipv4() const noexcept {
+ return type == addr_type::ipv4;
}
-bool netkit::sock::sock_addr::is_ipv6() const noexcept {
- return type == sock_addr_type::ipv6;
+bool netkit::sock::addr::is_ipv6() const noexcept {
+ return type == addr_type::ipv6;
}
-bool netkit::sock::sock_addr::is_file_path() const noexcept {
- return type == sock_addr_type::filename;
+bool netkit::sock::addr::is_file_path() const noexcept {
+ return type == addr_type::filename;
}
-std::string netkit::sock::sock_addr::get_ip() const {
- if (type == sock_addr_type::filename) {
+std::string netkit::sock::addr::get_ip() const {
+ if (type == addr_type::filename) {
throw parsing_error("sock_addr(): cannot get IP from a file path");
}
return this->ip;
}
-[[nodiscard]] std::filesystem::path netkit::sock::sock_addr::get_path() const {
- if (type != sock_addr_type::filename) {
+[[nodiscard]] std::filesystem::path netkit::sock::addr::get_path() const {
+ if (type != addr_type::filename) {
throw parsing_error("sock_addr(): cannot get path from an IP address or hostname");
}
return this->path;
}
-std::string netkit::sock::sock_addr::get_hostname() const {
+std::string netkit::sock::addr::get_hostname() const {
if (hostname.empty()) {
throw parsing_error("hostname is empty, use get_ip() instead");
}
- if (type == sock_addr_type::filename) {
+ if (type == addr_type::filename) {
throw parsing_error("sock_addr(): cannot get hostname from a file path");
}
return hostname;
}
-int netkit::sock::sock_addr::get_port() const {
- if (type == sock_addr_type::filename) {
+int netkit::sock::addr::get_port() const {
+ if (type == addr_type::filename) {
throw parsing_error("sock_addr(): cannot get port from a file path");
}
return port;
+}
+
+netkit::sock::addr_type netkit::sock::addr::get_type() const {
+ return type;
}
\ No newline at end of file
diff --git a/src/sock/openssl/ssl_sync_sock_openssl.cpp b/src/sock/openssl/ssl_sync_sock_openssl.cpp
index a22e723..93291bb 100644
--- a/src/sock/openssl/ssl_sync_sock_openssl.cpp
+++ b/src/sock/openssl/ssl_sync_sock_openssl.cpp
@@ -131,19 +131,19 @@ void netkit::sock::ssl_sync_sock::send(const std::string& buf) const {
static_cast(send(buf.data(), buf.size()));
}
-netkit::sock::sock_recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds) const {
+netkit::sock::recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds) const {
return recv_internal(timeout_seconds, nullptr, 0);
}
-netkit::sock::sock_recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds, const std::string& match) const {
+netkit::sock::recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds, const std::string& match) const {
return recv_internal(timeout_seconds, &match, 0);
}
-netkit::sock::sock_recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds, const std::string& match, size_t eof) const {
+netkit::sock::recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds, const std::string& match, size_t eof) const {
return recv_internal(timeout_seconds, &match, eof);
}
-netkit::sock::sock_recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds, size_t eof) const {
+netkit::sock::recv_result netkit::sock::ssl_sync_sock::recv(int timeout_seconds, size_t eof) const {
return recv_internal(timeout_seconds, nullptr, eof);
}
@@ -155,6 +155,10 @@ void netkit::sock::ssl_sync_sock::clear_overflow_bytes() const {
overflow_.clear();
}
+netkit::sock::addr netkit::sock::ssl_sync_sock::get_peer() const {
+ return underlying_sock_->get_peer();
+}
+
void netkit::sock::ssl_sync_sock::close() {
std::scoped_lock lk(state_mtx_);
@@ -343,7 +347,7 @@ void netkit::sock::ssl_sync_sock::drain_write_bio() const {
void netkit::sock::ssl_sync_sock::feed_read_bio_blocking() const {
auto res = underlying_sock_->primitive_recv();
- if (res.status == sock::sock_recv_status::closed) {
+ if (res.status == sock::recv_status::closed) {
if (!handshake_complete_) {
throw std::runtime_error("Socket closed during TLS handshake");
}
@@ -352,7 +356,7 @@ void netkit::sock::ssl_sync_sock::feed_read_bio_blocking() const {
return;
}
- if (res.status != sock::sock_recv_status::success)
+ if (res.status != sock::recv_status::success)
throw std::runtime_error("Socket read failed");
if (!res.data.empty()) {
@@ -369,9 +373,9 @@ void netkit::sock::ssl_sync_sock::ensure_ready() const {
if (!ssl_) throw std::runtime_error("SSL socket closed");
}
-netkit::sock::sock_recv_result netkit::sock::ssl_sync_sock::recv_internal(int, const std::string* match, size_t eof) const {
+netkit::sock::recv_result netkit::sock::ssl_sync_sock::recv_internal(int, const std::string* match, size_t eof) const {
ensure_ready();
- sock::sock_recv_result result;
+ sock::recv_result result;
if (!overflow_.empty()) {
result.data = std::exchange(overflow_, "");
@@ -392,15 +396,15 @@ netkit::sock::sock_recv_result netkit::sock::ssl_sync_sock::recv_internal(int, c
} else if (err == SSL_ERROR_WANT_WRITE) {
continue;
} else if (err == SSL_ERROR_ZERO_RETURN) {
- result.status = sock::sock_recv_status::closed;
+ result.status = sock::recv_status::closed;
break;
} else {
- result.status = sock::sock_recv_status::error;
+ result.status = sock::recv_status::error;
break;
}
}
- if (match) {
+ if (match && !match->empty()) {
auto pos = result.data.find(*match);
if (pos != std::string::npos) {
overflow_ = result.data.substr(pos + match->size());
diff --git a/src/sock/sock_peer.cpp b/src/sock/sock_peer.cpp
index 347cb14..9fe3f57 100644
--- a/src/sock/sock_peer.cpp
+++ b/src/sock/sock_peer.cpp
@@ -10,11 +10,10 @@
* @brief Implementation of the function to get the peer address of a socket.
*/
#include
-
-#include
-#include
-#include
#include
+#include
+#include
+#include
#ifdef NETKIT_UNIX
#include
#include
@@ -24,7 +23,7 @@
#include
#endif
-netkit::sock::sock_addr netkit::sock::get_peer(sock_fd_t sockfd) {
+netkit::sock::addr netkit::sock::get_peer(fd_t sockfd) {
sockaddr_storage addr_storage{};
socklen_t addr_len = sizeof(addr_storage);
@@ -47,11 +46,11 @@ netkit::sock::sock_addr netkit::sock::get_peer(sock_fd_t sockfd) {
throw ip_error("unsupported address family");
}
- sock_addr addr{};
+ addr addr{};
addr.ip = ip_str;
addr.port = port;
addr.type = (addr_storage.ss_family == AF_INET) ?
- sock_addr_type::ipv4 : sock_addr_type::ipv6;
+ addr_type::ipv4 : addr_type::ipv6;
return addr;
}
diff --git a/src/sock/sync_sock.cpp b/src/sock/sync_sock.cpp
index cfea641..f1e4103 100644
--- a/src/sock/sync_sock.cpp
+++ b/src/sock/sync_sock.cpp
@@ -18,7 +18,6 @@
#include
#include
#include
-#include
#elif NETKIT_UNIX
#include
#include
@@ -35,10 +34,10 @@ const sockaddr* netkit::sock::sync_sock::get_sa() const {
}
socklen_t netkit::sock::sync_sock::get_sa_len() const {
- if (addr.is_ipv4()) return sizeof(sockaddr_in);
- if (addr.is_ipv6()) return sizeof(sockaddr_in6);
- if (addr.is_file_path()) {
- const auto& path = addr.get_path();
+ if (addr_.is_ipv4()) return sizeof(sockaddr_in);
+ if (addr_.is_ipv6()) return sizeof(sockaddr_in6);
+ if (addr_.is_file_path()) {
+ const auto& path = addr_.get_path();
return static_cast(offsetof(sockaddr_un, sun_path) + path.string().size() + 1);
}
@@ -48,19 +47,19 @@ socklen_t netkit::sock::sync_sock::get_sa_len() const {
void netkit::sock::sync_sock::prep_sa() {
memset(&sa_storage, 0, sizeof(sa_storage));
- if (addr.is_ipv4()) {
+ if (addr_.is_ipv4()) {
auto* sa4 = reinterpret_cast(&sa_storage);
sa4->sin_family = AF_INET;
- sa4->sin_port = htons(addr.get_port());
- if (inet_pton(AF_INET, addr.get_ip().c_str(), &sa4->sin_addr) <= 0) {
+ sa4->sin_port = htons(addr_.get_port());
+ if (inet_pton(AF_INET, addr_.get_ip().c_str(), &sa4->sin_addr) <= 0) {
throw parsing_error("invalid IPv4 address");
}
- } else if (addr.is_ipv6()) {
+ } else if (addr_.is_ipv6()) {
auto* sa6 = reinterpret_cast(&sa_storage);
sa6->sin6_family = AF_INET6;
- sa6->sin6_port = htons(addr.get_port());
+ sa6->sin6_port = htons(addr_.get_port());
- std::string ip = addr.get_ip();
+ std::string ip = addr_.get_ip();
unsigned long scope = 0;
auto pos = ip.find('%');
@@ -76,10 +75,10 @@ void netkit::sock::sync_sock::prep_sa() {
if (scope != 0) {
sa6->sin6_scope_id = scope;
}
- } else if (addr.is_file_path()) {
+ } else if (addr_.is_file_path()) {
auto* sa_un = reinterpret_cast(&sa_storage);
sa_un->sun_family = AF_UNIX;
- const auto& path = addr.get_path().string();
+ const auto& path = addr_.get_path().string();
if (path.size() >= sizeof(sa_un->sun_path)) {
throw socket_error("UNIX socket path too long");
}
@@ -89,21 +88,21 @@ void netkit::sock::sync_sock::prep_sa() {
}
}
#ifdef NETKIT_UNIX
-void netkit::sock::sync_sock::set_sock_opts(sock_opt opts) const {
- if (opts & sock_opt::reuse_addr) {
+void netkit::sock::sync_sock::set_sock_opts(opt opts) const {
+ if (opts & opt::reuse_addr) {
::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opts, sizeof(opts));
- } else if (opts & sock_opt::no_reuse_addr) {
+ } else if (opts & opt::no_reuse_addr) {
::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, nullptr, 0);
}
- if (opts & sock_opt::no_delay) {
+ if (opts & opt::no_delay) {
::setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &opts, sizeof(opts));
}
- if (opts & sock_opt::keep_alive) {
+ if (opts & opt::keep_alive) {
::setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &opts, sizeof(opts));
- } else if (opts & sock_opt::no_keep_alive) {
+ } else if (opts & opt::no_keep_alive) {
::setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, nullptr, 0);
}
- if (opts & sock_opt::no_blocking) {
+ if (opts & opt::no_blocking) {
int flags = fcntl(this->sockfd, F_GETFL, 0);
if (flags < 0) {
::close(this->sockfd);
@@ -113,7 +112,7 @@ void netkit::sock::sync_sock::set_sock_opts(sock_opt opts) const {
::close(this->sockfd);
throw socket_error("failed to set socket to non-blocking mode");
}
- } else if (opts & sock_opt::blocking) {
+ } else if (opts & opt::blocking) {
int flags = fcntl(this->sockfd, F_GETFL, 0);
if (flags < 0) {
::close(this->sockfd);
@@ -127,47 +126,47 @@ void netkit::sock::sync_sock::set_sock_opts(sock_opt opts) const {
}
#endif
#ifdef NETKIT_WINDOWS
-void netkit::sock::sync_sock::set_sock_opts(sock_opt opts) const {
- if (opts & sock_opt::reuse_addr) {
+void netkit::sock::sync_sock::set_sock_opts(opt opts) const {
+ if (opts & opt::reuse_addr) {
BOOL optval = TRUE;
if (setsockopt(this->sockfd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&optval), sizeof(optval)) == SOCKET_ERROR) {
closesocket(this->sockfd);
throw socket_error("failed to set SO_REUSEADDR");
}
- } else if (opts & sock_opt::no_reuse_addr) {
+ } else if (opts & opt::no_reuse_addr) {
BOOL optval = FALSE;
if (setsockopt(this->sockfd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&optval), sizeof(optval)) == SOCKET_ERROR) {
closesocket(this->sockfd);
throw socket_error("failed to clear SO_REUSEADDR");
}
}
- if ((opts & sock_opt::no_delay) && type == sock_type::tcp) {
+ if ((opts & opt::no_delay) && type_ == type::tcp) {
BOOL optval = TRUE;
if (setsockopt(this->sockfd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&optval), sizeof(optval)) == SOCKET_ERROR) {
closesocket(this->sockfd);
throw socket_error("failed to set TCP_NODELAY");
}
}
- if (opts & sock_opt::keep_alive) {
+ if (opts & opt::keep_alive) {
BOOL optval = TRUE;
if (setsockopt(this->sockfd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&optval), sizeof(optval)) == SOCKET_ERROR) {
closesocket(this->sockfd);
throw socket_error("failed to set SO_KEEPALIVE");
}
- } else if (opts & sock_opt::no_keep_alive) {
+ } else if (opts & opt::no_keep_alive) {
BOOL optval = FALSE;
if (setsockopt(this->sockfd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&optval), sizeof(optval)) == SOCKET_ERROR) {
closesocket(this->sockfd);
throw socket_error("failed to clear SO_KEEPALIVE");
}
}
- if (opts & sock_opt::no_blocking) {
+ if (opts & opt::no_blocking) {
u_long mode = 1;
if (ioctlsocket(this->sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
closesocket(this->sockfd);
throw socket_error("failed to set socket to non-blocking mode");
}
- } else if (opts & sock_opt::blocking) {
+ } else if (opts & opt::blocking) {
u_long mode = 0;
if (ioctlsocket(this->sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
closesocket(this->sockfd);
@@ -178,16 +177,16 @@ void netkit::sock::sync_sock::set_sock_opts(sock_opt opts) const {
#endif
#ifdef NETKIT_UNIX
-netkit::sock::sync_sock::sync_sock(const sock_addr& addr, sock_type t, sock_opt opts) : addr(addr), type(t) {
+netkit::sock::sync_sock::sync_sock(const sock::addr& addr, sock::type t, opt opts) : addr_(addr), type_(t) {
this->sockfd = -1;
if (addr.get_ip().empty() && !addr.is_file_path()) {
throw socket_error("IP address/file path is empty");
}
- if (t != sock_type::unix) {
+ if (t != type::unix) {
this->sockfd = ::socket(addr.is_ipv6() ? AF_INET6 : AF_INET,
- t == sock_type::tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
+ t == type::tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
} else {
this->sockfd = ::socket(AF_UNIX, SOCK_STREAM, 0);
}
@@ -205,8 +204,8 @@ netkit::sock::sync_sock::sync_sock(const sock_addr& addr, sock_type t, sock_opt
this->prep_sa();
}
-netkit::sock::sync_sock::sync_sock(int existing_fd, const sock_addr& peer, sock_type t, sock_opt opts)
- : addr(peer), type(t), sockfd(existing_fd) {
+netkit::sock::sync_sock::sync_sock(int existing_fd, const sock::addr& peer, sock::type t, opt opts)
+ : addr_(peer), type_(t), sockfd(existing_fd) {
if (sockfd < 0) throw socket_error("invalid fd");
if (this->sockfd >= 0) {
this->set_sock_opts(opts);
@@ -218,10 +217,10 @@ netkit::sock::sync_sock::sync_sock(int existing_fd, const sock_addr& peer, sock_
}
#endif
#ifdef NETKIT_WINDOWS
-netkit::sock::sync_sock::sync_sock(const sock_addr& addr, sock_type t, sock_opt opts)
- : addr(addr), type(t) {
+netkit::sock::sync_sock::sync_sock(const sock::addr& in_addr, sock::type t, opt opts)
+ : addr_(in_addr), type_(t) {
- if (addr.get_ip().empty() && !addr.is_file_path()) {
+ if (this->addr_.get_ip().empty() && !this->addr_.is_file_path()) {
throw socket_error("IP address or file path is empty");
}
@@ -229,10 +228,10 @@ netkit::sock::sync_sock::sync_sock(const sock_addr& addr, sock_type t, sock_opt
int sock_type = SOCK_STREAM;
int protocol = 0;
- if (t != sock_type::unix) {
- domain = addr.is_ipv6() ? AF_INET6 : AF_INET;
- sock_type = (t == sock_type::tcp) ? SOCK_STREAM : SOCK_DGRAM;
- protocol = (t == sock_type::tcp) ? IPPROTO_TCP : IPPROTO_UDP;
+ if (t != type::unix) {
+ domain = this->addr_.is_ipv6() ? AF_INET6 : AF_INET;
+ sock_type = (t == type::tcp) ? SOCK_STREAM : SOCK_DGRAM;
+ protocol = (t == type::tcp) ? IPPROTO_TCP : IPPROTO_UDP;
} else {
protocol = 0;
}
@@ -270,12 +269,12 @@ netkit::sock::sync_sock::~sync_sock() {
}
#endif
-netkit::sock::sock_addr& netkit::sock::sync_sock::get_addr() {
- return this->addr;
+netkit::sock::addr& netkit::sock::sync_sock::get_addr() {
+ return this->addr_;
}
-const netkit::sock::sock_addr& netkit::sock::sync_sock::get_addr() const {
- return this->addr;
+const netkit::sock::addr& netkit::sock::sync_sock::get_addr() const {
+ return this->addr_;
}
#ifdef NETKIT_UNIX
void netkit::sock::sync_sock::connect() {
@@ -366,7 +365,7 @@ std::unique_ptr netkit::sock::sync_sock::accept()
auto peer = sock::get_peer(client_sockfd);
- return std::make_unique(client_sockfd, peer, this->type);
+ return std::make_unique(client_sockfd, peer, this->type_);
}
#endif
#ifdef NETKIT_WINDOWS
@@ -381,7 +380,7 @@ std::unique_ptr netkit::sock::sync_sock::accept()
}
auto peer = sock::get_peer(client_sockfd);
- auto handle = std::make_unique(peer, this->type);
+ auto handle = std::make_unique(peer, this->type_);
handle->sockfd = client_sockfd;
return handle;
@@ -435,7 +434,7 @@ void netkit::sock::sync_sock::clear_overflow_bytes() const {
old_bytes.clear();
}
#ifdef NETKIT_UNIX
-netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, const std::string& match, size_t eof) {
+netkit::sock::recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, const std::string& match, size_t eof) {
std::string data = old_bytes;
old_bytes.clear();
@@ -444,7 +443,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
old_bytes = data.substr(eof);
data.resize(eof);
}
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
if (!match.empty()) {
@@ -452,7 +451,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
if (pos != std::string::npos) {
old_bytes = data.substr(pos + match.size());
data.resize(pos + match.size());
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
}
@@ -462,7 +461,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
auto elapsed = std::chrono::steady_clock::now() - start;
auto remaining = std::chrono::seconds(timeout_seconds) - elapsed;
if (remaining <= std::chrono::seconds(0) && timeout_seconds != -1) {
- return {data, sock_recv_status::timeout};
+ return {data, recv_status::timeout};
}
timeval tv{};
@@ -477,7 +476,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
int ret = ::select(this->sockfd + 1, &readfds, nullptr, nullptr,
timeout_seconds == -1 ? nullptr : &tv);
if (ret < 0) throw socket_error("select() failed");
- if (ret == 0) return {data, sock_recv_status::timeout};
+ if (ret == 0) return {data, recv_status::timeout};
if (FD_ISSET(this->sockfd, &readfds)) {
size_t bytes_to_read = 8192;
@@ -491,7 +490,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
if (errno == EINTR) continue;
throw socket_error("recv() failed");
}
- if (received == 0) return {data, sock_recv_status::closed};
+ if (received == 0) return {data, recv_status::closed};
data.append(buf, static_cast(received));
@@ -500,7 +499,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
data.resize(eof);
}
if (eof != 0 && data.length() >= eof) {
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
if (!match.empty()) {
@@ -508,21 +507,21 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
if (pos != std::string::npos) {
old_bytes = data.substr(pos + match.size());
data.resize(pos + match.size());
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
}
}
}
}
-netkit::sock::sock_recv_result netkit::sock::sync_sock::primitive_recv() {
+netkit::sock::recv_result netkit::sock::sync_sock::primitive_recv() {
for (;;) {
char buf[8192];
ssize_t n = ::recv(this->sockfd, buf, sizeof(buf), 0);
if (n > 0)
- return {{buf, buf + n}, sock_recv_status::success};
+ return {{buf, buf + n}, recv_status::success};
if (n == 0)
- return {{}, sock_recv_status::closed};
+ return {{}, recv_status::closed};
if (errno == EINTR)
continue;
throw netkit::socket_error("recv failed");
@@ -530,7 +529,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::primitive_recv() {
}
#endif
#ifdef NETKIT_WINDOWS
-netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, const std::string& match, size_t eof) {
+netkit::sock::recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, const std::string& match, size_t eof) {
std::string data = old_bytes;
old_bytes.clear();
@@ -539,7 +538,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
old_bytes = data.substr(eof);
data.resize(eof);
}
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
if (!match.empty()) {
@@ -547,7 +546,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
if (pos != std::string::npos) {
old_bytes = data.substr(pos + match.size());
data.resize(pos + match.size());
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
}
@@ -560,7 +559,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
remaining = std::chrono::hours(24 * 365 * 100);
}
if (remaining <= std::chrono::seconds(0) && timeout_seconds != -1) {
- return {data, sock_recv_status::timeout};
+ return {data, recv_status::timeout};
}
timeval tv{};
@@ -578,7 +577,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
throw socket_error("select() failed");
}
if (ret == 0) {
- return {data, sock_recv_status::timeout};
+ return {data, recv_status::timeout};
}
if (FD_ISSET(this->sockfd, &readfds)) {
@@ -595,7 +594,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
throw socket_error("recv() failed");
}
if (received == 0) {
- return {data, sock_recv_status::closed};
+ return {data, recv_status::closed};
}
data.append(buf, static_cast(received));
@@ -605,7 +604,7 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
data.resize(eof);
}
if (eof != 0 && data.length() >= eof) {
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
if (!match.empty()) {
@@ -613,23 +612,23 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_s
if (pos != std::string::npos) {
old_bytes = data.substr(pos + match.size());
data.resize(pos + match.size());
- return {data, sock_recv_status::success};
+ return {data, recv_status::success};
}
}
}
}
}
-netkit::sock::sock_recv_result netkit::sock::sync_sock::primitive_recv() {
+netkit::sock::recv_result netkit::sock::sync_sock::primitive_recv() {
constexpr size_t buffer_size = 8192;
char buf[buffer_size];
for (;;) {
int n = ::recv(this->sockfd, buf, static_cast(buffer_size), 0);
if (n > 0) {
- return {std::string(buf, buf + n), sock_recv_status::success};
+ return {std::string(buf, buf + n), recv_status::success};
} else if (n == 0) {
- return {{}, sock_recv_status::closed};
+ return {{}, recv_status::closed};
} else {
int err = WSAGetLastError();
if (err == WSAEINTR || err == WSAEWOULDBLOCK || err == WSAEINPROGRESS) {
@@ -641,15 +640,15 @@ netkit::sock::sock_recv_result netkit::sock::sync_sock::primitive_recv() {
}
#endif
-netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_seconds) {
+netkit::sock::recv_result netkit::sock::sync_sock::recv(const int timeout_seconds) {
return recv(timeout_seconds, "", 0);
}
-netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, const std::string& match) {
+netkit::sock::recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, const std::string& match) {
return this->recv(timeout_seconds, match, 0);
}
-netkit::sock::sock_recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, size_t eof) {
+netkit::sock::recv_result netkit::sock::sync_sock::recv(const int timeout_seconds, size_t eof) {
return this->recv(timeout_seconds, "", eof);
}
@@ -678,6 +677,6 @@ void netkit::sock::sync_sock::close() {
sockfd = INVALID_SOCKET;
}
#endif
-[[nodiscard]] netkit::sock::sock_addr netkit::sock::sync_sock::get_peer() const {
+[[nodiscard]] netkit::sock::addr netkit::sock::sync_sock::get_peer() const {
return netkit::sock::get_peer(this->sockfd);
}
\ No newline at end of file
diff --git a/src/utility.cpp b/src/utility.cpp
index bab19a7..2912179 100644
--- a/src/utility.cpp
+++ b/src/utility.cpp
@@ -69,7 +69,7 @@ namespace netkit::utility {
if (isalnum(ch) || ch == '-' || ch == '_' || ch == '.' || ch == '~') {
ret += ch;
} else if (ch == ' ') {
- ret += '+';
+ ret += "%20";
} else {
ret += '%';
ret += "0123456789ABCDEF"[ch >> 4];
diff --git a/tests/c/definitions.h b/tests/c/definitions.h
new file mode 100644
index 0000000..9d0fcec
--- /dev/null
+++ b/tests/c/definitions.h
@@ -0,0 +1,18 @@
+#ifndef DEFINITIONS_H
+#define DEFINITIONS_H
+
+#include
+#include
+
+#define ASSERT(expr) do { \
+if (expr) { \
+printf("[ASSERT PASS] %s:%d: %s\n", \
+__FILE__, __LINE__, #expr); \
+} else { \
+fprintf(stderr, "[ASSERT FAIL] %s:%d: %s\n", \
+__FILE__, __LINE__, #expr); \
+abort(); \
+} \
+} while(0)
+
+#endif // DEFINITIONS_H
\ No newline at end of file
diff --git a/tests/c/test_main.c b/tests/c/test_main.c
new file mode 100644
index 0000000..42cf442
--- /dev/null
+++ b/tests/c/test_main.c
@@ -0,0 +1,7 @@
+#include "test_network_utility.h"
+#include "test_sock_addr.h"
+
+int main(void) {
+ test_network_utility();
+ test_sock_addr();
+}
\ No newline at end of file
diff --git a/tests/c/test_network_utility.c b/tests/c/test_network_utility.c
new file mode 100644
index 0000000..5396272
--- /dev/null
+++ b/tests/c/test_network_utility.c
@@ -0,0 +1,22 @@
+#include
+#include
+#include
+#include "definitions.h"
+
+void test_network_utility(void) {
+ char* ipv4 = "0.1.0.1";
+ ASSERT(netkit_network_is_ipv4(ipv4, strlen(ipv4)) == 1);
+
+ char* ipv6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ ASSERT(netkit_network_is_ipv6(ipv6, strlen(ipv6)) == 1);
+
+ char* invalid_ip = "999.999.999.999";
+ ASSERT(netkit_network_is_ipv4(invalid_ip, strlen(invalid_ip)) == 0);
+
+ char* invalid_ipv6 = "2001:0db8:85a3:0000:0000:8a2e:0370:zzzz";
+ ASSERT(netkit_network_is_ipv6(invalid_ipv6, strlen(invalid_ipv6)) == 0);
+
+ ASSERT(netkit_network_is_valid_port(8080) == 1);
+ ASSERT(netkit_network_is_valid_port(70000) == 0);
+ ASSERT(netkit_network_usable_ipv6_address_exists() == 1 || netkit_network_usable_ipv6_address_exists() == 0);
+}
\ No newline at end of file
diff --git a/tests/c/test_network_utility.h b/tests/c/test_network_utility.h
new file mode 100644
index 0000000..30857ed
--- /dev/null
+++ b/tests/c/test_network_utility.h
@@ -0,0 +1,6 @@
+#ifndef TEST_NETWORK_UTILITY_H
+#define TEST_NETWORK_UTILITY_H
+
+void test_network_utility(void);
+
+#endif
\ 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..380c0bd
--- /dev/null
+++ b/tests/c/test_sock_addr.c
@@ -0,0 +1,33 @@
+#include "definitions.h"
+
+#include
+#include
+#include
+#include
+#include
+
+void test_sock_addr(void) {
+ netkit_sock_addr_t* addr = netkit_sock_addr_create("google.com", 80, 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(SOCK_ADDR_HOSTNAME == netkit_sock_addr_get_type(addr));
+
+ free(hostname);
+ netkit_sock_addr_destroy(addr);
+
+ addr = netkit_sock_addr_create_unix("/tmp/socket");
+
+ ASSERT(addr != NULL);
+ ASSERT(netkit_sock_addr_get_type(addr) == SOCK_ADDR_FILENAME);
+
+ netkit_sock_addr_destroy(addr);
+}
\ No newline at end of file
diff --git a/tests/c/test_sock_addr.h b/tests/c/test_sock_addr.h
new file mode 100644
index 0000000..5ae4743
--- /dev/null
+++ b/tests/c/test_sock_addr.h
@@ -0,0 +1,6 @@
+#ifndef TEST_SOCK_ADDR_H
+#define TEST_SOCK_ADDR_H
+
+void test_sock_addr(void);
+
+#endif
\ No newline at end of file
diff --git a/tests/test.cpp b/tests/test.cpp
index b8a8a58..0d72619 100644
--- a/tests/test.cpp
+++ b/tests/test.cpp
@@ -1,33 +1,131 @@
#include
#include
-#include
#include
+#define CATCH_CONFIG_MAIN
+#include
+#include
+#include
-int main() {
- netkit::sock::sock_addr addr("google.com", 443, netkit::sock::sock_addr_type::hostname);
- std::unique_ptr _sock = std::make_unique(
- addr, netkit::sock::sock_type::tcp);
-
- netkit::sock::ssl_sync_sock sock((std::move(_sock)),
- netkit::sock::ssl_sync_sock::mode::client,
- netkit::sock::ssl_sync_sock::version::TLS_1_2,
- netkit::sock::ssl_sync_sock::verification::peer
- );
-
- sock.connect();
- sock.perform_handshake();
-
- constexpr std::string_view request = "GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n";
- sock.send(request.data());
- std::string response = sock.recv(-1).data;
- sock.close();
-
- std::ofstream file("response.txt");
- if (file.is_open()) {
- file << response;
- file.close();
- } else {
- std::cerr << "Failed to open file" << std::endl;
- }
- std::cout << "Response written to response.txt" << std::endl;
+TEST_CASE("Ensure addr works", "[sock_addr]") {
+ netkit::sock::addr addr("google.com", 443, netkit::sock::addr_type::hostname);
+ REQUIRE(addr.get_hostname() == "google.com");
+ REQUIRE(addr.get_ip().empty() == false);
+ REQUIRE(addr.get_port() == 443);
+ REQUIRE(addr.is_file_path() == false);
+
+ netkit::sock::addr addr2("/tmp/socket");
+ REQUIRE(addr2.get_path().string() == "/tmp/socket");
+ REQUIRE(addr2.is_file_path() == true);
+}
+
+TEST_CASE("Ensure network utility functions work", "[network_utility]") {
+ std::string valid_v4 = "1.0.1.0";
+ std::string invalid_v4 = "999.0.0.1";
+ std::string valid_v6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ std::string invalid_v6 = "2001:0db8:85a3:0000:0000:8a2e:0370:zzzz";
+ REQUIRE(netkit::network::is_ipv4(valid_v4) == true);
+ REQUIRE(netkit::network::is_ipv4(invalid_v4) == false);
+ REQUIRE(netkit::network::is_ipv6(valid_v6) == true);
+ REQUIRE(netkit::network::is_ipv6(invalid_v6) == false);
+ REQUIRE(netkit::network::is_valid_port(80) == true);
+ REQUIRE(netkit::network::is_valid_port(70000) == false);
}
+TEST_CASE("Ensure general utility functions work", "[utility]") {
+ REQUIRE(netkit::utility::convert_unix_millis_to_gmt(1700000000000) == "Tue, 14 Nov 2023 22:13:20 GMT");
+ REQUIRE(netkit::utility::generate_random_string().length() == 64);
+ REQUIRE(netkit::utility::generate_random_string(256).length() == 256);
+
+ std::set generated_strings;
+ for (int i = 0; i < 10000; i++) {
+ std::string rand_str = netkit::utility::generate_random_string(32);
+ REQUIRE(generated_strings.find(rand_str) == generated_strings.end());
+ generated_strings.insert(rand_str);
+ }
+
+ std::unordered_map fields = netkit::utility::parse_fields("key1=value1&key2=value2&key3=value3");
+ REQUIRE(fields.size() == 3);
+ REQUIRE(fields["key1"] == "value1");
+ REQUIRE(fields["key2"] == "value2");
+ REQUIRE(fields["key3"] == "value3");
+
+ std::unordered_map fields2 = netkit::utility::parse_fields("key1=value1&key2=&=value3&key4=value4&&key5=value5");
+ REQUIRE(fields2.size() == 5);
+ REQUIRE(fields2["key1"] == "value1");
+ REQUIRE(fields2["key2"].empty());
+ REQUIRE(fields2["key4"] == "value4");
+ REQUIRE(fields2["key5"] == "value5");
+
+ std::unordered_map content_types = {
+ {".html", "text/html"},
+ {".css", "text/css"},
+ {".js", "text/javascript"},
+ {".png", "image/png"},
+ {".jpg", "image/jpeg"},
+ {".jpeg", "image/jpeg"},
+ {".gif", "image/gif"},
+ {".txt", "text/plain"},
+ {".json", "application/json"},
+ {".xml", "application/xml"},
+ {".pdf", "application/pdf"},
+ };
+ for (const auto& [ext, expected_type] : content_types) {
+ REQUIRE(netkit::utility::get_appropriate_content_type(ext) == expected_type);
+ }
+
+ REQUIRE(netkit::utility::url_encode("hello world!") == "hello%20world%21");
+ REQUIRE(netkit::utility::url_decode("hello%20world%21") == "hello world!");
+
+ std::string original = "This is a test! 1234 ~`!@#$%^&*()_+-={}|[]\\:\";'<>?,./";
+ std::string encoded = netkit::utility::url_encode(original);
+ std::string decoded = netkit::utility::url_decode(encoded);
+
+ REQUIRE(decoded == original);
+
+ std::string encoded_encoded = netkit::utility::url_encode(encoded);
+ REQUIRE(encoded_encoded != encoded);
+ REQUIRE(netkit::utility::url_decode(encoded_encoded) == encoded);
+ REQUIRE(netkit::utility::url_decode(netkit::utility::url_decode(encoded_encoded)) == original);
+
+ auto output = netkit::utility::get_standard_cache_location();
+#ifdef NETKIT_WINDOWS
+ REQUIRE(output.find(std::getenv("LOCALAPPDATA")) != std::string::npos);
+#elif defined(NETKIT_MACOS)
+ REQUIRE(output.find("Library/Caches") != std::string::npos);
+#else
+ REQUIRE(output.find(".cache") != std::string::npos);
+#endif
+
+ std::string chunked_http = "4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n";
+
+ REQUIRE(netkit::utility::decode_chunked(chunked_http) == "Wikipedia in\r\n\r\nchunks.");
+
+ std::stringstream ss;
+ netkit::utility::write_string(ss, "Hello, World!");
+ REQUIRE(netkit::utility::read_string(ss) == "Hello, World!");
+
+ ss = {};
+ int32_t int_val = 123456789;
+ netkit::utility::write(ss, int_val);
+ ss.seekg(0);
+ int32_t int_val_read;
+ netkit::utility::read(ss, int_val_read);
+ REQUIRE(int_val == int_val_read);
+
+ std::vector data = {'A', 'B', 'C', 'D', 'E', 'F'};
+ std::string b64_encoded = netkit::utility::encode_base64(data);
+ REQUIRE(b64_encoded == "QUJDREVG");
+
+ std::vector b64_decoded = netkit::utility::decode_base64(b64_encoded);
+ REQUIRE(b64_decoded == data);
+
+ std::string to_split = "one,two,three,four,five";
+ auto tokens = netkit::utility::split(to_split, ",");
+ REQUIRE(tokens.size() == 5);
+ REQUIRE(tokens[0] == "one");
+ REQUIRE(tokens[1] == "two");
+ REQUIRE(tokens[2] == "three");
+ REQUIRE(tokens[3] == "four");
+ REQUIRE(tokens[4] == "five");
+ std::string joined = netkit::utility::join(tokens, ",");
+ REQUIRE(joined == to_split);
+}
\ No newline at end of file