home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of Windows 95.com 1996 September
/
WIN95_09962.iso
/
command
/
BLAT14.ZIP
/
GENSOCK
/
gensock.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-12
|
17KB
|
857 lines
// -*- C++ -*-
// generic socket DLL, winsock version
// disclaimer: a C programmer wrote this.
// $Id: gensock.cpp 1.15 1994/11/23 22:38:10 rushing Exp $
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
extern "C" {
#include <winsock.h>
#include "gensock.h"
}
#define SOCKET_BUFFER_SIZE 512
/* This is for NT */
#ifdef WIN32
HANDLE dll_module_handle;
#define GET_CURRENT_TASK dll_module_handle
#define TASK_HANDLE_TYPE HANDLE
#define GENSOCK_EXPORT
/* This is for WIN16 */
#else
HINSTANCE dll_module_handle;
#define GET_CURRENT_TASK GetCurrentTask()
#define TASK_HANDLE_TYPE HTASK
#define GENSOCK_EXPORT _export
#endif
int init_winsock (void);
void deinit_winsock (void);
//
//
//
#ifdef _DEBUG
void complain (char * message)
{
OutputDebugString (message);
}
#else
void complain (char * message)
{
MessageBox (NULL, message, "GENSOCK.DLL Error", MB_OK|MB_ICONHAND);
}
#endif
//
// ---------------------------------------------------------------------------
// container for a buffered SOCK_STREAM.
class connection
{
private:
SOCKET the_socket;
char * in_buffer;
char * out_buffer;
unsigned int in_index;
unsigned int out_index;
unsigned int in_buffer_total;
unsigned int out_buffer_total;
unsigned int last_winsock_error;
TASK_HANDLE_TYPE owner_task;
fd_set fds;
struct timeval timeout;
public:
connection (void);
~connection (void);
int get_connected (char * hostname, char * service);
SOCKET get_socket(void) { return (the_socket); }
TASK_HANDLE_TYPE get_owner_task(void) { return (owner_task); }
int get_buffer(int wait);
int close (void);
int getchar (int wait, char * ch);
int put_data (char * data, unsigned long length);
int put_data_buffered (char * data, unsigned long length);
int put_data_flush (void);
};
connection::connection (void)
{
the_socket = 0;
in_index = 0;
out_index = 0;
in_buffer_total = 0;
out_buffer_total = 0;
in_buffer = 0;
in_buffer = new char[SOCKET_BUFFER_SIZE];
out_buffer = new char[SOCKET_BUFFER_SIZE];
last_winsock_error = 0;
}
connection::~connection (void)
{
delete [] in_buffer;
}
int
gensock_is_a_number (char * string)
{
while (*string) {
if (!isdigit (*string)) {
return (0);
}
string++;
}
return (1);
}
//
// ---------------------------------------------------------------------------
//
int
connection::get_connected (char FAR * hostname, char FAR * service)
{
struct hostent FAR * hostentry;
struct servent FAR * serventry;
unsigned long ip_address;
struct sockaddr_in sa_in;
int our_port;
int not = 0;
int retval, err_code;
unsigned long ioctl_blocking = 1;
char message[512];
// if the ctor couldn't get a buffer
if (!in_buffer || !out_buffer)
return (ERR_CANT_MALLOC);
// --------------------------------------------------
// resolve the service name
//
// If they've specified a number, just use it.
if (gensock_is_a_number (service)) {
char * tail;
our_port = (int) strtol (service, &tail, 10);
if (tail == service) {
return (ERR_CANT_RESOLVE_SERVICE);
} else {
our_port = htons (our_port);
}
} else {
// we have a name, we must resolve it.
serventry = getservbyname (service, (LPSTR)"tcp");
if (serventry)
our_port = serventry->s_port;
else {
retval = WSAGetLastError();
// Chicago beta is throwing a WSANO_RECOVERY here...
if ((retval == WSANO_DATA) || (retval == WSANO_RECOVERY)) {
return (ERR_CANT_RESOLVE_SERVICE);
} else {
return (retval - 5000);
}
}
}
// --------------------------------------------------
// resolve the hostname/ipaddress
//
if ((ip_address = inet_addr (hostname)) != INADDR_NONE) {
sa_in.sin_addr.s_addr = ip_address;
}
else {
if ((hostentry = gethostbyname(hostname)) == NULL) {
return (ERR_CANT_RESOLVE_HOSTNAME);
}
sa_in.sin_addr.s_addr = *(long far *)hostentry->h_addr;
}
// --------------------------------------------------
// get a socket
//
if ((the_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
return (ERR_CANT_GET_SOCKET);
}
sa_in.sin_family = AF_INET;
sa_in.sin_port = our_port;
// set socket options. DONTLINGER will give us a more graceful disconnect
setsockopt(the_socket,
SOL_SOCKET,
SO_DONTLINGER,
(char *) ¬, sizeof(not));
// get a connection
if ((retval = connect (the_socket,
(struct sockaddr *)&sa_in,
sizeof(struct sockaddr_in))==SOCKET_ERROR)) {
switch ((err_code = WSAGetLastError())) {
/* twiddle our thumbs until the connect succeeds */
case WSAEWOULDBLOCK:
break;
case WSAECONNREFUSED:
return (ERR_CONNECTION_REFUSED);
break;
default:
wsprintf(message, "unexpected error %d from winsock\n", err_code);
complain(message);
return (ERR_CANT_CONNECT);
break;
}
}
owner_task = GET_CURRENT_TASK;
// Make this a non-blocking socket
ioctlsocket (the_socket, FIONBIO, &ioctl_blocking);
// make the FD_SET and timeout structures for later operations...
FD_ZERO (&fds);
FD_SET (the_socket, &fds);
// normal timeout, can be changed by the wait option.
timeout.tv_sec = 30;
timeout.tv_usec = 0;
return (0);
}
//
//---------------------------------------------------------------------------
//
// The 'wait' parameter, if set, says to return WAIT_A_BIT
// if there's no data waiting to be read.
int
connection::get_buffer(int wait)
{
int retval;
int bytes_read = 0;
unsigned long ready_to_read = 0;
// Use select to see if data is waiting...
FD_ZERO (&fds);
FD_SET (the_socket, &fds);
// if wait is set, we are polling, return immediately
if (wait) {
timeout.tv_sec = 0;
}
else {
timeout.tv_sec = 30;
}
if ((retval = select (0, &fds, NULL, NULL, &timeout))
== SOCKET_ERROR) {
char what_error[256];
int error_code = WSAGetLastError();
if (error_code == WSAEINPROGRESS && wait) {
return (WAIT_A_BIT);
}
wsprintf (what_error,
"connection::get_buffer() unexpected error from select: %d",
error_code);
complain (what_error);
}
// if we don't want to wait
if (!retval && wait) {
return (WAIT_A_BIT);
}
// we have data waiting...
bytes_read = recv (the_socket,
in_buffer,
SOCKET_BUFFER_SIZE,
0);
// just in case.
if (bytes_read == 0) {
// connection terminated (semi-) gracefully by the other side
return (ERR_NOT_CONNECTED);
}
if (bytes_read == SOCKET_ERROR) {
char what_error[256];
int ws_error = WSAGetLastError();
switch (ws_error) {
// all these indicate loss of connection (are there more?)
case WSAENOTCONN:
case WSAENETDOWN:
case WSAENETUNREACH:
case WSAENETRESET:
case WSAECONNABORTED:
case WSAECONNRESET:
return (ERR_NOT_CONNECTED);
break;
case WSAEWOULDBLOCK:
return (WAIT_A_BIT);
break;
default:
wsprintf (what_error,
"connection::get_buffer() unexpected error: %d",
ws_error);
complain (what_error);
}
}
// reset buffer indices.
in_buffer_total = bytes_read;
in_index = 0;
return (0);
}
//
//---------------------------------------------------------------------------
// get a character from this connection.
//
int
connection::getchar(int wait, char FAR * ch)
{
int retval;
if (in_index >= in_buffer_total) {
if ((retval = get_buffer(wait)))
return (retval);
}
*ch = in_buffer[in_index++];
return (0);
}
//
//---------------------------------------------------------------------------
// FIXME: should try to handle the fact that send can only take
// an int, not an unsigned long.
int
connection::put_data (char * data, unsigned long length)
{
int num_sent;
int retval;
FD_ZERO (&fds);
FD_SET (the_socket, &fds);
timeout.tv_sec = 30;
while (length > 0) {
if ((retval = select (0, NULL, &fds, NULL, &timeout)) == SOCKET_ERROR) {
char what_error[256];
int error_code = WSAGetLastError();
wsprintf (what_error,
"connection::put_data() unexpected error from select: %d",
error_code);
complain (what_error);
}
num_sent = send (the_socket,
data,
length > 1024 ? 1024 : (int)length,
0);
if (num_sent == SOCKET_ERROR) {
char what_error[256];
int ws_error = WSAGetLastError();
switch (ws_error) {
// this is the only error we really expect to see.
case WSAENOTCONN:
return (ERR_NOT_CONNECTED);
break;
// seems that we can still get a block
case WSAEWOULDBLOCK:
case WSAEINPROGRESS:
break;
default:
wsprintf (what_error,
"connection::put_data() unexpected error from send(): %d",
ws_error);
complain (what_error);
return (ERR_SENDING_DATA);
}
}
else {
length -= num_sent;
data += num_sent;
}
}
return (0);
}
//
//
// buffered output
//
int
connection::put_data_buffered (char * data, unsigned long length)
{
unsigned int sorta_sent = 0;
int retval;
while (length) {
if ((out_index + length) < SOCKET_BUFFER_SIZE) {
// we won't overflow, simply copy into the buffer
memcpy (out_buffer + out_index, data, (size_t) length);
out_index += (unsigned int) length;
length = 0;
}
else {
unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
// we will overflow, handle it
memcpy (out_buffer + out_index, data, orphaned_chunk);
// send this buffer...
if ((retval = put_data (out_buffer, SOCKET_BUFFER_SIZE))) {
return (retval);
}
length -= orphaned_chunk;
out_index = 0;
data += orphaned_chunk;
}
}
return (0);
}
int
connection::put_data_flush (void)
{
int retval;
if ((retval = put_data (out_buffer, out_index)))
return (retval);
else
out_index = 0;
return(0);
}
//
//---------------------------------------------------------------------------
//
int
connection::close (void)
{
if (closesocket(the_socket) == SOCKET_ERROR)
return (ERR_CLOSING);
else
return (0);
}
//
//---------------------------------------------------------------------------
// we keep lists of connections in this class
class connection_list
{
private:
connection * data;
connection_list * next;
public:
connection_list (void);
~connection_list (void);
void push (connection & conn);
// should really use pointer-to-memberfun for these
connection * find (SOCKET sock);
int how_many_are_mine (void);
void remove (socktag sock);
};
connection_list::connection_list (void)
{
next = 0;
}
connection_list::~connection_list(void)
{
delete data;
}
// add a new connection to the list
void
connection_list::push (connection & conn)
{
connection_list * new_conn;
new_conn = new connection_list();
new_conn->data = data;
new_conn->next = next;
data = &conn;
next = new_conn;
}
int
connection_list::how_many_are_mine(void)
{
TASK_HANDLE_TYPE current_task = GET_CURRENT_TASK;
connection_list * iter = this;
int num = 0;
while (iter->data) {
if (iter->data->get_owner_task() == current_task)
num++;
iter = iter->next;
}
return (num);
}
// find a particular socket's connection object.
connection *
connection_list::find (SOCKET sock)
{
connection_list * iter = this;
while (iter->data) {
if (iter->data->get_socket() == sock)
return (iter->data);
iter = iter->next;
}
return (0);
}
void
connection_list::remove (socktag sock)
{
// at the end
if (!data)
return;
// we can assume next is valid because
// the last node is always {0,0}
if (data == sock) {
delete data;
data = next->data;
next = next->next; // 8^)
return;
}
// recurse
next->remove(sock);
}
//
// ---------------------------------------------------------------------------
// global variables (shared by all DLL users)
connection_list global_socket_list;
int network_initialized;
//
//---------------------------------------------------------------------------
//
#ifndef WIN32
// the DLL entry routine
int FAR PASCAL LibMain (HINSTANCE hinstance,
WPARAM data_seg,
LPARAM heap_size,
LPSTR command_line)
{
network_initialized = 0;
dll_module_handle = hinstance;
return (1);
}
#else
extern "C" {
INT APIENTRY
LibMain (HANDLE hInst,
ULONG reason_called,
LPVOID reserved)
{
switch (reason_called) {
case DLL_PROCESS_ATTACH:
/* init */
dll_module_handle = hInst;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
default:
break;
}
return (1);
}
/*
* This wrapper is the actual entry point for the DLL. It ensures
* that the C RTL is correctly [de]initialized.
*/
BOOL WINAPI _CRT_INIT (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
BOOL WINAPI
dll_entry_point (HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpReserved)
{
/* Init the C run-time before calling any of your code */
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
if (!_CRT_INIT (hinstDLL, fdwReason, lpReserved))
return (FALSE);
else
LibMain (hinstDLL, fdwReason, lpReserved);
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_DETACH:
if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
return(FALSE);
break;
}
return (TRUE);
}
}
#endif
// ---------------------------------------------------------------------------
// C/DLL interface
//
int FAR PASCAL GENSOCK_EXPORT
gensock_connect (char FAR * hostname,
char FAR * service,
socktag FAR * pst)
{
int retval;
connection * conn = new connection;
if (!conn)
return (ERR_INITIALIZING);
// if this task hasn't opened any sockets yet, then
// call WSAStartup()
if (global_socket_list.how_many_are_mine() < 1)
init_winsock();
global_socket_list.push(*conn);
if ((retval = conn->get_connected (hostname, service))) {
gensock_close(conn);
*pst = 0;
return (retval);
}
*pst = (void FAR *) conn;
return (0);
}
//
//
//
int FAR PASCAL GENSOCK_EXPORT
gensock_getchar (socktag st, int wait, char FAR * ch)
{
connection * conn;
int retval = 0;
conn = (connection *) st;
if (!conn)
return (ERR_NOT_A_SOCKET);
if ((retval = conn->getchar(wait, ch)))
return (retval);
else
return (0);
}
//---------------------------------------------------------------------------
//
//
int FAR PASCAL GENSOCK_EXPORT
gensock_put_data (socktag st, char FAR * data, unsigned long length)
{
connection * conn;
int retval = 0;
conn = (connection *) st;
if (!conn)
return (ERR_NOT_A_SOCKET);
if ((retval = conn->put_data(data, length)))
return (retval);
return (0);
}
//---------------------------------------------------------------------------
//
//
int FAR PASCAL GENSOCK_EXPORT
gensock_put_data_buffered (socktag st, char FAR * data, unsigned long length)
{
connection * conn;
int retval = 0;
conn = (connection *) st;
if (!conn)
return (ERR_NOT_A_SOCKET);
if ((retval = conn->put_data_buffered (data, length)))
return (retval);
return (0);
}
//---------------------------------------------------------------------------
//
//
int FAR PASCAL GENSOCK_EXPORT
gensock_put_data_flush (socktag st)
{
connection * conn;
int retval = 0;
conn = (connection *) st;
if (!conn)
return (ERR_NOT_A_SOCKET);
if ((retval = conn->put_data_flush() ))
return (retval);
return (0);
}
//---------------------------------------------------------------------------
//
//
int FAR PASCAL GENSOCK_EXPORT
gensock_gethostname (char FAR * name, int namelen)
{
int retval;
if ((retval = gethostname(name, namelen))) {
return (retval - 5000);
}
else return (0);
}
//---------------------------------------------------------------------------
//
//
int FAR PASCAL GENSOCK_EXPORT
gensock_close (socktag st)
{
connection * conn;
int retval;
conn = (connection *) st;
if (!conn)
return (ERR_NOT_A_SOCKET);
if ((retval = conn->close()))
return (retval);
global_socket_list.remove((connection *)st);
if (global_socket_list.how_many_are_mine() < 1) {
deinit_winsock();
}
return (0);
}
//---------------------------------------------------------------------------
//
//
int
init_winsock(void)
{
int retval;
WSADATA winsock_data;
WORD version_required = 0x0101; /* Version 1.1 */
retval = WSAStartup (version_required, &winsock_data);
switch (retval) {
case 0:
/* successful */
break;
case WSASYSNOTREADY:
return (ERR_SYS_NOT_READY);
break;
case WSAEINVAL:
return (ERR_EINVAL);
break;
case WSAVERNOTSUPPORTED:
return (ERR_VER_NOT_SUPPORTED);
break;
}
network_initialized = 1;
return (0);
}
void
deinit_winsock(void)
{
network_initialized = 0;
WSACleanup();
}