1677 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1677 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*********************************************************************
 | |
| * Copyright (c) Intel Corporation 2011 - 2020
 | |
| * SPDX-License-Identifier: Apache-2.0
 | |
| **********************************************************************/
 | |
| 
 | |
| #ifdef MEMORY_CHECK
 | |
| #include <assert.h>
 | |
| #define MEMCHECK(x) x
 | |
| #else
 | |
| #define MEMCHECK(x)
 | |
| #endif
 | |
| 
 | |
| #if defined(WIN32)  && !defined(_MINCORE)
 | |
| #define _CRTDBG_MAP_ALLOC
 | |
| #include <stdlib.h>
 | |
| #include <crtdbg.h>
 | |
| #else
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #define SOCKET_ERROR            (-1)
 | |
| #define UNREFERENCED_PARAMETER(P) (P)
 | |
| #endif
 | |
| 
 | |
| #if defined(WINSOCK2)
 | |
| #include <winsock2.h>
 | |
| #include <ws2tcpip.h>
 | |
| #define MSG_NOSIGNAL 0
 | |
| #elif defined(WINSOCK1)
 | |
| #include <winsock.h>
 | |
| #include <wininet.h>
 | |
| #endif
 | |
| 
 | |
| #include "ILibParsers.h"
 | |
| #include "ILibAsyncSocket.h"
 | |
| #include "ILibRemoteLogging.h"
 | |
| 
 | |
| #include <assert.h>
 | |
| 
 | |
| //#ifndef WINSOCK2
 | |
| //#define SOCKET unsigned int
 | |
| //#endif
 | |
| 
 | |
| #define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)))
 | |
| 
 | |
| #ifdef SEMAPHORE_TRACKING
 | |
| #define SEM_TRACK(x) x
 | |
| void AsyncSocket_TrackLock(const char* MethodName, int Occurance, void *data)
 | |
| {
 | |
| 	char v[100];
 | |
| 	wchar_t wv[100];
 | |
| 	size_t l;
 | |
| 
 | |
| 	sprintf_s(v, 100, "  LOCK[%s, %d] (%x)\r\n",MethodName,Occurance,data);
 | |
| #ifdef WIN32
 | |
| 	mbstowcs_s(&l, wv, 100, v, 100);
 | |
| 	OutputDebugString(wv);
 | |
| #else
 | |
| 	printf(v);
 | |
| #endif
 | |
| }
 | |
| void AsyncSocket_TrackUnLock(const char* MethodName, int Occurance, void *data)
 | |
| {
 | |
| 	char v[100];
 | |
| 	wchar_t wv[100];
 | |
| 	size_t l;
 | |
| 
 | |
| 	sprintf_s(v, 100, "UNLOCK[%s, %d] (%x)\r\n",MethodName,Occurance,data);
 | |
| #ifdef WIN32
 | |
| 	mbstowcs_s(&l, wv, 100, v, 100);
 | |
| 	OutputDebugString(wv);
 | |
| #else
 | |
| 	printf(v);
 | |
| #endif
 | |
| }
 | |
| #else
 | |
| #define SEM_TRACK(x)
 | |
| #endif
 | |
| 
 | |
| struct ILibAsyncSocket_SendData
 | |
| {
 | |
| 	char* buffer;
 | |
| 	int bufferSize;
 | |
| 	int bytesSent;
 | |
| 
 | |
| 	struct sockaddr_in6 remoteAddress;
 | |
| 
 | |
| 	int UserFree;
 | |
| 	struct ILibAsyncSocket_SendData *Next;
 | |
| };
 | |
| 
 | |
| typedef struct ILibAsyncSocketModule
 | |
| {
 | |
| 	ILibTransport Transport;
 | |
| 	// DO NOT MODIFY THE ABOVE FIELDS (ILibTransport)
 | |
| 
 | |
| 	unsigned int PendingBytesToSend;
 | |
| 	unsigned int TotalBytesSent;
 | |
| 
 | |
| #if defined(WIN32)
 | |
| 	SOCKET internalSocket;
 | |
| #elif defined(_POSIX)
 | |
| 	int internalSocket;
 | |
| #endif
 | |
| 
 | |
| 	// The IPv4/IPv6 compliant address of the remote endpoint. We are not going to be using IPv6 all the time,
 | |
| 	// but we use the IPv6 structure to allocate the meximum space we need.
 | |
| 	struct sockaddr_in6 RemoteAddress;
 | |
| 
 | |
| 	// Local interface of a given socket. This module will bind to any interface, but the actual interface used
 | |
| 	// is stored here.
 | |
| 	struct sockaddr_in6 LocalAddress;
 | |
| 
 | |
| 	// Source address. Here is stored the actual source of a packet, usualy used with UDP where the source
 | |
| 	// of the traffic changes.
 | |
| 	struct sockaddr_in6 SourceAddress;
 | |
| 
 | |
| #ifdef MICROSTACK_PROXY
 | |
| 	// The address and port of a HTTPS proxy
 | |
| 	struct sockaddr_in6 ProxyAddress;
 | |
| 	char ProxiedHost[255];
 | |
| 	int ProxyState;
 | |
| 	char* ProxyUser;
 | |
| 	char* ProxyPass;
 | |
| #endif
 | |
| 
 | |
| 	ILibAsyncSocket_OnData OnData;
 | |
| 	ILibAsyncSocket_OnConnect OnConnect;
 | |
| 	ILibAsyncSocket_OnDisconnect OnDisconnect;
 | |
| 	ILibAsyncSocket_OnSendOK OnSendOK;
 | |
| 	ILibAsyncSocket_OnInterrupt OnInterrupt;
 | |
| 
 | |
| 	ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceeded;
 | |
| 	ILibAsyncSocket_OnBufferReAllocated OnBufferReAllocated;
 | |
| 
 | |
| 	void *LifeTime;
 | |
| 	void *user;
 | |
| 	void *user2;
 | |
| 	int user3;
 | |
| 	int PAUSE;
 | |
| 	int FinConnect;
 | |
| 	int BeginPointer;
 | |
| 	int EndPointer;
 | |
| 	char* buffer;
 | |
| 	int MallocSize;
 | |
| 	int InitialSize;
 | |
| 
 | |
| 	struct ILibAsyncSocket_SendData *PendingSend_Head;
 | |
| 	struct ILibAsyncSocket_SendData *PendingSend_Tail;
 | |
| 	sem_t SendLock;
 | |
| 
 | |
| 	int MaxBufferSize;
 | |
| 	int MaxBufferSizeExceeded;
 | |
| 	void *MaxBufferSizeUserObject;
 | |
| 
 | |
| 	// Added for TLS support
 | |
| 	long long timeout_lastActivity;
 | |
| 	int timeout_milliSeconds;
 | |
| 	ILibAsyncSocket_TimeoutHandler timeout_handler;
 | |
| }ILibAsyncSocketModule;
 | |
| 
 | |
| void ILibAsyncSocket_PostSelect(void* object,int slct, fd_set *readset, fd_set *writeset, fd_set *errorset);
 | |
| void ILibAsyncSocket_PreSelect(void* object,fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime);
 | |
| const int ILibMemory_ASYNCSOCKET_CONTAINERSIZE = (const int)sizeof(ILibAsyncSocketModule);
 | |
| 
 | |
| typedef enum ILibAsyncSocket_TLSPlainText_ContentType
 | |
| {
 | |
| 	ILibAsyncSocket_TLSPlainText_ContentType_ChangeCipherSpec = 20,
 | |
| 	ILibAsyncSocket_TLSPlainText_ContentType_Alert = 21,
 | |
| 	ILibAsyncSocket_TLSPlainText_ContentType_Handshake = 22,
 | |
| 	ILibAsyncSocket_TLSPlainText_ContentType_ApplicationData = 23
 | |
| }ILibAsyncSocket_TLSPlainText_ContentType;
 | |
| typedef enum ILibAsyncSocket_TLSHandshakeType
 | |
| {
 | |
| 	ILibAsyncSocket_TLSHandshakeType_hello = 0,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_clienthello = 1,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_serverhello = 2,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_certificate = 11,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_serverkeyexchange = 12,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_certificaterequest = 13,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_serverhellodone = 14,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_certificateverify = 15,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_clientkeyexchange = 16,
 | |
| 	ILibAsyncSocket_TLSHandshakeType_finished = 20
 | |
| }ILibAsyncSocket_TLSHandshakeType;
 | |
| 
 | |
| //
 | |
| // An internal method called by Chain as Destroy, to cleanup AsyncSocket
 | |
| //
 | |
| // <param name="socketModule">The AsyncSocketModule</param>
 | |
| void ILibAsyncSocket_Destroy(void *socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	struct ILibAsyncSocket_SendData *temp, *current;
 | |
| 
 | |
| 	// Call the interrupt event if necessary
 | |
| 	if (!ILibAsyncSocket_IsFree(module))
 | |
| 	{
 | |
| 		if (module->OnInterrupt != NULL) module->OnInterrupt(module, module->user);
 | |
| 	}
 | |
| 
 | |
| 	// Close socket if necessary
 | |
| 	if (module->internalSocket != ~0)
 | |
| 	{
 | |
| #if defined(WIN32)
 | |
| #if defined(WINSOCK2)
 | |
| 		shutdown(module->internalSocket, SD_BOTH);
 | |
| #endif
 | |
| 		closesocket(module->internalSocket);
 | |
| #elif defined(_POSIX)
 | |
| 		shutdown(module->internalSocket, SHUT_RDWR);
 | |
| 		close(module->internalSocket);
 | |
| #endif
 | |
| 		module->internalSocket = (SOCKET)~0;
 | |
| 	}
 | |
| 
 | |
| 	// Free the buffer if necessary
 | |
| 	if (module->buffer != NULL)
 | |
| 	{
 | |
| 		if (module->buffer != ILibScratchPad2) free(module->buffer);
 | |
| 		module->buffer = NULL;
 | |
| 		module->MallocSize = 0;
 | |
| 	}
 | |
| 
 | |
| 	// Clear all the data that is pending to be sent
 | |
| 	temp = current = module->PendingSend_Head;
 | |
| 	while (current != NULL)
 | |
| 	{
 | |
| 		temp = current->Next;
 | |
| 		if (current->UserFree == 0) free(current->buffer);
 | |
| 		free(current);
 | |
| 		current = temp;
 | |
| 	}
 | |
| 
 | |
| 	module->FinConnect = 0;
 | |
| 	module->user = NULL;
 | |
| 	sem_destroy(&(module->SendLock));
 | |
| }
 | |
