solidc
Robust collection of general-purpose cross-platform C libraries and data structures designed for rapid and safe development in C
Loading...
Searching...
No Matches
socket.c
1#include "../include/socket.h"
2
3#include <errno.h>
4#include <stdint.h>
5#include <stdlib.h>
6#include <string.h>
7
8#ifdef _WIN32
9static void printLastErrorMessage(const char* prefix) {
10 LPSTR errorText = NULL;
11 // create format flags
12 DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
13
14 FormatMessageA(flags, NULL, (DWORD)WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errorText,
15 0, NULL);
16
17 if (errorText != NULL) {
18 fprintf(stderr, "%s failed with error %d: %s\n", prefix, WSAGetLastError(), errorText);
19 LocalFree(errorText);
20 } else {
21 fprintf(stderr, "%s failed with unknown error %d\n", prefix, WSAGetLastError());
22 }
23}
24#endif
25
26#ifdef _WIN32
27void socket_initialize() {
28 WSADATA wsaData;
29 WSAStartup(MAKEWORD(2, 2), &wsaData);
30}
31
32void socket_cleanup() { WSACleanup(); }
33#else
34void socket_initialize(void) {
35 // do nothing
36}
37void socket_cleanup(void) {
38 // do nothing
39}
40#endif
41
42// Create a socket
43Socket* socket_create(int domain, int type, int protocol) {
44 Socket* sock = (Socket*)malloc(sizeof(Socket));
45 if (!sock) {
46 perror("malloc");
47 return NULL;
48 }
49#ifdef _WIN32
50 sock->handle = socket(domain, type, protocol);
51 if (sock->handle == INVALID_SOCKET) {
52 free(sock);
53 // print the error message
54 printLastErrorMessage("socket");
55 return NULL;
56 }
57#else
58 sock->handle = socket(domain, type, protocol);
59 if (sock->handle == -1) {
60 free(sock);
61 return NULL;
62 }
63#endif
64 return sock;
65}
66
67// Close a socket and free the memory
68int socket_close(Socket* sock) {
69 int ret = -1;
70 if (sock) {
71#ifdef _WIN32
72 ret = closesocket(sock->handle);
73#else
74 ret = close(sock->handle);
75#endif
76 free(sock);
77 }
78 return ret;
79}
80
81// Bind a socket to an address
82int socket_bind(Socket* sock, const struct sockaddr* addr, socklen_t addrlen) {
83 int ret = -1;
84#ifdef _WIN32
85 if ((ret = bind(sock->handle, addr, addrlen)) != 0) {
86 printLastErrorMessage("bind");
87 }
88#else
89 ret = bind(sock->handle, addr, addrlen);
90 if (ret != 0) {
91 perror("bind");
92 ret = 0;
93 }
94#endif
95 return ret;
96}
97
98int socket_listen(Socket* sock, int backlog) { return listen(sock->handle, backlog); }
99
100// Accept an incoming connection
101Socket* socket_accept(Socket* sock, struct sockaddr* addr, socklen_t* addrlen) {
102 Socket* client = (Socket*)malloc(sizeof(Socket));
103 if (!client) {
104 perror("malloc");
105 return NULL;
106 }
107#ifdef _WIN32
108 client->handle = accept(sock->handle, addr, addrlen);
109 if (client->handle == INVALID_SOCKET) {
110 free(client);
111 printLastErrorMessage("accept");
112 return NULL;
113 }
114#else
115 client->handle = accept(sock->handle, addr, addrlen);
116 if (client->handle == -1) {
117 perror("accept");
118 free(client);
119 return NULL;
120 }
121#endif
122 return client;
123}
124
125// Connect to a remote socket
126int socket_connect(Socket* sock, const struct sockaddr* addr, socklen_t addrlen) {
127 int ret = -1;
128 if (connect(sock->handle, addr, addrlen) == 0) {
129 ret = 0;
130 } else {
131 perror("connect");
132 }
133 return ret;
134}
135
136/*
137Read from a socket. Returns the number of bytes read, or -1 on error.
138buffer: Points to a buffer where the message should be stored.
139size: Specifies the length in bytes of the buffer pointed to by the buffer
140argument.
141
142flags: Specifies the type of message reception.
143See man 2 recv for more information.
144 */
145ssize_t socket_recv(Socket* sock, void* buffer, size_t size, int flags) {
146 ssize_t bytes = 0;
147 bytes = recv(sock->handle, buffer, size, flags);
148 return bytes;
149}
150
151/* Write to a socket. Returns the number of bytes written, or -1 on error.
152
153buffer: Points to the buffer containing the message to send.
154length: Specifies the length of the message in bytes.
155flags: Specifies the type of message transmission
156See man 2 send for more information.
157*/
158ssize_t socket_send(Socket* sock, const void* buffer, size_t size, int flags) {
159 return send(sock->handle, buffer, size, flags);
160}
161
162// Get the socket file descriptor
163int socket_fd(Socket* sock) { return sock->handle; }
164
165int socket_error(void) {
166#ifdef _WIN32
167 return WSAGetLastError();
168#else
169 return errno;
170#endif
171}
172
173void socket_strerror(int err, char* buffer, size_t size) {
174 if (!buffer || size == 0) return;
175
176#ifdef _WIN32
177 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD)err, 0, buffer, (DWORD)size, NULL);
178 /* Ensure NUL-termination (FormatMessageA does not guarantee it on short buffers). */
179 buffer[size - 1] = '\0';
180#else
181#if defined(__GLIBC__) || defined(__linux__)
182 /*
183 * GNU strerror_r: returns char* which may point to a static string
184 * rather than `buffer`. Copy it into the caller's buffer if needed.
185 */
186 char* msg = strerror_r(err, buffer, size);
187 if (msg != buffer && msg != NULL) {
188 strncpy(buffer, msg, size - 1);
189 buffer[size - 1] = '\0';
190 }
191#else
192 /* POSIX strerror_r (macOS, BSD): always writes into buffer, returns int. */
193 strerror_r(err, buffer, size);
194 buffer[size - 1] = '\0'; /* ensure NUL-termination */
195#endif
196#endif
197}
198
199// Get the socket option
200int socket_get_option(Socket* sock, int level, int optname, void* optval, socklen_t* optlen) {
201 int ret = -1;
202#ifdef _WIN32
203 ret = getsockopt(sock->handle, level, optname, (char*)optval, optlen);
204#else
205 ret = getsockopt(sock->handle, level, optname, optval, optlen);
206#endif
207 return ret;
208}
209
210// Set the socket option
211int socket_set_option(Socket* sock, int level, int optname, const void* optval, socklen_t optlen) {
212 int ret = -1;
213
214#ifdef _WIN32
215 ret = setsockopt(sock->handle, level, optname, (const char*)optval, optlen);
216#else
217 ret = setsockopt(sock->handle, level, optname, optval, optlen);
218#endif
219 return ret;
220}
221
222int socket_reuse_port(Socket* sock, int enable) {
223#ifdef _WIN32
224 // Enable SO_REUSEADDR
225 if (setsockopt(sock->handle, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(int)) == SOCKET_ERROR) {
226 perror("setsockopt");
227 fprintf(stderr, "setsockopt SO_REUSEADDR failed\n");
228 return 1;
229 }
230
231 // Enable SO_EXCLUSIVEADDRUSE
232 if (setsockopt(sock->handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&enable, sizeof(int)) == SOCKET_ERROR) {
233 perror("setsockopt");
234 fprintf(stderr, "setsockopt SO_EXCLUSIVEADDRUSE failed\n");
235 return 1;
236 }
237 return 0;
238#else
239 int ret = 0;
240 // Always set SO_REUSEADDR
241 if (setsockopt(sock->handle, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) != 0) {
242 ret = -1;
243 }
244
245// SO_REUSEPORT is available on Linux and modern BSD/macOS
246#ifdef SO_REUSEPORT
247 if (setsockopt(sock->handle, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) != 0) {
248 ret = -1;
249 }
250#endif
251
252 return ret;
253#endif
254}
255
256// Get the socket address
257int socket_get_address(Socket* sock, struct sockaddr* addr, socklen_t* addrlen) {
258 int ret = -1;
259#ifdef _WIN32
260 ret = getsockname(sock->handle, addr, addrlen);
261#else
262 ret = getsockname(sock->handle, addr, addrlen);
263#endif
264 return ret;
265}
266
267// Get the socket peer address
268int socket_get_peer_address(Socket* sock, struct sockaddr* addr, socklen_t* addrlen) {
269 return getpeername(sock->handle, addr, addrlen);
270}
271
272int socket_type(Socket* sock) {
273 int ret = -1;
274 int type = 0;
275 socklen_t len = sizeof(type);
276 if (socket_get_option(sock, SOL_SOCKET, SO_TYPE, &type, &len) == 0) {
277 ret = type;
278 }
279 return ret;
280}
281
282int socket_family(Socket* sock) {
283 int domain = -1;
284 socklen_t len = sizeof(domain);
285#ifdef _WIN32
286 domain = socket_get_option(sock, SOL_SOCKET, SO_TYPE, &domain, &len);
287#elif defined(__APPLE__)
288 (void)len;
289 // macOS doesn't support SO_DOMAIN, use getsockname instead
290 struct sockaddr_storage addr;
291 socklen_t addr_len = sizeof(addr);
292 if (getsockname(sock->handle, (struct sockaddr*)&addr, &addr_len) == 0) {
293 domain = addr.ss_family;
294 }
295#else
296 socket_get_option(sock, SOL_SOCKET, SO_DOMAIN, &domain, &len);
297#endif
298 return domain;
299}
300
301// set non-blocking mode
302int socket_set_non_blocking(Socket* sock, int enable) {
303#ifdef _WIN32
304 u_long mode = enable ? 1 : 0;
305 return ioctlsocket(sock->handle, FIONBIO, &mode);
306#else
307 int flags = fcntl(sock->handle, F_GETFL, 0);
308 if (flags == -1) {
309 perror("fcntl");
310 return -1;
311 }
312 if (enable) {
313 flags |= O_NONBLOCK;
314 } else {
315 flags &= ~O_NONBLOCK;
316 }
317 return fcntl(sock->handle, F_SETFL, flags);
318#endif
319}
320
321// Create an IPv4 address
322// Allocates a new sockaddr_in and set the address and port.
323struct sockaddr_in* socket_ipv4_address(const char* ip, uint16_t port) {
324 struct sockaddr_in* addr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
325 if (!addr) {
326 perror("malloc");
327 return NULL;
328 }
329 memset(addr, 0, sizeof(struct sockaddr_in));
330 addr->sin_family = AF_INET;
331 addr->sin_port = htons(port);
332 addr->sin_addr.s_addr = inet_addr(ip);
333 return addr;
334}
335
336// Create an IPv6 address
337// Allocates a new sockaddr_in6 and set the address and port.
338struct sockaddr_in6* socket_ipv6_address(const char* ip, uint16_t port) {
339 struct sockaddr_in6* addr = (struct sockaddr_in6*)malloc(sizeof(struct sockaddr_in6));
340 if (!addr) {
341 perror("malloc");
342 return NULL;
343 }
344 memset(addr, 0, sizeof(struct sockaddr_in6));
345 addr->sin6_family = AF_INET6;
346 addr->sin6_port = htons(port);
347 inet_pton(AF_INET6, ip, &addr->sin6_addr);
348 return addr;
349}