614 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			614 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*********************************************************************
 | |
| * Copyright (c) Intel Corporation 2011 - 2020
 | |
| * SPDX-License-Identifier: Apache-2.0
 | |
| **********************************************************************/
 | |
| 
 | |
| #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>
 | |
| #elif defined(WINSOCK1)
 | |
| #include <winsock.h>
 | |
| #include <wininet.h>
 | |
| #endif
 | |
| 
 | |
| #include "ILibParsers.h"
 | |
| #include "ILibAsyncServerSocket.h"
 | |
| #include "ILibAsyncSocket.h"
 | |
| 
 | |
| #define DEBUGSTATEMENT(x)
 | |
| 
 | |
| #define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)))
 | |
| 
 | |
| typedef struct ILibAsyncServerSocketModule
 | |
| {
 | |
| 	ILibChain_Link ChainLink;
 | |
| 
 | |
| 	int MaxConnection;
 | |
| 	void **AsyncSockets;
 | |
| 	ILibServerScope scope;
 | |
| 
 | |
| 	SOCKET ListenSocket;
 | |
| 	unsigned short portNumber, initialPortNumber;
 | |
| 	int listening;
 | |
| 	int loopbackFlag;
 | |
| 
 | |
| 	ILibAsyncServerSocket_OnReceive OnReceive;
 | |
| 	ILibAsyncServerSocket_OnConnect OnConnect;
 | |
| 	ILibAsyncServerSocket_OnDisconnect OnDisconnect;
 | |
| 	ILibAsyncServerSocket_OnInterrupt OnInterrupt;
 | |
| 	ILibAsyncServerSocket_OnSendOK OnSendOK;
 | |
| 
 | |
| 	void *Tag;
 | |
| 	int Tag2;
 | |
| 
 | |
| }ILibAsyncServerSocketModule;
 | |
| struct ILibAsyncServerSocket_Data
 | |
| {
 | |
| 	struct ILibAsyncServerSocketModule *module;
 | |
| 	ILibAsyncServerSocket_BufferReAllocated Callback;
 | |
| 	void *user;
 | |
| };
 | |
| 
 | |
| const int ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE = (const int)sizeof(ILibAsyncServerSocketModule);
 | |
| 
 | |
| 
 | |
| /*! \fn ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule)
 | |
| \brief Returns the user Tag associated with the AsyncServer
 | |
| \param ILibAsyncSocketModule The ILibAsyncServerSocket to query
 | |
| \returns The user Tag
 | |
| */
 | |
| void *ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule)
 | |
