mirror of
https://github.com/alliedmodders/amxmodx.git
synced 2025-03-13 14:10:25 +03:00
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
This commit is contained in:
parent
9674e13cfb
commit
3c49064308
@ -19,9 +19,9 @@
|
|||||||
// Module info
|
// Module info
|
||||||
#define MODULE_NAME "Sockets"
|
#define MODULE_NAME "Sockets"
|
||||||
#define MODULE_VERSION AMXX_VERSION
|
#define MODULE_VERSION AMXX_VERSION
|
||||||
#define MODULE_AUTHOR "HLSW Dev Team"
|
#define MODULE_AUTHOR "AMX Mod X Dev Team"
|
||||||
#define MODULE_URL "http://www.hlsw.net/"
|
#define MODULE_URL "http://www.amxmodx.org"
|
||||||
#define MODULE_LOGTAG "SOCKET"
|
#define MODULE_LOGTAG "SOCKETS"
|
||||||
#define MODULE_LIBRARY "sockets"
|
#define MODULE_LIBRARY "sockets"
|
||||||
#define MODULE_LIBCLASS ""
|
#define MODULE_LIBCLASS ""
|
||||||
// If you want the module not to be reloaded on mapchange, remove / comment out the next line
|
// If you want the module not to be reloaded on mapchange, remove / comment out the next line
|
||||||
|
@ -37,12 +37,6 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#endif
|
#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
|
#ifdef _WIN32
|
||||||
bool g_winsock_initialized = false;
|
bool g_winsock_initialized = false;
|
||||||
#endif
|
#endif
|
||||||
@ -50,23 +44,63 @@
|
|||||||
static char *g_send2_buffer = nullptr;
|
static char *g_send2_buffer = nullptr;
|
||||||
static int g_send2_buffer_length = 0;
|
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)
|
static cell AMX_NATIVE_CALL socket_open(AMX *amx, cell *params)
|
||||||
{
|
{
|
||||||
int length = 0;
|
// Socket flags and backwards compatibility
|
||||||
char *hostname = MF_GetAmxString(amx, params[1], 0, &length);
|
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]);
|
cell *error = MF_GetAmxAddr(amx, params[4]);
|
||||||
*error = 0;
|
*error = 0;
|
||||||
|
|
||||||
bool libc_errors = false;
|
unsigned int flags = 0;
|
||||||
|
bool libc_errors = false, nonblocking_socket = false;
|
||||||
|
|
||||||
if((*params / sizeof(cell)) == 5)
|
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;
|
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]);
|
ke::SafeSprintf(port_number, sizeof(port_number), "%d", params[2]);
|
||||||
|
|
||||||
int sockfd = -1, getaddrinfo_status = -1, connect_status = -1;
|
int sockfd = -1, getaddrinfo_status = -1, connect_status = -1;
|
||||||
|
bool setnonblocking_status = false, connect_inprogress = false;
|
||||||
struct addrinfo hints, *server_info, *server;
|
struct addrinfo hints, *server_info, *server;
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
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)
|
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;
|
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((sockfd = socket(server->ai_family, server->ai_socktype, server->ai_protocol)) != -1)
|
||||||
{
|
{
|
||||||
if((connect_status = connect(sockfd, server->ai_addr, server->ai_addrlen)) == -1)
|
if(nonblocking_socket)
|
||||||
{
|
setnonblocking_status = setnonblocking(sockfd);
|
||||||
*error = libc_errors ? errno : ERROR_WHILE_CONNECTING;
|
|
||||||
close(sockfd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*error = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(*error == 0)
|
|
||||||
*error = libc_errors ? errno : ERROR_CREATE_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
} while(connect_status != 0 && (server = server->ai_next) != nullptr);
|
if(nonblocking_socket == false || (nonblocking_socket && setnonblocking_status == true))
|
||||||
|
|
||||||
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((connect_status = connect(sockfd, server->ai_addr, server->ai_addrlen)) == -1)
|
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;
|
connect_inprogress = true;
|
||||||
else
|
else
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
@ -202,7 +149,7 @@ static cell AMX_NATIVE_CALL socket_open_nb(AMX *amx, cell *params)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(*error == 0)
|
if(*error == 0)
|
||||||
*error = setnonblocking_status;
|
*error = libc_errors ? errno : SOCK_ERROR_CREATE_SOCKET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -211,7 +158,7 @@ static cell AMX_NATIVE_CALL socket_open_nb(AMX *amx, cell *params)
|
|||||||
*error = errno;
|
*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);
|
freeaddrinfo(server_info);
|
||||||
|
|
||||||
@ -224,7 +171,7 @@ static cell AMX_NATIVE_CALL socket_open_nb(AMX *amx, cell *params)
|
|||||||
// native socket_close(_socket);
|
// native socket_close(_socket);
|
||||||
static cell AMX_NATIVE_CALL socket_close(AMX *amx, cell *params)
|
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);
|
// 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]);
|
cell *data = MF_GetAmxAddr(amx, params[2]);
|
||||||
|
char *buffer = g_send2_buffer;
|
||||||
|
|
||||||
while(length--)
|
while(length--)
|
||||||
*g_send2_buffer++ = (char)*data++;
|
*buffer++ = (char)*data++;
|
||||||
|
|
||||||
return send(sockfd, g_send2_buffer, length, 0);
|
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_ZERO(&readfds);
|
||||||
FD_SET(sockfd, &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);
|
// 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_ZERO(&writefds);
|
||||||
FD_SET(sockfd, &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[] =
|
AMX_NATIVE_INFO sockets_natives[] =
|
||||||
{
|
{
|
||||||
{"socket_open", socket_open},
|
{"socket_open", socket_open},
|
||||||
{"socket_open_nb", socket_open_nb},
|
|
||||||
|
|
||||||
{"socket_close", socket_close},
|
{"socket_close", socket_close},
|
||||||
|
|
||||||
|
@ -21,18 +21,21 @@
|
|||||||
#pragma loadlib sockets
|
#pragma loadlib sockets
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Backwards compatibility and error reporting
|
// Socket flags
|
||||||
#define ERROR_CREATE_SOCKET 1 // Couldn't create a socket
|
#define SOCK_NON_BLOCKING (1 << 0) // Set the socket a nonblocking
|
||||||
#define ERROR_SERVER_UNKNOWN 2 // Server unknown
|
#define SOCK_LIBC_ERRORS (1 << 1) // Enable libc error reporting
|
||||||
#define ERROR_WHILE_CONNECTING 3 // Error while connecting
|
|
||||||
|
|
||||||
#define DISABLE_LIBC_ERRORS 0 // Old, default error reporting
|
// Error reporting
|
||||||
#define ENABLE_LIBC_ERRORS 1 // Enable libc 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
|
#define SOCKET_TCP 1
|
||||||
|
|
||||||
// Use SOCKET_UDP for UDP Socket connections
|
|
||||||
#define SOCKET_UDP 2
|
#define SOCKET_UDP 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,36 +52,25 @@
|
|||||||
* https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h
|
* https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h
|
||||||
* https://msdn.microsoft.com/en-us/library/ms740668.aspx
|
* https://msdn.microsoft.com/en-us/library/ms740668.aspx
|
||||||
*
|
*
|
||||||
* @param _hostname Node to connect to
|
* @note The currently available bit flags are:
|
||||||
* @param _port Service to connect to
|
* - SOCK_NON_BLOCKING : if set, the socket would be on nonblocking mode
|
||||||
* @param _protocol Connect via SOCKET_TCP or SOCKET_UDP
|
* - SOCK_LIBC_ERRORS : if set, the new libc errors will be seen on _error instead of the old, made up ones
|
||||||
* @param _error Set an error code here if anything goes wrong
|
* @note If no flags are set, the behaviour of the function will not be modified (default blocking mode and error reporting).
|
||||||
* @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 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
|
* @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
|
||||||
* -1 on failure
|
* @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
|
||||||
*/
|
|
||||||
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
|
|
||||||
*
|
*
|
||||||
* @param _hostname Node to connect to
|
* @param _hostname Node to connect to
|
||||||
* @param _port Service to connect to
|
* @param _port Service to connect to
|
||||||
* @param _protocol Connect via SOCKET_TCP or SOCKET_UDP
|
* @param _protocol Connect via SOCKET_TCP or SOCKET_UDP
|
||||||
* @param _error Set an error code here if anything goes wrong
|
* @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
|
* @return A socket descriptor (a positive integer) on success
|
||||||
* -1 on failure
|
* -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
|
* Closes a socket
|
||||||
|
Loading…
x
Reference in New Issue
Block a user