CAEN Utility  2.0.2
Utilities for CAEN projects
CAENSocket.c
Go to the documentation of this file.
1 /******************************************************************************
2 *
3 * CAEN SpA - Software Division
4 * Via Vetraia, 11 - 55049 - Viareggio ITALY
5 * +39 0594 388 398 - www.caen.it
6 *
7 *******************************************************************************
8 *
9 * Copyright (C) 2019-2022 CAEN SpA
10 *
11 * This file is part of the CAEN Utility.
12 *
13 * The CAEN Utility is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 3 of the License, or (at your option) any later version.
17 *
18 * The CAEN Utility is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with the CAEN Utility; if not, see
25 * https://www.gnu.org/licenses/.
26 *
27 * SPDX-License-Identifier: LGPL-3.0-or-later
28 *
29 ***************************************************************************/
37 #ifdef _WIN32
38 #include <WS2tcpip.h>
39 #endif
40 
41 #include <CAENSocket.h>
42 
43 #include <limits.h>
44 
45 #ifndef _WIN32
46 #include <netdb.h>
47 #include <arpa/inet.h> // inet_ntop
48 #include <sys/socket.h> // setsockopt, socklen_t, AF_INET
49 #include <unistd.h> // close
50 #endif
51 
52 #include <CAENLogger.h>
53 #include <CAENMultiplatform.h>
54 #include <CAENThread.h>
55 
56 INIT_C_LOGGER("CAENSocket.log", "CAENSocket.c");
57 
58 #ifdef _WIN32
59 static void _printWinError(const char *function, DWORD hResult) {
60  // see https://stackoverflow.com/a/455533/3287591
61  LPSTR errorText = NULL;
62  const DWORD r = FormatMessageA(
63  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
64  NULL,
65  hResult,
66  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
67  (LPSTR)&errorText,
68  0,
69  NULL
70  );
71  if (r == 0) {
72  // FormatMessage failed
73  logMsg(c_logger_Severity_ERROR, "%s() failed: %"PRIu32".", function, (uint32_t)hResult);
74  return;
75  }
76  logMsg(c_logger_Severity_ERROR, "%s() failed: %s.", function, errorText);
77  LocalFree(errorText);
78 }
79 #endif
80 
81 int32_t c_socket_init(void) {
83 #ifdef _WIN32
84  WSADATA wsaData;
85  const int r = WSAStartup(WINSOCK_VERSION, &wsaData);
86  switch (r) {
87  case 0:
88  break;
89  case WSASYSNOTREADY:
90  case WSAVERNOTSUPPORTED:
91  case WSAEINPROGRESS:
92  case WSAEPROCLIM:
93  case WSAEFAULT:
94  default:
95  _printWinError("WSAStartup", r);
97  break;
98  }
99 #else
100 #endif
101  return ret;
102 }
103 
104 int32_t c_socket_cleanup(void) {
105  int ret = c_Socket_ErrorCode_Success;
106 #ifdef _WIN32
107  const int r = WSACleanup();
108  switch (r) {
109  case 0:
110  break;
111  case WSANOTINITIALISED:
112  case WSAENETDOWN:
113  case WSAEINPROGRESS:
114  default:
115  _printWinError("WSACleanup", r);
117  break;
118  }
119 #else
120 #endif
121  return ret;
122 }
123 
125  c_socket_t *sckt = c_malloc(sizeof(*sckt));
126  if (sckt == NULL)
127  return NULL;
128  sckt->socket = c_socket_invalid;
129  c_socket_init();
130  c_mutex_init(&sckt->mutex);
131  return sckt;
132 }
133 
135  if (c_unlikely(sckt == NULL))
136  return;
137  c_socket_reset(sckt);
138  c_socket_cleanup(); // WSACleanup must match each call to WSAStartup
139  c_mutex_destroy(&sckt->mutex);
140  c_free(sckt);
141 }
142 
144  if (c_unlikely(sckt == NULL))
145  return;
146  if (sckt->socket == c_socket_invalid)
147  return;
148 
149  int iret;
150 
151  // On Windows socket() returns a handle to a kernel object, so it must be closed with closesocket().
152 #ifdef _WIN32
153  iret = closesocket(sckt->socket);
154 #else
155  iret = close(sckt->socket);
156 #endif
157 
158  if (iret == c_socket_error) {
159  logMsg(c_logger_Severity_ERROR, "%s(): close/closesocket failed. Error %d.", __func__, c_socket_errno);
160  }
161 
162  sckt->socket = c_socket_invalid;
163 }
164 
165 c_ssize_t c_recv(const c_socket_t *sckt, void *buffer, size_t totSize) {
166  if (c_unlikely(sckt == NULL)) {
167  logMsg(c_logger_Severity_ERROR, "%s(): Invalid c_socket_t.", __func__);
168  return -1;
169  }
170 
171  /*
172  * Windows:
173  * Size parameter type of recv is int: the cast from size_t to int may introduce sneaky bugs
174  * if totSize > INT_MAX.
175  *
176  * Linux:
177  * Size parameter type of recv is size_t, but the maximum accepted value is SSIZE_MAX: the
178  * cast from size_t to ssize_t may introduce sneaky bugs if totSize > SSIZE_MAX.
179  */
180  if (c_unlikely(totSize > c_socket_max_ssize)) {
181  logMsg(c_logger_Severity_WARNING, "%s(): Packet too large (size '%zu' > max '%zu' bytes).", __func__, totSize, c_socket_max_ssize);
183  }
184 
185  const c_ssize_t totalToRead = (c_ssize_t)totSize; // totSize now can be casted safely
186  c_ssize_t stillToRead = totalToRead;
187 
188  char *buffer_temp = (char*)buffer;
189 
190  do {
191  const c_ssize_t nbytes = recv(sckt->socket, buffer_temp, stillToRead, 0);
192  if (nbytes == c_socket_error) {
193  logMsg(c_logger_Severity_ERROR, "%s(): recv() failed. Error %d.", __func__, c_socket_errno);
194  return nbytes;
195  }
196  else if (nbytes == 0) {
197  // recv returning 0 means a graceful disconnection on both POSIX and Windows.
198  // cast to intmax_t required because c_ssize_t specifier is system dependent.
199  logMsg(c_logger_Severity_INFO, "%s(): client disconnected (requested=%"PRIdMAX", remaining=%"PRIdMAX").", __func__, (intmax_t)totalToRead, (intmax_t)stillToRead);
200  return totalToRead - stillToRead; // Anyway, return the actual number of bytes read
201  }
202  stillToRead -= nbytes;
203  buffer_temp += nbytes;
204  } while (stillToRead > 0);
205 
206  if (stillToRead != 0) {
207  logMsg(c_logger_Severity_ERROR, "%s(): Message not completely read.", __func__);
208  }
209 
210  return totalToRead - stillToRead;
211 }
212 
213 c_ssize_t c_recv_unlock(c_socket_t *sckt, void *buffer, size_t totSize) {
214  if (c_unlikely(sckt == NULL)) {
215  logMsg(c_logger_Severity_ERROR, "%s(): Invalid c_socket_t.", __func__);
216  return -1;
217  }
218 
219  const c_ssize_t ret = c_recv(sckt, buffer, totSize);
220 
222  logMsg(c_logger_Severity_ERROR, "%s(): Error in c_mutex_unlock().", __func__);
223  }
224 
225  return ret;
226 }
227 
228 c_ssize_t c_send(const c_socket_t *sckt, const void *buffer, size_t totSize) {
229  if (c_unlikely(sckt == NULL)) {
230  logMsg(c_logger_Severity_ERROR, "%s(): Invalid c_socket_t.", __func__);
231  return -1;
232  }
233 
234  /*
235  * Windows:
236  * Size parameter type of send is int: the cast from size_t to int may introduce sneaky bugs
237  * if totSize > INT_MAX.
238  *
239  * Linux:
240  * Size parameter type of send is size_t, but the maximum accepted value is SSIZE_MAX: the
241  * cast from size_t to ssize_t may introduce sneaky bugs if totSize > SSIZE_MAX.
242  */
243  if (c_unlikely(totSize > c_socket_max_ssize)) {
244  logMsg(c_logger_Severity_WARNING, "%s(): Packet too large (size '%zu' > max '%zu' bytes).", __func__, totSize, c_socket_max_ssize);
246  }
247 
248  const c_ssize_t totalToSend = (c_ssize_t)totSize; // totSize now can be casted safely
249  c_ssize_t stillToSend = totalToSend;
250 
251  const char* buffer_temp = (const char*)buffer;
252 
253  do {
254  const c_ssize_t nbytes = send(sckt->socket, buffer_temp, stillToSend, 0);
255  if (nbytes == c_socket_error) {
256  logMsg(c_logger_Severity_ERROR, "%s(): send() failed. Error %d.", __func__, c_socket_errno);
257  return nbytes;
258  }
259  stillToSend -= nbytes;
260  buffer_temp += nbytes;
261  } while (stillToSend > 0);
262 
263  if (stillToSend != 0) {
264  logMsg(c_logger_Severity_ERROR, "%s(): Message not completely sent.", __func__);
265  }
266 
267  return totalToSend - stillToSend;
268 }
269 
270 c_ssize_t c_send_lock(c_socket_t *sckt, const void *buffer, size_t totSize) {
271  if (c_unlikely(sckt == NULL)) {
272  logMsg(c_logger_Severity_ERROR, "%s(): Invalid c_socket_t.", __func__);
273  return -1;
274  }
275 
277  logMsg(c_logger_Severity_ERROR, "%s(): Error in c_mutex_lock().", __func__);
278  return c_socket_error;
279  }
280 
281  return c_send(sckt, buffer, totSize);
282 }
283 
285  c_socket_t *sckt = c_socket_new();
286  if (sckt == NULL) {
287  logMsg(c_logger_Severity_ERROR, "%s(): Error in c_socket_new().", __func__);
288  return sckt;
289  }
290  sckt->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
291  return sckt;
292 }
293 
294 int c_bind(const c_socket_t *sckt, const struct sockaddr *addr, c_socklen_t addrlen) {
295  return bind(sckt->socket, addr, addrlen);
296 }
297 
298 int c_listen(const c_socket_t *sckt, int backlog) {
299  return listen(sckt->socket, backlog);
300 }
301 
302 c_use_decl_annotations c_socket_t* c_accept(const c_socket_t *sckt, struct sockaddr *addr, c_socklen_t*addrlen) {
303  c_socket_t *client = c_socket_new();
304  if (client == NULL) {
305  logMsg(c_logger_Severity_ERROR, "%s(): Error in c_socket_new().", __func__);
306  return client;
307  }
308  client->socket = accept(sckt->socket, addr, addrlen);
309  return client;
310 }
311 
312 int c_connect(const c_socket_t *sckt, const struct sockaddr *addr, c_socklen_t addrlen) {
313  return connect(sckt->socket, addr, addrlen);
314 }
315 
316 
317 int32_t c_socket_server_init(c_socket_t **server, uint32_t inaddr, uint16_t *_port) {
318  int32_t ret = c_Socket_ErrorCode_Success;
319  int iret;
320  struct sockaddr_in hints = { 0 };
321  int on = 1;
322  struct sockaddr_in addr_server = { 0 };
323  c_socklen_t addr_server_len = sizeof(addr_server);
324  uint16_t port = *_port;
325 
326  if (server == NULL)
328 
329  c_socket_t *server_local = c_tcp_socket();
330  if (server_local == NULL || server_local->socket == c_socket_invalid) {
331  logMsg(c_logger_Severity_ERROR, "%s(): socket() failed. Error %d.", __func__, c_socket_errno);
333  goto QuitFunction;
334  }
335  logMsg(c_logger_Severity_DEBUG, "Socket created.");
336 
337  iret = setsockopt(server_local->socket, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
338  if (iret == c_socket_error) {
339  logMsg(c_logger_Severity_ERROR, "%s(): setsockopt() failed. Error %d.", __func__, c_socket_errno);
341  goto QuitFunction;
342  }
343 
344  hints.sin_family = AF_INET;
345  hints.sin_addr.s_addr = c_hton32(inaddr);
346  hints.sin_port = c_hton16(port);
347  iret = c_bind(server_local, (struct sockaddr*)&hints, sizeof(hints));
348  if (iret == c_socket_error) {
349  logMsg(c_logger_Severity_ERROR, "%s(): bind() failed. Error %d.", __func__, c_socket_errno);
351  goto QuitFunction;
352  }
353  logMsg(c_logger_Severity_DEBUG, "Socket bind success.");
354 
355  iret = c_listen(server_local, SOMAXCONN);
356  if (iret == c_socket_error) {
357  logMsg(c_logger_Severity_ERROR, "%s(): listen() failed. Error %d.", __func__, c_socket_errno);
359  goto QuitFunction;
360  }
361 
362  iret = getsockname(server_local->socket, (struct sockaddr*)&addr_server, &addr_server_len);
363  if (iret == c_socket_error) {
364  if (port == 0) {
365  logMsg(c_logger_Severity_ERROR, "getsockname() failed with error %d. This call is mandatory because port is automatic.", c_socket_errno);
367  goto QuitFunction;
368  }
369  else {
370  logMsg(c_logger_Severity_WARNING, "Ready to receive connections. getsockname() failed. Error %d. Going on anyway.", c_socket_errno);
371  }
372  }
373  else {
374  char buf[INET_ADDRSTRLEN];
375  if (inet_ntop(AF_INET, &addr_server.sin_addr, buf, sizeof(buf)) != NULL)
376  logMsg(c_logger_Severity_INFO, "Ready to receive connections from '%s' on port '%"PRIu16"'.", buf, c_ntoh16(addr_server.sin_port));
377  else
378  logMsg(c_logger_Severity_INFO, "Ready to receive connections on port '%"PRIu16"'. inet_ntop() failed. Error %d. Going on anyway.", c_ntoh16(addr_server.sin_port), c_socket_errno);
379  if (port == 0)
380  *_port = c_ntoh16(addr_server.sin_port);
381  }
382 
383 QuitFunction:
384  if (ret != c_Socket_ErrorCode_Success) {
385  logMsg(c_logger_Severity_INFO, "%s(): failed. Error %"PRIi32, __func__, ret);
386  // TODO CHECK more cleanup needed here?
387  c_socket_delete(server_local);
388  }
389  else {
390  *server = server_local;
391  }
392  return ret;
393 }
394 
395 int32_t c_socket_server_accept(const c_socket_t *server, c_socket_t **client) {
396  if (server == NULL || client == NULL)
398 
399  struct sockaddr_in addr_client = { 0 };
400  c_socklen_t addr_client_len = sizeof(addr_client);
401 
402  *client = NULL;
403  logMsg(c_logger_Severity_INFO, "Waiting for connection...");
404 
405  c_socket_t *client_local = c_accept(server, (struct sockaddr*)&addr_client, &addr_client_len);
406  if (client_local == NULL || client_local->socket == c_socket_invalid) {
407  logMsg(c_logger_Severity_ERROR, "%s(): accept() failed. Error %d.", __func__, c_socket_errno);
408  if (client_local != NULL)
409  c_socket_delete(client_local);
411  }
412 
413  *client = client_local;
414 
415  // Client connected
416  char buf[INET_ADDRSTRLEN];
417  if (inet_ntop(AF_INET, &addr_client.sin_addr, buf, sizeof(buf)) != NULL)
418  logMsg(c_logger_Severity_INFO, "Connected to '%s'.", buf);
419  else
420  logMsg(c_logger_Severity_WARNING, "Connected to a client. inet_ntop() failed. Error %d. Going on anyway.", c_socket_errno);
421 
423 }
424 
425 int32_t c_socket_client_sockaddr_connect(c_socket_t **client, const struct sockaddr *addr_server, c_socklen_t addrlen) {
426  int32_t ret = c_Socket_ErrorCode_Success;
427  int iret;
428 
429  if (client == NULL)
431 
432  c_socket_t *client_local = c_tcp_socket();
433  if (client_local == NULL || client_local->socket == c_socket_invalid) {
434  logMsg(c_logger_Severity_ERROR, "socket() failed. Error %d.", c_socket_errno);
436  goto QuitFunction;
437  }
438  logMsg(c_logger_Severity_INFO, "Socket created.");
439 
440  int on = 1;
441  iret = setsockopt(client_local->socket, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
442  if (iret == c_socket_error) {
443  logMsg(c_logger_Severity_ERROR, "%s(): setsockopt() failed. Error %d.", __func__, c_socket_errno);
445  goto QuitFunction;
446  }
447 
448  iret = c_connect(client_local, addr_server, addrlen);
449  if (iret == c_socket_error) {
450  logMsg(c_logger_Severity_ERROR, "%s(): connect() failed. Error %d.", __func__, c_socket_errno);
452  goto QuitFunction;
453  }
454 
455  logMsg(c_logger_Severity_INFO, "Connected");
456 
457 QuitFunction:
458  if (ret != c_Socket_ErrorCode_Success) {
459  logMsg(c_logger_Severity_INFO, "%s(): failed. Error %"PRIi32, __func__, ret);
460  c_socket_delete(client_local);
461  client_local = NULL;
462  }
463  *client = client_local;
464  return ret;
465 }
466 
467 int32_t c_socket_client_connect(c_socket_t **client, const char *hostname, uint16_t port) {
468  struct sockaddr_in addr_server = { 0 };
469  int32_t ret = c_Socket_ErrorCode_Success;
470 
471  if (client == NULL)
473 
474  // resolve hostname
475  if ((ret = c_socket_init()) != c_Socket_ErrorCode_Success)
476  return ret;
477  struct hostent *he = gethostbyname(hostname);
479  return ret;
480  if (he == NULL) {
481  logMsg(c_logger_Severity_ERROR, "gethostbyname() failed. Error %d.", c_socket_h_errno);
483  goto QuitFunction;
484  }
485 
486  c_memcpy(&addr_server.sin_addr.s_addr, he->h_addr, he->h_length);
487  addr_server.sin_family = AF_INET;
488  addr_server.sin_port = c_hton16(port);
489 
490  if ((ret = c_socket_client_sockaddr_connect(client, (struct sockaddr*)&addr_server, sizeof(addr_server))) != c_Socket_ErrorCode_Success) {
491  logMsg(c_logger_Severity_ERROR, "%s(): connect() to '%s:%"PRIu16"' failed. Error %"PRIi32".", __func__, hostname, c_ntoh16(addr_server.sin_port), ret);
492  goto QuitFunction;
493  }
494 
495  logMsg(c_logger_Severity_INFO, "Connected to '%s:%"PRIu16"'.", hostname, c_ntoh16(addr_server.sin_port));
496 
497 QuitFunction:
498  if (ret != c_Socket_ErrorCode_Success)
499  logMsg(c_logger_Severity_INFO, "%s(): failed. Error %"PRIi32".", __func__, ret);
500  return ret;
501 }
int32_t c_mutex_lock(c_mutex_t *m)
Definition: CAENThread.c:316
c_mutex_t mutex
A mutex, use by some functions.
static void * c_memcpy(void *__restrict dest, const void *__restrict src, size_t size)
Wrapper to memcpy(dest, src, size), with check for NULL arguments.
void c_free(void *ptr)
int32_t c_mutex_init(c_mutex_t *m)
Definition: CAENThread.c:288
int32_t c_socket_client_sockaddr_connect(c_socket_t **client, const struct sockaddr *addr_server, c_socklen_t addrlen)
Definition: CAENSocket.c:425
int c_listen(const c_socket_t *sckt, int backlog)
Definition: CAENSocket.c:298
int32_t c_mutex_destroy(c_mutex_t *m)
Definition: CAENThread.c:390
int32_t c_socket_cleanup(void)
Definition: CAENSocket.c:104
void c_socket_delete(c_socket_t *sckt)
Definition: CAENSocket.c:134
c_socket_t * c_accept(const c_socket_t *sckt, struct sockaddr *addr, c_socklen_t *addrlen)
Definition: CAENSocket.c:302
#define c_socket_max_ssize
Maximum value accepted as size by send() and recv(). Type is size_t.
c_socket_t * c_tcp_socket(void)
Definition: CAENSocket.c:284
c_socket_type_t socket
The socket.
c_ssize_t c_send_lock(c_socket_t *sckt, const void *buffer, size_t totSize)
Definition: CAENSocket.c:270
#define c_socket_error
Functions like send() and recv() return -1 in case of error. Type is c_ssize_t.
c_ssize_t c_recv_unlock(c_socket_t *sckt, void *buffer, size_t totSize)
Definition: CAENSocket.c:213
int32_t c_socket_client_connect(c_socket_t **client, const char *hostname, uint16_t port)
Definition: CAENSocket.c:467
TCP/IP functions.
#define c_socket_invalid
Functions like accept() return -1 in case of error. Type is c_socket_type_t.
#define c_unlikely(x)
Definition: CAENUtility.h:293
static uint16_t c_hton16(uint16_t x)
Functions to handle threads, inspired to C11 threads.h.
void * c_malloc(size_t size)
Generic wrappers to platform-dependent functions.
#define INIT_C_LOGGER(fname, mname)
Definition: CAENLogger.h:139
#define c_socket_h_errno
Network database operations like gethostbyname() set the error into h_errno variable. Type is int.
static uint32_t c_hton32(uint32_t x)
ssize_t c_ssize_t
Return type of send() and recv().
int c_connect(const c_socket_t *sckt, const struct sockaddr *addr, c_socklen_t addrlen)
Definition: CAENSocket.c:312
c_socket_t * c_socket_new(void)
Definition: CAENSocket.c:124
int32_t c_socket_server_init(c_socket_t **server, uint32_t inaddr, uint16_t *_port)
Definition: CAENSocket.c:317
socklen_t c_socklen_t
See definition on Windows case.
Logger implemented in C.
#define logMsg(s,...)
Definition: CAENLogger.h:157
int32_t c_socket_server_accept(const c_socket_t *server, c_socket_t **client)
Definition: CAENSocket.c:395
int c_bind(const c_socket_t *sckt, const struct sockaddr *addr, c_socklen_t addrlen)
Definition: CAENSocket.c:294
#define c_use_decl_annotations
Definition: CAENUtility.h:278
c_ssize_t c_send(const c_socket_t *sckt, const void *buffer, size_t totSize)
Definition: CAENSocket.c:228
c_ssize_t c_recv(const c_socket_t *sckt, void *buffer, size_t totSize)
Definition: CAENSocket.c:165
int32_t c_socket_init(void)
Definition: CAENSocket.c:81
void c_socket_reset(c_socket_t *sckt)
Definition: CAENSocket.c:143
static uint16_t c_ntoh16(uint16_t x)
int32_t c_mutex_unlock(c_mutex_t *m)
Definition: CAENThread.c:367
#define c_socket_errno
Socket-related functions set the error into errno variable. Type is int.