lsock003.h
C++ header source for httpproxy, 784 lines. Raw file
/*
LKS Sockets interface v0.03
LSOCK 0.04
TODO: Concurrency
LSOCK 0.03
Added data buffering to lsockets to enable more advanced blocking functions:
LSock_ReadLine
Reads up to a new line character
LSock_ReadUpTo
Reads up to a specified character pattern
LSock_WriteLine
Sends a buffer and then a newline character
LSock_WriteString
Sends a null-terminated string (without sending the null character)
TODO: Error checking everywhere
TODO: Check if there can be any concurrency problems...
TODO: Make GetSocketLocal and GetSocketRemote actually work!!
TODO: Make a GetSocketStats function
TODO: Make sure bytes_send and bytes_recv are updated everywhere they need to be.
TODO: SetSocketUserData and GetSocketUserData
TODO: Make the string searching function work properly! (eg. find "ab" in "aabb" would fail at the moment)
Made LSock_Startup and LSock_Shutdown call WSAStartup and WSACleanup.
LSOCK 0.02
Basic lsocket functionality (blocking only)
*/
#ifndef __INCLUDE_LSOCK003
#define __INCLUDE_LSOCK003
#include <windows.h>
#include <winsock.h>
#include <stdlib.h>
#include <stdio.h>
#include "lmem003.h"
//---- DATA TYPES ------------------------------------------------------------//
typedef LMEM_HANDLE LSOCK;
#define LSOCK_TYPE_NONE 0x0000
#define LSOCK_TYPE_UNCONNECTED 0x0001
#define LSOCK_TYPE_LISTENING 0x0002
#define LSOCK_TYPE_SERVER 0x0003
#define LSOCK_TYPE_CLIENT 0x0004
#define LSOCK_TYPE_MASK 0x00FF
#define LSOCK_PROTO_TCPIP 0x0100
#define LSOCK_PROTO_UDPIP 0x0200
#define LSOCK_PROTO_MASK 0xFF00
#define LSOCK_ERROR 0xFFFFFFFF
//---- PRIVATE DATA TYPES ----------------------------------------------------//
//---- LSOCK_INFO - Socket Info structure
typedef struct {
SOCKET s; // WinSock socket aociated with this LSock
UINT type; // Combination of the TYPE and PROTO flags defined above
ULONG ip_local, ip_remote; // Local and remote IP addresses and ports
UINT port_local, port_remote;
ULONG bytes_send, bytes_recv; // Send and receive total bytes for this connection
ULONG buffer_size; // Current allocation size of read buffer
ULONG buffer_min, buffer_max; // Minimum and maximum allowed allocation sizes
ULONG buffer_delta; // Buffer resize ammount
ULONG buffer_len; // Current number of byte in buffer
LMEM_HANDLE buffer_handle; // LMem heap block handle of read buffer
LPVOID userdata;
CRITICAL_SECTION sync; // For synchronising accesses to this socket
} LSOCK_INFO, *LSOCK_PINFO;
// Default values
#define LSOCK_BUFFER_MAX 8192
#define LSOCK_BUFFER_MIN 64
#define LSOCK_BUFFER_DELTA 256
//---- PRIVATE DATA ----------------------------------------------------------//
//#define LSOCK_DEBUG
LMEM_HANDLE lsock_heap;
UINT lsock_startup_count = 0;
//---- DEBUG ROUTINES --------------------------------------------------------//
/*
Functions for recording debug messages and events
In a non-debug build these all equate to null routines
LSock_Debug_Startup
LSock_Debug_Shutdown
LSock_Debug_Write
LSock_LogWrite
*/
#ifdef LSOCK_DEBUG
HANDLE LSock_log_output;
void LSock_Debug_Startup() {
AllocConsole();
LSock_log_output = GetStdHandle(STD_OUTPUT_HANDLE);
}
void LSock_Debug_Shutdown() {
}
void LSock_Debug_Write(char *msg) {
int i;
unsigned long j;
for(i=0; msg[i]; i++);
WriteConsole(LSock_log_output, msg, i, &j, NULL);
}
void LSock_Debug_WriteInt(int val) {
char msg[12];
sprintf(msg, "%d", val);
LSock_Debug_Write(msg);
}
void LSock_Debug_WriteHex(char *buf, int len) {
char msg[12];
int i;
for(i=0; i<len; i++) {
sprintf(msg, "%02X ", (int)buf[i]);
LSock_Debug_Write(msg);
}
}
#define LSock_LogWrite(a) LSock_Debug_Write(a)
#else
#define LSock_Debug_Startup(a)
#define LSock_Debug_Shutdown(a)
#define LSock_Debug_Write(a)
#define LSock_Debug_WriteInt(a)
#define LSock_Debug_WriteHex(a, b)
#define LSock_LogWrite(a)
#endif
//---- INITIALISATION ROUTINES -----------------------------------------------//
/*
LSock_Startup
LSock_Shutdown
*/
//---- LSock_Startup - Initialises LSock library
// Returns TRUE on success, FALSE on error.
BOOL LSock_Startup() {
WSADATA info;
if(!lsock_startup_count) {
if(WSAStartup(0x0101, &info) == SOCKET_ERROR) {
return FALSE;
}
LMem_Startup(); // TODO: Make this return BOOL and check it here
LSock_Debug_Startup();
LSock_LogWrite("LSock_Startup - v0.003 Build B\n");
lsock_heap = LMem_Heap_Create(); // TODO: Error check this
}
lsock_startup_count ++;
return TRUE;
}
//---- LSock_Shutdown - Closes down the LSock library
// Returns TRUE if the library has been shut down, FALSE if this function
// needs to be called more times to shut down properly.
BOOL LSock_Shutdown() {
if(!lsock_startup_count) return TRUE;
lsock_startup_count--;
if(!lsock_startup_count) {
LSock_LogWrite("LSock_Shutdown\n");
LSock_Debug_Shutdown();
LMem_Shutdown();
WSACleanup();
return TRUE;
}
return FALSE;
}
//---- CONNECTION ROUTINES ---------------------------------------------------//
/*
Routines for creating and managing connections
LSock_Listen
LSock_Accept
LSock_Connect
LSock_CloseSocket
LSock_GetSocketLocal
LSock_GetSocketRemote
*/
//---- LSock_Listen - Listens on a port.
// Returns the listening lsock handle
LSOCK LSock_Listen(UINT port) {
LSOCK s;
LSOCK_PINFO p;
SOCKET sockServer;
SOCKADDR_IN saServ;
LSock_LogWrite("LSock_Listen - start\n");
//- Allocate lsock structure
s = LMem_Heap_Alloc(lsock_heap, sizeof(LSOCK_INFO));
p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, s);
//- Create a listening socket
sockServer = socket(AF_INET, SOCK_STREAM, 0);
if(sockServer == INVALID_SOCKET) {
LSock_LogWrite("LSock_Listen - error #1\n");
LMem_Heap_Free(lsock_heap, s);
return LSOCK_ERROR;
}
// Bind the socket to the internet address
saServ.sin_family = AF_INET;
saServ.sin_port = htons(port);
saServ.sin_addr.s_addr = INADDR_ANY;
if(bind(sockServer, (LPSOCKADDR)&saServ, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) {
LSock_LogWrite("LSock_Listen - error #2\n");
closesocket(sockServer);
LMem_Heap_Free(lsock_heap, s);
return LSOCK_ERROR;
}
// Listen for connections
if(listen(sockServer,0) == SOCKET_ERROR) {
LSock_LogWrite("LSock_Listen - error #3\n");
closesocket(sockServer);
LMem_Heap_Free(lsock_heap, s);
return LSOCK_ERROR;
}
// Store details
p->type = LSOCK_TYPE_LISTENING | LSOCK_PROTO_TCPIP;
p->s = sockServer;
p->buffer_handle = LMEM_ERROR;
LSock_LogWrite("LSock_Listen - end\n");
return s;
}
//---- LSock_Accept - Accepts a connection on a listening socket. Blocks if one not available.
LSOCK LSock_Accept(LSOCK lsServer) {
SOCKADDR_IN sa;
int i = sizeof(SOCKADDR_IN);
LSOCK ls;
LSOCK_PINFO p;
LSOCK_PINFO pServer;
LSock_LogWrite("LSock_Accept - start\n");
// Allocate lsock structure
ls = LMem_Heap_Alloc(lsock_heap, sizeof(LSOCK_INFO));
p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
pServer = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, lsServer);
// Accept connection
p->s = accept(pServer->s, (LPSOCKADDR)&sa, NULL);
if(p->s == INVALID_SOCKET) {
LMem_Heap_Free(lsock_heap, ls);
return INVALID_SOCKET;
}
// Create data buffer
p->buffer_min = LSOCK_BUFFER_MIN;
p->buffer_max = LSOCK_BUFFER_MAX;
p->buffer_delta = LSOCK_BUFFER_DELTA;
p->buffer_size = p->buffer_min;
p->buffer_handle = LMem_Heap_Alloc(lsock_heap, p->buffer_size);
p->buffer_len = 0;
// Store details
p->type = LSOCK_TYPE_SERVER | LSOCK_PROTO_TCPIP;
p->ip_remote = ntohl(sa.sin_addr.s_addr);
p->port_remote = ntohs(sa.sin_port);
getsockname(p->s, (LPSOCKADDR)&sa, &i);
p->ip_local = ntohl(sa.sin_addr.s_addr);
p->port_local = ntohs(sa.sin_port);
LSock_LogWrite("LSock_Accept - end\n");
return ls;
}
//---- LSock_Connect - Connects to a server
LSOCK LSock_Connect(char *server, ULONG ulServer, UINT port) {
LSOCK ls;
LSOCK_PINFO p;
SOCKET s;
SOCKADDR_IN sa;
LPHOSTENT lpHost;
ULONG saddr;
int i;
LSock_LogWrite("LSock_Connect - start\n");
//- Allocate lsock structure
ls = LMem_Heap_Alloc(lsock_heap, sizeof(LSOCK_INFO));
p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
//- Create a socket
s = socket(AF_INET, SOCK_STREAM, 0);
if(s == INVALID_SOCKET) {
return LSOCK_ERROR;
}
//- Find the server
if(server) {
if((lpHost = gethostbyname(server)) == NULL) {
closesocket(s);
return LSOCK_ERROR;
}
sa.sin_family = AF_INET;
sa.sin_addr = *((LPIN_ADDR)*lpHost->h_addr_list);
sa.sin_port = htons(port);
saddr = ntohl(sa.sin_addr.s_addr);
} else {
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(ulServer);
saddr = ulServer;
}
//- Connect to server
if(connect(s, (LPSOCKADDR)&sa, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) {
closesocket(s);
return LSOCK_ERROR;
}
//- Get local details
getsockname(s, (LPSOCKADDR)&sa, &i);
// Create data buffer
p->buffer_min = LSOCK_BUFFER_MIN;
p->buffer_max = LSOCK_BUFFER_MAX;
p->buffer_delta = LSOCK_BUFFER_DELTA;
p->buffer_size = p->buffer_min;
p->buffer_handle = LMem_Heap_Alloc(lsock_heap, p->buffer_size);
p->buffer_len = 0;
// Store details
p->s = s;
p->type = LSOCK_TYPE_CLIENT | LSOCK_PROTO_TCPIP;
p->ip_remote = saddr;
p->port_remote = port;
p->ip_local = ntohl(sa.sin_addr.s_addr);
p->port_local = ntohs(sa.sin_port);
LSock_LogWrite("LSock_Connect - end\n");
return ls;
}
//---- LSock_CloseSocket - Closes a socket
int LSock_CloseSocket(LSOCK ls) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
int i = closesocket(p->s);
LSock_LogWrite("LSock_CloseSocket - start\n");
if(p->buffer_handle != LMEM_ERROR) {
LMem_Heap_Free(lsock_heap, p->buffer_handle);
}
LMem_Heap_Free(lsock_heap, ls);
LSock_LogWrite("LSock_CloseSocket - end\n");
if(i) return LSOCK_ERROR;
return 0;
}
//---- LSock_GetSocketLocal
void LSock_GetSocketLocal(LSOCK ls, ULONG *ip, UINT *port) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
LSock_LogWrite("LSock_GetSocketLocal - called\n");
*port = p->port_local;
*ip = p->ip_local;
}
//---- LSock_GetSocketRemote
void LSock_GetSocketRemote(LSOCK ls, ULONG *ip, UINT *port) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
LSock_LogWrite("LSock_GetSocketRemote - called\n");
*port = p->port_remote;
*ip = p->ip_remote;
}
//---- SOCKET INFO MANIPULATION ROUTINES (Private) ---------------------------//
/*
LSock_Buffer_Find
LSock_Buffer_Get
LSock_Buffer_Remove
*/
//---- LSock_Buffer_Find - Find a character string in the socket's buffer
// Returns -1 if bytes not found. Starts at given start poition
int LSock_Buffer_Find(LSOCK ls, int start, char *find, int findlen) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
char *buffer = (char *)LMem_Heap_GetPtr(lsock_heap, p->buffer_handle);
int i, j;
// Throw out any error cases
if(!find) return -1;
if(findlen <= 0) return -1;
if(findlen > p->buffer_len) return -1;
if(start < 0) return -1;
// Find characters in buffer
for(i=start, j=0; i<p->buffer_len && j<findlen; i++) {
if(buffer[i] != find[j]) j=0;
if(buffer[i] == find[j]) j++;
}
// Return -1 if not found or the location if it is
if(j < findlen) return -1;
return i-j;
}
//---- LSock_Buffer_Get - Copies a block of bytes from a socket's buffer
// Returns the ammount actually copied
// If buf is NULL then it returns 0
int LSock_Buffer_Get(LSOCK ls, char *buf, int len) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
char *buffer = (char *)LMem_Heap_GetPtr(lsock_heap, p->buffer_handle);
int copylen = len;
int i;
// Throw out error cases
if(len <= 0) return 0;
if(!buf) return 0;
// Copy data up to len bytes
if(copylen > p->buffer_len) copylen = p->buffer_len;
for(i=0; i<copylen; i++) buf[i] = buffer[i];
return copylen;
}
//---- LSock_Buffer_Remove - Remove a block of bytes from a socket's buffer
// Returns the size of the remaining data in the buffer
int LSock_Buffer_Remove(LSOCK ls, int len) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
char *buffer = (char *)LMem_Heap_GetPtr(lsock_heap, p->buffer_handle);
int i;
if(len <= 0) return p->buffer_len;
if(p->buffer_len < len) {
p->buffer_len = 0;
} else {
for(i=len; i<p->buffer_len; i++) buffer[i-len] = buffer[i];
p->buffer_len -= len;
}
return p->buffer_len;
}
//---- DATA TRANSFER ROUTINES ------------------------------------------------//
/*
Reading and writing data
LSock_ReadData
LSock_WriteData
LSock_WriteString
LSock_ReadLine
LSock_WriteLine
*/
//---- LSock_ReadData - Reads data from a socket
// Blocks until it can read some data or socket is closed by another thread.
// TODO: Use LSock_Buffer_* functions in here instead of for loops :)
int LSock_ReadData(LSOCK ls, char *buf, int len) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
int i;
if(p->buffer_handle != LMEM_ERROR && p->buffer_len > 0) {
// Return data from socket buffer if there is any
char *buffer = (char *)LMem_Heap_GetPtr(lsock_heap, p->buffer_handle);
if(p->buffer_len > len) {
// If the buffer contains more than the user is requesting read that much
// and move the rest of the data along in the buffer
for(i=0; i<len; i++) buf[i] = buffer[i];
for(i=len; i<p->buffer_len; i++) buffer[i-len] = buffer[i];
p->buffer_len -= len;
i = len;
} else {
// Otherwise return all the data inthe buffer
for(i=0; i<p->buffer_len; i++) buf[i] = buffer[i];
p->buffer_len = 0;
}
} else {
// No data in socket buffer -> receive from network
i = recv(p->s, buf, len, 0);
}
p->bytes_recv += i;
if(i == 0) i = LSOCK_ERROR;
return i;
}
//---- LSock_WriteData - Sends data on a socket.
// Unlike send this will make sure all the data is sent. Will block until data sent.
int LSock_WriteData(LSOCK ls, char *buf, int len) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
int i, j=0;
while(j<len) {
i = send(p->s, buf+j, len-j, 0);
if(i == SOCKET_ERROR) return LSOCK_ERROR;
j += i;
p->bytes_send += i;
}
return j;
}
//---- LSock_WriteString - Sends a string (not including the null terminator)
int LSock_WriteString(LSOCK ls, char *buf) {
int i;
for(i=0; buf[i]; i++);
return LSock_WriteData(ls, buf, i);
}
//---- LSock_ReadLine - Reads data up to the next newline character
// Reads up to a new line character or len bytes
// Passing a NULL buf removes up to len chars of the next line
// Returns LSOCK_ERROR on error - either socket error or buffer filled and no newline
// Otherwise the number of characters in the line is returned - even if there were too many
// The discard flag says whether this function hould throw away any excess chars on the line
int LSock_ReadLine(LSOCK ls, char *buf, int len, BOOL discard) {
LSOCK_PINFO p = (LSOCK_PINFO)LMem_Heap_GetPtr(lsock_heap, ls);
char *buffer = (char *)LMem_Heap_GetPtr(lsock_heap, p->buffer_handle);
int linelen;
char nl[2] = {0x0D, 0x0A};
int i, j;
// Check if there's already a newline in the buffer
linelen = LSock_Buffer_Find(ls, 0, nl, 2);
// Otherwise read data untilwe get a newline
while(linelen == -1 && p->buffer_len < p->buffer_max) {
// Expand the buffer if there's less than delta free and the buffer isn't already at max size
if(p->buffer_size-p->buffer_len < p->buffer_delta && p->buffer_size < p->buffer_max) {
p->buffer_size += p->buffer_delta;
if(p->buffer_size > p->buffer_max) p->buffer_size = p->buffer_max;
buffer = (char *)LMem_Heap_ReAlloc(lsock_heap, p->buffer_handle, p->buffer_size);
}
// Read more data if buffer not full
if(p->buffer_size > p->buffer_len) {
i = recv(p->s, buffer+p->buffer_len, p->buffer_size-p->buffer_len, 0);
if(i <= 0) return LSOCK_ERROR;
p->buffer_len += i;
//;; LSock_Debug_WriteHex(buffer, p->buffer_len);
//;; LSock_Debug_Write("\n");
// Check if there's a newline in the new data
linelen = LSock_Buffer_Find(ls, 0 /*TODO: p->buffer_len-i*/, nl, 2);
//;; LSock_Debug_WriteInt(linelen);
//;; LSock_Debug_Write("\n");
} else {
return LSOCK_ERROR;
}
}
// Return the line if we've found one
if(linelen > -1) {
if(linelen > len) {
if(buf) LSock_Buffer_Get(ls, buf, len);
if(discard) {
LSock_Buffer_Remove(ls, linelen+2);
} else {
LSock_Buffer_Remove(ls, len);
}
return linelen;
} else {
if(buf) LSock_Buffer_Get(ls, buf, linelen);
LSock_Buffer_Remove(ls, linelen+2);
return linelen;
}
}
// Otherwise error
return LSOCK_ERROR;
}
//---- LSock_WriteLine - Writes data from a null terminated string
// If this function returns an error you don't know how much data was actually sent.
int LSock_WriteLine(LSOCK ls, char *buf) {
int i = LSock_WriteString(ls, buf);
int j;
char newline[2] = {0x0D, 0x0A};
if(i == LSOCK_ERROR) return LSOCK_ERROR;
j = LSock_WriteData(ls, newline, 2);
if(j == LSOCK_ERROR) return LSOCK_ERROR;
return i+j;
}
//---- END OF CODE -----------------------------------------------------------//
#endif