Update sockets module (#301)
* Module rewrite - WinSock version changed from 1.1 to 2.2. - Properly check for WinSock initialization on OnAmxxAttach/Detach. - Now natives will not be added if we can't start up WinSock. - socket_open() is now IP version agnostic (both IPv4 and IPv6 are supported). - Error reporting has been changed on socket_open(), a new parameter called _libc_errors has been added, and, if enabled, libc errors will be returned instead of the previous made-up errors. - socket_close() now returns a value on success/failure. - Added non-blocking sockets at socket_open_nb(). - Added socket_is_writable() to check if a socket is ready for write. - Added socket_is_readable() as an alias to socket_change(). - Code rewritten to be more readable, it should be self-explaining now. * Update docs and fix AMBuild Updated documentation following the guidelines * 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 * Fixes for the docs Fix some typos, shorten lines, document SOCK_ERROR_EINPROGRESS * Document magic numbers No more magic * Revert "Document magic numbers" This reverts commit 0f233292063400ea7fdbcd5e5d5cd6e54f8cd71c. * More docs fixes * Fix broken socket_send2() * Add error checking in send2() * Remove stock It should not be needed because nb sockets should always be checked or writability * Fix some identations * Fix return codes doc * Fix socket_recv() regression strncopy will stop on a null byte, that makes the function unusable to receive binary data * More docs typo fixes
This commit is contained in:
parent
7a7d0442aa
commit
6a553122b1
|
@ -8,6 +8,10 @@ binary.sources = [
|
|||
'sockets.cpp',
|
||||
]
|
||||
|
||||
binary.compiler.defines += [
|
||||
'HAVE_STDINT_H',
|
||||
]
|
||||
|
||||
if builder.target_platform == 'windows':
|
||||
binary.sources += ['version.rc']
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\;..\..\..\public;..\..\..\public\sdk;..\..\..\public\amtl;..\..\third_party;..\..\third_party\hashing;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SOCKETS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;HAVE_STDINT_H;_DEBUG;_WINDOWS;_USRDLL;SOCKETS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
|
@ -66,7 +66,7 @@
|
|||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(OutDir)sockets.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -79,7 +79,7 @@
|
|||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\;..\..\..\public;..\..\..\public\sdk;..\..\..\public\amtl;..\..\third_party;..\..\third_party\hashing;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SOCKETS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;HAVE_STDINT_H;NDEBUG;_WINDOWS;_USRDLL;SOCKETS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>
|
||||
|
@ -88,7 +88,7 @@
|
|||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
|
||||
// Copyright (C) The AMX Mod X Development Team.
|
||||
//
|
||||
// Codebase from Ivan, -g-s-ivan@web.de (AMX 0.9.3)
|
||||
// Modification by Olaf Reusch, kenterfie@hlsw.de (AMXX 0.16, AMX 0.96)
|
||||
// Modification by David Anderson, dvander@tcwonline.org (AMXx 0.20)
|
||||
//
|
||||
// This software is licensed under the GNU General Public License, version 3 or higher.
|
||||
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||
// https://alliedmods.net/amxmodx-license
|
||||
|
@ -15,233 +11,334 @@
|
|||
// Sockets Module
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "amxxmodule.h"
|
||||
#include <amtl/am-string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Windows */
|
||||
#include <winsock.h>
|
||||
#include <io.h>
|
||||
#define socklen_t int
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#undef errno
|
||||
#undef close
|
||||
|
||||
#define errno WSAGetLastError()
|
||||
#define close(sockfd) closesocket(sockfd)
|
||||
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#else
|
||||
/* Unix/Linux */
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#define closesocket(s) close(s)
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
// AMX Headers
|
||||
#include "amxxmodule.h"
|
||||
#ifdef _WIN32
|
||||
bool g_winsock_initialized = false;
|
||||
#endif
|
||||
|
||||
#define SOCKET_TCP 1
|
||||
#define SOCKET_UDP 2
|
||||
static char *g_send2_buffer = nullptr;
|
||||
static int g_send2_buffer_length = 0;
|
||||
|
||||
// And global Variables:
|
||||
|
||||
// native socket_open(_hostname[], _port, _protocol = SOCKET_TCP, &_error);
|
||||
static cell AMX_NATIVE_CALL socket_open(AMX *amx, cell *params) /* 2 param */
|
||||
bool setnonblocking(int sockfd)
|
||||
{
|
||||
unsigned int port = params[2];
|
||||
int len;
|
||||
char* hostname = MF_GetAmxString(amx,params[1],0,&len); // Get the hostname from AMX
|
||||
cell *err = MF_GetAmxAddr(amx, params[4]);
|
||||
if(len == 0) { // just to prevent to work with a nonset hostname
|
||||
*err = 2; // server unknown
|
||||
return -1;
|
||||
}
|
||||
*err = 0; // params[4] is error backchannel
|
||||
struct sockaddr_in server;
|
||||
struct hostent *host_info;
|
||||
unsigned long addr;
|
||||
int sock=-1;
|
||||
int contr;
|
||||
// Create a Socket
|
||||
sock = socket(AF_INET, params[3]==SOCKET_TCP?SOCK_STREAM:SOCK_DGRAM, 0);
|
||||
if (sock < 0) {
|
||||
// Error, couldn't create a socket, so set an error and return.
|
||||
*err = 1;
|
||||
return -1;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
unsigned long flags = 1;
|
||||
|
||||
// Clear the server structure (set everything to 0)
|
||||
memset( &server, 0, sizeof (server));
|
||||
// Test the hostname, and resolve if needed
|
||||
if ((addr = inet_addr(hostname)) != INADDR_NONE) {
|
||||
// seems like hostname is a numeric ip, so put it into the structure
|
||||
memcpy( (char *)&server.sin_addr, &addr, sizeof(addr));
|
||||
}
|
||||
else {
|
||||
// hostname is a domain, so resolve it to an ip
|
||||
host_info = gethostbyname(hostname);
|
||||
if (host_info == NULL) {
|
||||
// an error occured, the hostname is unknown
|
||||
*err = 2; // server unknown
|
||||
return -1;
|
||||
}
|
||||
// If not, put it in the Server structure
|
||||
memcpy( (char *)&server.sin_addr, host_info->h_addr, host_info->h_length);
|
||||
}
|
||||
// Set the type of the Socket
|
||||
server.sin_family = AF_INET;
|
||||
// Change the port to network byte order, and put it into the structure
|
||||
server.sin_port = htons(port);
|
||||
return (ioctlsocket(sockfd, FIONBIO, &flags) == 0);
|
||||
#else
|
||||
|
||||
// Not, let's try to open a connection to the server
|
||||
contr = connect(sock, (struct sockaddr*)&server, sizeof( server));
|
||||
if (contr < 0) {
|
||||
// If an error occured cancel
|
||||
*err = 3; //error while connecting
|
||||
return -1;
|
||||
}
|
||||
// Everything went well, so return the socket
|
||||
return sock;
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
|
||||
unsigned int flags = 0;
|
||||
bool libc_errors = false, nonblocking_socket = false;
|
||||
|
||||
if((*params / sizeof(cell)) == 5)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
char port_number[6];
|
||||
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));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = params[3];
|
||||
|
||||
if((getaddrinfo_status = getaddrinfo(hostname, port_number, &hints, &server_info)) != 0)
|
||||
{
|
||||
*error = libc_errors ? getaddrinfo_status : SOCK_ERROR_SERVER_UNKNOWN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
server = server_info;
|
||||
|
||||
do
|
||||
{
|
||||
if((sockfd = socket(server->ai_family, server->ai_socktype, server->ai_protocol)) != -1)
|
||||
{
|
||||
if(nonblocking_socket)
|
||||
setnonblocking_status = setnonblocking(sockfd);
|
||||
|
||||
if(nonblocking_socket == false || (nonblocking_socket && setnonblocking_status == true))
|
||||
{
|
||||
if((connect_status = connect(sockfd, server->ai_addr, server->ai_addrlen)) == -1)
|
||||
{
|
||||
*error = libc_errors ? errno : SOCK_ERROR_WHILE_CONNECTING;
|
||||
|
||||
if(nonblocking_socket && (errno == EINPROGRESS || errno == EWOULDBLOCK))
|
||||
connect_inprogress = true;
|
||||
else
|
||||
close(sockfd);
|
||||
}
|
||||
else
|
||||
{
|
||||
*error = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*error == 0)
|
||||
*error = libc_errors ? errno : SOCK_ERROR_CREATE_SOCKET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*error == 0)
|
||||
*error = errno;
|
||||
}
|
||||
|
||||
} while((nonblocking_socket && connect_inprogress == false) && connect_status != 0 && (server = server->ai_next) != nullptr);
|
||||
|
||||
freeaddrinfo(server_info);
|
||||
|
||||
if(sockfd == -1 || server == nullptr)
|
||||
return -1;
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
// native socket_close(_socket);
|
||||
static cell AMX_NATIVE_CALL socket_close(AMX *amx, cell *params) /* 2 param */
|
||||
static cell AMX_NATIVE_CALL socket_close(AMX *amx, cell *params)
|
||||
{
|
||||
int socket = params[1];
|
||||
//PRINT_CONSOLE("Function: Close | Socket: %i\n", socket);
|
||||
#ifdef _WIN32 // On windows, check whether the sockets are initialized correctly
|
||||
closesocket(socket);
|
||||
#else
|
||||
// Close the socket (linux/unix styled systems)
|
||||
close(socket);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// native socket_change(_socket, _timeout=100000);
|
||||
// 1 sec =1000000 usec
|
||||
static cell AMX_NATIVE_CALL socket_change(AMX *amx, cell *params) /* 2 param */
|
||||
{
|
||||
int socket = params[1];
|
||||
unsigned int timeout = params[2];
|
||||
//PRINT_CONSOLE("Function: Change | Socket: %i | Timeout: %i\n", socket, timeout);
|
||||
// We need both a timeout structure and a fdset for our filedescriptor
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
// Fill in ...
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(socket, &rfds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout;
|
||||
// Now we "select", which will show us if new data is waiting in the socket's buffer
|
||||
if (select(socket+1, &rfds, NULL, NULL, &tv) > 0)
|
||||
return 1; // Ok, new data, return it
|
||||
else
|
||||
return 0; // No new data, return it
|
||||
return (close(params[1]) == -1) ? 0 : 1;
|
||||
}
|
||||
|
||||
// native socket_recv(_socket, _data[], _length);
|
||||
static cell AMX_NATIVE_CALL socket_recv(AMX *amx, cell *params) /* 2 param */
|
||||
static cell AMX_NATIVE_CALL socket_recv(AMX *amx, cell *params)
|
||||
{
|
||||
int socket = params[1];
|
||||
int length = params[3];
|
||||
int tmp = -1;
|
||||
// First we dynamicly allocate a block of heap memory for recieving our data
|
||||
char *tmpchar = new char[length];
|
||||
if(tmpchar == NULL) return -1; // If we didn't got a block, we have to quit here to avoid sigsegv
|
||||
// And set it all to 0, because the memory could contain old trash
|
||||
memset(tmpchar, 0, length);
|
||||
// Now we recieve
|
||||
tmp = recv(socket, tmpchar, length-1, 0);
|
||||
if (tmp == -1)
|
||||
int sockfd = params[1];
|
||||
int length = params[3];
|
||||
|
||||
char *recv_buffer = new char[length];
|
||||
|
||||
if(recv_buffer == nullptr)
|
||||
return -1;
|
||||
|
||||
memset(recv_buffer, 0, length);
|
||||
|
||||
int bytes_received = -1;
|
||||
bytes_received = recv(sockfd, recv_buffer, length - 1, 0);
|
||||
|
||||
if(bytes_received == -1)
|
||||
{
|
||||
delete [] tmpchar;
|
||||
delete[] recv_buffer;
|
||||
return -1;
|
||||
}
|
||||
// And put a copy of our recieved data into amx's string
|
||||
tmpchar[tmp]='\0';
|
||||
int nlen = 0;
|
||||
//int max = params[3];
|
||||
int max = length-1;
|
||||
const char* src = tmpchar;
|
||||
cell* dest = MF_GetAmxAddr(amx,params[2]);
|
||||
while(max--&&nlen<tmp){
|
||||
*dest++ = (cell)*src++;
|
||||
nlen++;
|
||||
}
|
||||
*dest = 0;
|
||||
// And we need to free up the space to avoid wasting memory
|
||||
delete [] tmpchar;
|
||||
// And finnally, return the what recv returnd
|
||||
return tmp;
|
||||
|
||||
recv_buffer[bytes_received] = '\0';
|
||||
|
||||
cell* destination = MF_GetAmxAddr(amx, params[2]);
|
||||
|
||||
int current_length = 0;
|
||||
int max_length = length - 1;
|
||||
|
||||
while(max_length-- && current_length < bytes_received)
|
||||
{
|
||||
*destination++ = (cell)*recv_buffer++;
|
||||
current_length++;
|
||||
}
|
||||
|
||||
*destination = 0;
|
||||
|
||||
delete[] recv_buffer;
|
||||
|
||||
return bytes_received;
|
||||
}
|
||||
|
||||
// native socket_send(_socket, _data[], _length);
|
||||
static cell AMX_NATIVE_CALL socket_send(AMX *amx, cell *params) /* 3 param */
|
||||
static cell AMX_NATIVE_CALL socket_send(AMX *amx, cell *params)
|
||||
{
|
||||
// We get the string from amx
|
||||
int len;
|
||||
int socket = params[1];
|
||||
char* data = MF_GetAmxString(amx,params[2],0,&len);
|
||||
// And send it to the socket
|
||||
return send(socket, data, len, 0);
|
||||
}
|
||||
int sockfd = params[1];
|
||||
int length = 0;
|
||||
|
||||
static char *g_buffer = NULL;
|
||||
static size_t g_buflen = 0;
|
||||
char *data = MF_GetAmxString(amx, params[2], 0, &length);
|
||||
|
||||
return send(sockfd, data, length, 0);
|
||||
}
|
||||
|
||||
// native socket_send2(_socket, _data[], _length);
|
||||
static cell AMX_NATIVE_CALL socket_send2(AMX *amx, cell *params) /* 3 param */
|
||||
static cell AMX_NATIVE_CALL socket_send2(AMX *amx, cell *params)
|
||||
{
|
||||
// We get the string from amx
|
||||
int len = params[3];
|
||||
int socket = params[1];
|
||||
if ((size_t)len > g_buflen)
|
||||
int sockfd = params[1];
|
||||
int length = params[3];
|
||||
|
||||
if(length > g_send2_buffer_length)
|
||||
{
|
||||
delete [] g_buffer;
|
||||
g_buffer = new char[len+1];
|
||||
g_buflen = len;
|
||||
delete[] g_send2_buffer;
|
||||
|
||||
g_send2_buffer = new char[length + 1];
|
||||
|
||||
if(g_send2_buffer == nullptr)
|
||||
{
|
||||
g_send2_buffer_length = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_send2_buffer_length = length;
|
||||
}
|
||||
|
||||
cell *pData = MF_GetAmxAddr(amx, params[2]);
|
||||
char *pBuffer = g_buffer;
|
||||
cell *data = MF_GetAmxAddr(amx, params[2]);
|
||||
char *buffer = g_send2_buffer;
|
||||
|
||||
while (len--)
|
||||
*pBuffer++ = (char)*pData++;
|
||||
while(length--)
|
||||
*buffer++ = (char)*data++;
|
||||
|
||||
// And send it to the socket
|
||||
return send(socket, g_buffer, params[3], 0);
|
||||
return send(sockfd, g_send2_buffer, params[3], 0);
|
||||
}
|
||||
|
||||
AMX_NATIVE_INFO sockets_natives[] = {
|
||||
// native socket_change(_socket, _timeout = 100000);
|
||||
static cell AMX_NATIVE_CALL socket_change(AMX *amx, cell *params)
|
||||
{
|
||||
int sockfd = params[1];
|
||||
unsigned int timeout = params[2];
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout;
|
||||
|
||||
fd_set readfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sockfd, &readfds);
|
||||
|
||||
return (select(sockfd + 1, &readfds, nullptr, nullptr, &tv) > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
// native socket_is_readable(_socket, _timeout = 100000);
|
||||
static cell AMX_NATIVE_CALL socket_is_readable(AMX *amx, cell *params)
|
||||
{
|
||||
return socket_change(amx, params);
|
||||
}
|
||||
|
||||
// native socket_is_writable(_socket, _timeout = 100000);
|
||||
static cell AMX_NATIVE_CALL socket_is_writable(AMX *amx, cell *params)
|
||||
{
|
||||
int sockfd = params[1];
|
||||
unsigned int timeout = params[2];
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout;
|
||||
|
||||
fd_set writefds;
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(sockfd, &writefds);
|
||||
|
||||
return (select(sockfd + 1, nullptr, &writefds, nullptr, &tv) > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
AMX_NATIVE_INFO sockets_natives[] =
|
||||
{
|
||||
{"socket_open", socket_open},
|
||||
|
||||
{"socket_close", socket_close},
|
||||
{"socket_change", socket_change},
|
||||
|
||||
{"socket_recv", socket_recv},
|
||||
|
||||
{"socket_send", socket_send},
|
||||
{"socket_send2", socket_send2},
|
||||
|
||||
{"socket_change", socket_change},
|
||||
{"socket_is_readable", socket_is_readable},
|
||||
{"socket_is_writable", socket_is_writable},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
void OnAmxxAttach()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA WSAData;
|
||||
int errorcode = WSAStartup(MAKEWORD(2, 2), &WSAData);
|
||||
|
||||
if(errorcode != 0)
|
||||
{
|
||||
MF_Log("[%s]: WSAStartup failed with error code %d. Natives will not be available.", MODULE_LOGTAG, errorcode);
|
||||
return;
|
||||
}
|
||||
|
||||
g_winsock_initialized = true;
|
||||
#endif
|
||||
|
||||
MF_AddNatives(sockets_natives);
|
||||
// And, if win32, we have to specially start up the winsock environment
|
||||
#ifdef _WIN32
|
||||
short wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
wVersionRequested = MAKEWORD (1, 1);
|
||||
if (WSAStartup (wVersionRequested, &wsaData) != 0) {
|
||||
MF_Log("Sockets Module: Error while starting up winsock environment.!");
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void OnAmxxDetach()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
delete [] g_buffer;
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
if(g_winsock_initialized)
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
delete[] g_send2_buffer;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
|
||||
// Copyright (C) The AMX Mod X Development Team.
|
||||
//
|
||||
// Codebase from Ivan, -g-s-ivan@web.de (AMX 0.9.3)
|
||||
// Modification by Olaf Reusch, kenterfie@hlsw.de (AMXX 0.16, AMX 0.96)
|
||||
//
|
||||
// This software is licensed under the GNU General Public License, version 3 or higher.
|
||||
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||
// https://alliedmods.net/amxmodx-license
|
||||
|
@ -24,43 +21,159 @@
|
|||
#pragma loadlib sockets
|
||||
#endif
|
||||
|
||||
// Use SOCKET_TCP for TCP Socket connections
|
||||
|
||||
/**
|
||||
* Socket connection type (TCP/UDP)
|
||||
*/
|
||||
#define SOCKET_TCP 1
|
||||
|
||||
// Use SOCKET_UDP for UDP Socket connections
|
||||
|
||||
#define SOCKET_UDP 2
|
||||
|
||||
/* Opens a new connection to hostname:port via protocol (either SOCKET_TCP or SOCKET_UDP),
|
||||
* returns a socket (positive) or negative or zero on error.
|
||||
* States of error:
|
||||
* 0 - no error
|
||||
* 1 - error while creating socket
|
||||
* 2 - couldn't resolve hostname
|
||||
* 3 - couldn't connect to given hostname:port
|
||||
/**
|
||||
* Socket flags
|
||||
*/
|
||||
#define SOCK_NON_BLOCKING (1 << 0) /* Set the socket a nonblocking */
|
||||
#define SOCK_LIBC_ERRORS (1 << 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 */
|
||||
|
||||
/**
|
||||
* Connects to the given node and service via TCP/UDP.
|
||||
*
|
||||
* @note There's 2 types of error reporting on this function that you can use.
|
||||
* @note Default error codes:
|
||||
* 0 - No error
|
||||
* 1 - Error while creating socket
|
||||
* 2 - Couldn't resolve hostname
|
||||
* 3 - Couldn't connect
|
||||
* @note New, more expressive libc error codes:
|
||||
* 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 The currently available bit flags are:
|
||||
* - SOCK_NON_BLOCKING : if set, the socket will be on nonblocking mode
|
||||
* - SOCK_LIBC_ERRORS : if set, the new libc errors will be seen on _error
|
||||
*
|
||||
* @note If no flags are set, the behaviour of the function will not be modified.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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(const _hostname[], _port, _protocol = SOCKET_TCP, &_error, _flags = 0);
|
||||
|
||||
native socket_open(const _hostname[], _port, _protocol = SOCKET_TCP, &_error);
|
||||
|
||||
/* Closes a Socket */
|
||||
|
||||
/**
|
||||
* Closes a socket.
|
||||
*
|
||||
* @param _socket Socket descriptor
|
||||
*
|
||||
* @return 1 on success
|
||||
* 0 on failure
|
||||
*/
|
||||
native socket_close(_socket);
|
||||
|
||||
/* Recieves Data to string with the given length */
|
||||
|
||||
/**
|
||||
* Receives data.
|
||||
*
|
||||
* @note The amount of bytes than you end up receiving can be less than the one you expected.
|
||||
*
|
||||
* @param _socket Socket descriptor
|
||||
* @param _data Array to save the data
|
||||
* @param _length Length of the array
|
||||
*
|
||||
* @return Amount of bytes received
|
||||
* 0 if the peer closed the connection
|
||||
* -1 on failure
|
||||
*/
|
||||
native socket_recv(_socket, _data[], _length);
|
||||
|
||||
/* Sends data to the Socket */
|
||||
|
||||
/**
|
||||
* Sends data.
|
||||
*
|
||||
* @note The amount of bytes that end up being sent may be lower than the total length of the array,
|
||||
* check the return value and send the rest if needed.
|
||||
*
|
||||
* @param _socket Socket descriptor
|
||||
* @param _data Array with the data to send
|
||||
* @param _length Length of the array
|
||||
*
|
||||
* @return Amount of bytes sent
|
||||
* -1 on failure
|
||||
*/
|
||||
native socket_send(_socket, const _data[], _length);
|
||||
|
||||
/* Same as socket_send but Data can contain null bytes */
|
||||
|
||||
/**
|
||||
* Sends data that can contain null bytes.
|
||||
*
|
||||
* @note The amount of bytes that end up being sent may be lower than the total length of the array,
|
||||
* check the return value and send the rest if needed.
|
||||
*
|
||||
* @note strlen(_data) will return the wrong length if the array contains null bytes.
|
||||
*
|
||||
* @param _socket Socket descriptor
|
||||
* @param _data Array with the data to send
|
||||
* @param _length Length of the array
|
||||
*
|
||||
* @return Amount of bytes sent
|
||||
* -1 on failure
|
||||
*/
|
||||
native socket_send2(_socket, const _data[], _length);
|
||||
|
||||
/* This function will return true if the state (buffer content) have changed within the last recieve or
|
||||
* the timeout, where timeout is a value in µSeconds, (1 sec =1000000 µsec).
|
||||
* Use to check if new data is in your socket. */
|
||||
/**
|
||||
* Backwards compatible function.
|
||||
*
|
||||
* @deprecated Renamed to socket_is_readable()
|
||||
*/
|
||||
#pragma deprecated Use socket_is_readable() instead
|
||||
native socket_change(_socket, _timeout = 100000);
|
||||
|
||||
native socket_change(_socket, _timeout=100000);
|
||||
/**
|
||||
* Checks if a socket is marked as readable.
|
||||
*
|
||||
* @note You can use this function to make sure there's something on the socket and avoid a blocking call.
|
||||
* @note Set _timeout to 0 avoid blocking the call.
|
||||
* @note A socket will become readable if there's any data or an EOF.
|
||||
*
|
||||
* @param _socket Socket descriptor
|
||||
* @param _timeout Amount of time to block the call waiting for the socket to be marked as readable or
|
||||
* for the timeout to expire, in µSeconds (1 sec = 1000000 µsec)
|
||||
*
|
||||
* @return 1 if the socket is marked as readable
|
||||
* 0 otherwise
|
||||
*/
|
||||
native socket_is_readable(_socket, _timeout = 100000);
|
||||
|
||||
/**
|
||||
* Checks if a socket is marked as writable.
|
||||
*
|
||||
* @note Use this function to check if a nonblocking socket is ready to be used.
|
||||
* @note Set _timeout to 0 avoid blocking the call.
|
||||
* @note An UDP socket is always writable.
|
||||
*
|
||||
* @param _socket Socket descriptor
|
||||
* @param _timeout Amount of time to block the call waiting for the socket to be marked as writable or
|
||||
* for the timeout to expire, in µSeconds (1 sec = 1000000 µsec)
|
||||
*
|
||||
* @return 1 if the socket is marked as writable
|
||||
* 0 otherwise
|
||||
*/
|
||||
native socket_is_writable(_socket, _timeout = 100000);
|
||||
|
|
Loading…
Reference in New Issue
Block a user