From 3c490643082e3d1d5866e7c46ca2df0b17b69ba7 Mon Sep 17 00:00:00 2001 From: Javivi Date: Sat, 2 Apr 2016 22:06:10 +0200 Subject: [PATCH] Fixs for the module - Fixed the backwards compatibility with the return codes - Merged socket_connect and socket_connect_nb - Added a 5th parameter to socket_open that takes bit flags to enable the new features (libc errors & nonblocking sockets) - Fixed an error on socket_send2 that caused the buffet not to start from the beginning if multiple calls were made - Updated docs - [docs] Prefixed error codes with SOCK_ - [docs] Added the new flags SOCK_NON_BLOCKING and SOCK_LIBC_ERRORS - [docs] Added a new stock called SOCK_ERROR_EINPROGRESS(error) to be used when checking if a newly created nonblocking socket is connecting --- modules/sockets/moduleconfig.h | 6 +- modules/sockets/sockets.cpp | 179 ++++++++++++--------------------- plugins/include/sockets.inc | 52 ++++------ 3 files changed, 88 insertions(+), 149 deletions(-) diff --git a/modules/sockets/moduleconfig.h b/modules/sockets/moduleconfig.h index 103be9ee..0c8e3850 100644 --- a/modules/sockets/moduleconfig.h +++ b/modules/sockets/moduleconfig.h @@ -19,9 +19,9 @@ // Module info #define MODULE_NAME "Sockets" #define MODULE_VERSION AMXX_VERSION -#define MODULE_AUTHOR "HLSW Dev Team" -#define MODULE_URL "http://www.hlsw.net/" -#define MODULE_LOGTAG "SOCKET" +#define MODULE_AUTHOR "AMX Mod X Dev Team" +#define MODULE_URL "http://www.amxmodx.org" +#define MODULE_LOGTAG "SOCKETS" #define MODULE_LIBRARY "sockets" #define MODULE_LIBCLASS "" // If you want the module not to be reloaded on mapchange, remove / comment out the next line diff --git a/modules/sockets/sockets.cpp b/modules/sockets/sockets.cpp index 740d5ebe..b59dfcbc 100644 --- a/modules/sockets/sockets.cpp +++ b/modules/sockets/sockets.cpp @@ -37,12 +37,6 @@ #include #endif -// Backwards compatibility -#define ERROR_CREATE_SOCKET 1 // Couldn't create a socket -#define ERROR_SERVER_UNKNOWN 2 // Server unknown -#define ERROR_WHILE_CONNECTING 3 // Error while connecting -#define ERROR_EHOSTUNREACH 113 // libc error code: No route to host - #ifdef _WIN32 bool g_winsock_initialized = false; #endif @@ -50,23 +44,63 @@ static char *g_send2_buffer = nullptr; static int g_send2_buffer_length = 0; -// native socket_open(_hostname[], _port, _protocol = SOCKET_TCP, &_error, _libc_errors = DISABLE_LIBC_ERRORS); + +bool setnonblocking(int sockfd) +{ +#ifdef _WIN32 + unsigned long flags = 1; + + return (ioctlsocket(sockfd, FIONBIO, &flags) == 0); +#else + + int flags = -1; + + if((flags = fcntl(sockfd, F_GETFL, 0)) == -1) + return false; + + if(fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) + return false; + + return true; +#endif +} + +// native socket_open(_hostname[], _port, _protocol = SOCKET_TCP, &_error, _flags = 0); static cell AMX_NATIVE_CALL socket_open(AMX *amx, cell *params) { - int length = 0; - char *hostname = MF_GetAmxString(amx, params[1], 0, &length); + // Socket flags and backwards compatibility + enum + { + SOCK_NON_BLOCKING = (1 << 0), + SOCK_LIBC_ERRORS = (1 << 1) + }; + + #define SOCK_ERROR_OK 0 // No error + #define SOCK_ERROR_CREATE_SOCKET 1 // Couldn't create a socket + #define SOCK_ERROR_SERVER_UNKNOWN 2 // Server unknown + #define SOCK_ERROR_WHILE_CONNECTING 3 // Error while connecting + #define ERROR_EHOSTUNREACH 113 // libc error code: No route to host + + int hostname_length = 0; + char *hostname = MF_GetAmxString(amx, params[1], 0, &hostname_length); cell *error = MF_GetAmxAddr(amx, params[4]); *error = 0; - bool libc_errors = false; + unsigned int flags = 0; + bool libc_errors = false, nonblocking_socket = false; if((*params / sizeof(cell)) == 5) - libc_errors = (params[5] == 1) ? true : false; - - if(length == 0) { - *error = libc_errors ? ERROR_EHOSTUNREACH : ERROR_SERVER_UNKNOWN; + flags = params[5]; + + nonblocking_socket = (flags & SOCK_NON_BLOCKING) != 0; + libc_errors = (flags & SOCK_LIBC_ERRORS) != 0; + } + + if(hostname_length == 0) + { + *error = libc_errors ? ERROR_EHOSTUNREACH : SOCK_ERROR_SERVER_UNKNOWN; return -1; } @@ -74,6 +108,7 @@ static cell AMX_NATIVE_CALL socket_open(AMX *amx, cell *params) ke::SafeSprintf(port_number, sizeof(port_number), "%d", params[2]); int sockfd = -1, getaddrinfo_status = -1, connect_status = -1; + bool setnonblocking_status = false, connect_inprogress = false; struct addrinfo hints, *server_info, *server; memset(&hints, 0, sizeof(hints)); @@ -82,7 +117,7 @@ static cell AMX_NATIVE_CALL socket_open(AMX *amx, cell *params) if((getaddrinfo_status = getaddrinfo(hostname, port_number, &hints, &server_info)) != 0) { - *error = libc_errors ? getaddrinfo_status : ERROR_SERVER_UNKNOWN; + *error = libc_errors ? getaddrinfo_status : SOCK_ERROR_SERVER_UNKNOWN; return -1; } @@ -92,104 +127,16 @@ static cell AMX_NATIVE_CALL socket_open(AMX *amx, cell *params) { if((sockfd = socket(server->ai_family, server->ai_socktype, server->ai_protocol)) != -1) { - if((connect_status = connect(sockfd, server->ai_addr, server->ai_addrlen)) == -1) - { - *error = libc_errors ? errno : ERROR_WHILE_CONNECTING; - close(sockfd); - } - else - { - *error = 0; - } - } - else - { - if(*error == 0) - *error = libc_errors ? errno : ERROR_CREATE_SOCKET; - } + if(nonblocking_socket) + setnonblocking_status = setnonblocking(sockfd); - } while(connect_status != 0 && (server = server->ai_next) != nullptr); - - freeaddrinfo(server_info); - - if(sockfd == -1 || server == nullptr) - return -1; - - return sockfd; -} - -int set_nonblocking(int sockfd) -{ -#ifdef _WIN32 - unsigned long flags = 1; - - if(ioctlsocket(sockfd, FIONBIO, &flags) == 0) - return 0; - else - return errno; -#else - int flags = -1; - - if((flags = fcntl(sockfd, F_GETFL, 0)) == -1) - return errno; - - if(fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) - return errno; - - return 0; -#endif -} - -// native socket_open_nb(_hostname[], _port, _protocol = SOCKET_TCP, &_error); -static cell AMX_NATIVE_CALL socket_open_nb(AMX *amx, cell *params) -{ - int length = 0; - char *hostname = MF_GetAmxString(amx, params[1], 0, &length); - - cell *error = MF_GetAmxAddr(amx, params[4]); - *error = 0; - - if(length == 0) - { - *error = ERROR_EHOSTUNREACH; - return -1; - } - - char port_number[6]; - ke::SafeSprintf(port_number, sizeof(port_number), "%d", params[2]); - - int sockfd = -1, getaddrinfo_status = -1, connect_status = -1, setnonblocking_status = -1; - bool connect_inprogress = false; - struct addrinfo hints, *server_info, *server; - - memset(&hints, 0, sizeof(hints)); - - // Both hostname and port should be numeric to prevent the name resolution service from being called and potentially blocking the call for a long time - hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = params[3]; - - if((getaddrinfo_status = getaddrinfo(hostname, port_number, &hints, &server_info)) != 0) - { - *error = getaddrinfo_status; - return -1; - } - - server = server_info; - - do - { - if((sockfd = socket(server->ai_family, server->ai_socktype, server->ai_protocol)) != -1) - { - setnonblocking_status = set_nonblocking(sockfd); - - if(setnonblocking_status == 0) + if(nonblocking_socket == false || (nonblocking_socket && setnonblocking_status == true)) { if((connect_status = connect(sockfd, server->ai_addr, server->ai_addrlen)) == -1) { - *error = errno; + *error = libc_errors ? errno : SOCK_ERROR_WHILE_CONNECTING; - if(*error == EINPROGRESS) + if(nonblocking_socket && (errno == EINPROGRESS || errno == EWOULDBLOCK)) connect_inprogress = true; else close(sockfd); @@ -202,7 +149,7 @@ static cell AMX_NATIVE_CALL socket_open_nb(AMX *amx, cell *params) else { if(*error == 0) - *error = setnonblocking_status; + *error = libc_errors ? errno : SOCK_ERROR_CREATE_SOCKET; } } else @@ -211,8 +158,8 @@ static cell AMX_NATIVE_CALL socket_open_nb(AMX *amx, cell *params) *error = errno; } - } while(connect_inprogress == false && connect_status != 0 && (server = server->ai_next) != nullptr); - + } while((nonblocking_socket && connect_inprogress == false) && connect_status != 0 && (server = server->ai_next) != nullptr); + freeaddrinfo(server_info); if(sockfd == -1 || server == nullptr) @@ -224,7 +171,7 @@ static cell AMX_NATIVE_CALL socket_open_nb(AMX *amx, cell *params) // native socket_close(_socket); static cell AMX_NATIVE_CALL socket_close(AMX *amx, cell *params) { - return (close(params[1]) == -1) ? -1 : 1; + return (close(params[1]) == -1) ? 0 : 1; } // native socket_recv(_socket, _data[], _length); @@ -285,9 +232,10 @@ static cell AMX_NATIVE_CALL socket_send2(AMX *amx, cell *params) } cell *data = MF_GetAmxAddr(amx, params[2]); + char *buffer = g_send2_buffer; while(length--) - *g_send2_buffer++ = (char)*data++; + *buffer++ = (char)*data++; return send(sockfd, g_send2_buffer, length, 0); } @@ -306,7 +254,7 @@ static cell AMX_NATIVE_CALL socket_change(AMX *amx, cell *params) FD_ZERO(&readfds); FD_SET(sockfd, &readfds); - return (select(sockfd + 1, &readfds, nullptr, nullptr, &tv) > 0) ? 1 : -1; + return (select(sockfd + 1, &readfds, nullptr, nullptr, &tv) > 0) ? 1 : 0; } // native socket_is_readable(_socket, _timeout = 100000); @@ -329,13 +277,12 @@ static cell AMX_NATIVE_CALL socket_is_writable(AMX *amx, cell *params) FD_ZERO(&writefds); FD_SET(sockfd, &writefds); - return (select(sockfd + 1, nullptr, &writefds, nullptr, &tv) > 0) ? 1 : -1; + return (select(sockfd + 1, nullptr, &writefds, nullptr, &tv) > 0) ? 1 : 0; } AMX_NATIVE_INFO sockets_natives[] = { {"socket_open", socket_open}, - {"socket_open_nb", socket_open_nb}, {"socket_close", socket_close}, diff --git a/plugins/include/sockets.inc b/plugins/include/sockets.inc index 7c2af53d..c9da9e80 100755 --- a/plugins/include/sockets.inc +++ b/plugins/include/sockets.inc @@ -21,18 +21,21 @@ #pragma loadlib sockets #endif -// Backwards compatibility and error reporting -#define ERROR_CREATE_SOCKET 1 // Couldn't create a socket -#define ERROR_SERVER_UNKNOWN 2 // Server unknown -#define ERROR_WHILE_CONNECTING 3 // Error while connecting +// Socket flags +#define SOCK_NON_BLOCKING (1 << 0) // Set the socket a nonblocking +#define SOCK_LIBC_ERRORS (1 << 1) // Enable libc error reporting -#define DISABLE_LIBC_ERRORS 0 // Old, default error reporting -#define ENABLE_LIBC_ERRORS 1 // Enable libc error reporting +// Error reporting +#define SOCK_ERROR_OK 0 // No error +#define SOCK_ERROR_CREATE_SOCKET 1 // Couldn't create a socket +#define SOCK_ERROR_SERVER_UNKNOWN 2 // Server unknown +#define SOCK_ERROR_WHILE_CONNECTING 3 // Error while connecting -// Use SOCKET_TCP for TCP Socket connections +// Nonblocking sockets error handler +stock SOCK_ERROR_EINPROGRESS(error) return (error == 0 || error == 10035 || error == 10036 || error == 115) + +// Socket connection type (TCP/UDP) #define SOCKET_TCP 1 - -// Use SOCKET_UDP for UDP Socket connections #define SOCKET_UDP 2 /** @@ -49,36 +52,25 @@ * https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h * https://msdn.microsoft.com/en-us/library/ms740668.aspx * - * @param _hostname Node to connect to - * @param _port Service to connect to - * @param _protocol Connect via SOCKET_TCP or SOCKET_UDP - * @param _error Set an error code here if anything goes wrong - * @param _libc_errors Optional parameter that defaults to DISABLE_LIBC_ERRORS. If set to ENABLE_LIBC_ERRORS the new libc errors will be set on _error instead of the old, made up ones + * @note The currently available bit flags are: + * - SOCK_NON_BLOCKING : if set, the socket would be on nonblocking mode + * - SOCK_LIBC_ERRORS : if set, the new libc errors will be seen on _error instead of the old, made up ones + * @note If no flags are set, the behaviour of the function will not be modified (default blocking mode and error reporting). + * @note Multiple flags may be set at the same time using the | operator. For example, SOCK_NON_BLOCKING|SOCK_LIBC_ERRORS will create a nonblocking socket with libc error codes. * - * @return A socket descriptor (a positive integer) on success - * -1 on failure -*/ -native socket_open(const _hostname[], _port, _protocol = SOCKET_TCP, &_error, _libc_errors = DISABLE_LIBC_ERRORS); - -/** - * Connects on nonblocking mode to the given node and service via TCP/UDP. - * - * @note _hostname should be numeric to avoid calling the name resolution server and potentially blocking the call - * @note The returned socket descriptor may be still connecting, further checks should be done with socket_is_writable() - * @note Unlike in socket_open() libc error codes are used. They can be found at: - * https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html - * https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h - * https://msdn.microsoft.com/en-us/library/ms740668.aspx + * @note If you're creating a new nonblocking socket, _hostname should be numeric to avoid calling the name resolution server and potentially blocking the call + * @note If the socket is a nonblocking one, the returned socket descriptor may be still connecting and further checks should be done with socket_is_writable() before trying to send data * * @param _hostname Node to connect to * @param _port Service to connect to * @param _protocol Connect via SOCKET_TCP or SOCKET_UDP * @param _error Set an error code here if anything goes wrong - + * @param _flags Optional bit flags that change the behaviour of the function + * * @return A socket descriptor (a positive integer) on success * -1 on failure */ -native socket_open_nb(const _hostname[], _port, _protocol = SOCKET_TCP, &_error); +native socket_open(const _hostname[], _port, _protocol = SOCKET_TCP, &_error, _flags = 0); /** * Closes a socket