| {
 | |
| 	return (((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag);
 | |
| }
 | |
| /*! \fn ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag)
 | |
| \brief Sets the user Tag associated with the AsyncServer
 | |
| \param ILibAsyncSocketModule The ILibAsyncServerSocket to save the tag to
 | |
| \param tag The value to save
 | |
| */
 | |
| void ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag)
 | |
| {
 | |
| 	((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag = tag;
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule)
 | |
| \brief Returns the user Tag associated with the AsyncServer
 | |
| \param ILibAsyncSocketModule The ILibAsyncServerSocket to query
 | |
| \returns The user Tag
 | |
| */
 | |
| int ILibAsyncServerSocket_GetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule)
 | |
| {
 | |
| 	return(((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag2);
 | |
| }
 | |
| /*! \fn ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag)
 | |
| \brief Sets the user Tag associated with the AsyncServer
 | |
| \param ILibAsyncSocketModule The ILibAsyncServerSocket to save the tag to
 | |
| \param tag The value to save
 | |
| */
 | |
| void ILibAsyncServerSocket_SetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, int tag)
 | |
| {
 | |
| 	((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag2 = tag;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Internal method called by ILibAsyncSocket, to signal an interrupt condition
 | |
| //
 | |
| // <param name="socketModule">The ILibAsyncServerSocket that was interrupted</param>
 | |
| // <param name="user">The associated user tag</param>
 | |
| void ILibAsyncServerSocket_OnInterruptSink(ILibAsyncSocket_SocketModule socketModule, void *user)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user;
 | |
| 	if (data == NULL) return;
 | |
| 	if (data->module->OnInterrupt != NULL) data->module->OnInterrupt(data->module, socketModule, data->user);
 | |
| 	free(user);
 | |
| }
 | |
| //
 | |
| // Chain PreSelect handler
 | |
| //
 | |
| // <param name="socketModule"></param>
 | |
| // <param name="readset"></param>
 | |
| // <param name="writeset"></param>
 | |
| // <param name="errorset"></param>
 | |
| // <param name="blocktime"></param>
 | |
| void ILibAsyncServerSocket_PreSelect(void* socketModule, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime)
 | |
| {
 | |
| 	struct ILibAsyncServerSocketModule *module = (struct ILibAsyncServerSocketModule*)socketModule;
 | |
| 	int flags,i;
 | |
| 
 | |
| 	UNREFERENCED_PARAMETER( writeset );
 | |
| 	UNREFERENCED_PARAMETER( errorset );
 | |
| 	UNREFERENCED_PARAMETER( blocktime );
 | |
| 
 | |
| 	//
 | |
| 	// The socket isn't put in listening mode, until the chain is started.
 | |
| 	// If this variable == 0, that means we need to do that.
 | |
| 	//
 | |
| 	if (module->listening == 0)
 | |
| 	{
 | |
| 		//
 | |
| 		// Set the socket to non-block mode, so we can play nice and share the thread
 | |
| 		//
 | |
| #ifdef WIN32
 | |
| 		flags = 1;
 | |
| 		if(ioctlsocket(module->ListenSocket, FIONBIO, (u_long *)(&flags)) == SOCKET_ERROR) ILIBCRITICALEXIT2(246, WSAGetLastError()); 
 | |
| #elif _POSIX
 | |
| 		flags = fcntl(module->ListenSocket, F_GETFL,0);
 | |
| 		fcntl(module->ListenSocket, F_SETFL, O_NONBLOCK | flags);
 | |
| #endif
 | |
| 
 | |
| 		//
 | |
| 		// Put the socket in Listen, and add it to the fdset for the Select loop
 | |
| 		//
 | |
| 		module->listening = 1;
 | |
| 		listen(module->ListenSocket, 4);
 | |
| 		#if defined(WIN32)
 | |
| 		#pragma warning( push, 3 ) // warning C4127: conditional expression is constant
 | |
| 		#endif
 | |
| 		FD_SET(module->ListenSocket, readset);
 | |
| 		#if defined(WIN32)
 | |
| 		#pragma warning( pop )
 | |
| 		#endif
 | |
| 	}
 | |
| 	else if (module->ListenSocket != ~0)
 | |
| 	{
 | |
| 		// Only put the ListenSocket in the readset, if we are able to handle a new socket
 | |
| 		for(i = 0; i < module->MaxConnection; ++i)
 | |
| 		{
 | |
| 			if (ILibAsyncSocket_IsFree(module->AsyncSockets[i]) != 0)
 | |
| 			{
 | |
| 				#if defined(WIN32)
 | |
| 				#pragma warning( push, 3 ) // warning C4127: conditional expression is constant
 | |
| 				#endif
 | |
| 				FD_SET(module->ListenSocket, readset);
 | |
| 				#if defined(WIN32)
 | |
| 				#pragma warning( pop )
 | |
| 				#endif
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| /*! \fn ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback)
 | |
| \brief Set the callback handler for when the internal data buffer has been resized
 | |
| \param AsyncServerSocketToken The ILibAsyncServerSocket to query
 | |
| \param ConnectionToken The specific connection to set the callback with
 | |
| \param Callback The callback handler to set
 | |
| */
 | |
| void ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)ILibAsyncSocket_GetUser(ConnectionToken);
 | |
| 	UNREFERENCED_PARAMETER( AsyncServerSocketToken );
 | |
| 	if (data != NULL) data->Callback = Callback;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Chain PostSelect handler
 | |
| //
 | |
| // <param name="socketModule"></param>
 | |
| // <param name="slct"></param>
 | |
| // <param name="readset"></param>
 | |
| // <param name="writeset"></param>
 | |
| // <param name="errorset"></param>
 | |
| void ILibAsyncServerSocket_PostSelect(void* socketModule, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data;
 | |
| 	struct sockaddr_in6 addr;
 | |
| #ifdef _POSIX
 | |
| 	socklen_t addrlen;
 | |
| #else
 | |
| 	int addrlen;
 | |
| #endif
 | |
| 
 | |
| 	struct ILibAsyncServerSocketModule *module = (struct ILibAsyncServerSocketModule*)socketModule;
 | |
| 	int i,flags;
 | |
| #ifdef WIN32
 | |
| 	SOCKET NewSocket;
 | |
| #elif defined( _POSIX)
 | |
| 	int NewSocket;
 | |
| #endif
 | |
| 
 | |
| 	UNREFERENCED_PARAMETER( slct );
 | |
| 	UNREFERENCED_PARAMETER( writeset );
 | |
| 	UNREFERENCED_PARAMETER( errorset );
 | |
| 
 | |
| 	if (FD_ISSET(module->ListenSocket, readset) != 0)
 | |
| 	{
 | |
| 		//
 | |
| 		// There are pending TCP connection requests
 | |
| 		//
 | |
| 		for(i = 0; i < module->MaxConnection; ++i)
 | |
| 		{
 | |
| 			//
 | |
| 			// Check to see if we have available resources to handle this connection request
 | |
| 			//
 | |
| 			if (ILibAsyncSocket_IsFree(module->AsyncSockets[i]) != 0)
 | |
| 			{
 | |
| 				addrlen = sizeof(addr);
 | |
| 				NewSocket = accept(module->ListenSocket, (struct sockaddr*)&addr, &addrlen); // Klocwork claims we could lose the resource acquired fom the declaration, but that is not possible in this case
 | |
| 				
 | |
| 				if (NewSocket != ~0)
 | |
| 				{
 | |
| 					//printf("Accepting new connection, socket = %d\r\n", NewSocket);
 | |
| 					//
 | |
| 					// Set this new socket to non-blocking mode, so we can play nice and share thread
 | |
| 					//
 | |
| #ifdef WIN32
 | |
| 					flags = 1;
 | |
| 					if (ioctlsocket(NewSocket, FIONBIO, (u_long *)(&flags)) == SOCKET_ERROR) ILIBCRITICALEXIT2(246, WSAGetLastError());
 | |
| #elif _POSIX
 | |
| 					flags = fcntl(NewSocket, F_GETFL,0);
 | |
| 					fcntl(NewSocket, F_SETFL, O_NONBLOCK|flags);
 | |
| #endif
 | |
| 					//
 | |
| 					// Instantiate a module to contain all the data about this connection
 | |
| 					//
 | |
| 					if ((data = (struct ILibAsyncServerSocket_Data*)malloc(sizeof(struct ILibAsyncServerSocket_Data))) == NULL) ILIBCRITICALEXIT(254);
 | |
| 					memset(data, 0, sizeof(struct ILibAsyncServerSocket_Data));
 | |
| 					data->module = (struct ILibAsyncServerSocketModule*)socketModule;
 | |
| 
 | |
| 					ILibAsyncSocket_UseThisSocket(module->AsyncSockets[i], NewSocket, &ILibAsyncServerSocket_OnInterruptSink, data);
 | |
| 					ILibAsyncSocket_SetRemoteAddress(module->AsyncSockets[i], (struct sockaddr*)&addr);
 | |
| 
 | |
| 					if (module->OnConnect != NULL)
 | |
| 					{
 | |
| 						// Notify the user about this new connection
 | |
| 						module->OnConnect(module, module->AsyncSockets[i], &(data->user));
 | |
| 					}
 | |
| 				}
 | |
| 				else {break;}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| } // Klocwork claims that we could lose the resource acquired in the declaration, but that is not possible in this case
 | |
| //
 | |
| // Chain Destroy handler
 | |
| //
 | |
| // <param name="socketModule"></param>
 | |
| void ILibAsyncServerSocket_Destroy(void *socketModule)
 | |
| {
 | |
| 	struct ILibAsyncServerSocketModule *module =(struct ILibAsyncServerSocketModule*)socketModule;
 | |
| 
 | |
| 	free(module->AsyncSockets);
 | |
| 	if (module->ListenSocket != (SOCKET)~0)
 | |
| 	{
 | |
| #ifdef WIN32
 | |
| 		closesocket(module->ListenSocket);
 | |
| #elif _POSIX
 | |
| 		close(module->ListenSocket);
 | |
| #endif
 | |
| 		module->ListenSocket = (SOCKET)~0;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| //
 | |
| // Internal method dispatched by the OnData event of the underlying ILibAsyncSocket
 | |
| //
 | |
| // <param name="socketModule"></param>
 | |
| // <param name="buffer"></param>
 | |
| // <param name="p_beginPointer"></param>
 | |
| // <param name="endPointer"></param>
 | |
| // <param name="OnInterrupt"></param>
 | |
| // <param name="user"></param>
 | |
| // <param name="PAUSE"></param>
 | |
| void ILibAsyncServerSocket_OnData(ILibAsyncSocket_SocketModule socketModule,char* buffer,int *p_beginPointer, int endPointer,void (**OnInterrupt)(void *AsyncSocketMoudle, void *user),void **user, int *PAUSE)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)(*user);
 | |
| 	int bpointer = *p_beginPointer;
 | |
| 
 | |
| 	UNREFERENCED_PARAMETER( OnInterrupt );
 | |
| 
 | |
| 	// Pass the received data up
 | |
| 	if (data != NULL && data->module->OnReceive != NULL)
 | |
| 	{
 | |
| 		data->module->OnReceive(data->module, socketModule, buffer, &bpointer, endPointer, &(data->module->OnInterrupt), &(data->user), PAUSE);
 | |
| 		if (ILibAsyncSocket_IsFree(socketModule))
 | |
| 		{
 | |
| 			*p_beginPointer = endPointer;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			*p_beginPointer = bpointer;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| // 
 | |
| // Internal method dispatched by the OnConnect event of the underlying ILibAsyncSocket. In this case, it will only occur when TLS connection is established.
 | |
| // 
 | |
| // <param name="socketModule"></param>
 | |
| // <param name="user"></param>
 | |
| void ILibAsyncServerSocket_OnConnectSink(ILibAsyncSocket_SocketModule socketModule, int Connected, void *user)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user;
 | |
| 
 | |
| 	if (data == NULL) return;
 | |
| 	if (Connected == 0)
 | |
| 	{
 | |
| 		// Connection Failed, clean up
 | |
| 		free(data);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (data->module->OnConnect != NULL) data->module->OnConnect(data->module, socketModule, &(data->user));
 | |
| }
 | |
| // 
 | |
| // Internal method dispatched by the OnDisconnect event of the underlying ILibAsyncSocket
 | |
| // 
 | |
| // <param name="socketModule"></param>
 | |
| // <param name="user"></param>
 | |
| void ILibAsyncServerSocket_OnDisconnectSink(ILibAsyncSocket_SocketModule socketModule, void *user)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user;
 | |
| 
 | |
| 	// Pass this Disconnect event up
 | |
| 	if (data == NULL) return;
 | |
| 	if (data->module->OnDisconnect != NULL) data->module->OnDisconnect(data->module, socketModule, data->user);
 | |
| 
 | |
| 	// If the chain is shutting down, we need to free some resources
 | |
| 	if (ILibIsChainBeingDestroyed(data->module->ChainLink.ParentChain) == 0) free(data);
 | |
| }
 | |
| // 
 | |
| // Internal method dispatched by the OnSendOK event of the underlying ILibAsyncSocket
 | |
| // 
 | |
| // <param name="socketModule"></param>
 | |
| // <param name="user"></param>
 | |
| void ILibAsyncServerSocket_OnSendOKSink(ILibAsyncSocket_SocketModule socketModule, void *user)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user;
 | |
| 
 | |
| 	// Pass the OnSendOK event up
 | |
| 	if (data != NULL && data->module->OnSendOK != NULL) data->module->OnSendOK(data->module, socketModule, data->user);
 | |
| }
 | |
| // 
 | |
| // Internal method dispatched by ILibAsyncSocket, to signal that the buffers have been reallocated
 | |
| // 
 | |
| // <param name="ConnectionToken">The ILibAsyncSocket sender</param>
 | |
| // <param name="user">The ILibAsyncServerSocket_Data object</param>
 | |
| // <param name="offSet">The offset to the new buffer location</param>
 | |
| void ILibAsyncServerSocket_OnBufferReAllocated(ILibAsyncSocket_SocketModule ConnectionToken, void *user, ptrdiff_t offSet)
 | |
| {
 | |
| 	struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user;
 | |
| 	if (data!=NULL && data->Callback!=NULL)
 | |
| 	{
 | |
| 		//
 | |
| 		// If someone above us, has registered for this callback, we need to fire it,
 | |
| 		// with the correct user object
 | |
| 		//
 | |
| 		data->Callback(data->module,ConnectionToken,data->user,offSet);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ILibAsyncServerSocket_ResumeListeningSink(void *chain, void* user)
 | |
| {
 | |
| 	ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)user;
 | |
| 
 | |
| 	int ra = 1;
 | |
| 	int off = 0;
 | |
| 	int receivingAddressLength = sizeof(struct sockaddr_in6);
 | |
| 	struct sockaddr_in6 localif;
 | |
| 	struct sockaddr_in6 localAddress;
 | |
| 
 | |
| 	UNREFERENCED_PARAMETER(chain);
 | |
| 
 | |
| 	memset(&localif, 0, sizeof(struct sockaddr_in6));
 | |
| 	if (m->loopbackFlag != 2 && ILibDetectIPv6Support())
 | |
| 	{
 | |
| 		// Setup the IPv6 any or loopback address, this socket will also work for IPv4 traffic on IPv6 stack
 | |
| 		localif.sin6_family = AF_INET6;
 | |
| 		localif.sin6_addr = (m->loopbackFlag != 0 ? in6addr_loopback : in6addr_any);
 | |
| 		localif.sin6_port = htons(m->initialPortNumber);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// IPv4-only detected
 | |
| 		localif.sin6_family = AF_INET;
 | |
| #ifdef WINSOCK2
 | |
| 		((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((m->loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY));
 | |
| #else 
 | |
| 		((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((m->loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY));
 | |
| #endif
 | |
| 		((struct sockaddr_in*)&localif)->sin_port = htons(m->initialPortNumber);
 | |
| 	}
 | |
| 
 | |
| 	// Get our listening socket
 | |
| 	if ((m->ListenSocket = socket(localif.sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { m->ListenSocket = (SOCKET)~0;  return; }
 | |
| 
 | |
| 	// Setup the IPv6 & IPv4 support on same socket
 | |
| 	if (localif.sin6_family == AF_INET6) if (setsockopt(m->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253);
 | |
| 
 | |
| #ifdef SO_NOSIGPIPE
 | |
| 	setsockopt(m->ListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&ra, sizeof(int));  // Turn off SIGPIPE if writing to disconnected socket
 | |
| #endif
 | |
| 
 | |
| #if defined(WIN32)
 | |
| 	// On Windows. Lets make sure no one else can bind to this addr/port. This stops socket hijacking (not a problem on Linux).
 | |
| 	if (setsockopt(m->ListenSocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253);
 | |
| #else
 | |
| 	// On Linux. Setting the re-use on a TCP socket allows reuse of the socket even in timeout state. Allows for fast stop/start (Not a problem on Windows).
 | |
| 	if (setsockopt(m->ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253);
 | |
| #endif
 | |
| 
 | |
| 	// Bind the socket
 | |
| #if defined(WIN32)
 | |
| 	if (bind(m->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { closesocket(m->ListenSocket); m->ListenSocket = (SOCKET)~0; return; }
 | |
| #else
 | |
| 	if (bind(m->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { close(m->ListenSocket); m->ListenSocket = (SOCKET)~0; return; }
 | |
| #endif
 | |
| 
 | |
| 	// Fetch the local port number
 | |
| #if defined(WINSOCK2)
 | |
| 	getsockname(m->ListenSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength);
 | |
| #else
 | |
| 	getsockname(m->ListenSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength);
 | |
| #endif
 | |
| 	if (localAddress.sin6_family == AF_INET6) m->portNumber = ntohs(localAddress.sin6_port); else m->portNumber = ntohs(((struct sockaddr_in*)&localAddress)->sin_port);
 | |
| 	m->listening = 0;
 | |
| }
 | |
| void ILibAsyncServerSocket_StopListeningSink(void *chain, void* user)
 | |
| {
 | |
| 	ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)user;
 | |
| 	UNREFERENCED_PARAMETER(chain);
 | |
| 
 | |
| 	if (m->ListenSocket != (SOCKET)~0)
 | |
| 	{
 | |
| #ifdef WIN32
 | |
| 		closesocket(m->ListenSocket);
 | |
| #else
 | |
| 		close(m->ListenSocket);
 | |
| #endif
 | |
| 		m->ListenSocket = (SOCKET)~0;
 | |
| 	}
 | |
| }
 | |
| //! Take the server socket out of listening mode, rejecting new incoming connection requests
 | |
| /*!
 | |
| 	\param module ILibAsyncServerSocket_ServerModule Server Listening Module
 | |
| */
 | |
| void ILibAsyncServerSocket_StopListening(ILibAsyncServerSocket_ServerModule module)
 | |
| {
 | |
| 	ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)module;
 | |
| 	ILibChain_RunOnMicrostackThread(m->ChainLink.ParentChain, ILibAsyncServerSocket_StopListeningSink, m);
 | |
| }
 | |
| //! Put the server socket back in listening mode, to allow new incoming connection requests
 | |
| /*!
 | |
| 	\param module ILibAsyncServerSocket_ServerModule Server Listening Module
 | |
| */
 | |
| void ILibAsyncServerSocket_ResumeListening(ILibAsyncServerSocket_ServerModule module)
 | |
| {
 | |
| 	ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)module;
 | |
| 	ILibChain_RunOnMicrostackThread(m->ChainLink.ParentChain, ILibAsyncServerSocket_ResumeListeningSink, m);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*! \fn ILibCreateAsyncServerSocketModule(void *Chain, int MaxConnections, int PortNumber, int initialBufferSize, ILibAsyncServerSocket_OnConnect OnConnect,ILibAsyncServerSocket_OnDisconnect OnDisconnect,ILibAsyncServerSocket_OnReceive OnReceive,ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK)
 | |
| \brief Instantiates a new ILibAsyncServerSocket
 | |
| \param Chain The chain to add this module to. (Chain must <B>not</B> be running)
 | |
| \param MaxConnections The max number of simultaneous connections that will be allowed
 | |
| \param PortNumber The port number to bind to. 0 will select a random port
 | |
| \param initialBufferSize The initial size of the receive buffer
 | |
| \param loopbackFlag 0 to bind to ANY, 1 to bind to IPv6 loopback first, 2 to bind to IPv4 loopback first.
 | |
| \param OnConnect Function Pointer that triggers when a connection is established
 | |
| \param OnDisconnect Function Pointer that triggers when a connection is closed
 | |
| \param OnReceive Function Pointer that triggers when data is received
 | |
| \param OnInterrupt Function Pointer that triggers when connection interrupted
 | |
| \param OnSendOK Function Pointer that triggers when pending sends are complete
 | |
| \param ServerAutoFreeMemorySize Size of AutoFreeMemory on Server to co-allocate
 | |
| \param SessionAutoFreeMemorySize Size of AutoFreeMemory on Session to co-allocate
 | |
| \returns An ILibAsyncServerSocket module
 | |
| */
 | |
| ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(void *Chain, int MaxConnections, unsigned short PortNumber, int initialBufferSize, int loopbackFlag, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize)
 | |
| {
 | |
| 	int i;
 | |
| 	int ra = 1;
 | |
| 	int off = 0;
 | |
| 	int receivingAddressLength = sizeof(struct sockaddr_in6);
 | |
| 	struct sockaddr_in6 localif;
 | |
| 	struct sockaddr_in6 localAddress;
 | |
| 	struct ILibAsyncServerSocketModule *RetVal;
 | |
| 
 | |
| 	memset(&localif, 0, sizeof(struct sockaddr_in6));
 | |
| 	if (loopbackFlag != 2 && ILibDetectIPv6Support())
 | |
| 	{
 | |
| 		// Setup the IPv6 any or loopback address, this socket will also work for IPv4 traffic on IPv6 stack
 | |
| 		localif.sin6_family = AF_INET6;
 | |
| 		localif.sin6_addr = (loopbackFlag != 0?in6addr_loopback:in6addr_any);
 | |
| 		localif.sin6_port = htons(PortNumber);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// IPv4-only detected
 | |
| 		localif.sin6_family = AF_INET;
 | |
| #ifdef WINSOCK2
 | |
| 		((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY));
 | |
| #else 
 | |
| 		((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY));
 | |
| #endif
 | |
| 		((struct sockaddr_in*)&localif)->sin_port = htons(PortNumber);
 | |
| 	}
 | |
| 
 | |
| 	// Instantiate a new AsyncServer module
 | |
| 	RetVal = (struct ILibAsyncServerSocketModule*)ILibChain_Link_Allocate(sizeof(struct ILibAsyncServerSocketModule), ServerUserMappedMemorySize);
 | |
| 	
 | |
| 	RetVal->ChainLink.PreSelectHandler = &ILibAsyncServerSocket_PreSelect;
 | |
| 	RetVal->ChainLink.PostSelectHandler = &ILibAsyncServerSocket_PostSelect;
 | |
| 	RetVal->ChainLink.DestroyHandler = &ILibAsyncServerSocket_Destroy;
 | |
| 	RetVal->ChainLink.ParentChain = Chain;
 | |
| 	RetVal->OnConnect = OnConnect;
 | |
| 	RetVal->OnDisconnect = OnDisconnect;
 | |
| 	RetVal->OnInterrupt = OnInterrupt;
 | |
| 	RetVal->OnSendOK = OnSendOK;
 | |
| 	RetVal->OnReceive = OnReceive;
 | |
| 	RetVal->MaxConnection = MaxConnections;
 | |
| 	RetVal->AsyncSockets = (void**)malloc(MaxConnections * sizeof(void*));
 | |
| 	if (RetVal->AsyncSockets == NULL) { free(RetVal); ILIBMARKPOSITION(253); return NULL; }
 | |
| 	RetVal->portNumber = (unsigned short)PortNumber;
 | |
| 	RetVal->loopbackFlag = loopbackFlag;
 | |
| 	RetVal->initialPortNumber = PortNumber;
 | |
| 
 | |
| 	// Get our listening socket
 | |
| 	if ((RetVal->ListenSocket = socket(localif.sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { free(RetVal->AsyncSockets); free(RetVal); return 0; }
 | |
| 
 | |
| 	// Setup the IPv6 & IPv4 support on same socket
 | |
| 	if (localif.sin6_family == AF_INET6) if (setsockopt(RetVal->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253);
 | |
| 
 | |
| #ifdef SO_NOSIGPIPE
 | |
| 	setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&ra, sizeof(int));  // Turn off SIGPIPE if writing to disconnected socket
 | |
| #endif
 | |
| 
 | |
| #if defined(WIN32)
 | |
| 	// On Windows. Lets make sure no one else can bind to this addr/port. This stops socket hijacking (not a problem on Linux).
 | |
| 	if (setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253);
 | |
| #else
 | |
| 	// On Linux. Setting the re-use on a TCP socket allows reuse of the socket even in timeout state. Allows for fast stop/start (Not a problem on Windows).
 | |
| 	if (setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253);
 | |
| #endif
 | |
| 
 | |
| 	// Bind the socket
 | |
| #if defined(WIN32)
 | |
| 	if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { closesocket(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; }
 | |
| #else
 | |
| 	if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { close(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; }
 | |
| #endif
 | |
| 
 | |
| 	// Fetch the local port number
 | |
| #if defined(WINSOCK2)
 | |
| 	getsockname(RetVal->ListenSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength);
 | |
| #else
 | |
| 	getsockname(RetVal->ListenSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength);
 | |
| #endif
 | |
| 	if (localAddress.sin6_family == AF_INET6) RetVal->portNumber = ntohs(localAddress.sin6_port); else RetVal->portNumber = ntohs(((struct sockaddr_in*)&localAddress)->sin_port);
 | |
| 
 | |
| 	// Create our socket pool
 | |
| 	for(i = 0; i < MaxConnections; ++i)
 | |
| 	{
 | |
| 		RetVal->AsyncSockets[i] = ILibCreateAsyncSocketModuleWithMemory(Chain, initialBufferSize, &ILibAsyncServerSocket_OnData, &ILibAsyncServerSocket_OnConnectSink, &ILibAsyncServerSocket_OnDisconnectSink, &ILibAsyncServerSocket_OnSendOKSink, SessionUserMappedMemorySize);
 | |
| 		//
 | |
| 		// We want to know about any buffer reallocations, because anything above us may want to know
 | |
| 		//
 | |
| 		ILibAsyncSocket_SetReAllocateNotificationCallback(RetVal->AsyncSockets[i], &ILibAsyncServerSocket_OnBufferReAllocated);
 | |
| 	}
 | |
| 	ILibAddToChain(Chain, RetVal);
 | |
| 
 | |
| 	return(RetVal);
 | |
| }
 | |
| 
 | |
| /*! \fn ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule)
 | |
| \brief Returns the port number the server is bound to
 | |
| \param ServerSocketModule The ILibAsyncServer to query
 | |
| \returns The listening port number
 | |
| */
 | |
| unsigned short ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule)
 | |
| {
 | |
| 	return(((struct ILibAsyncServerSocketModule*)ServerSocketModule)->portNumber);
 | |
| }
 |