| /*! \fn ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback)
 | |
| \brief Set the callback handler for when the internal data buffer has been resized
 | |
| \param AsyncSocketToken The specific connection to set the callback with
 | |
| \param Callback The callback handler to set
 | |
| */
 | |
| void ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback)
 | |
| {
 | |
| 	if (AsyncSocketToken != NULL) { ((struct ILibAsyncSocketModule*)AsyncSocketToken)->OnBufferReAllocated = Callback; }
 | |
| }
 | |
| 
 | |
| ILibTransport_DoneState ILibAsyncSocket_TransportSend(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done)
 | |
| {
 | |
| 	UNREFERENCED_PARAMETER(done);
 | |
| 	return((ILibTransport_DoneState)ILibAsyncSocket_Send(transport, buffer, bufferLength, (enum ILibAsyncSocket_MemoryOwnership)ownership));
 | |
| }
 | |
| 
 | |
| 
 | |
| /*! \fn ILibCreateAsyncSocketModule(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect,ILibAsyncSocket_OnSendOK OnSendOK)
 | |
| \brief Creates a new AsyncSocketModule
 | |
| \param Chain The chain to add this module to. (Chain must <B>not</B> be running)
 | |
| \param initialBufferSize The initial size of the receive buffer
 | |
| \param OnData Function Pointer that triggers when Data is received
 | |
| \param OnConnect Function Pointer that triggers upon successfull connection establishment
 | |
| \param OnDisconnect Function Pointer that triggers upon disconnect
 | |
| \param OnSendOK Function Pointer that triggers when pending sends are complete
 | |
| \param AutoFreeMemorySize Amount of memory to create along side the object, that will be freed when the parent object is freed
 | |
| \returns An ILibAsyncSocket token
 | |
| */
 | |
| ILibAsyncSocket_SocketModule ILibCreateAsyncSocketModuleWithMemory(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect, ILibAsyncSocket_OnSendOK OnSendOK, int UserMappedMemorySize)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *RetVal = (struct ILibAsyncSocketModule*)ILibChain_Link_Allocate(sizeof(struct ILibAsyncSocketModule), UserMappedMemorySize);
 | |
| 
 | |
| 	RetVal->Transport.IdentifierFlags = ILibTransports_AsyncSocket;
 | |
| 	RetVal->Transport.SendPtr = &ILibAsyncSocket_TransportSend;
 | |
| 	RetVal->Transport.ClosePtr = &ILibAsyncSocket_Disconnect;
 | |
| 	RetVal->Transport.PendingBytesPtr = &ILibAsyncSocket_GetPendingBytesToSend;
 | |
| 	RetVal->Transport.ChainLink.ParentChain = Chain;
 | |
| 
 | |
| 	if (initialBufferSize != 0)
 | |
| 	{
 | |
| 		// Use a new buffer
 | |
| 		if ((RetVal->buffer = (char*)malloc(initialBufferSize)) == NULL) ILIBCRITICALEXIT(254);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// Use a static buffer, often used for UDP.
 | |
| 		initialBufferSize = sizeof(ILibScratchPad2);
 | |
| 		RetVal->buffer = ILibScratchPad2;
 | |
| 	}
 | |
| 	RetVal->Transport.ChainLink.PreSelectHandler = &ILibAsyncSocket_PreSelect;
 | |
| 	RetVal->Transport.ChainLink.PostSelectHandler = &ILibAsyncSocket_PostSelect;
 | |
| 	RetVal->Transport.ChainLink.DestroyHandler = &ILibAsyncSocket_Destroy;
 | |
| 	RetVal->internalSocket = (SOCKET)~0;
 | |
| 	RetVal->OnData = OnData;
 | |
| 	RetVal->OnConnect = OnConnect;
 | |
| 	RetVal->OnDisconnect = OnDisconnect;
 | |
| 	RetVal->OnSendOK = OnSendOK;
 | |
| 	RetVal->InitialSize = initialBufferSize;
 | |
| 	RetVal->MallocSize = initialBufferSize;
 | |
| 	RetVal->LifeTime = ILibGetBaseTimer(Chain); //ILibCreateLifeTime(Chain);
 | |
| 
 | |
| 	sem_init(&(RetVal->SendLock), 0, 1);
 | |
| 
 | |
| 	ILibAddToChain(Chain, RetVal);
 | |
| 
 | |
| 	return((void*)RetVal);
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_ClearPendingSend(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Clears all the pending data to be sent for an AsyncSocket
 | |
| \param socketModule The ILibAsyncSocket to clear
 | |
| */
 | |
| void ILibAsyncSocket_ClearPendingSend(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	struct ILibAsyncSocket_SendData *data, *temp;
 | |
| 
 | |
| 	data = module->PendingSend_Head;
 | |
| 	module->PendingSend_Tail = NULL;
 | |
| 	module->PendingSend_Head = NULL;
 | |
| 	module->PendingBytesToSend = 0;
 | |
| 	while (data != NULL)
 | |
| 	{
 | |
| 		temp = data->Next;
 | |
| 		// We only need to free this if we have ownership of this memory
 | |
| 		if (data->UserFree == 0) free(data->buffer);
 | |
| 		free(data);
 | |
| 		data = temp;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, int remoteAddress, unsigned short remotePort, enum ILibAsyncSocket_MemoryOwnership UserFree)
 | |
| \brief Sends data on an AsyncSocket module to a specific destination. (Valid only for <B>UDP</B>)
 | |
| \param socketModule The ILibAsyncSocket module to send data on
 | |
| \param buffer The buffer to send
 | |
| \param length The length of the buffer to send
 | |
| \param remoteAddress The IPAddress of the destination 
 | |
| \param remotePort The Port number of the destination
 | |
| \param UserFree Flag indicating memory ownership. 
 | |
| \returns \a ILibAsyncSocket_SendStatus indicating the send status
 | |
| */
 | |
| enum ILibAsyncSocket_SendStatus ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, struct sockaddr *remoteAddress, enum ILibAsyncSocket_MemoryOwnership UserFree)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	struct ILibAsyncSocket_SendData *data;
 | |
| 	int bytesSent = 0;
 | |
| 	enum ILibAsyncSocket_SendStatus retVal = ILibAsyncSocket_ALL_DATA_SENT;
 | |
| 	
 | |
| 	// If the socket is empty, return now.
 | |
| 	if (socketModule == NULL) return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
 | |
| 
 | |
| 	// Setup a new send data structure
 | |
| 	if ((data = (struct ILibAsyncSocket_SendData*)malloc(sizeof(struct ILibAsyncSocket_SendData))) == NULL) ILIBCRITICALEXIT(254);
 | |
| 	memset(data, 0, sizeof(struct ILibAsyncSocket_SendData));
 | |
| 	data->buffer = buffer;
 | |
| 	data->bufferSize = length;
 | |
| 	data->bytesSent = 0;
 | |
| 	data->UserFree = UserFree;
 | |
| 	data->Next = NULL;
 | |
| 
 | |
| 	// Copy the address to the send data structure
 | |
| 	memset(&(data->remoteAddress), 0, sizeof(struct sockaddr_in6));
 | |
| 	if (remoteAddress != NULL) memcpy_s(&(data->remoteAddress), sizeof(struct sockaddr_in6), remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family));
 | |
| 
 | |
| 	SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_Send", 1, module);)
 | |
| 	sem_wait(&(module->SendLock));
 | |
| 
 | |
| 	if (module->internalSocket == ~0)
 | |
| 	{
 | |
| #ifdef WIN32
 | |
| 		ILIBMESSAGE2("internalSocket ERROR", WSAGetLastError());			
 | |
| #endif
 | |
| 		// Too Bad, the socket closed
 | |
| 		if (UserFree == 0) free(buffer);
 | |
| 		free(data);
 | |
| 		SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 2, module);)
 | |
| 		sem_post(&(module->SendLock));
 | |
| 		return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
 | |
| 	}
 | |
| 
 | |
| 	module->PendingBytesToSend += length;
 | |
| 	if (module->PendingSend_Tail != NULL || module->FinConnect == 0)
 | |
| 	{
 | |
| 		// There are still bytes that are pending to be sent, or pending connection, so we need to queue this up
 | |
| 		if (module->PendingSend_Tail == NULL)
 | |
| 		{
 | |
| 			module->PendingSend_Tail = data;
 | |
| 			module->PendingSend_Head = data;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			module->PendingSend_Tail->Next = data;
 | |
| 			module->PendingSend_Tail = data;
 | |
| 		}
 | |
| 		
 | |
| 		retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET;
 | |
| 
 | |
| 		if (UserFree == ILibAsyncSocket_MemoryOwnership_USER)
 | |
| 		{
 | |
| 			// If we don't own this memory, we need to copy the buffer,
 | |
| 			// because the user may free this memory before we have a chance to send it
 | |
| 			if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254);
 | |
| 			memcpy_s(data->buffer, data->bufferSize, buffer, length);
 | |
| 			MEMCHECK(assert(length <= data->bufferSize);)
 | |
| 			data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// There is no data pending to be sent, so lets go ahead and try to send this data
 | |
| 		module->PendingSend_Tail = data;
 | |
| 		module->PendingSend_Head = data;
 | |
| 
 | |
| 		if (remoteAddress == NULL)
 | |
| 		{
 | |
| 			// Set MSG_NOSIGNAL since we don't want to get Broken Pipe signals in Linux, ignored if Windows.
 | |
| 			bytesSent = send(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize-module->PendingSend_Head->bytesSent, MSG_NOSIGNAL);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			bytesSent = sendto(module->internalSocket, module->PendingSend_Head->buffer+module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize-module->PendingSend_Head->bytesSent, MSG_NOSIGNAL, (struct sockaddr*)remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family));
 | |
| 		}
 | |
