1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-06 00:13:33 +00:00
Files
MeshAgent/microstack/ILibAsyncSocket.c
Bryan Roe bc482d2eba 1. Fixed bug where resolve fail resulted in Linux agent accidentally closing FD(0)
2. Updated logic, so calling close on an unconnected socket will still propagate a connect failure
2022-03-04 15:35:16 -08:00

2628 lines
96 KiB
C

/*
Copyright 2006 - 2022 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifdef MEMORY_CHECK
#include <assert.h>
#define MEMCHECK(x) x
#else
#define MEMCHECK(x)
#endif
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(_MINCORE)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#if defined(WINSOCK2)
#include <winsock2.h>
#include <ws2tcpip.h>
#define MSG_NOSIGNAL 0
#elif defined(WINSOCK1)
#include <winsock.h>
#include <wininet.h>
#endif
#ifdef __APPLE__ // OSX
#define MSG_NOSIGNAL SO_NOSIGPIPE
#endif
#ifdef _VX_CPU // VxWorks
#define MSG_NOSIGNAL 0
#endif
#include "ILibParsers.h"
#include "ILibAsyncSocket.h"
#include "ILibRemoteLogging.h"
#ifdef _DEBUG
#include "ILibCrypto.h"
#endif
#ifdef _POSIX
#include <sys/socket.h>
#include <sys/un.h>
#endif
#ifndef MICROSTACK_NOTLS
#include <openssl/err.h>
#include <openssl/ssl.h>
#endif
#include <assert.h>
#if defined(_TLSLOG)
#define TLSLOG1 printf
#else
#define TLSLOG1(...) ;
#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
char ILibAsyncSocket_ScratchPad[65535];
typedef struct ILibAsyncSocket_SendData
{
char* buffer;
int bufferSize;
int bytesSent;
struct sockaddr_in6 remoteAddress;
ILibAsyncSocket_MemoryOwnership UserFree;
struct ILibAsyncSocket_SendData *Next;
}ILibAsyncSocket_SendData;
typedef struct ILibAsyncSocketModule
{
ILibTransport Transport;
#if defined(_WIN32_WCE) || defined(WIN32)
SOCKET internalSocket;
#elif defined(_POSIX)
int internalSocket;
#endif
// DO NOT MODIFY THE ABOVE FIELDS (ILibTransport, internalSocket)
unsigned int PendingBytesToSend;
unsigned int TotalBytesSent;
#ifdef _POSIX
struct sockaddr_un DomainAddress;
#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 ProxiedRemoteHost[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;
ILibSpinLock SendLock;
int MaxBufferSize;
int MaxBufferSizeExceeded;
void *MaxBufferSizeUserObject;
// Added for TLS support
#ifndef MICROSTACK_NOTLS
int TLS_HandshakeError_Occurred;
int SSLConnect;
SSL* ssl;
SSL_CTX *ssl_ctx;
BIO *readBio, *writeBio;
BUF_MEM *readBioBuffer, *writeBioBuffer;
char readBioBuffer_mem[MEMORYCHUNKSIZE];
int TLSHandshakeCompleted;
#ifdef MICROSTACK_TLS_DETECT
int TLSChecked;
#endif
#endif
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;
#ifndef MICROSTACK_NOTLS
#ifdef _REMOTELOGGING
char* ILibAsyncSocket_ContentTypeString(ILibAsyncSocket_TLSPlainText_ContentType val)
{
char *retVal = "unknown";
switch (val)
{
case ILibAsyncSocket_TLSPlainText_ContentType_ChangeCipherSpec:
retVal = "ChangeCipherSpec";
break;
case ILibAsyncSocket_TLSPlainText_ContentType_Alert:
retVal = "Alert";
break;
case ILibAsyncSocket_TLSPlainText_ContentType_Handshake:
retVal = "Handshake";
break;
case ILibAsyncSocket_TLSPlainText_ContentType_ApplicationData:
retVal = "ApplicationData";
break;
}
return retVal;
}
char* ILibAsyncSocket_HandshakeString(ILibAsyncSocket_TLSHandshakeType val)
{
char *retVal = "unknown";
switch (val)
{
case ILibAsyncSocket_TLSHandshakeType_hello:
retVal = "hello";
break;
case ILibAsyncSocket_TLSHandshakeType_clienthello:
retVal = "clienthello";
break;
case ILibAsyncSocket_TLSHandshakeType_serverhello:
retVal = "serverhello";
break;
case ILibAsyncSocket_TLSHandshakeType_certificate:
retVal = "certificate";
break;
case ILibAsyncSocket_TLSHandshakeType_serverkeyexchange:
retVal = "serverkeyexchange";
break;
case ILibAsyncSocket_TLSHandshakeType_certificaterequest:
retVal = "certificaterequest";
break;
case ILibAsyncSocket_TLSHandshakeType_serverhellodone:
retVal = "serverhellodone";
break;
case ILibAsyncSocket_TLSHandshakeType_certificateverify:
retVal = "certificateverify";
break;
case ILibAsyncSocket_TLSHandshakeType_clientkeyexchange:
retVal = "clientkeyexchange";
break;
case ILibAsyncSocket_TLSHandshakeType_finished:
retVal = "finished";
break;
}
return retVal;
}
#else
#define ILibAsyncSocket_HandshakeString(val) ""
#define ILibAsyncSocket_ContentTypeString(val) ""
#endif
int ILibAsyncSocket_TLSDetect(ILibAsyncSocketModule *module, char* buffer, int offset, int endPointer)
{
if (endPointer < 5) { return 0; }
ILibAsyncSocket_TLSPlainText_ContentType contentType = (ILibAsyncSocket_TLSPlainText_ContentType)buffer[offset+0];
unsigned char versionMajor = buffer[offset+1];
//unsigned char versionMinor = buffer[offset+2];
//unsigned short length = ntohs(((unsigned short*)(buffer+offset+3))[0]);
ILibAsyncSocket_TLSHandshakeType tlsHandshakeType = (ILibAsyncSocket_TLSHandshakeType)(buffer+offset+5)[0];
UNREFERENCED_PARAMETER(endPointer);
UNREFERENCED_PARAMETER(module);
if(contentType == ILibAsyncSocket_TLSPlainText_ContentType_Handshake && versionMajor >= 1 && (tlsHandshakeType == ILibAsyncSocket_TLSHandshakeType_hello || tlsHandshakeType == ILibAsyncSocket_TLSHandshakeType_clienthello || tlsHandshakeType == ILibAsyncSocket_TLSHandshakeType_serverhello))
{
return 1;
}
else
{
return 0;
}
}
void ILibAsyncSocket_TLSHeader_Parse(ILibAsyncSocketModule *module, char* buffer, int offset, int endPointer)
{
#if defined(_REMOTELOGGING) || defined(MICROSTACK_TLS_DETECT)
ILibAsyncSocket_TLSPlainText_ContentType contentType = (ILibAsyncSocket_TLSPlainText_ContentType)buffer[offset + 0];
#endif
#ifdef _REMOTELOGGING
unsigned char versionMajor = buffer[offset + 1];
unsigned char versionMinor = buffer[offset+2];
ILibAsyncSocket_TLSHandshakeType tlsHandshakeType = (ILibAsyncSocket_TLSHandshakeType)(buffer + offset + 5)[0];
#endif
UNREFERENCED_PARAMETER(endPointer);
#ifdef _REMOTELOGGING
ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_2, "ILibAsyncSocket(%p) TLS Header Parsing: TLS v%u.%u , Type[%s]", (void*)module, versionMajor - 2, versionMinor - 1, ILibAsyncSocket_ContentTypeString(contentType));
if (contentType == ILibAsyncSocket_TLSPlainText_ContentType_Handshake)
{
ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Handshake Step = %s", ILibAsyncSocket_HandshakeString(tlsHandshakeType));
}
#endif
#ifdef MICROSTACK_TLS_DETECT
if(contentType == ILibAsyncSocket_TLSPlainText_ContentType_ApplicationData) { module->TLSHandshakeCompleted = 1; }
#endif
}
int ILibAsyncSocket_IsUsingTls(ILibAsyncSocket_SocketModule AsyncSocketToken)
{
return(((struct ILibAsyncSocketModule*)AsyncSocketToken)->ssl != NULL ? 1:0);
}
#endif
//
// 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);
}
#ifndef MICROSTACK_NOTLS
// If this is an SSL socket, free the SSL state
if (module->ssl != NULL)
{
SSL_TRACE1("SSL_free()");
SSL_free(module->ssl); // Frees SSL session and BIO buffer at the same time
module->ssl = NULL;
SSL_TRACE2("SSL_free()");
}
#endif
// Close socket if necessary
if (module->internalSocket != ~0)
{
#if defined(_WIN32_WCE) || 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 != ILibAsyncSocket_ScratchPad) 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;
#ifndef MICROSTACK_NOTLS
module->SSLConnect = 0;
#endif
}
/*! \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.ChainLink.MetaData = ILibMemory_SmartAllocate_FromString("ILibAsyncSocket");
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(ILibAsyncSocket_ScratchPad);
RetVal->buffer = ILibAsyncSocket_ScratchPad;
}
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);
ILibSpinLock_Init(&(RetVal->SendLock));
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 == ILibAsyncSocket_MemoryOwnership_CHAIN) free(data->buffer);
free(data);
data = temp;
}
}
void ILibAsyncSocket_SendError(ILibAsyncSocket_SocketModule socketModule)
{
struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
module->PAUSE = 1;
ILibAsyncSocket_ClearPendingSend(module); // This causes a segfault
ILibSpinLock_UnLock(&(module->SendLock));
// Ensure Calling On_Disconnect with MicroStackThread
ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
ILibSpinLock_Lock(&(module->SendLock));
}
ILibSpinLock *ILibAsyncSocket_GetSpinLock(ILibAsyncSocket_SocketModule socketModule)
{
return(&((struct ILibAsyncSocketModule*)socketModule)->SendLock);
}
/*! \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 remoteAddress The IPAddress of the destination
\param count The number of triplets passed in the ellipses. A triplet consists of {char* buffer, int length, ILibAsyncSocket_MemoryOwnership userFree}
\returns \a ILibAsyncSocket_SendStatus indicating the send status
*/
ILibAsyncSocket_SendStatus ILibAsyncSocket_SendTo_MultiWrite(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress, unsigned int count, ...)
{
struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
struct ILibAsyncSocket_SendData *data;
int bytesSent = 0;
enum ILibAsyncSocket_SendStatus retVal = ILibAsyncSocket_ALL_DATA_SENT;
unsigned int vi;
char *buffer;
size_t bufferLen;
ILibAsyncSocket_MemoryOwnership UserFree;
int lockOverride = ((count & ILibAsyncSocket_LOCK_OVERRIDE) == ILibAsyncSocket_LOCK_OVERRIDE) ? (count ^= ILibAsyncSocket_LOCK_OVERRIDE, 1) : 0;
// If the socket is empty, return now.
if (socketModule == NULL) return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
int notok = 0;
va_list vlist;
va_start(vlist, count);
#ifndef MICROSTACK_NOTLS
if (module->ssl != NULL)
{
if (lockOverride == 0) { ILibSpinLock_Lock(&(module->SendLock)); }
for (vi = 0; vi < count; ++vi)
{
buffer = va_arg(vlist, char*);
bufferLen = va_arg(vlist, size_t);
UserFree = va_arg(vlist, ILibAsyncSocket_MemoryOwnership);
if (bufferLen > INT32_MAX || notok != 0)
{
if (UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(buffer); }
notok = 1;
continue;
}
SSL_TRACE1("SSL_write()");
SSL_write(module->ssl, buffer, (int)bufferLen); // No dataloss, becuase we capped at INT32_MAX
SSL_TRACE2("SSL_write()");
TLSLOG1("SSL_write[%d]: %d bytes...\n", module->internalSocket, bufferLen);
if (UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(buffer); }
}
va_end(vlist);
if (notok == 0)
{
if (module->PendingSend_Tail == NULL)
{
// No pending data, so we can send now
if (module->writeBioBuffer->length > 0)
{
BIO_clear_retry_flags(module->writeBio); // Klocwork reports this could block, but this is a memory bio, so it will never block.
bytesSent = send(module->internalSocket, module->writeBioBuffer->data, (int)(module->writeBioBuffer->length), 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
TLSLOG1("--> SOCKET WRITE[%d]: %d bytes...\n", module->internalSocket, bytesSent);
#ifdef WIN32
if ((bytesSent > 0 && bytesSent < (int)(module->writeBioBuffer->length)) || (bytesSent < 0 && WSAGetLastError() == WSAEWOULDBLOCK))
#else
if ((bytesSent > 0 && bytesSent < (int)(module->writeBioBuffer->length)) || (bytesSent < 0 && errno == EWOULDBLOCK))
#endif
{
// Still Pending Data to be sent
data = (ILibAsyncSocket_SendData*)ILibMemory_Allocate(sizeof(ILibAsyncSocket_SendData), bytesSent < 0 ? 0 : (int)(module->writeBioBuffer->length) - bytesSent, NULL, NULL);
data->UserFree = ILibAsyncSocket_MemoryOwnership_BIO;
data->bytesSent = 0;
module->PendingSend_Head = module->PendingSend_Tail = data;
if (bytesSent > 0)
{
// Some data was sent, so we need to pull the data and buffer it
module->PendingSend_Head->buffer = ILibMemory_GetExtraMemory(data, sizeof(ILibAsyncSocket_SendData));
module->PendingSend_Head->bufferSize = (int)(module->writeBioBuffer->length) - bytesSent;
memcpy_s(module->PendingSend_Head->buffer, module->PendingSend_Head->bufferSize, module->writeBioBuffer->data + bytesSent, module->PendingSend_Head->bufferSize);
module->TotalBytesSent += bytesSent;
module->PendingBytesToSend = (unsigned int)(module->PendingSend_Head->bufferSize);
TLSLOG1(" --> BUFFERING[%d]: %d bytes...\n", module->internalSocket, module->PendingSend_Head->bufferSize);
ignore_result(BIO_reset(module->writeBio));
}
else if (bytesSent < 0)
{
TLSLOG1(" -- > [INCOMPLETE] Accumulated into BIOBUFFER[%d]\n", module->internalSocket);
}
retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET;
}
else if (bytesSent == module->writeBioBuffer->length)
{
retVal = ILibAsyncSocket_ALL_DATA_SENT;
ignore_result(BIO_reset(module->writeBio));
module->TotalBytesSent += bytesSent;
module->PendingBytesToSend = (unsigned int)(module->writeBioBuffer->length);
TLSLOG1(" --> COMPLETE[%d]\n", module->internalSocket);
}
else
{
retVal = ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
ILibAsyncSocket_SendError(module);
}
}
else
{
// Something went wrong
retVal = ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
ILibAsyncSocket_SendError(module);
}
}
else
{
// Send will happen in ILibAsyncSocket_PostSelect()
retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET;
module->PendingBytesToSend = (unsigned int)(module->writeBioBuffer->length);
TLSLOG1(" --> [IN PROGRESS] Accumulated into BIOBUFFER[%d]...\n", module->internalSocket);
}
}
else
{
retVal = ILibAsyncSocket_BUFFER_TOO_LARGE;
ILibAsyncSocket_SendError(module);
}
if (lockOverride == 0) { ILibSpinLock_UnLock(&(module->SendLock)); }
if (retVal != ILibAsyncSocket_ALL_DATA_SENT && !ILibIsRunningOnChainThread(module->Transport.ChainLink.ParentChain)) ILibForceUnBlockChain(module->Transport.ChainLink.ParentChain);
return retVal;
}
#endif
// If we got here, we aren't doing TLS
if (lockOverride == 0) { ILibSpinLock_Lock(&(module->SendLock)); }
if (module->internalSocket == ~0)
{
// Too Bad, the socket closed
for (vi = 0; vi < count; ++vi)
{
buffer = va_arg(vlist, char*);
bufferLen = va_arg(vlist, int);
UserFree = va_arg(vlist, ILibAsyncSocket_MemoryOwnership);
if (UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(buffer); }
}
if (lockOverride == 0) { ILibSpinLock_UnLock(&(module->SendLock)); }
va_end(vlist);
return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
}
for (vi = 0; vi < count; ++vi)
{
buffer = va_arg(vlist, char*);
bufferLen = va_arg(vlist, size_t);
UserFree = va_arg(vlist, ILibAsyncSocket_MemoryOwnership);
if (bufferLen > INT32_MAX || notok != 0)
{
notok = 1;
if (UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(buffer); }
continue;
}
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
data = (ILibAsyncSocket_SendData*)ILibMemory_Allocate(sizeof(ILibAsyncSocket_SendData), 0, NULL, NULL);
data->bufferSize = (int)bufferLen; // No dataloss, capped to INT32_MAX
module->PendingBytesToSend += (int)bufferLen;
if (UserFree == ILibAsyncSocket_MemoryOwnership_USER)
{
if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254);
memcpy_s(data->buffer, data->bufferSize, buffer, bufferLen);
data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN;
}
else
{
data->buffer = buffer;
data->UserFree = UserFree;
}
data->bytesSent = 0;
if (remoteAddress != NULL) memcpy_s(&(data->remoteAddress), sizeof(struct sockaddr_in6), remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family));
data->Next = NULL;
if (module->PendingSend_Tail == NULL)
{
module->PendingSend_Head = module->PendingSend_Tail = data;
}
else
{
module->PendingSend_Tail->Next = data;
module->PendingSend_Tail = data;
}
}
else if (module->PendingSend_Tail == NULL && module->FinConnect != 0)
{
// No pending data, so we can try to send now
if (remoteAddress == NULL || remoteAddress->sa_family == AF_UNIX)
{
// Set MSG_NOSIGNAL since we don't want to get Broken Pipe signals in Linux, ignored if Windows.
bytesSent = send(module->internalSocket, buffer, (int)bufferLen, MSG_NOSIGNAL); // No dataloss, capped to INT32_MAX
}
else
{
bytesSent = sendto(module->internalSocket, buffer, (int)bufferLen, MSG_NOSIGNAL, (struct sockaddr*)remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family)); // No dataloss, capped to INT32_MAX
}
#ifdef WIN32
if ((bytesSent > 0 && bytesSent < (int)bufferLen) || (bytesSent < 0 && WSAGetLastError() == WSAEWOULDBLOCK))
#else
if ((bytesSent > 0 && bytesSent < (int)bufferLen) || (bytesSent < 0 && errno == EWOULDBLOCK))
#endif
{
// Not all data was sent
if (bytesSent < 0) { bytesSent = 0; }
data = (ILibAsyncSocket_SendData*)ILibMemory_Allocate(sizeof(ILibAsyncSocket_SendData), 0, NULL, NULL);
if (UserFree == ILibAsyncSocket_MemoryOwnership_USER)
{
data->bufferSize = (int)bufferLen - bytesSent; // No dataloss, capped to INT32_MAX
if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254);
memcpy_s(data->buffer, data->bufferSize, buffer + bytesSent, data->bufferSize);
data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN;
}
else
{
data->buffer = buffer;
data->bufferSize = (int)bufferLen; // No dataloss, capped to INT32_MAX
data->bytesSent = bytesSent;
data->UserFree = UserFree;
}
module->PendingSend_Head = module->PendingSend_Tail = data;
retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET;
}
else if (bytesSent == (int)bufferLen) // No dataloss, capped to INT32_MAX
{
// All Data was sent
retVal = ILibAsyncSocket_ALL_DATA_SENT;
if (UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(buffer); }
}
else
{
retVal = ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
ILibAsyncSocket_SendError(module);
}
if (bytesSent > 0)
{
module->TotalBytesSent += bytesSent; module->PendingBytesToSend -= bytesSent;
if ((int)(module->PendingBytesToSend) < 0) { module->PendingBytesToSend = 0; }
}
}
}
va_end(vlist);
if (lockOverride == 0) { ILibSpinLock_UnLock(&(module->SendLock)); }
if (notok != 0)
{
retVal = ILibAsyncSocket_BUFFER_TOO_LARGE;
ILibAsyncSocket_SendError(module);
}
if (retVal != ILibAsyncSocket_ALL_DATA_SENT && !ILibIsRunningOnChainThread(module->Transport.ChainLink.ParentChain)) 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_WCE) || defined(WIN32)
SOCKET s;
#else
int s;
#endif
#ifndef MICROSTACK_NOTLS
SSL *wasssl;
#endif
struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
if (module == NULL) { return; }
ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_1, "AsyncSocket[%p] << DISCONNECT", (void*)module);
ILibSpinLock_Lock(&(module->SendLock));
module->timeout_handler = NULL;
module->timeout_milliSeconds = 0;
#ifndef MICROSTACK_NOTLS
wasssl = module->ssl;
if (module->ssl != NULL)
{
SSL_TRACE1("ILibAsyncSocket_Disconnect()");
SSL_shutdown(module->ssl);
ILibSpinLock_UnLock(&(module->SendLock));
SSL_free(module->ssl); // Frees SSL session and both BIO buffers at the same time
ILibSpinLock_Lock(&(module->SendLock));
module->ssl = NULL;
SSL_TRACE2("ILibAsyncSocket_Disconnect()");
}
#endif
// 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_WCE) || defined(WIN32)
#if defined(WINSOCK2)
shutdown(s, SD_SEND);
#endif
closesocket(s);
#elif defined(_POSIX)
shutdown(s, SHUT_WR);
close(s);
#endif
}
// Since the socket is closing, we need to clear the data that is pending to be sent
ILibAsyncSocket_ClearPendingSend(socketModule);
ILibSpinLock_UnLock(&(module->SendLock));
#ifndef MICROSTACK_NOTLS
if (wasssl == NULL)
{
#endif
// 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
#ifndef MICROSTACK_NOTLS
}
else
{
// This was a SSL socket, fire the event notifying the user. Depending on connection state, we event differently
if (module->SSLConnect == 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed
if (module->SSLConnect != 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected
}
#endif
module->FinConnect = 0;
module->user = NULL;
#ifndef MICROSTACK_NOTLS
module->SSLConnect = 0;
#endif
}
/*! \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, v;
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)
{
PRINTERROR(); 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));
#ifdef _POSIX
memset(&(module->DomainAddress), 0, sizeof(struct sockaddr_un));
#endif
// Setup
if (remoteInterface != NULL)
{
if (remoteInterface->sa_family == AF_UNIX)
{
#ifdef _POSIX
memcpy_s(&(module->DomainAddress), sizeof(struct sockaddr_un), remoteInterface, sizeof(struct sockaddr_un));
#endif
module->RemoteAddress.sin6_family = AF_UNIX;
}
else
{
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 && module->RemoteAddress.sin6_family != AF_UNIX)
{
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 != NULL)
{
if (localInterface->sa_family == AF_INET && ((struct sockaddr_in*)localInterface)->sin_port != 0) { PRINTERROR(); ILIBCRITICALEXIT(253); }
if (localInterface->sa_family == AF_INET6 && ((struct sockaddr_in6*)localInterface)->sin6_port != 0) { PRINTERROR(); ILIBCRITICALEXIT(253); }
}
#endif
// Allocate a new socket
if (module->RemoteAddress.sin6_family != AF_UNIX)
{
if (((struct sockaddr_in*)localInterface)->sin_family == AF_UNSPEC || (module->internalSocket = ILibGetSocket(localInterface, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
module->internalSocket = ~0;
module->FinConnect = -1;
ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
return;
}
}
else
{
if ((int)(module->internalSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { ILIBCRITICALEXIT(253); return; }
}
// Initialise the buffer pointers, since no data is in them yet.
module->FinConnect = 0;
#ifndef MICROSTACK_NOTLS
module->SSLConnect = 0;
#endif
module->BeginPointer = 0;
module->EndPointer = 0;
if (module->RemoteAddress.sin6_family != AF_UNIX)
{
// 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_WCE) || 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 && module->RemoteAddress.sin6_family != AF_UNIX)
{
if ((v=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 (module->RemoteAddress.sin6_family != AF_UNIX)
{
if ((v=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;
}
}
else
{
#ifdef _POSIX
if ((v=connect(module->internalSocket, (struct sockaddr *)&(module->DomainAddress), SUN_LEN(&(module->DomainAddress)))) < 0)
#endif
{
// 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 (v != 0 && 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
void ILibAsyncSocket_ClearProxySettings(void *socketModule)
{
struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
memset(&(module->ProxyAddress), 0, sizeof(struct sockaddr_in6));
memset(module->ProxiedRemoteHost, 0, sizeof(module->ProxiedRemoteHost));
module->ProxyState = 0;
}
//! 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));
memset(module->ProxiedRemoteHost, 0, sizeof(module->ProxiedRemoteHost));
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);
}
void ILibAsyncSocket_ConnectToProxyEx(void* socketModule, struct sockaddr *localInterface, char *remoteAddressAndPort, 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;
size_t proxylen = strnlen_s(remoteAddressAndPort, sizeof(module->ProxiedRemoteHost) - 1);
memcpy_s(module->ProxiedRemoteHost, sizeof(module->ProxiedRemoteHost), remoteAddressAndPort, proxylen);
if (proxyAddress != NULL) memcpy_s(&(module->ProxyAddress), sizeof(struct sockaddr_in6), proxyAddress, INET_SOCKADDR_LENGTH(proxyAddress->sa_family));
ILibAsyncSocket_ConnectTo(socketModule, localInterface, NULL, InterruptPtr, user);
}
#endif
#ifndef MICROSTACK_NOTLS
ILibAsyncSocket_SendStatus ILibAsyncSocket_ProcessEncryptedBuffer(ILibAsyncSocketModule *Reader)
{
int j;
ILibAsyncSocket_SendData *data;
ILibAsyncSocket_SendStatus retVal = ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR;
ILibSpinLock_Lock(&(Reader->SendLock));
if (Reader->writeBioBuffer->length > 0)
{
if (Reader->PendingSend_Tail == NULL)
{
// No Pending Sends
if (Reader->FinConnect == 0)
{
// Not connected yet, so lets make sure we send this stuff later
data = (ILibAsyncSocket_SendData*)ILibMemory_Allocate(sizeof(ILibAsyncSocket_SendData), 0, NULL, NULL);
data->UserFree = ILibAsyncSocket_MemoryOwnership_BIO;
Reader->PendingSend_Head = Reader->PendingSend_Tail = data;
retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET;
}
else
{
BIO_clear_retry_flags(Reader->writeBio);
j = send(Reader->internalSocket, Reader->writeBioBuffer->data, (int)(Reader->writeBioBuffer->length), MSG_NOSIGNAL); // Klockwork says this can block, but it won't, because it's a nonblocking socket
if (j > 0)
{
if (j < (int)(Reader->writeBioBuffer->length))
{
// Not all data was sent
data = (ILibAsyncSocket_SendData*)ILibMemory_Allocate((int)sizeof(ILibAsyncSocket_SendData), (int)Reader->writeBioBuffer->length - j, NULL, NULL);
data->buffer = ILibMemory_GetExtraMemory(data, (int)sizeof(ILibAsyncSocket_SendData));
data->bufferSize = (int)Reader->writeBioBuffer->length - j;
data->UserFree = ILibAsyncSocket_MemoryOwnership_BIO;
memcpy_s(data->buffer, data->bufferSize, Reader->writeBioBuffer->data + j, data->bufferSize);
Reader->PendingSend_Head = Reader->PendingSend_Tail = data;
retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET;
ignore_result(BIO_reset(Reader->writeBio));
}
else if (j == (int)(Reader->writeBioBuffer->length))
{
// All Data was sent
ignore_result(BIO_reset(Reader->writeBio));
retVal = ILibAsyncSocket_ALL_DATA_SENT;
}
}
}
}
else
{
// Pending Sends
// Don't need to do anything, becuase it'll get picked up in the PostSelect
}
}
ILibSpinLock_UnLock(&(Reader->SendLock));
return retVal;
}
#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)
{
#ifndef MICROSTACK_NOTLS
int sslerror = -1;
SSL *wasssl;
int j;
#endif
int bytesReceived = 0;
#ifdef WIN32
int len;
#else
socklen_t len;
#endif
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)
{
#ifndef MICROSTACK_NOTLS
#ifdef MICROSTACK_TLS_DETECT
if (Reader->ssl != NULL && Reader->TLSChecked == 0)
{
#ifndef __APPLE__
bytesReceived = recv(Reader->internalSocket, Reader->buffer, Reader->MallocSize, MSG_PEEK | MSG_NOSIGNAL);
if (ILibAsyncSocket_TLSDetect(Reader, Reader->buffer, 0, bytesReceived) == 0)
{
SSL_free(Reader->ssl);
Reader->ssl = NULL;
if (Reader->OnConnect != NULL)
{
if (Reader->OnConnect != NULL) Reader->OnConnect(Reader, -1, Reader->user);
}
}
#endif
Reader->TLSChecked = 1;
}
#endif
#endif
// Read data off the non-SSL, generic socket.
// Set the receive address buffer size and read from the socket.
#ifdef WIN32
len = (int)sizeof(struct sockaddr_in6);
#else
len = (socklen_t)sizeof(struct sockaddr_in6);
#endif
#ifndef MICROSTACK_NOTLS
if (Reader->ssl != NULL)
{
BIO_clear_retry_flags(Reader->readBio);
if (Reader->RemoteAddress.sin6_family == AF_UNIX)
{
bytesReceived = recv(Reader->internalSocket, Reader->readBioBuffer_mem + Reader->readBioBuffer->length, (int)(Reader->readBioBuffer->max - Reader->readBioBuffer->length), 0);
}
else
{
bytesReceived = recvfrom(Reader->internalSocket, Reader->readBioBuffer_mem + Reader->readBioBuffer->length, (int)(Reader->readBioBuffer->max - Reader->readBioBuffer->length), 0, (struct sockaddr*)&(Reader->SourceAddress), &len);
}
if (bytesReceived > 0)
{
Reader->readBioBuffer->length += bytesReceived;
if (Reader->TLSHandshakeCompleted == 0)
{
SSL_TRACE1("SSL_handshake()");
switch ((sslerror = SSL_do_handshake(Reader->ssl)))
{
case 0:
// Handshake Failed!
while ((sslerror = ERR_get_error()) != 0)
{
ERR_error_string_n(sslerror, ILibScratchPad, sizeof(ILibScratchPad));
}
// TODO: We should probably do something
break;
case 1:
Reader->SSLConnect = Reader->TLSHandshakeCompleted = 1;
if (Reader->OnConnect != NULL)
{
Reader->OnConnect(Reader, -1, Reader->user);
#ifdef _DEBUG
//util_savekeys(Reader->ssl); // SAVES TLS PRIVATE KEYS - WARNING: !!! THIS CODE SHOULD ALWAYS BE COMMENTED OUT !!!!
#endif
}
ILibAsyncSocket_ProcessEncryptedBuffer(Reader);
break;
default:
// SSL_WANT_READ most likely
sslerror = SSL_get_error(Reader->ssl, sslerror);
if (sslerror == SSL_ERROR_SSL)
{
Reader->TLS_HandshakeError_Occurred = 1;
bytesReceived = -1;
}
else
{
ILibAsyncSocket_ProcessEncryptedBuffer(Reader);
}
break;
}
SSL_TRACE2("SSL_handshake()");
}
// Even if we get completed the TLS handshake, we must still read if data remains, this is possible with TLS 1.3
if ((Reader->TLSHandshakeCompleted == 1) && (Reader->readBioBuffer->length > 0))
{
SSL_TRACE1("SSL_read()");
while ((j = SSL_read(Reader->ssl, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer))>0)
{
// We got new TLS Data
if (j > 0)
{
Reader->EndPointer += j;
if (Reader->MallocSize - Reader->EndPointer == 0)
{
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);
}
}
}
if (j < 0)
{
sslerror = SSL_get_error(Reader->ssl, j);
if (Reader->writeBioBuffer->length > 0)
{
ILibAsyncSocket_ProcessEncryptedBuffer(Reader);
}
}
SSL_TRACE2("SSL_read()");
}
}
if (Reader->readBioBuffer->length == 0)
{
ignore_result(BIO_reset(Reader->readBio));
Reader->readBioBuffer->length = 0;
}
}
else
#endif
{
#if defined(WINSOCK2)
if (Reader->RemoteAddress.sin6_family == AF_UNIX)
{
bytesReceived = recv(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0);
}
else
{
bytesReceived = recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), &len);
}
#else
if (Reader->RemoteAddress.sin6_family == AF_UNIX)
{
bytesReceived = (int)recv(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0);
}
else
{
bytesReceived = (int)recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), &len);
}
#endif
if (Reader->RemoteAddress.sin6_family != AF_UNIX)
{
ILib6to4((struct sockaddr*)&(Reader->SourceAddress));
ILibRemoteLogging_printf(ILibChainGetLogger(Reader->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] recv 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 (Reader->buffer != NULL)
{
if (iPointer <= (Reader->EndPointer - Reader->BeginPointer))
{
assert(iPointer <= (Reader->EndPointer - Reader->BeginPointer));
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) { 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_WCE) || 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);
#ifndef MICROSTACK_NOTLS
wasssl = Reader->ssl;
if (Reader->ssl != NULL)
{
SSL_free(Reader->ssl); // Frees SSL session and BIO buffer at the same time
ILibSpinLock_Lock(&(Reader->SendLock));
Reader->ssl = NULL;
ILibSpinLock_UnLock(&(Reader->SendLock));
}
#endif
//
// Inform the user the socket has closed
//
Reader->timeout_handler = NULL;
Reader->timeout_milliSeconds = 0;
#ifndef MICROSTACK_NOTLS
if (wasssl != NULL)
{
// This was a SSL socket, fire the event notifying the user. Depending on connection state, we event differently
if (Reader->SSLConnect == 0 && Reader->OnConnect != NULL) { Reader->OnConnect(Reader, 0, Reader->user); } // Connection Failed
if (Reader->SSLConnect != 0 && Reader->OnDisconnect != NULL) { Reader->OnDisconnect(Reader, Reader->user); } // Socket Disconnected
}
else
{
#endif
// 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
#ifndef MICROSTACK_NOTLS
}
Reader->SSLConnect = 0;
#endif
Reader->FinConnect = 0;
//
// If we need to free the buffer, do so
//
if (Reader->buffer != NULL)
{
if (Reader->buffer != ILibAsyncSocket_ScratchPad) 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_SetTimeoutEx(ILibAsyncSocket_SocketModule socketModule, int timeoutMilliseconds, ILibAsyncSocket_TimeoutHandler timeoutHandler)
{
struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
module->timeout_milliSeconds = timeoutMilliseconds;
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);
ILibSpinLock_Lock(&(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)
{
ILibSpinLock_UnLock(&(module->SendLock));
h(module, module->user);
ILibSpinLock_Lock(&(module->SendLock));
if (module->timeout_milliSeconds != 0)
{
*blocktime = module->timeout_milliSeconds;
module->timeout_lastActivity = ILibGetUptime();
}
}
}
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
}
}
ILibSpinLock_UnLock(&(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)
{
#ifndef MICROSTACK_NOTLS
SSL *wasssl;
#endif
struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule;
// If this is an SSL socket, close down the SSL state
#ifndef MICROSTACK_NOTLS
if ((wasssl = module->ssl) != NULL)
{
SSL_TRACE1("ILibAsyncSocket_PrivateShutdown()");
SSL_free(module->ssl); // Frees SSL session and BIO buffer at the same time
module->ssl = NULL;
SSL_TRACE2("ILibAsyncSocket_PrivateShutdown()");
}
#endif
// Now shutdown the socket and set it to zero
#if defined(_WIN32_WCE) || 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;
#ifndef MICROSTACK_NOTLS
if (wasssl != NULL)
{
// This was a SSL socket, fire the event notifying the user. Depending on connection state, we event differently
if (module->SSLConnect == 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed
if (module->SSLConnect != 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected
}
else
{
#endif
// 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
#ifndef MICROSTACK_NOTLS
}
module->SSLConnect = 0;
#endif
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;
#ifdef WIN32
int len;
#else
socklen_t len;
#endif
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 );
ILibSpinLock_Lock(&(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 if(fd_read!=0)
{
// 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 && module->RemoteAddress.sin6_family != AF_UNIX)
{
char *ptr1, *ptr2;
int len2;
int slen = sizeof(struct sockaddr_in6);
serr = 555; // Fake proxy error
len2 = recvfrom(module->internalSocket, ILibScratchPad2, 1024, 0, (struct sockaddr*)&(module->SourceAddress), (socklen_t*)&slen);
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)
{
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
ILibSpinLock_UnLock(&(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
#ifdef WIN32
len = (int)sizeof(struct sockaddr_in6);
#else
len = (socklen_t)sizeof(struct sockaddr_in6);
#endif
if (module->RemoteAddress.sin6_family != AF_UNIX)
{
#if defined(WINSOCK2)
getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), &len);
#else
getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), &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_WCE) || 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 && module->RemoteAddress.sin6_family != AF_UNIX)
{
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)
{
if (module->ProxiedRemoteHost[0] != 0)
{
len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\n\r\n", module->ProxiedRemoteHost, module->ProxiedRemoteHost);
}
else
{
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);
if (module->ProxiedRemoteHost[0] != 0)
{
len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\nProxy-authorization: basic %s\r\n\r\n", module->ProxiedRemoteHost, module->ProxiedRemoteHost, ProxyAuth);
}
else
{
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); // Klockwork says this could block, but it can't becuase socket was set to nonblock
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.
ILibSpinLock_UnLock(&(module->SendLock));
return;
}
if (module->ProxyState == 2) module->ProxyState = 3;
#endif
// Connection Complete
triggerWriteSet = 1;
}
// Unlock before fireing the event
ILibSpinLock_UnLock(&(module->SendLock));
// If we did connect, we got more things to do
if (triggerWriteSet != 0)
{
module->timeout_lastActivity = ILibGetUptime();
#ifndef MICROSTACK_NOTLS
if (module->ssl_ctx != NULL)
{
// Make this call to setup the SSL stuff
ILibAsyncSocket_SetSSLContext(module, module->ssl_ctx, ILibAsyncSocket_TLS_Mode_Client);
}
else
#endif
{
// 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
ILibSpinLock_UnLock(&(module->SendLock));
if (triggerReadSet != 0 || triggerResume != 0) ILibProcessAsyncSocket(module, triggerReadSet);
}
}
ILibSpinLock_Lock(&(module->SendLock));
// Write Handling
if (module->FinConnect > 0 && module->internalSocket != ~0 && fd_write != 0 && module->PendingSend_Head != NULL && (module->ProxyState != 1))
{
//
// 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;
#ifndef MICROSTACK_NOTLS
if (module->ssl != NULL)
{
while (TRY_TO_SEND != 0)
{
// First check to see if there is a buffer for us to send
if (module->PendingSend_Head->buffer != NULL && module->PendingSend_Head->bytesSent != module->PendingSend_Head->bufferSize)
{
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
TLSLOG1(" << Draining[%d]: %d >>\n", module->internalSocket, bytesSent);
if (bytesSent > 0)
{
module->PendingSend_Head->bytesSent += bytesSent;
if (module->PendingSend_Head->bytesSent == module->PendingSend_Head->bufferSize && module->PendingSend_Head->UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN)
{
free(module->PendingSend_Head->buffer);
module->PendingSend_Head->buffer = NULL;
TLSLOG1(" --> DRAINED --<\n");
break;
}
else
{
if (module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent == 0 && module->PendingSend_Head->UserFree == ILibAsyncSocket_MemoryOwnership_BIO)
{
TLSLOG1(" --> DRAINED (BIOBUFFER still has %d bytes)\n", (int)(module->writeBioBuffer->length));
}
else
{
TLSLOG1(" --> REMAINING: %d bytes --<\n", module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent);
}
}
}
if (bytesSent <= 0)
{
TRY_TO_SEND = 0;
}
}
else
{
break;
}
}
if (TRY_TO_SEND == 0) { break; }
if (module->writeBioBuffer->length > 0 && TRY_TO_SEND != 0)
{
BIO_clear_retry_flags(module->writeBio);
bytesSent = (int)send(module->internalSocket, module->writeBioBuffer->data, (int)(module->writeBioBuffer->length), 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
TLSLOG1(" << BIOBUFFER[%d] drain: %d of %d bytes >>\n", module->internalSocket, bytesSent, (int)module->writeBioBuffer->length);
#ifdef WIN32
if ((bytesSent > 0 && bytesSent < (int)(module->writeBioBuffer->length)) || (bytesSent < 0 && WSAGetLastError() == WSAEWOULDBLOCK))
#else
if ((bytesSent > 0 && bytesSent < (int)(module->writeBioBuffer->length)) || (bytesSent < 0 && errno == EWOULDBLOCK))
#endif
{
if (bytesSent > 0)
{
// Some Data was sent, so we need to grab all the data from SSL and buffer it
module->PendingSend_Head->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN;
module->PendingSend_Head->buffer = ILibMemory_Allocate((int)module->writeBioBuffer->length - bytesSent, 0, NULL, NULL);
module->PendingSend_Head->bufferSize = (int)module->writeBioBuffer->length - bytesSent;
module->PendingSend_Head->bytesSent = 0;
memcpy_s(module->PendingSend_Head->buffer, module->PendingSend_Head->bufferSize, module->writeBioBuffer->data + bytesSent, module->PendingSend_Head->bufferSize);
TLSLOG1(" <<-- BUFFERING[%d]: %d bytes -->>\n", module->internalSocket, module->PendingSend_Head->bufferSize);
module->TotalBytesSent += bytesSent;
module->PendingBytesToSend = (unsigned int)(module->PendingSend_Head->bufferSize);
ignore_result(BIO_reset(module->writeBio));
}
else
{
// No Data was sent, so we can just leave the data in the writeBio, and fetch it later
}
TRY_TO_SEND = 0;
}
else if(bytesSent == module->writeBioBuffer->length)
{
// All data was sent
ignore_result(BIO_reset(module->writeBio));
module->TotalBytesSent += bytesSent;
module->PendingBytesToSend = (unsigned int)(module->writeBioBuffer->length);
if (module->PendingSend_Head->buffer != NULL && module->PendingSend_Head->UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(module->PendingSend_Head->buffer); }
free(module->PendingSend_Head);
module->PendingSend_Head = module->PendingSend_Tail = NULL;
TRY_TO_SEND = 0;
}
else
{
// Something went wrong
ignore_result(BIO_reset(module->writeBio));
if (module->PendingSend_Head->buffer != NULL && module->PendingSend_Head->UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(module->PendingSend_Head->buffer); }
free(module->PendingSend_Head);
module->PendingSend_Head = module->PendingSend_Tail = NULL;
TRY_TO_SEND = 0;
}
}
else
{
// All data was sent
ignore_result(BIO_reset(module->writeBio));
module->TotalBytesSent += bytesSent;
module->PendingBytesToSend = (unsigned int)(module->writeBioBuffer->length);
if (module->PendingSend_Head->buffer != NULL && module->PendingSend_Head->UserFree == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(module->PendingSend_Head->buffer); }
free(module->PendingSend_Head);
module->PendingSend_Head = module->PendingSend_Tail = NULL;
TRY_TO_SEND = 0;
TLSLOG1(" ** CLEARING **\n");
}
}
else
#endif
{
if (module->PendingSend_Head->remoteAddress.sin6_family == 0 || module->PendingSend_Head->remoteAddress.sin6_family == AF_UNIX)
{
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;
if ((int)module->PendingBytesToSend < 0) { module->PendingBytesToSend = 0; }
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 = 0;
}
}
}
#ifndef MICROSTACK_NOTLS
if (bytesSent == -1 && module->ssl == NULL)
#else
if (bytesSent == -1)
#endif
{
// Error, clean up everything
TRY_TO_SEND = 0;
#if defined(_WIN32_WCE) || 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);
}
}
#ifndef MICROSTACK_NOTLS
else if (bytesSent == -1 && module->ssl != NULL)
{
// OpenSSL returned an error
TRY_TO_SEND = 0;
int sslerr = SSL_get_error(module->ssl, -1);
if (sslerr != SSL_ERROR_WANT_WRITE && sslerr != SSL_ERROR_WANT_READ)
{
// There was an error sending
ILibAsyncSocket_ClearPendingSend(socketModule);
ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL);
}
}
#endif
}
// This triggers OnSendOK, if all the pending data has been sent.
if (module->PendingSend_Head == NULL && bytesSent != -1) { TriggerSendOK = 1; }
ILibSpinLock_UnLock(&(module->SendLock));
#ifndef MICROSTACK_NOTLS
if (TriggerSendOK != 0 && (module->ssl == NULL || module->SSLConnect != 0))
#else
if (TriggerSendOK != 0)
#endif
{
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
{
ILibSpinLock_UnLock(&(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
*/
#ifndef MICROSTACK_NOTLS
//! Associate an OpenSSL Context Object
/*!
\ingroup TLSGroup
\param socketModule ILibAsyncSocket_SocketModule to associate the SSL Context
\param ssl_ctx SSL_CTX Context object
\param server ILibAsyncSocket_TLS_Mode Configuration
*/
SSL* ILibAsyncSocket_SetSSLContextEx(ILibAsyncSocket_SocketModule socketModule, SSL_CTX *ssl_ctx, ILibAsyncSocket_TLS_Mode server, char *hostName)
{
if (socketModule != NULL)
{
struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule;
if (ssl_ctx == NULL) return(NULL);
if (module->ssl_ctx == NULL)
{
module->ssl_ctx = ssl_ctx;
}
// If a socket is ready, setup SSL right now (otherwise, we will do this upon connection).
if (module->internalSocket != 0 && module->internalSocket != ~0 && module->ssl == NULL)
{
int status;
#ifdef MICROSTACK_TLS_DETECT
module->TLSChecked = server == ILibAsyncSocket_TLS_Mode_Server_with_TLSDetectLogic ? 0 : 1;
#endif
SSL_TRACE1("SetSSLContextEx()");
module->ssl = SSL_new(ssl_ctx);
module->TLSHandshakeCompleted = 0;
module->readBio = BIO_new_mem_buf(module->readBioBuffer_mem, (int)sizeof(module->readBioBuffer_mem));
module->writeBio = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(module->readBio, -1);
BIO_set_mem_eof_return(module->writeBio, -1);
SSL_set_bio(module->ssl, module->readBio, module->writeBio);
BIO_get_mem_ptr(module->readBio, &(module->readBioBuffer));
BIO_get_mem_ptr(module->writeBio, &(module->writeBioBuffer));
module->readBioBuffer->length = 0;
if (server == ILibAsyncSocket_TLS_Mode_Client)
{
if (hostName != NULL) { SSL_set_tlsext_host_name(module->ssl, hostName); }
SSL_set_connect_state(module->ssl);
status = SSL_do_handshake(module->ssl);
if (status <= 0) { status = SSL_get_error(module->ssl, status); }
if (status == SSL_ERROR_WANT_READ)
{
ILibAsyncSocket_ProcessEncryptedBuffer(module);
// We're going to drop out now, becuase we need to check for received data
}
}
else
{
SSL_set_accept_state(module->ssl); // Setup server SSL state
}
SSL_TRACE2("SetSSLContextEx()");
return(module->ssl);
}
}
return NULL;
}
//! Get's the OpenSSL Context Structure
/*!\
\ingroup TLSGroup
\param socketModule ILibAsyncSocket_SocketModule to fetch the SSL Context from
\return OpenSSL Context Structure [NULL if not set]
*/
SSL_CTX *ILibAsyncSocket_GetSSLContext(ILibAsyncSocket_SocketModule socketModule)
{
struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule;
return module->ssl_ctx;
}
SSL* ILibAsyncSocket_GetSSL(ILibAsyncSocket_SocketModule socketModule)
{
return(((ILibAsyncSocketModule*)socketModule)->ssl);
}
#endif
//! 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_WCE) || 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;
#ifndef MICROSTACK_NOTLS
module->SSLConnect = 0;
#endif
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 != ILibAsyncSocket_ScratchPad)
{
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_WCE) || 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
}
int ILibAsyncSocket_IsDomainSocket(ILibAsyncSocket_SocketModule socketModule)
{
return(((struct ILibAsyncSocketModule*)socketModule)->RemoteAddress.sin6_family == AF_UNIX ? 1 : 0);
}
/*! \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(WIN32)
getsockname(module->internalSocket, localAddress, (int*)&receivingAddressLength);
#else
if (getsockname(module->internalSocket, localAddress, (socklen_t*)&receivingAddressLength) < 0)
{
receivingAddressLength = sizeof(struct sockaddr_in);
if (getsockname(module->internalSocket, localAddress, (socklen_t*)&receivingAddressLength) < 0) { receivingAddressLength = 0; }
}
#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));
}
//! 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_WCE) || 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;
}
void ILibAsyncSocket_UpdateCallbacks(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect, ILibAsyncSocket_OnSendOK OnSendOK)
{
((ILibAsyncSocketModule*)module)->OnData = OnData;
((ILibAsyncSocketModule*)module)->OnConnect = OnConnect;
((ILibAsyncSocketModule*)module)->OnDisconnect = OnDisconnect;
((ILibAsyncSocketModule*)module)->OnSendOK = OnSendOK;
}
#ifndef MICROSTACK_NOTLS
int ILibAsyncSocket_TLS_WasHandshakeError(ILibAsyncSocket_SocketModule socketModule)
{
return(((struct ILibAsyncSocketModule*)socketModule)->TLS_HandshakeError_Occurred || ((struct ILibAsyncSocketModule*)socketModule)->TLSHandshakeCompleted == 0 ? 1 : 0);
}
//! Gets the Peer's TLS Certificate
/*!
\ingroup TLSGroup
\b NOTE: Must call X509_free() on the certificate when done with it!
\param socketModule ILibAsyncSocket_SocketModule to query
\return Peer's TLS Certificate. NULL if none was presented. Must call X509_free() when done with the certificate.
*/
X509 *ILibAsyncSocket_SslGetCert(ILibAsyncSocket_SocketModule socketModule)
{
X509 *ret;
SSL_TRACE1("ILibAsyncSocket_SslGetCert()");
struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule;
ret = SSL_get_peer_certificate(sm->ssl);
SSL_TRACE2("ILibAsyncSocket_SslGetCert()");
return(ret);
}
//! Get's the Cert Chain presented by the Peer
/*!
\ingroup TLSGroup
\b NOTE: X509 Cert's Reference Count is not incremented.
\param socketModule ILibAsyncSocket_SocketModule to query
\return Cert Chain presented by Peer. NULL if none presented
*/
STACK_OF(X509) *ILibAsyncSocket_SslGetCerts(ILibAsyncSocket_SocketModule socketModule)
{
STACK_OF(X509) *ret;
SSL_TRACE1("ILibAsyncSocket_SslGetCerts()");
struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule;
ret = SSL_get_peer_cert_chain(sm->ssl);
SSL_TRACE2("ILibAsyncSocket_SslGetCerts()");
return (ret);
}
#endif