/* * Created by yuuta on 7/22/22. * * A simple cross-platform (Unix and Windows) implementation of a socket client. */ #include "net.h" #include #include #include #include #ifdef WIN32 #include /* For DWORD */ #include #include #define errno_sock WSAGetLastError() #else #include #include #include #include #define closesocket(socket) close(socket) #define errno_sock errno #endif int net_connect(const char *host, const uint16_t port, SOCKET *sock) { int r; struct addrinfo *res; char service[6]; snprintf(service, 6, "%u", port); if ((r = getaddrinfo(host, service, NULL, &res))) { return r; } SOCKET fd; if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) { const int e = errno_sock; freeaddrinfo(res); return e; } if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { const int e = errno_sock; closesocket(fd); freeaddrinfo(res); return e; } freeaddrinfo(res); *sock = fd; return 0; } int net_read(const SOCKET *sock, void *buffer, const size_t length, size_t *read, const unsigned int timeout) { #ifdef WIN32 DWORD to = timeout * 1000; if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof to)) { #else struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = 0; if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv)) { #endif return errno_sock; } int bytes; if ((bytes = (int) recv(*sock, buffer, length, 0)) <= 0) { if (bytes < 0) { const int e = errno_sock; #ifdef WIN32 if (e == WSAETIMEDOUT) { #else if (e == EAGAIN) { #endif return NET_TIMEOUT; } return e; } return NET_CLOSED; } *read = bytes; return 0; } int net_send(const SOCKET *sock, const void *buffer, size_t len) { size_t pos; const uint8_t *ptr = buffer; int retval; for (pos = 0U; pos < len; pos += retval) { size_t n = len - pos; retval = (int) send(*sock, &ptr[pos], (int) n, 0); if (retval <= 0) { return errno_sock; } } return 0; } int net_init(void) { #ifdef WIN32 int r; WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); if ((r = WSAStartup(wVersionRequested, &wsaData))) { fprintf(stderr, "WSAStartup failed with error: %d\n", r); return r; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { fprintf(stderr, "Could not find a usable version of Winsock.dll\n"); WSACleanup(); return 1; } #endif return 0; } void net_close(const SOCKET *sock) { closesocket(*sock); } #ifdef WIN32 static __declspec( thread ) LPTSTR err_buf = NULL; #else static _Thread_local char err_buf[1024]; #endif void net_free(void) { #ifdef WIN32 WSACleanup(); if (err_buf) { LocalFree(err_buf); err_buf = NULL; } #endif } char *net_strerror(int errnum) { #ifdef WIN32 if (err_buf) { LocalFree(err_buf); err_buf = NULL; } FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &err_buf, 0, NULL); if (!err_buf) { return "Unknown error"; } #else int r; if ((r = strerror_r(errnum, err_buf, sizeof(err_buf) / sizeof(char)))) { snprintf(err_buf, 1024, "%d (%d)", errnum, r); } #endif return err_buf; }