|
|
#include "std.h"
// This is the communications mask used by our serial port. More may be
// necessary, but for right now, this seems to work.
#define TCPIP_NAME L"TCP/IP"
// This GUID is used to identify objects opened by this library. It is
// placed in the m_Secret member of the SOCKET structure. Any external
// interface accepting a SOCKET object as a parameter should check this
// out before using the structure.
// {29566A75-BCDE-4bba-BC6A-EA652C0651D9}
static const GUID uuidTCPIPObjectGuid = { 0x29566a75, 0xbcde, 0x4bba, { 0xbc, 0x6a, 0xea, 0x65, 0x2c, 0x6, 0x51, 0xd9 } };
// Structure defining an open serial port object. All external users of this
// library will only have a void pointer to one of these, and the structure is
// not published anywhere. This abstration makes it more difficult for the
// user to mess things up.
typedef struct __TCPIP { GUID m_Secret; // Identifies this as a tcpip socket
SOCKET m_Socket; // SOCKET handle
HANDLE m_hAbort; // Event signalled when port is closing
HANDLE m_hReadMutex; // Only one thread allowed to read a port
HANDLE m_hWriteMutex; // Only one thread allowed to read a port
HANDLE m_hCloseMutex; // Only one thread allowed to close a port
HANDLE m_hReadComplete; // Event to signal read completion
HANDLE m_hWriteComplete; // Event to signal write completion
} TCPIP, *PTCPIP;
extern PVOID APIENTRY lhcOpen( PCWSTR pcszPortSpec);
extern BOOL APIENTRY lhcRead( PVOID pObject, PVOID pBuffer, DWORD dwSize, PDWORD pdwBytesRead);
extern BOOL APIENTRY lhcWrite( PVOID pObject, PVOID pBuffer, DWORD dwSize);
extern BOOL APIENTRY lhcClose( PVOID pObject);
extern DWORD APIENTRY lhcGetLibraryName( PWSTR pszBuffer, DWORD dwSize);
BOOL lhcpAcquireWithAbort( HANDLE hMutex, HANDLE hAbort);
BOOL lhcpAcquireReadWithAbort( PTCPIP pObject);
BOOL lhcpAcquireWriteWithAbort( PTCPIP pObject);
BOOL lhcpAcquireCloseWithAbort( PTCPIP pObject);
BOOL lhcpAcquireReadAndWrite( PTCPIP pObject);
BOOL lhcpReleaseRead( PTCPIP pObject);
BOOL lhcpReleaseWrite( PTCPIP pObject);
BOOL lhcpReleaseClose( PTCPIP pObject);
BOOL lhcpIsValidObject( PTCPIP pObject);
PTCPIP lhcpCreateNewObject();
void lhcpDeleteObject( PTCPIP pObject);
BOOL lhcpParseParameters( PCWSTR pcszPortSpec, PWSTR* pszHostName, PWSTR* pszInetAddress, SOCKADDR_IN** Address);
void lhcpParseParametersFree( PWSTR* pszHostName, PWSTR* pszInetAddress, SOCKADDR_IN** Address);
BOOL lhcpSetCommState( HANDLE hPort, DWORD dwBaudRate);
BOOL lhcpReadTCPIP( PTCPIP pObject, PVOID pBuffer, DWORD dwSize, PDWORD pdwBytesRead);
BOOL lhcpWriteTCPIP( PTCPIP pObject, PVOID pBuffer, DWORD dwSize);
BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, // handle to the DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved) // reserved
{ WSADATA WsaData; int dResult;
switch (fdwReason) { case DLL_PROCESS_ATTACH: dResult = WSAStartup( MAKEWORD(2,0), &WsaData); if (dResult!=ERROR_SUCCESS) { SetLastError( dResult); return FALSE; } break; case DLL_PROCESS_DETACH: dResult = WSACleanup(); if (dResult!=ERROR_SUCCESS) { SetLastError( dResult); return FALSE; } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: default: break; }
return TRUE; }
BOOL lhcpAcquireWithAbort(HANDLE hMutex, HANDLE hAbort) { HANDLE hWaiters[2]; DWORD dwWaitResult;
hWaiters[0] = hAbort; hWaiters[1] = hMutex;
// We should honour the m_hAbort event, since this is signalled when the
// port is closed by another thread
dwWaitResult = WaitForMultipleObjects( 2, hWaiters, FALSE, INFINITE);
if (WAIT_OBJECT_0==dwWaitResult) { goto Error; } else if ((WAIT_OBJECT_0+1)!=dwWaitResult) { // This should never, ever happen - so I will put a debug breapoint
// in here (checked only).
#ifdef DBG
DebugBreak(); #endif
goto Error; }
return TRUE; // We have acquired the write mutex
Error: return FALSE; // We have aborted
}
BOOL lhcpAcquireReadWithAbort(PTCPIP pObject) { return lhcpAcquireWithAbort( pObject->m_hReadMutex, pObject->m_hAbort); }
BOOL lhcpAcquireWriteWithAbort(PTCPIP pObject) { return lhcpAcquireWithAbort( pObject->m_hWriteMutex, pObject->m_hAbort); }
BOOL lhcpAcquireCloseWithAbort(PTCPIP pObject) { return lhcpAcquireWithAbort( pObject->m_hCloseMutex, pObject->m_hAbort); }
BOOL lhcpAcquireReadAndWrite(PTCPIP pObject) { HANDLE hWaiters[2]; DWORD dwWaitResult;
hWaiters[0] = pObject->m_hReadMutex; hWaiters[1] = pObject->m_hWriteMutex;
dwWaitResult = WaitForMultipleObjects( 2, hWaiters, TRUE, 1000); // Timeout after 1 second
if (WAIT_OBJECT_0!=dwWaitResult) { goto Error; }
return TRUE; // We have acquired the write mutex
Error: return FALSE; // We have aborted
}
BOOL lhcpReleaseRead(PTCPIP pObject) { return ReleaseMutex( pObject->m_hReadMutex); }
BOOL lhcpReleaseWrite(PTCPIP pObject) { return ReleaseMutex( pObject->m_hWriteMutex); }
BOOL lhcpReleaseClose(PTCPIP pObject) { return ReleaseMutex( pObject->m_hCloseMutex); }
BOOL lhcpIsValidObject(PTCPIP pObject) { BOOL bResult;
__try { bResult = IsEqualGUID( &uuidTCPIPObjectGuid, &pObject->m_Secret); } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError( ERROR_INVALID_HANDLE); bResult = FALSE; goto Done; }
Done: return bResult; }
PTCPIP lhcpCreateNewObject() { PTCPIP pObject = (PTCPIP)malloc( sizeof(TCPIP)); if (pObject) { pObject->m_Secret = uuidTCPIPObjectGuid; pObject->m_Socket = INVALID_SOCKET; pObject->m_hAbort = NULL; pObject->m_hReadMutex = NULL; // Only one thread allowed to read a port
pObject->m_hWriteMutex = NULL; // Only one thread allowed to read a port
pObject->m_hCloseMutex = NULL; // Only one thread allowed to read a port
pObject->m_hReadComplete = NULL; // Event to signal read completion
pObject->m_hWriteComplete = NULL; // Event to signal write completion
} else { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } return pObject; }
void lhcpDeleteObject(PTCPIP pObject) { if (pObject==NULL) { return; } ZeroMemory( &(pObject->m_Secret), sizeof(pObject->m_Secret)); if (pObject->m_Socket!=INVALID_SOCKET) { closesocket( pObject->m_Socket); } if (pObject->m_hAbort!=NULL) { CloseHandle( pObject->m_hAbort); } if (pObject->m_hReadMutex!=NULL) { CloseHandle( pObject->m_hReadMutex); } if (pObject->m_hWriteMutex!=NULL) { CloseHandle( pObject->m_hWriteMutex); } if (pObject->m_hCloseMutex!=NULL) { CloseHandle( pObject->m_hCloseMutex); } if (pObject->m_hReadComplete!=NULL) { CloseHandle( pObject->m_hReadComplete); } if (pObject->m_hWriteComplete!=NULL) { CloseHandle( pObject->m_hWriteComplete); } FillMemory( pObject, sizeof(TCPIP), 0x00);
free( pObject); }
BOOL lhcpParseParameters( PCWSTR pcszPortSpec, PWSTR* pszHostName, PWSTR* pszInetAddress, SOCKADDR_IN** Address) { DWORD dwPort; DWORD dwAddress; PSTR pszAddress = NULL; PSTR pszPort = NULL; struct hostent* pHost = NULL; int dStringLength = 0; PWSTR pszCount = (PWSTR)pcszPortSpec;
*pszHostName = NULL; *pszInetAddress = NULL; *Address = NULL;
// First off, we need to do a quick check for a valid looking target. If
// we are definitely looking at something invalid, why make the user wait?
while (*pszCount!='\0') { if (!(iswalpha(*pszCount) || iswdigit(*pszCount) || (*pszCount==L'_') || (*pszCount==L'.') || (*pszCount==L':') || (*pszCount==L'-'))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY); goto Error; } pszCount++; }
dStringLength = WideCharToMultiByte( CP_ACP, 0, pcszPortSpec, -1, NULL, 0, NULL, NULL);
if (0==dStringLength) { goto Error; }
pszAddress = (PSTR)malloc( dStringLength);
if (NULL==pszAddress) { SetLastError( ERROR_NOT_ENOUGH_MEMORY); goto Error; }
dStringLength = WideCharToMultiByte( CP_ACP, 0, pcszPortSpec, -1, pszAddress, dStringLength, NULL, NULL);
if (0==dStringLength) { goto Error; }
// Let's see if there is a port specified
pszPort = strchr( pszAddress, ':');
if (NULL==pszPort) { // No port was specified, so what we have here is an attempt to
// connect to the default telnet port (23). I will point the port
// pointer at a null character.
pszPort = pszAddress + strlen(pszAddress); dwPort = 23; } else { *pszPort++ = '\0'; dwPort = 0; }
while ((*pszPort)!='\0') { if ('0'<=(*pszPort) && (*pszPort)<='9') { dwPort *= 10; dwPort += ((*pszPort) - '0'); if (dwPort>0xffff) // Check for maximum port number
{ dwPort=0; // The port number is not valid
break; } pszPort++; // Look at the next character
} else { dwPort = 0; // The port number is not valid
break; } }
if (dwPort==0) { SetLastError( ERROR_INVALID_PARAMETER); goto Error; }
// We have decoded the port, now we need to get the hostentry for
// the target server.
// Firstly check whether this is a dotted internet address.
dwAddress = (DWORD)inet_addr( pszAddress);
dwAddress = (dwAddress==0) ? INADDR_NONE : dwAddress;
if (dwAddress==INADDR_NONE) { // This is not a dotted address, or is invalid.
// Check for a machine name
pHost = gethostbyname( pszAddress);
if (pHost==NULL) { // This is not a valid address, so we need to return an error
SetLastError(WSAGetLastError()); goto Error; } else { dwAddress = *((DWORD*)(pHost->h_addr)); } } else { pHost = NULL; }
// This takes too long. If the user has used a dotted address, then
// that is all that he will see.
/*
else { // Attempt to get the host name (for prettyness)
pHost = gethostbyaddr( (char*)&dwAddress, sizeof(IN_ADDR), AF_INET); } */
*Address = malloc( sizeof(SOCKADDR_IN));
if (NULL==*Address) { SetLastError( ERROR_NOT_ENOUGH_MEMORY); goto Error; }
ZeroMemory( *Address, sizeof(SOCKADDR_IN));
if (pHost==NULL) { // This address does not resolve to a name, so we must just go
// the IP number passed to us.
*pszHostName = NULL; } else { // We have a hostent entry to populate this with
dStringLength = MultiByteToWideChar( CP_ACP, 0, pHost->h_name, -1, NULL, 0);
if (dStringLength==0) { goto Error; }
*pszHostName = malloc( (dStringLength + 7) * sizeof(WCHAR));
if (NULL==*pszHostName) { SetLastError( ERROR_NOT_ENOUGH_MEMORY); goto Error; }
dStringLength = MultiByteToWideChar( CP_ACP, 0, pHost->h_name, -1, *pszHostName, dStringLength);
if (dStringLength==0) { goto Error; }
if (dwPort==23) { wcscat( *pszHostName, L":telnet"); } else { PWSTR pszConnectionPort = *pszHostName + wcslen(*pszHostName);
swprintf( pszConnectionPort, L":%u", dwPort & 0xFFFF); }
}
(**Address).sin_family = AF_INET; (**Address).sin_port = htons((USHORT)dwPort); (**Address).sin_addr.S_un.S_addr = (ULONG)dwAddress;
*pszInetAddress = malloc( 22 * sizeof(WCHAR));
if (*pszInetAddress==NULL) { goto Error; }
swprintf( *pszInetAddress, L"%u.%u.%u.%u:%u", (DWORD)((**Address).sin_addr.S_un.S_un_b.s_b1), (DWORD)((**Address).sin_addr.S_un.S_un_b.s_b2), (DWORD)((**Address).sin_addr.S_un.S_un_b.s_b3), (DWORD)((**Address).sin_addr.S_un.S_un_b.s_b4), (DWORD)ntohs((**Address).sin_port));
free(pszAddress);
return TRUE;
Error: lhcpParseParametersFree( pszHostName, pszInetAddress, Address);
if (pszAddress!=NULL) { free(pszAddress); }
return FALSE; }
void lhcpParseParametersFree( PWSTR* pszHostName, PWSTR* pszInetAddress, SOCKADDR_IN** Address) { if (*pszHostName!=NULL) { free(*pszHostName); *pszHostName = NULL; } if (*pszInetAddress!=NULL) { free(*pszInetAddress); *pszInetAddress = NULL; } if (*Address!=NULL) { free(*Address); *Address = NULL; } }
BOOL lhcpReadTCPIP( PTCPIP pObject, PVOID pBuffer, DWORD dwSize, PDWORD pdwBytesRead) { int dBytesRead;
dBytesRead = recv( pObject->m_Socket, (char*)pBuffer, (int)dwSize, 0);
if (dBytesRead==SOCKET_ERROR) { SetLastError(WSAGetLastError()); return FALSE; } else if (dBytesRead==0) // graceful closure has occurred
{ SetLastError( ERROR_INVALID_HANDLE); return FALSE; } else { *pdwBytesRead = (DWORD)dBytesRead; return TRUE; } }
BOOL lhcpWriteTCPIP( PTCPIP pObject, PVOID pBuffer, DWORD dwSize) { int dBytesSent;
dBytesSent = send( pObject->m_Socket, (char FAR*)pBuffer, (int)dwSize, 0);
if (dBytesSent==SOCKET_ERROR) { SetLastError(WSAGetLastError()); wprintf(L"SEND error: %u\n", GetLastError()); return FALSE; } else { return TRUE; } }
extern PVOID APIENTRY lhcOpen(PCWSTR pcszPortSpec) { BOOL bResult; int dResult; PWSTR pszHostName; PWSTR pszInetAddr; SOCKADDR_IN* SockAddr; SOCKADDR_IN saLocal; PTCPIP pObject = NULL; int On = 1;
bResult = lhcpParseParameters( pcszPortSpec, &pszHostName, &pszInetAddr, &SockAddr);
if (!bResult) { goto Error; }
// Allocate space and initialize the serial port object
pObject = lhcpCreateNewObject();
if (NULL==pObject) { goto Error; }
// Open the serial port
pObject->m_Socket = socket( SockAddr->sin_family, SOCK_STREAM, 0);
if (INVALID_SOCKET==pObject->m_Socket) { goto Error; }
ZeroMemory( &saLocal, sizeof(saLocal));
saLocal.sin_family = AF_INET; saLocal.sin_port = 0; saLocal.sin_addr.S_un.S_addr = INADDR_ANY;
dResult = bind( pObject->m_Socket, (SOCKADDR*)&saLocal, sizeof(SOCKADDR_IN));
if (dResult==SOCKET_ERROR) { SetLastError( WSAGetLastError()); wprintf(L"BIND error: %u\n", GetLastError()); Sleep(1000); goto Error; }
dResult = setsockopt( pObject->m_Socket, IPPROTO_TCP, TCP_NODELAY, (char *)&On, sizeof(On));
if (dResult==SOCKET_ERROR) { SetLastError( WSAGetLastError()); wprintf(L"SETSOCKOPT error: %u\n", GetLastError()); Sleep(1000); goto Error; }
dResult = connect( pObject->m_Socket, (SOCKADDR*)SockAddr, sizeof(SOCKADDR_IN));
if (dResult==SOCKET_ERROR) { SetLastError( WSAGetLastError()); goto Error; }
// This event will be set when we want to close the port
pObject->m_hAbort = CreateEvent( NULL, TRUE, FALSE, NULL);
if (NULL==pObject->m_hAbort) { goto Error; }
// This event will be used for overlapped reading from the port
pObject->m_hReadComplete = CreateEvent( NULL, TRUE, FALSE, NULL);
if (NULL==pObject->m_hReadComplete) { goto Error; }
// This event will be used for overlapped writing to the port
pObject->m_hWriteComplete = CreateEvent( NULL, TRUE, FALSE, NULL);
if (NULL==pObject->m_hWriteComplete) { goto Error; }
// This mutex will ensure that only one thread can read at a time
pObject->m_hReadMutex = CreateMutex( NULL, FALSE, NULL);
if (NULL==pObject->m_hReadMutex) { goto Error; }
// This mutex will ensure that only one thread can write at a time
pObject->m_hWriteMutex = CreateMutex( NULL, FALSE, NULL);
if (NULL==pObject->m_hWriteMutex) { goto Error; }
// This mutex will ensure that only one thread can close the port
pObject->m_hCloseMutex = CreateMutex( NULL, FALSE, NULL);
if (NULL==pObject->m_hCloseMutex) { goto Error; }
// Free up the temporary memory used to parse the parameters
lhcpParseParametersFree( &pszHostName, &pszInetAddr, &SockAddr);
// Return a pointer to the new object
return pObject;
Error: lhcpParseParametersFree( &pszHostName, &pszInetAddr, &SockAddr); lhcpDeleteObject( pObject);
return NULL; }
extern BOOL APIENTRY lhcRead( PVOID pObject, PVOID pBuffer, DWORD dwSize, PDWORD pdwBytesRead) { OVERLAPPED Overlapped; DWORD dwEventMask; BOOL bResult;
// Firstly, we need to check whether the pointer that got passed in
// points to a valid TCPIP object
if (!lhcpIsValidObject(pObject)) { goto NoMutex; }
bResult = lhcpAcquireReadWithAbort( (PTCPIP)pObject);
if (!bResult) { SetLastError( ERROR_INVALID_HANDLE); goto NoMutex; }
// We should now have a valid serial port event, so let's read the port.
bResult = lhcpReadTCPIP( (PTCPIP)pObject, pBuffer, dwSize, pdwBytesRead);
if (!bResult) { goto Error; }
lhcpReleaseRead( (PTCPIP)pObject); return TRUE;
Error: lhcpReleaseRead( (PTCPIP)pObject); NoMutex: return FALSE; }
extern BOOL APIENTRY lhcWrite( PVOID pObject, PVOID pBuffer, DWORD dwSize) { OVERLAPPED Overlapped; BOOL bResult;
// Firstly, we need to check whether the pointer that got passed in
// points to a valid TCPIP object
if (!lhcpIsValidObject(pObject)) { goto NoMutex; }
// Block until it is your turn
bResult = lhcpAcquireWriteWithAbort( pObject);
if (!bResult) { SetLastError( ERROR_INVALID_HANDLE); goto NoMutex; }
// Wait for something to happen to the serial port
bResult = lhcpWriteTCPIP( (PTCPIP)pObject, pBuffer, dwSize);
if (!bResult) { goto Error; }
lhcpReleaseWrite( (PTCPIP)pObject); return TRUE;
Error: lhcpReleaseWrite( (PTCPIP)pObject); NoMutex: return FALSE; }
extern BOOL APIENTRY lhcClose(PVOID pObject) { BOOL bResult; int dSockResult;
// Firstly, we need to check whether the pointer that got passed in
// points to a valid TCPIP object
if (!lhcpIsValidObject(pObject)) { goto NoMutex; }
// We need to ensure that we are the only thread closing this object
bResult = lhcpAcquireCloseWithAbort( pObject);
if (!bResult) { SetLastError( ERROR_INVALID_HANDLE); goto NoMutex; }
// Signal everyone to quit doing what they're doing. Any new threads
// calling lhcRead and lhcWrite will be immediately sent packing, since
// the m_hAbort event is waited on along with the relevant mutex.
bResult = SetEvent( ((PTCPIP)pObject)->m_hAbort);
// This abort flag will not cause blocking socket reads and writes to quit
// immediately. The only way to make this happen is to close the socket
// gracefully. So here we go...
dSockResult = closesocket( ((PTCPIP)pObject)->m_Socket);
if (dSockResult==SOCKET_ERROR) { SetLastError(WSAGetLastError()); goto Error; } else { // This will cause all subsequent attempts to use the socket to fail
((PTCPIP)pObject)->m_Socket = INVALID_SOCKET; }
// Now acquire the read and write mutexes so that no-one else will try to
// access this object to read or write. Abort does not apply, since we
// have already signalled it. We know that we are closing, and we need
// the read and write mutexes.
bResult = lhcpAcquireReadAndWrite( (PTCPIP)pObject);
if (!bResult) { SetLastError( ERROR_INVALID_HANDLE); goto Error; }
// Closes all of the open handles, erases the secret and frees up the
// memory associated with the object. We can close the mutex objects,
// even though we are the owners, since we can guarantee that no-one
// else is waiting on them. The m_hAbort event being signalled will
// ensure this.
lhcpDeleteObject( (PTCPIP)pObject);
return TRUE;
Error: lhcpReleaseClose( (PTCPIP)pObject); lhcpDeleteObject( (PTCPIP)pObject); NoMutex: return FALSE; }
extern DWORD APIENTRY lhcGetLibraryName( PWSTR pszBuffer, DWORD dwSize) { DWORD dwNameSize = wcslen(TCPIP_NAME)+1;
// If zero is passed in as the buffer length, we will return the
// required buffer size in characters, as calulated above. If the
// incoming buffer size is not zero, and smaller than the required
// buffer size, we return 0 (failure) with a valid error code. Notice
// that in the case where the incoming size is zero, we don't touch
// the buffer pointer at all.
if (dwSize!=0 && dwSize < dwNameSize) { SetLastError( ERROR_INSUFFICIENT_BUFFER); dwNameSize = 0; } else { wcscpy( pszBuffer, TCPIP_NAME); }
return dwNameSize; }
extern void APIENTRY lhcUsage() { wprintf( L"TCP/IP connection string:\n\n" L" <host>[:<port>]\n\n" L"where <host> is the host name or IP address to connect to, and <port>\n" L"optionally specifies the TCP/IP port to use (Default=23). For example\n" L"172.31.224.64:6002 would connect to a TCP/IP server with an IP address\n" L"of 172.31.224.64 on port 6002.\n"); }
|