| 
 | |
| 		module->timeout_lastActivity = ILibGetUptime();
 | |
| 
 | |
| 		if (bytesSent > 0)
 | |
| 		{
 | |
| 			// We were able to send something, so lets increment the counters
 | |
| 			module->PendingSend_Head->bytesSent += bytesSent;
 | |
| 			module->PendingBytesToSend -= bytesSent;
 | |
| 			module->TotalBytesSent += bytesSent;
 | |
| 		}
 | |
| 
 | |
| 		if (bytesSent == -1)
 | |
| 		{
 | |
| 			// Send returned an error, so lets figure out what it was, as it could be normal
 | |
| #if  defined(WIN32)
 | |
| 			bytesSent = WSAGetLastError();
 | |
| 			if (bytesSent != WSAEWOULDBLOCK)
 | |
| #elif defined(_POSIX)
 | |
| 			if (errno != EWOULDBLOCK)
 | |
| #endif
 | |
| 			{
 | |
| 				// printf("ILibAsyncSocket_Send ERROR %d\r\n", errno);
 | |
| 
 | |
| #ifdef WIN32
 | |
| 				ILIBMESSAGE2("ILibAsyncSocket_Send ERROR", WSAGetLastError());
 | |
| #endif // WIN32
 | |
| 
 | |
| 				// Most likely the socket closed while we tried to send
 | |
| 				module->PAUSE = 1;
 | |
| 				ILibAsyncSocket_ClearPendingSend(module); // This causes a segfault
 | |
| 				SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 3, module);)
 | |
| 				sem_post(&(module->SendLock));
 | |
| 
 | |
| 				// Ensure Calling On_Disconnect with MicroStackThread
 | |
| 				ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
 | |
| 
 | |
| 				return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
 | |
| 			}
 | |
| 		}
 | |
| 		if (module->PendingSend_Head->bytesSent == module->PendingSend_Head->bufferSize)
 | |
| 		{
 | |
| 			// All of the data has been sent
 | |
| 			if (UserFree == 0) free(module->PendingSend_Head->buffer);
 | |
| 			module->PendingSend_Tail = NULL;
 | |
| 			free(module->PendingSend_Head);
 | |
| 			module->PendingSend_Head = NULL;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			// All of the data wasn't sent, so we need to copy the buffer
 | |
| 			// if we don't own the memory, because the user may free the
 | |
| 			// memory, before we have a chance to complete sending it.
 | |
| 			if (UserFree == ILibAsyncSocket_MemoryOwnership_USER)
 | |
| 			{
 | |
| 				if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254);
 | |
| 				memcpy_s(data->buffer, data->bufferSize, buffer, length);
 | |
| 				MEMCHECK(assert(length <= data->bufferSize);)
 | |
| 				data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN;
 | |
| 			}
 | |
| 			retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET;
 | |
| 			ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_3, "AsyncSocket[%p] NOT_ALL_DATA_SENT", (void*)module);
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 	SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 4, module);)
 | |
| 	sem_post(&(module->SendLock));
 | |
| 	if (retVal != ILibAsyncSocket_ALL_DATA_SENT) ILibForceUnBlockChain(module->Transport.ChainLink.ParentChain);
 | |
| 	return (retVal);
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Disconnects an ILibAsyncSocket
 | |
| \param socketModule The ILibAsyncSocket to disconnect
 | |
| */
 | |
| void ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| #if defined(WIN32)
 | |
| 	SOCKET s;
 | |
| #else
 | |
| 	int s;
 | |
| #endif
 | |
| 
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 
 | |
| 	ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_1, "AsyncSocket[%p] << DISCONNECT", (void*)module);
 | |
| 
 | |
| 	SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_Disconnect", 1, module);)
 | |
| 	sem_wait(&(module->SendLock));
 | |
| 	module->timeout_handler = NULL;
 | |
| 	module->timeout_milliSeconds = 0;
 | |
| 
 | |
| 
 | |
| 	if (module->internalSocket != ~0)
 | |
| 	{
 | |
| 		// There is an associated socket that is still valid, so we need to close it
 | |
| 		module->PAUSE = 1;
 | |
| 		s = module->internalSocket;
 | |
| 		module->internalSocket = (SOCKET)~0;
 | |
| 		if (s != -1)
 | |
| 		{
 | |
| #if  defined(WIN32)
 | |
| #if defined(WINSOCK2)
 | |
| 			shutdown(s, SD_BOTH);
 | |
| #endif
 | |
| 			closesocket(s);
 | |
| #elif defined(_POSIX)
 | |
| 			shutdown(s, SHUT_RDWR);
 | |
| 			close(s);
 | |
| #endif
 | |
| 		}
 | |
| 
 | |
| 		// Since the socket is closing, we need to clear the data that is pending to be sent
 | |
| 		ILibAsyncSocket_ClearPendingSend(socketModule);
 | |
| 		SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Disconnect", 2, module);)
 | |
| 		sem_post(&(module->SendLock));
 | |
| 
 | |
| 			// This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently
 | |
| 			if (module->FinConnect <= 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed
 | |
| 			if (module->FinConnect > 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected
 | |
| 
 | |
| 		module->FinConnect = 0;
 | |
| 		module->user = NULL;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Disconnect", 3, module);)
 | |
| 		sem_post(&(module->SendLock));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ILibProcessAsyncSocket(struct ILibAsyncSocketModule *Reader, int pendingRead);
 | |
| void ILibAsyncSocket_Callback(ILibAsyncSocket_SocketModule socketModule, int connectDisconnectReadWrite)
 | |
| {
 | |
| 	if (socketModule != NULL)
 | |
| 	{
 | |
| 		struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 		if (connectDisconnectReadWrite == 0) // Connected
 | |
| 		{
 | |
| 			memset(&(module->LocalAddress), 0, sizeof(struct sockaddr_in6));
 | |
| 
 | |
| 			module->FinConnect = 1;
 | |
| 			module->PAUSE = 0;
 | |
| 
 | |
| 				// No SSL, tell application we are connected.
 | |
| 				module->OnConnect(module, -1, module->user);			
 | |
| 		}
 | |
| 		else if (connectDisconnectReadWrite == 1) // Disconnected
 | |
| 			ILibAsyncSocket_Disconnect(module);
 | |
| 		else if (connectDisconnectReadWrite == 2) // Data read
 | |
| 			ILibProcessAsyncSocket(module, 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_ConnectTo(ILibAsyncSocket_SocketModule socketModule, int localInterface, int remoteInterface, int remotePortNumber, ILibAsyncSocket_OnInterrupt InterruptPtr,void *user)
 | |
| \brief Attempts to establish a TCP connection
 | |
| \param socketModule The ILibAsyncSocket to initiate the connection
 | |
| \param localInterface The interface to use to establish the connection
 | |
| \param remoteInterface The remote interface to connect to
 | |
| \param InterruptPtr Function Pointer that triggers if connection attempt is interrupted
 | |
| \param user User object that will be passed to the \a OnConnect method
 | |
| */
 | |
| void ILibAsyncSocket_ConnectTo(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteInterface, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user)
 | |
| {
 | |
| 	int flags = 1;
 | |
| 	char *tmp;
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	struct sockaddr_in6 any;
 | |
| 
 | |
| 	// If there is something going on and we try to connect using this socket, fail! This is not supposed to happen.
 | |
| 	if (module->internalSocket != -1)
 | |
| 	{
 | |
| 		ILIBCRITICALEXIT2(253, (int)(module->internalSocket));
 | |
| 	}
 | |
| 
 | |
| 	// Clean up
 | |
| 	memset(&(module->RemoteAddress), 0, sizeof(struct sockaddr_in6));
 | |
| 	memset(&(module->LocalAddress) , 0, sizeof(struct sockaddr_in6));
 | |
| 	memset(&(module->SourceAddress), 0, sizeof(struct sockaddr_in6));
 | |
| 
 | |
| 	// Setup
 | |
| 	memcpy_s(&(module->RemoteAddress), sizeof(struct sockaddr_in6), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sa_family));
 | |
| 	module->PendingBytesToSend = 0;
 | |
| 	module->TotalBytesSent = 0;
 | |
| 	module->PAUSE = 0;
 | |
| 	module->user = user;
 | |
| 	module->OnInterrupt = InterruptPtr;
 | |
| 	if ((tmp = (char*)realloc(module->buffer, module->InitialSize)) == NULL) ILIBCRITICALEXIT(254);
 | |
| 	module->buffer = tmp;
 | |
| 	module->MallocSize = module->InitialSize;
 | |
| 
 | |
| 	// If localInterface is NULL, we will assume INADDRANY - IPv4/IPv6 based on remote address
 | |
| 	if (localInterface == NULL)
 | |
| 	{
 | |
| 		memset(&any, 0, sizeof(struct sockaddr_in6));
 | |
| 		#ifdef MICROSTACK_PROXY
 | |
| 		if (module->ProxyAddress.sin6_family == 0) any.sin6_family = remoteInterface->sa_family; else any.sin6_family = module->ProxyAddress.sin6_family;
 | |
| 		#else
 | |
| 		any.sin6_family = remoteInterface->sa_family;
 | |
| 		#endif
 | |
| 		localInterface = (struct sockaddr*)&any;
 | |
| 	}
 | |
| 
 | |
| 	// The local port should always be zero
 | |
| #ifdef _DEBUG
 | |
| 	if (localInterface->sa_family == AF_INET && ((struct sockaddr_in*)localInterface)->sin_port != 0) { ILIBCRITICALEXIT(253); }
 | |
| 	if (localInterface->sa_family == AF_INET6 && ((struct sockaddr_in6*)localInterface)->sin6_port != 0) { ILIBCRITICALEXIT(253); }
 | |
| #endif
 | |
| 
 | |
| 	// Allocate a new socket
 | |
| 	if ((module->internalSocket = ILibGetSocket(localInterface, SOCK_STREAM, IPPROTO_TCP)) == 0) { ILIBCRITICALEXIT(253); return; }
 | |
| 
 | |
| 	// Initialise the buffer pointers, since no data is in them yet.
 | |
| 	module->FinConnect = 0;
 | |
| 	module->BeginPointer = 0;
 | |
| 	module->EndPointer = 0;
 | |
| 
 | |
| 	// Turn on keep-alives for the socket
 | |
| 	if (setsockopt(module->internalSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flags, sizeof(flags)) != 0) ILIBCRITICALERREXIT(253);
 | |
| 
 | |
| 	// Set the socket to non-blocking mode, because we need to play nice and share the MicroStack thread
 | |
| #if defined(WIN32)
 | |
| 	ioctlsocket(module->internalSocket, FIONBIO, (u_long *)(&flags));
 | |
| #elif defined(_POSIX)
 | |
| 	flags = fcntl(module->internalSocket, F_GETFL,0);
 | |
| 	fcntl(module->internalSocket, F_SETFL, O_NONBLOCK | flags);
 | |
| #endif
 | |
| 
 | |
| 	// Connect the socket, and force the chain to unblock, since the select statement doesn't have us in the fdset yet.
 | |
| #ifdef MICROSTACK_PROXY
 | |
| 	if (module->ProxyAddress.sin6_family != 0)
 | |
| 	{
 | |
| 		if (connect(module->internalSocket, (struct sockaddr*)&(module->ProxyAddress), INET_SOCKADDR_LENGTH(module->ProxyAddress.sin6_family)) != -1)
 | |
| 		{
 | |
| 			// Connect failed. Set a short time and call disconnect.
 | |
| 			module->FinConnect = -1;
 | |
| 			ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| #endif
 | |
| 		if (connect(module->internalSocket, (struct sockaddr*)remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sa_family)) != -1)
 | |
| 	{
 | |
| 		// Connect failed. Set a short time and call disconnect.
 | |
| 		module->FinConnect = -1;
 | |
| 		ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| #ifdef _DEBUG
 | |
| 	#ifdef _POSIX
 | |
| 		if (errno != EINPROGRESS) // The result of the connect should always be "WOULD BLOCK" on Linux. But sometimes this fails.
 | |
| 		{
 | |
| 			// This happens when the interface is no longer available. Disconnect socket.
 | |
| 			module->FinConnect = -1;
 | |
| 			ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 	#endif
 | |
| 	#ifdef WIN32
 | |
| 		{
 | |
| 			if (GetLastError() != WSAEWOULDBLOCK) // The result of the connect should always be "WOULD BLOCK" on Windows.
 | |
| 			{
 | |
| 				// This happens when the interface is no longer available. Disconnect socket.
 | |
| 				module->FinConnect = -1;
 | |
| 				ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
 | |
| 				return;
 | |
| 			}
 | |
| 		}
 | |
| 	#endif
 | |
| #endif
 | |
| 
 | |
| 	ILibForceUnBlockChain(module->Transport.ChainLink.ParentChain);
 | |
| }
 | |
| 
 | |
| #ifdef MICROSTACK_PROXY
 | |
| //! Connect using an HTTPS proxy. If "proxyAddress" is set to NULL, this call acts just to a normal connect call without a proxy.
 | |
| /*!
 | |
| 	\param socketModule ILibAsyncSocket Client to initiate the connection
 | |
| 	\param localInterface Local endpoint to originate the connection request from
 | |
| 	\param remoteAddress Destination endpoint to connect to
 | |
| 	\param proxyAddress Proxy Server to relay the connection thru.
 | |
| 	\param proxyUser Proxy Server username (Username is stored by reference, so this memory must remain valid for duration of connection)
 | |
| 	\param proxyPass Proxy Server password (Password is stored by reference, so this memory must remain valid for duration of connection)
 | |
| 	\param InterruptPtr Event handler triggered if connection request is interrupted
 | |
| 	\param user Custom user state data
 | |
| */
 | |
| void ILibAsyncSocket_ConnectToProxy(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteAddress, struct sockaddr *proxyAddress, char* proxyUser, char* proxyPass, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	memset(&(module->ProxyAddress), 0, sizeof(struct sockaddr_in6));
 | |
| 	module->ProxyState = 0;
 | |
| 	module->ProxyUser = proxyUser; // Proxy user & password are kept by reference!!!
 | |
| 	module->ProxyPass = proxyPass;
 | |
| 
 | |
| 	if (proxyAddress != NULL) memcpy_s(&(module->ProxyAddress), sizeof(struct sockaddr_in6), proxyAddress, INET_SOCKADDR_LENGTH(proxyAddress->sa_family));
 | |
| 	ILibAsyncSocket_ConnectTo(socketModule, localInterface, remoteAddress, InterruptPtr, user);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // Internal method called when data is ready to be processed on an ILibAsyncSocket
 | |
| //
 | |
| // <param name="Reader">The ILibAsyncSocket with pending data</param>
 | |
| void ILibProcessAsyncSocket(struct ILibAsyncSocketModule *Reader, int pendingRead)
 | |
| {
 | |
| 	int bytesReceived = 0;
 | |
| 	int len;
 | |
| 	char *temp;
 | |
| 
 | |
| 	if (Reader->PAUSE > 0)
 | |
| 	{
 | |
| 		ILibRemoteLogging_printf(ILibChainGetLogger(Reader->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] is PAUSED", (void*)Reader);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	//
 | |
| 	// Try to read from the socket
 | |
| 	//
 | |
| 	if (pendingRead != 0)
 | |
| 	{
 | |
| 		{
 | |
| 			// Read data off the non-SSL, generic socket.
 | |
| 			// Set the receive address buffer size and read from the socket.
 | |
| 			len = sizeof(struct sockaddr_in6);
 | |
| #if defined(WINSOCK2)
 | |
| 			bytesReceived = recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), (int*)&len);
 | |
| #else
 | |
| 			bytesReceived = (int)recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), (socklen_t*)&len);
 | |
| #endif
 | |
| #ifdef WIN32
 | |
| #ifdef _DEBUG
 | |
| 			if (bytesReceived == SOCKET_ERROR) ILIBMESSAGE2("Failed socket recvfrom function", WSAGetLastError());
 | |
| #endif
 | |
| #endif
 | |
| 			ILib6to4((struct sockaddr*)&(Reader->SourceAddress));
 | |
| 			ILibRemoteLogging_printf(ILibChainGetLogger(Reader->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] recv (NON-TLS) returned %d", (void*)Reader, bytesReceived);
 | |
| 		}
 | |
| 		if (bytesReceived > 0)
 | |
| 		{
 | |
| 			//
 | |
| 			// Data was read, so increment our counters
 | |
| 			//
 | |
| 			Reader->EndPointer += bytesReceived;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//
 | |
| 	// Event OnData up the stack, to process any data that is available
 | |
| 	//
 | |
| 	while (Reader->internalSocket != ~0 && Reader->PAUSE <= 0 && Reader->BeginPointer != Reader->EndPointer && Reader->EndPointer != 0)
 | |
| 	{
 | |
| 		int iPointer = 0;
 | |
| 
 | |
| 		if (Reader->OnData != NULL)
 | |
| 		{
 | |
| 			Reader->OnData(Reader, Reader->buffer + Reader->BeginPointer, &(iPointer), Reader->EndPointer - Reader->BeginPointer, &(Reader->OnInterrupt), &(Reader->user), &(Reader->PAUSE));
 | |
| 			if (iPointer == 0) { break; }
 | |
| 			Reader->BeginPointer += iPointer;
 | |
| 		}
 | |
| 	}
 | |
| 	if (Reader->BeginPointer == Reader->EndPointer) { Reader->BeginPointer = Reader->EndPointer = 0; }
 | |
| 
 | |
| 
 | |
| 	if (bytesReceived <= 0 && pendingRead != 0)
 | |
| 	{
 | |
| 		// If a UDP packet is larger than the buffer, drop it.
 | |
| #if defined(WINSOCK2)
 | |
| 		if (bytesReceived == SOCKET_ERROR && WSAGetLastError() == 10040) { ILIBMESSAGE2("Failed bytesReceived", WSAGetLastError()); return; }
 | |
| #else
 | |
| 		// TODO: Linux errno
 | |
| 		//if (bytesReceived == -1 && errno != 0) printf("ERROR: errno = %d, %s\r\n", errno, strerror(errno));
 | |
| #endif
 | |
| 
 | |
| 		//
 | |
| 		// This means the socket was gracefully closed by the remote endpoint
 | |
| 		//
 | |
| 		SEM_TRACK(AsyncSocket_TrackLock("ILibProcessAsyncSocket", 1, Reader);)
 | |
| 			ILibAsyncSocket_ClearPendingSend(Reader);
 | |
| 		SEM_TRACK(AsyncSocket_TrackUnLock("ILibProcessAsyncSocket", 2, Reader);)
 | |
| 
 | |
| #if  defined(WIN32)
 | |
| #if defined(WINSOCK2)
 | |
| 			shutdown(Reader->internalSocket, SD_BOTH);
 | |
| #endif
 | |
| 		closesocket(Reader->internalSocket);
 | |
| #elif defined(_POSIX)
 | |
| 			shutdown(Reader->internalSocket, SHUT_RDWR);
 | |
| 		close(Reader->internalSocket);
 | |
| #endif
 | |
| 		Reader->internalSocket = (SOCKET)~0;
 | |
| 
 | |
| 		ILibAsyncSocket_ClearPendingSend(Reader);
 | |
| 	
 | |
| 		//
 | |
| 		// Inform the user the socket has closed
 | |
| 		//
 | |
| 		Reader->timeout_handler = NULL;
 | |
| 		Reader->timeout_milliSeconds = 0;
 | |
| 			// This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently
 | |
| 			if (Reader->FinConnect <= 0 && Reader->OnConnect != NULL) { Reader->OnConnect(Reader, 0, Reader->user); } // Connection Failed
 | |
| 			if (Reader->FinConnect > 0 && Reader->OnDisconnect != NULL) { Reader->OnDisconnect(Reader, Reader->user); } // Socket Disconnected
 | |
| 		Reader->FinConnect = 0;
 | |
| 
 | |
| 		//
 | |
| 		// If we need to free the buffer, do so
 | |
| 		//
 | |
| 		if (Reader->buffer != NULL)
 | |
| 		{
 | |
| 			if (Reader->buffer != ILibScratchPad2) free(Reader->buffer);
 | |
| 			Reader->buffer = NULL;
 | |
| 			Reader->MallocSize = 0;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// 
 | |
| 		// Only do these checks if the socket was not closed, otherwise we're wasting time
 | |
| 		//
 | |
| 
 | |
| 		//
 | |
| 		// Check to see if we need to move any data, to maximize buffer space
 | |
| 		//
 | |
| 		if (Reader->BeginPointer > 0)
 | |
| 		{
 | |
| 			//
 | |
| 			// We can save some cycles by moving the data back to the top
 | |
| 			// of the buffer, instead of just allocating more memory.
 | |
| 			//
 | |
| 			temp = Reader->buffer + Reader->BeginPointer;
 | |
| 			
 | |
| 			memmove_s(Reader->buffer, Reader->MallocSize, temp, Reader->EndPointer - Reader->BeginPointer);
 | |
| 			Reader->EndPointer -= Reader->BeginPointer;
 | |
| 			Reader->BeginPointer = 0;
 | |
| 
 | |
| 			//
 | |
| 			// Even though we didn't allocate new memory, we still moved data in the buffer, 
 | |
| 			// so we need to inform people of that, because it might be important
 | |
| 			//
 | |
| 			if (Reader->OnBufferReAllocated != NULL) Reader->OnBufferReAllocated(Reader, Reader->user, temp - Reader->buffer);
 | |
| 		}
 | |
| 
 | |
| 		//
 | |
| 		// Check to see if we should grow the buffer
 | |
| 		//
 | |
| 		if (Reader->MallocSize - Reader->EndPointer < 1024 && (Reader->MaxBufferSize == 0 || Reader->MallocSize < Reader->MaxBufferSize))
 | |
| 		{
 | |
| 			Reader->MallocSize = (Reader->MallocSize + MEMORYCHUNKSIZE < Reader->MaxBufferSize) ? (Reader->MallocSize + MEMORYCHUNKSIZE) : (Reader->MaxBufferSize == 0 ? (Reader->MallocSize + MEMORYCHUNKSIZE) : Reader->MaxBufferSize);
 | |
| 
 | |
| 			temp = Reader->buffer;
 | |
| 			if ((Reader->buffer = (char*)realloc(Reader->buffer, Reader->MallocSize)) == NULL) ILIBCRITICALEXIT(254);
 | |
| 			//
 | |
| 			// If this realloc moved the buffer somewhere, we need to inform people of it
 | |
| 			//
 | |
| 			if (Reader->buffer != temp && Reader->OnBufferReAllocated != NULL) Reader->OnBufferReAllocated(Reader, Reader->user, Reader->buffer - temp);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Returns the user object
 | |
| \param socketModule The ILibAsyncSocket token to fetch the user object from
 | |
| \returns The user object
 | |
| */
 | |
| void *ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	return(socketModule == NULL?NULL:((struct ILibAsyncSocketModule*)socketModule)->user);
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_SetUser(ILibAsyncSocket_SocketModule socketModule, void* user)
 | |
| {
 | |
| 	if (socketModule == NULL) return;
 | |
| 	((struct ILibAsyncSocketModule*)socketModule)->user = user;
 | |
| }
 | |
| 
 | |
| void *ILibAsyncSocket_GetUser2(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	return(socketModule == NULL?NULL:((struct ILibAsyncSocketModule*)socketModule)->user2);
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_SetUser2(ILibAsyncSocket_SocketModule socketModule, void* user2)
 | |
| {
 | |
| 	if (socketModule == NULL) return;
 | |
| 	((struct ILibAsyncSocketModule*)socketModule)->user2 = user2;
 | |
| }
 | |
| 
 | |
| int ILibAsyncSocket_GetUser3(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	return(socketModule == NULL?-1:((struct ILibAsyncSocketModule*)socketModule)->user3);
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_SetUser3(ILibAsyncSocket_SocketModule socketModule, int user3)
 | |
| {
 | |
| 	if (socketModule == NULL) return;
 | |
| 	((struct ILibAsyncSocketModule*)socketModule)->user3 = user3;
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_SetTimeout(ILibAsyncSocket_SocketModule socketModule, int timeoutSeconds, ILibAsyncSocket_TimeoutHandler timeoutHandler)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	module->timeout_milliSeconds = timeoutSeconds * 1000;
 | |
| 	module->timeout_handler = timeoutHandler;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Chained PreSelect handler for ILibAsyncSocket
 | |
| //
 | |
| // <param name="readset"></param>
 | |
| // <param name="writeset"></param>
 | |
| // <param name="errorset"></param>
 | |
| // <param name="blocktime"></param>
 | |
| void ILibAsyncSocket_PreSelect(void* socketModule,fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	if (module->internalSocket == -1) return; // If there is not internal socket, just return now.
 | |
| 
 | |
| 	ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "AsyncSocket[%p] entered PreSelect", (void*)module);
 | |
| 
 | |
| 	SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PreSelect", 1, module);)
 | |
| 	sem_wait(&(module->SendLock));
 | |
| 
 | |
| 	if (module->internalSocket != -1)
 | |
| 	{
 | |
| 		if (module->timeout_milliSeconds != 0)
 | |
| 		{
 | |
| 			// User has set idle timeout, so we need to start checking
 | |
| 			if (module->timeout_lastActivity == 0)
 | |
| 			{
 | |
| 				// No activity yet on socket, so set the idle timeout for the full duration
 | |
| 				*blocktime = module->timeout_milliSeconds;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				long long activity = ILibGetUptime() - module->timeout_lastActivity; // number of milliseconds since last activity
 | |
| 				if (activity >= module->timeout_milliSeconds) 
 | |
| 				{
 | |
| 					// Idle Timeout Occured
 | |
| 					ILibAsyncSocket_TimeoutHandler h = module->timeout_handler;
 | |
| 					module->timeout_milliSeconds = 0;
 | |
| 					module->timeout_handler = NULL;
 | |
| 					if (h != NULL)
 | |
| 					{
 | |
| 						sem_post(&(module->SendLock));
 | |
| 						h(module, module->user);
 | |
| 						sem_wait(&(module->SendLock));
 | |
| 						if (module->timeout_milliSeconds != 0) { *blocktime = module->timeout_milliSeconds; }
 | |
| 					}
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					// Idle Timeout did not occur yet, so set the blocktime for the rest of the timeout
 | |
| 					*blocktime = (int)(module->timeout_milliSeconds - activity);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (module->PAUSE < 0) *blocktime = 0;
 | |
| 		if (module->FinConnect == 0)
 | |
| 		{
 | |
| 			// Not Connected Yet
 | |
| 			#if defined(WIN32)
 | |
| 			#pragma warning( push, 3 ) // warning C4127: conditional expression is constant
 | |
| 			#endif
 | |
| 			FD_SET(module->internalSocket, writeset);
 | |
| 			FD_SET(module->internalSocket, errorset);
 | |
| 			#if defined(WIN32)
 | |
| 			#pragma warning( pop )
 | |
| 			#endif
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			if (module->PAUSE == 0) // Only if this is zero. <0 is resume, so we want to process first
 | |
| 			{
 | |
| 				// Already Connected, just needs reading
 | |
| 				#if defined(WIN32)
 | |
| 				#pragma warning( push, 3 ) // warning C4127: conditional expression is constant
 | |
| 				#endif
 | |
| 				FD_SET(module->internalSocket, readset);
 | |
| 				FD_SET(module->internalSocket, errorset);
 | |
| 				#if defined(WIN32)
 | |
| 				#pragma warning( pop )
 | |
| 				#endif
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (module->PendingSend_Head != NULL)
 | |
| 		{
 | |
| 			// If there is pending data to be sent, then we need to check when the socket is writable
 | |
| 			#if defined(WIN32)
 | |
| 			#pragma warning( push, 3 ) // warning C4127: conditional expression is constant
 | |
| 			#endif
 | |
| 			FD_SET(module->internalSocket, writeset);
 | |
| 			#if defined(WIN32)
 | |
| 			#pragma warning( pop )
 | |
| 			#endif
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PreSelect", 2, module);)
 | |
| 	sem_post(&(module->SendLock));
 | |
| 
 | |
| 	ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "...AsyncSocket[%p] exited PreSelect", (void*)module);
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_PrivateShutdown(void* socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 
 | |
| 	// If this is an SSL socket, close down the SSL state
 | |
| 
 | |
| 	// Now shutdown the socket and set it to zero
 | |
| 	#if  defined(WIN32)
 | |
| 	#if defined(WINSOCK2)
 | |
| 		shutdown(module->internalSocket, SD_BOTH);
 | |
| 	#endif
 | |
| 		closesocket(module->internalSocket);
 | |
| 	#elif defined(_POSIX)
 | |
| 		shutdown(module->internalSocket, SHUT_RDWR);
 | |
| 		close(module->internalSocket);
 | |
| 	#endif
 | |
| 	module->internalSocket = (SOCKET)~0;
 | |
| 	module->timeout_handler = NULL;
 | |
| 	module->timeout_milliSeconds = 0;
 | |
| 
 | |
| 		// This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently
 | |
| 		if (module->FinConnect <= 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed
 | |
| 		if (module->FinConnect > 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected
 | |
| 	module->FinConnect = 0;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Chained PostSelect handler for ILibAsyncSocket
 | |
| //
 | |
| // <param name="socketModule"></param>
 | |
| // <param name="slct"></param>
 | |
| // <param name="readset"></param>
 | |
| // <param name="writeset"></param>
 | |
| // <param name="errorset"></param>
 | |
| void ILibAsyncSocket_PostSelect(void* socketModule, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset)
 | |
| {
 | |
| 	int TriggerSendOK = 0;
 | |
| 	struct ILibAsyncSocket_SendData *temp;
 | |
| 	int bytesSent = 0;
 | |
| 	int flags, len;
 | |
| 	int TRY_TO_SEND = 1;
 | |
| 	int triggerReadSet = 0;
 | |
| 	int triggerResume = 0;
 | |
| 	int triggerWriteSet = 0;
 | |
| 	int serr = 0, serrlen = sizeof(serr);
 | |
| 	int fd_error, fd_read, fd_write;
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 
 | |
| 	// If there is no internal socket or no events, just return now.
 | |
| 	if (module->internalSocket == -1 || module->FinConnect == -1) return;
 | |
| 	fd_error = FD_ISSET(module->internalSocket, errorset);
 | |
| 	fd_read = FD_ISSET(module->internalSocket, readset);
 | |
| 	fd_write = FD_ISSET(module->internalSocket, writeset);
 | |
| 
 | |
| 	ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "AsyncSocket[%p] entered PostSelect", (void*)module);
 | |
| 
 | |
| 
 | |
| 
 | |
| 	UNREFERENCED_PARAMETER( slct );
 | |
| 
 | |
| 	SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PostSelect", 1, module);)
 | |
| 	sem_wait(&(module->SendLock)); // Lock!
 | |
| 	
 | |
| 	//if (fd_error != 0) printf("ILibAsyncSocket_PostSelect-ERROR\r\n");
 | |
| 	//if (fd_read != 0) printf("ILibAsyncSocket_PostSelect-READ\r\n");
 | |
| 	//if (fd_write != 0) printf("ILibAsyncSocket_PostSelect-WRITE\r\n");
 | |
| 
 | |
| 	//
 | |
| 	// Error Handling. If the ERROR flag is set we have a problem. If not, we must check the socket status for an error.
 | |
| 	// Yes, this is odd, but it's possible for a socket to report a read set and still have an error, in this past this
 | |
| 	// was not handled and caused a lot of problems.
 | |
| 	//
 | |
| 	if (fd_error != 0)
 | |
| 	{
 | |
| 		serr = 1;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// Fetch the socket error code
 | |
| #if defined(WINSOCK2)
 | |
| 		getsockopt(module->internalSocket, SOL_SOCKET, SO_ERROR, (char*)&serr, (int*)&serrlen);
 | |
| #else
 | |
| 		getsockopt(module->internalSocket, SOL_SOCKET, SO_ERROR, (char*)&serr, (socklen_t*)&serrlen);
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	#ifdef MICROSTACK_PROXY
 | |
| 	// Handle proxy, we need to read the proxy response, all of it and not a byte more.
 | |
| 	if (module->FinConnect == 1 && module->ProxyState == 1 && serr == 0 && fd_read != 0)
 | |
| 	{
 | |
| 		char *ptr1, *ptr2;
 | |
| 		int len2, len3;
 | |
| 		serr = 555; // Fake proxy error
 | |
| 		len2 = recv(module->internalSocket, ILibScratchPad2, 1024, MSG_PEEK | MSG_NOSIGNAL);
 | |
| 		if (len2 > 0 && len2 < 1024)
 | |
| 		{
 | |
| 			ILibScratchPad2[len2] = 0;
 | |
| 			ptr1 = strstr(ILibScratchPad2, "\r\n\r\n");
 | |
| 			ptr2 = strstr(ILibScratchPad2, " 200 ");
 | |
| 			if (ptr1 != NULL && ptr2 != NULL && ptr2 < ptr1)
 | |
| 			{
 | |
| 				len3 = (int)((ptr1 + 4) - ILibScratchPad2);
 | |
| 				recv(module->internalSocket, ILibScratchPad2, len3, MSG_NOSIGNAL);
 | |
| 				module->FinConnect = 0; // Let pretend we never connected, this will trigger all the connection stuff.
 | |
| 				module->ProxyState = 2; // Move the proxy connection state forward.
 | |
| 				serr = 0;				// Proxy connected collectly.
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	#endif
 | |
| 
 | |
| 	// If there are any errors, shutdown this socket
 | |
| 	if (serr != 0)
 | |
| 	{
 | |
| 		// Unlock before fireing the event
 | |
| 		SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);)
 | |
| 		sem_post(&(module->SendLock));
 | |
| 		ILibAsyncSocket_PrivateShutdown(module);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// There are no errors, lets keep processing the socket normally
 | |
| 		if (module->FinConnect == 0)
 | |
| 		{
 | |
| 			// Check to see if the socket is connected
 | |
| #ifdef MICROSTACK_PROXY
 | |
| 			if (fd_write != 0 || module->ProxyState == 2)
 | |
| #else
 | |
| 			if (fd_write != 0)
 | |
| #endif
 | |
| 			{
 | |
| 				// Connected
 | |
| 				len = sizeof(struct sockaddr_in6);
 | |
| #if defined(WINSOCK2)
 | |
| 				getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), (int*)&len);
 | |
| #else
 | |
| 				getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), (socklen_t*)&len);
 | |
| #endif
 | |
| 				module->FinConnect = 1;
 | |
| 				module->PAUSE = 0;
 | |
| 
 | |
| 				// Set the socket to non-blocking mode, so we can play nice and share the thread
 | |
| 				#if defined(WIN32)
 | |
| 					flags = 1;
 | |
| 					ioctlsocket(module->internalSocket, FIONBIO, (u_long *)(&flags));
 | |
| 				#elif defined(_POSIX)
 | |
| 				flags = fcntl(module->internalSocket, F_GETFL,0);
 | |
| 				fcntl(module->internalSocket, F_SETFL, O_NONBLOCK|flags);
 | |
| 				#endif
 | |
| 
 | |
| 				// If this is a proxy connection, send the proxy connect header now.
 | |
| #ifdef MICROSTACK_PROXY
 | |
| 				if (module->ProxyAddress.sin6_family != 0 && module->ProxyState == 0)
 | |
| 				{
 | |
| 					int len2;
 | |
| 					ILibInet_ntop((int)(module->RemoteAddress.sin6_family), (void*)&(((struct sockaddr_in*)&(module->RemoteAddress))->sin_addr), ILibScratchPad, 4096);
 | |
| 					if (module->ProxyUser == NULL || module->ProxyPass == NULL)
 | |
| 					{
 | |
| 						len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s:%u HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\n\r\n", ILibScratchPad, ntohs(module->RemoteAddress.sin6_port), ILibScratchPad);
 | |
| 					} else {
 | |
| 						char* ProxyAuth = NULL;
 | |
| 						len2 = sprintf_s(ILibScratchPad2, 4096, "%s:%s", module->ProxyUser, module->ProxyPass);
 | |
| 						len2 = ILibBase64Encode((unsigned char*)ILibScratchPad2, len2, (unsigned char**)&ProxyAuth);
 | |
| 						len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s:%u HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\nProxy-authorization: basic %s\r\n\r\n", ILibScratchPad, ntohs(module->RemoteAddress.sin6_port), ILibScratchPad, ProxyAuth);
 | |
| 						if (ProxyAuth != NULL) free(ProxyAuth);
 | |
| 					}
 | |
| 					module->timeout_lastActivity = ILibGetUptime();
 | |
| 					send(module->internalSocket, ILibScratchPad2, len2, MSG_NOSIGNAL);
 | |
| 					module->ProxyState = 1;
 | |
| 					// TODO: Set timeout. If the proxy does not respond, we need to close this connection.
 | |
| 					// On the other hand... This is not generally a problem, proxies will disconnect after a timeout anyway.
 | |
| 					
 | |
| 					SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);)
 | |
| 					sem_post(&(module->SendLock));
 | |
| 					return;
 | |
| 				}
 | |
| 				if (module->ProxyState == 2) module->ProxyState = 3;
 | |
| #endif
 | |
| 
 | |
| 				// Connection Complete
 | |
| 				triggerWriteSet = 1;
 | |
| 			}
 | |
| 
 | |
| 			// Unlock before fireing the event
 | |
| 			SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);)
 | |
| 			sem_post(&(module->SendLock));
 | |
| 
 | |
| 			// If we did connect, we got more things to do
 | |
| 			if (triggerWriteSet != 0)
 | |
| 			{
 | |
| 				module->timeout_lastActivity = ILibGetUptime();
 | |
| 				{
 | |
| 					// If this is a normal socket, event the connection now.
 | |
| 					if (module->OnConnect != NULL) module->OnConnect(module, -1, module->user);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			// Connected socket, we need to read data
 | |
| 			if (fd_read != 0)
 | |
| 			{
 | |
| 				module->timeout_lastActivity = ILibGetUptime();
 | |
| 				triggerReadSet = 1; // Data Available
 | |
| 			}
 | |
| 			else if (module->PAUSE < 0)
 | |
| 			{
 | |
| 				// Someone resumed a paused connection, but the FD_SET was not triggered because there is no new data on the socket.
 | |
| 				module->timeout_lastActivity = ILibGetUptime();
 | |
| 				triggerResume = 1;
 | |
| 				++module->PAUSE;
 | |
| 				ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] was RESUMED with no new data on socket", (void*)module);
 | |
| 			}
 | |
| 
 | |
| 			// Unlock before fireing the event
 | |
| 			SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);)
 | |
| 			sem_post(&(module->SendLock));
 | |
| 
 | |
| 			if (triggerReadSet != 0 || triggerResume != 0) ILibProcessAsyncSocket(module, triggerReadSet);
 | |
| 		}
 | |
| 	}
 | |
| 	SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);)
 | |
| 
 | |
| 
 | |
| 	SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PostSelect", 1, module);)
 | |
| 	sem_wait(&(module->SendLock));
 | |
| 	// Write Handling
 | |
| 	if (module->FinConnect > 0 && module->internalSocket != ~0 && fd_write != 0 && module->PendingSend_Head != NULL)
 | |
| 	{
 | |
| 		//
 | |
| 		// Keep trying to send data, until we are told we can't
 | |
| 		//
 | |
| 		module->timeout_lastActivity = ILibGetUptime();
 | |
| 		while (TRY_TO_SEND != 0)
 | |
| 		{
 | |
| 			if (module->PendingSend_Head == NULL) break;
 | |
| 			if (module->PendingSend_Head->remoteAddress.sin6_family == 0)
 | |
| 			{
 | |
| 				bytesSent = (int)send(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent, MSG_NOSIGNAL); // Klocwork reports that this could block while holding a lock... This socket has been set to O_NONBLOCK, so that will never happen
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				bytesSent = (int)sendto(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent, MSG_NOSIGNAL, (struct sockaddr*)&module->PendingSend_Head->remoteAddress, INET_SOCKADDR_LENGTH(module->PendingSend_Head->remoteAddress.sin6_family)); // Klocwork reports that this could block while holding a lock... This socket has been set to O_NONBLOCK, so that will never happen
 | |
| 			}
 | |
| 
 | |
| 			if (bytesSent == 0) { TRY_TO_SEND = 0; } //To avoid get stuck in an infinite loop when bytesSent == 0
 | |
| 
 | |
| 			if (bytesSent > 0)
 | |
| 			{
 | |
| 				module->PendingBytesToSend -= bytesSent;
 | |
| 				module->TotalBytesSent += bytesSent;
 | |
| 				module->PendingSend_Head->bytesSent += bytesSent;
 | |
| 				if (module->PendingSend_Head->bytesSent == module->PendingSend_Head->bufferSize)
 | |
| 				{
 | |
| 					// Finished Sending this block
 | |
| 					if (module->PendingSend_Head == module->PendingSend_Tail)
 | |
| 					{
 | |
| 						module->PendingSend_Tail = NULL;
 | |
| 					}
 | |
| 					if (module->PendingSend_Head->UserFree == 0)
 | |
| 					{
 | |
| 						free(module->PendingSend_Head->buffer);
 | |
| 					}
 | |
| 					temp = module->PendingSend_Head->Next;
 | |
| 					free(module->PendingSend_Head);
 | |
| 					module->PendingSend_Head = temp;
 | |
| 					if (module->PendingSend_Head == NULL) { TRY_TO_SEND = 0; }
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					// We sent data, but not everything that needs to get sent was sent, try again
 | |
| 					TRY_TO_SEND = 1;
 | |
| 				}
 | |
| 			}
 | |
| 			if (bytesSent == -1)
 | |
| 			{
 | |
| 				// Error, clean up everything
 | |
| 				TRY_TO_SEND = 0;
 | |
| #if  defined(WIN32)
 | |
| 				if (WSAGetLastError() != WSAEWOULDBLOCK)
 | |
| #elif defined(_POSIX)
 | |
| 				if (errno != EWOULDBLOCK)
 | |
| #endif
 | |
| 				{
 | |
| 					// There was an error sending
 | |
| 					ILibAsyncSocket_ClearPendingSend(socketModule);
 | |
| 					ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		// This triggers OnSendOK, if all the pending data has been sent.
 | |
| 		if (module->PendingSend_Head == NULL && bytesSent != -1) { TriggerSendOK = 1; }
 | |
| 		SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 2, module);)
 | |
| 		sem_post(&(module->SendLock));
 | |
| 		if (TriggerSendOK != 0)
 | |
| 		{
 | |
| 			module->OnSendOK(module, module->user);
 | |
| 			if (module->Transport.SendOkPtr != NULL) { module->Transport.SendOkPtr(module); }
 | |
| 		}
 | |
| 
 | |
| 		if (bytesSent == 0) { ILibAsyncSocket_ClearPendingSend(socketModule); } //If bytesSent == 0 then clear pending data
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 2, module);)
 | |
| 		sem_post(&(module->SendLock));
 | |
| 	}
 | |
| 
 | |
| 	ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "...AsyncSocket[%p] exited PostSelect", (void*)module);
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Determines if an ILibAsyncSocket is in use
 | |
| \param socketModule The ILibAsyncSocket to query
 | |
| \returns 0 if in use, nonzero otherwise
 | |
| */
 | |
| int ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	return((module == NULL || module->internalSocket==~0)?1:0);
 | |
| }
 | |
| 
 | |
| int ILibAsyncSocket_IsConnected(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	return module->FinConnect;
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Returns the number of bytes that are pending to be sent
 | |
| \param socketModule The ILibAsyncSocket to query
 | |
| \returns Number of pending bytes
 | |
| */
 | |
| unsigned int ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	return(module->PendingBytesToSend);
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Returns the total number of bytes that have been sent, since the last reset
 | |
| \param socketModule The ILibAsyncSocket to query
 | |
| \returns Number of bytes sent
 | |
| */
 | |
| unsigned int ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	return(module->TotalBytesSent);
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Resets the total bytes sent counter
 | |
| \param socketModule The ILibAsyncSocket to reset
 | |
| */
 | |
| void ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	module->TotalBytesSent = 0;
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer)
 | |
| \brief Returns the buffer associated with an ILibAsyncSocket
 | |
| \param socketModule The ILibAsyncSocket to obtain the buffer from
 | |
| \param[out] buffer The buffer
 | |
| \param[out] BeginPointer Stating offset of the buffer
 | |
| \param[out] EndPointer Length of buffer
 | |
| */
 | |
| void ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 
 | |
| 	*buffer = module->buffer;
 | |
| 	*BeginPointer = module->BeginPointer;
 | |
| 	*EndPointer = module->EndPointer;
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_ModuleOnConnect(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	if (module != NULL && module->OnConnect != NULL) module->OnConnect(module, -1, module->user);
 | |
| }
 | |
| 
 | |
| //! Set the SSL client context used by all connections done by this socket module. The SSL context must
 | |
| //! be set before using this module. If left to NULL, all connections are in the clear using TCP.
 | |
| //!
 | |
| //! This is utilized by the ILibAsyncServerSocket module
 | |
| /*!
 | |
| 	\param socketModule The ILibAsyncSocket to modify
 | |
| 	\param ssl_ctx The ssl_ctx structure
 | |
| 	\param server ILibAsyncSocket_TLS_Mode Configuration setting to set
 | |
| */
 | |
| 
 | |
| //! Sets the remote address field.
 | |
| //! This is utilized by the ILibAsyncServerSocket module
 | |
| /*!
 | |
| 	\param socketModule ILibAsyncSocket_SocketModule to modify
 | |
| 	\param remoteAddress The remote endpoint to set
 | |
| */
 | |
| void ILibAsyncSocket_SetRemoteAddress(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress)
 | |
| {
 | |
| 	if (socketModule != NULL)
 | |
| 	{
 | |
| 		struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 		memcpy_s(&(module->RemoteAddress), sizeof(struct sockaddr_in6), remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule,void* UseThisSocket,ILibAsyncSocket_OnInterrupt InterruptPtr,void *user)
 | |
| \brief Associates an actual socket with ILibAsyncSocket
 | |
| \par
 | |
| Instead of calling \a ConnectTo, you can call this method to associate with an already
 | |
| connected socket.
 | |
| \param socketModule The ILibAsyncSocket to associate
 | |
| \param UseThisSocket The socket to associate
 | |
| \param InterruptPtr Function Pointer that triggers when the TCP connection is interrupted
 | |
| \param user User object to associate with this session
 | |
| */
 | |
| #if  defined(WIN32)
 | |
| void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, SOCKET UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user)
 | |
| #elif defined(_POSIX)
 | |
| void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, int UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user)
 | |
| #endif
 | |
| {
 | |
| 	int flags;
 | |
| 	char *tmp;
 | |
| 	struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 
 | |
| 	module->PendingBytesToSend = 0;
 | |
| 	module->TotalBytesSent = 0;
 | |
| 	module->internalSocket = UseThisSocket;
 | |
| 	module->OnInterrupt = InterruptPtr;
 | |
| 	module->user = user;
 | |
| 	module->FinConnect = 1;
 | |
| 	module->PAUSE = 0;
 | |
| 
 | |
| 	ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_1, "AsyncSocket[%p] Initialized", (void*)module);
 | |
| 
 | |
| 	//
 | |
| 	// If the buffer is too small/big, we need to realloc it to the minimum specified size
 | |
| 	//
 | |
| 	if (module->buffer != ILibScratchPad2)
 | |
| 	{
 | |
| 		if ((tmp = (char*)realloc(module->buffer, module->InitialSize)) == NULL) ILIBCRITICALEXIT(254);
 | |
| 		module->buffer = tmp;
 | |
| 		module->MallocSize = module->InitialSize;
 | |
| 	}
 | |
| 	module->BeginPointer = 0;
 | |
| 	module->EndPointer = 0;
 | |
| 
 | |
| 	//
 | |
| 	// Make sure the socket is non-blocking, so we can play nice and share the thread
 | |
| 	//
 | |
| #if defined(WIN32)
 | |
| 	flags = 1;
 | |
| 	ioctlsocket(module->internalSocket, FIONBIO,(u_long *)(&flags));
 | |
| #elif defined(_POSIX)
 | |
| 	flags = fcntl(module->internalSocket,F_GETFL,0);
 | |
| 	fcntl(module->internalSocket,F_SETFL,O_NONBLOCK|flags);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Returns the Remote Interface of a connected session
 | |
| \param socketModule The ILibAsyncSocket to query
 | |
| \param[in,out] remoteAddress The remote interface
 | |
| \returns Number of bytes written into remoteAddress
 | |
| */
 | |
| int ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	if (module->RemoteAddress.sin6_family != 0)
 | |
| 	{
 | |
| 		memcpy_s(remoteAddress, sizeof(struct sockaddr_in6), &(module->RemoteAddress), INET_SOCKADDR_LENGTH(module->RemoteAddress.sin6_family));
 | |
| 		return INET_SOCKADDR_LENGTH(module->RemoteAddress.sin6_family);
 | |
| 	}
 | |
| 	memcpy_s(remoteAddress, sizeof(struct sockaddr_in6), &(module->SourceAddress), INET_SOCKADDR_LENGTH(module->SourceAddress.sin6_family));
 | |
| 	return INET_SOCKADDR_LENGTH(module->SourceAddress.sin6_family);
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Returns the Local Interface of a connected session, in network order
 | |
| \param socketModule The ILibAsyncSocket to query
 | |
| \param[in,out] localAddress The local interface
 | |
| \returns The number of bytes written to localAddress
 | |
| */
 | |
| int ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *localAddress)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	int receivingAddressLength = sizeof(struct sockaddr_in6);
 | |
| 
 | |
| 	if (module->LocalAddress.sin6_family !=0)
 | |
| 	{
 | |
| 		memcpy_s(localAddress, INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family), &(module->LocalAddress), INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family));
 | |
| 		return INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| #if defined(WINSOCK2)
 | |
| 		getsockname(module->internalSocket, localAddress, (int*)&receivingAddressLength);
 | |
| #else
 | |
| 		getsockname(module->internalSocket, localAddress, (socklen_t*)&receivingAddressLength);
 | |
| #endif
 | |
| 		return receivingAddressLength;
 | |
| 	}
 | |
| }
 | |
| //! Get's the locally bound port
 | |
| /*!
 | |
| 	\param socketModule ILibAsyncSocket_SocketModule object to query
 | |
| 	\return The locallly bound port
 | |
| */
 | |
| unsigned short ILibAsyncSocket_GetLocalPort(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	int receivingAddressLength = sizeof(struct sockaddr_in6);
 | |
| 	struct sockaddr_in6 localAddress;
 | |
| 
 | |
| 	if (module->LocalAddress.sin6_family == AF_INET6) return ntohs(module->LocalAddress.sin6_port);
 | |
| 	if (module->LocalAddress.sin6_family == AF_INET) return ntohs((((struct sockaddr_in*)(&(module->LocalAddress)))->sin_port));
 | |
| #if defined(WINSOCK2)
 | |
| 	getsockname(module->internalSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength);
 | |
| #else
 | |
| 	getsockname(module->internalSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength);
 | |
| #endif
 | |
| 	if (localAddress.sin6_family == AF_INET6) return ntohs(localAddress.sin6_port);
 | |
| 	if (localAddress.sin6_family == AF_INET) return ntohs((((struct sockaddr_in*)(&localAddress))->sin_port));
 | |
| 	return 0;
 | |
| }
 | |
| /*! \fn ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Pauses a session
 | |
| \par
 | |
| Sessions can be paused, such that further data is not read from the socket until resumed. NOTE: MUST be called from Microstack thread
 | |
| \param socketModule The ILibAsyncSocket to pause.
 | |
| */
 | |
| void ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	if (socketModule == NULL) { return; }
 | |
| 
 | |
| 	sm->PAUSE = 1;
 | |
| }
 | |
| /*! \fn ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule)
 | |
| \brief Resumes a paused session
 | |
| \par
 | |
| Sessions can be paused, such that further data is not read from the socket until resumed
 | |
| \param socketModule The ILibAsyncSocket to resume
 | |
| */
 | |
| void ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	if (sm == NULL) { return; }
 | |
| 	ILibRemoteLogging_printf(ILibChainGetLogger(sm->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] was RESUMED", (void*)socketModule);
 | |
| 	if (sm->PAUSE > 0)
 | |
| 	{
 | |
| 		ILibRemoteLogging_printf(ILibChainGetLogger(sm->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Unblocking Chain");
 | |
| 		sm->PAUSE = -1;
 | |
| 		ILibForceUnBlockChain(sm->Transport.ChainLink.ParentChain);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module)
 | |
| \brief Obtain the underlying raw socket
 | |
| \param module The ILibAsyncSocket to query
 | |
| \returns The pointer to raw socket
 | |
| */
 | |
| void* ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module;
 | |
| 	return(&(sm->internalSocket));
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_Shutdown(ILibAsyncSocket_SocketModule module)
 | |
| {
 | |
| 	ILibAsyncSocket_PrivateShutdown(module);
 | |
| }
 | |
| 
 | |
| //! Sets the local endpoint associated with this ILibAsyncSocket
 | |
| /*!
 | |
| 	\param module ILibAsyncSocket_SocketModule to modify
 | |
| 	\param LocalAddress Local endpoint to set
 | |
| */
 | |
| void ILibAsyncSocket_SetLocalInterface(ILibAsyncSocket_SocketModule module, struct sockaddr *LocalAddress)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module;
 | |
| 	memcpy_s(&(sm->LocalAddress), sizeof(struct sockaddr_in6), LocalAddress, INET_SOCKADDR_LENGTH(LocalAddress->sa_family));
 | |
| }
 | |
| //! Sets the maximum size that the internal buffer can be grown
 | |
| /*!
 | |
| 	\param module ILibAsyncSocket_SocketModule to configure
 | |
| 	\param maxSize Maximum size in bytes
 | |
| 	\param OnBufferSizeExceededCallback ILibAsyncSocket_OnBufferSizeExceeded handler to be dispatched if the max size is exceeded
 | |
| 	\param user Custom user state data
 | |
| */
 | |
| void ILibAsyncSocket_SetMaximumBufferSize(ILibAsyncSocket_SocketModule module, int maxSize, ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceededCallback, void *user)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module;
 | |
| 	sm->MaxBufferSize = maxSize;
 | |
| 	sm->OnBufferSizeExceeded = OnBufferSizeExceededCallback;
 | |
| 	sm->MaxBufferSizeUserObject = user;
 | |
| }
 | |
| //! Sets the SendOK event handler
 | |
| /*!
 | |
| 	\param module ILibAsyncSocket_SocketModule to configure
 | |
| 	\param OnSendOK ILibAsyncSocket_OnSendOK handler to dispatch on an OnSendOK event
 | |
| */
 | |
| void ILibAsyncSocket_SetSendOK(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnSendOK OnSendOK)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module;
 | |
| 	sm->OnSendOK = OnSendOK;
 | |
| }
 | |
| 
 | |
| //! Determines if the specified IPv6 address is a link local address
 | |
| /*!
 | |
| 	\param LocalAddress IPv6 Address
 | |
| 	\return 1 = Link Local, 0 = Not Link Local
 | |
| */
 | |
| int ILibAsyncSocket_IsIPv6LinkLocal(struct sockaddr *LocalAddress)
 | |
| {
 | |
| 	struct sockaddr_in6 *x = (struct sockaddr_in6*)LocalAddress;
 | |
| #if  defined(WIN32)
 | |
| 	if (LocalAddress->sa_family == AF_INET6 && x->sin6_addr.u.Byte[0] == 0xFE && x->sin6_addr.u.Byte[1] == 0x80) return 1;
 | |
| #else
 | |
| 	if (LocalAddress->sa_family == AF_INET6 && x->sin6_addr.s6_addr[0] == 0xFE && x->sin6_addr.s6_addr[1] == 0x80) return 1;
 | |
| #endif
 | |
| 	return 0;
 | |
| }
 | |
| //! Determines if the internal socket is a link local socket
 | |
| /*!
 | |
| 	\param socketModule ILibAsyncSocket_SocketModule to query
 | |
| 	\return 0 = Not Link Local, 1 = Link Local
 | |
| */
 | |
| int ILibAsyncSocket_IsModuleIPv6LinkLocal(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	return ILibAsyncSocket_IsIPv6LinkLocal((struct sockaddr*)&(module->LocalAddress));
 | |
| }
 | |
| //! Returns 1 if the ILibAsyncSocket was disconnected because the buffer size was exceeded
 | |
| /*!
 | |
| 	\param socketModule ILibAsyncSocket_SocketModule to query
 | |
| 	\return 1 = BufferSizeExceeded
 | |
| */
 | |
| int ILibAsyncSocket_WasClosedBecauseBufferSizeExceeded(ILibAsyncSocket_SocketModule socketModule)
 | |
| {
 | |
| 	struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule;
 | |
| 	return(sm->MaxBufferSizeExceeded);
 | |
| }
 | |
| 
 | |
| void ILibAsyncSocket_UpdateOnData(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnData OnData)
 | |
| {
 | |
| 	((ILibAsyncSocketModule*)module)->OnData = OnData;
 | |
| }
 |