tOops! Network and error modules added. ;) (Plus, link to "dopewars -h" added to Win32 installer) - vaccinewars - be a doctor and try to vaccinate the world
(HTM) git clone git://src.adamsgaard.dk/vaccinewars
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 2d06fc42ffd5356336f0c442bffa716cdf1e5980
(DIR) parent 75487908600e5379fe2f8412948355e8df683006
(HTM) Author: Ben Webb <ben@salilab.org>
Date: Mon, 1 Oct 2001 18:14:27 +0000
Oops! Network and error modules added. ;) (Plus, link to "dopewars -h" added to Win32 installer)
Diffstat:
A src/error.c | 162 ++++++++++++++++++++++++++++++
A src/error.h | 58 ++++++++++++++++++++++++++++++
A src/network.c | 657 +++++++++++++++++++++++++++++++
A src/network.h | 152 +++++++++++++++++++++++++++++++
M win32/filelist | 4 +++-
5 files changed, 1032 insertions(+), 1 deletion(-)
---
(DIR) diff --git a/src/error.c b/src/error.c
t@@ -0,0 +1,162 @@
+/* error.c Error-handling routines for dopewars */
+/* Copyright (C) 1998-2001 Ben Webb */
+/* Email: ben@bellatrix.pcl.ox.ac.uk */
+/* WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/ */
+
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h> /* For GString functions */
+#include <string.h> /* For strerror */
+
+#ifdef CYGWIN
+#include <windows.h> /* For FormatMessage() etc. */
+#include <winsock.h> /* For WSAxxx constants */
+#else
+#include <netdb.h> /* For h_errno error codes */
+#endif
+
+#include "error.h"
+#include "nls.h"
+
+typedef struct _ErrTable {
+ gint code;
+ gchar *string;
+} ErrTable;
+
+void ClearError(LastError *error) {
+ error->type=NULL;
+}
+
+gboolean IsError(LastError *error) {
+ return (error->type!=NULL);
+}
+
+void SetError(LastError *error,ErrorType *type,gint code) {
+ error->type=type;
+ error->code=code;
+}
+
+static void LookupErrorCode(GString *str,gint code,ErrTable *table,
+ gchar *fallbackstr) {
+ for (;table && table->string;table++) {
+ if (code==table->code) {
+ g_string_append(str,_(table->string));
+ return;
+ }
+ }
+ g_string_sprintfa(str,fallbackstr,code);
+}
+
+/* "Custom" error handling */
+static ErrTable CustomErrStr[] = {
+ { E_FULLBUF,N_("Connection dropped due to full buffer") },
+ { 0,NULL }
+};
+
+void CustomAppendError(GString *str,LastError *error) {
+ LookupErrorCode(str,error->code,CustomErrStr,_("Internal error code %d"));
+}
+
+static ErrorType ETCustom = { CustomAppendError };
+ErrorType *ET_CUSTOM = &ETCustom;
+
+/* "errno" error handling */
+void ErrnoAppendError(GString *str,LastError *error) {
+ g_string_append(str,strerror(error->code));
+}
+
+static ErrorType ETErrno = { ErrnoAppendError };
+ErrorType *ET_ERRNO = &ETErrno;
+
+#ifdef CYGWIN
+
+/* Winsock error handling */
+static ErrTable WSAErrStr[] = {
+/* These are the explanations of the various Windows Sockets error codes */
+ { WSANOTINITIALISED,N_("WinSock has not been properly initialised") },
+ { WSAENETDOWN,N_("The network subsystem has failed") },
+ { WSAEADDRINUSE,N_("Address already in use") },
+ { WSAENETDOWN,N_("Cannot reach the network") },
+ { WSAETIMEDOUT,N_("The connection timed out") },
+ { WSAEMFILE,N_("Out of file descriptors") },
+ { WSAENOBUFS,N_("Out of buffer space") },
+ { WSAEOPNOTSUPP,N_("Operation not supported") },
+ { WSAECONNABORTED,N_("Connection aborted due to failure") },
+ { WSAECONNRESET,N_("Connection reset by remote host") },
+ { WSAECONNREFUSED,N_("Connection refused") },
+ { WSAEAFNOSUPPORT,N_("Address family not supported") },
+ { WSAEPROTONOSUPPORT,N_("Protocol not supported") },
+ { WSAESOCKTNOSUPPORT,N_("Socket type not supported") },
+ { WSAHOST_NOT_FOUND,N_("Host not found") },
+ { WSATRY_AGAIN,N_("Temporary name server error - try again later") },
+ { WSANO_RECOVERY,N_("Failed to contact nameserver") },
+ { WSANO_DATA,N_("Valid name, but no DNS data record present") },
+ { 0,NULL }
+};
+
+void WinsockAppendError(GString *str,LastError *error) {
+ LookupErrorCode(str,error->code,WSAErrStr,_("Network error code %d"));
+}
+
+static ErrorType ETWinsock = { WinsockAppendError };
+ErrorType *ET_WINSOCK = &ETWinsock;
+
+/* Standard Win32 "GetLastError" handling */
+void Win32AppendError(GString *str,LastError *error) {
+ LPTSTR lpMsgBuf;
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,error->code,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
+ (LPTSTR)&lpMsgBuf,0,NULL);
+ g_string_append(str,lpMsgBuf);
+ LocalFree(lpMsgBuf);
+}
+
+static ErrorType ETWin32 = { Win32AppendError };
+ErrorType *ET_WIN32 = &ETWin32;
+
+#else
+
+/* h_errno error handling */
+static ErrTable DNSErrStr[] = {
+/* These are the explanations of the various name server error codes */
+ { HOST_NOT_FOUND,N_("Host not found") },
+ { TRY_AGAIN,N_("Temporary name server error - try again later") },
+ { 0,NULL }
+};
+
+void HErrnoAppendError(GString *str,LastError *error) {
+ LookupErrorCode(str,error->code,DNSErrStr,_("Name server error code %d"));
+}
+
+static ErrorType ETHErrno = { HErrnoAppendError };
+ErrorType *ET_HERRNO = ÐErrno;
+
+#endif /* CYGWIN */
+
+void g_string_assign_error(GString *str,LastError *error) {
+ g_string_truncate(str,0);
+ g_string_append_error(str,error);
+}
+
+void g_string_append_error(GString *str,LastError *error) {
+ if (!error->type) return;
+ (*error->type->AppendErrorString)(str,error);
+}
(DIR) diff --git a/src/error.h b/src/error.h
t@@ -0,0 +1,58 @@
+/* error.h Header file for dopewars error-handling routines */
+/* Copyright (C) 1998-2001 Ben Webb */
+/* Email: ben@bellatrix.pcl.ox.ac.uk */
+/* WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/ */
+
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+
+#ifndef __ERROR_H__
+#define __ERROR_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+struct _LastError;
+typedef struct _ErrorType {
+ void (*AppendErrorString)(GString *str,struct _LastError *error);
+} ErrorType;
+
+typedef struct _LastError {
+ gint code;
+ ErrorType *type;
+} LastError;
+
+extern ErrorType *ET_CUSTOM,*ET_ERRNO;
+#ifdef CYGWIN
+extern ErrorType *ET_WIN32,*ET_WINSOCK;
+#else
+extern ErrorType *ET_HERRNO;
+#endif
+
+typedef enum {
+ E_FULLBUF
+} CustomErrorCode;
+
+void ClearError(LastError *error);
+gboolean IsError(LastError *error);
+void SetError(LastError *error,ErrorType *type,gint code);
+void g_string_assign_error(GString *str,LastError *error);
+void g_string_append_error(GString *str,LastError *error);
+
+#endif /* __ERROR_H__ */
(DIR) diff --git a/src/network.c b/src/network.c
t@@ -0,0 +1,657 @@
+/* network.c Low-level networking routines */
+/* Copyright (C) 1998-2001 Ben Webb */
+/* Email: ben@bellatrix.pcl.ox.ac.uk */
+/* WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/ */
+
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef NETWORKING
+
+#ifdef CYGWIN
+#include <windows.h> /* For datatypes such as BOOL */
+#include <winsock.h> /* For network functions */
+#else
+#include <sys/types.h> /* For size_t etc. */
+#include <sys/socket.h> /* For struct sockaddr etc. */
+#include <netinet/in.h> /* For struct sockaddr_in etc. */
+#include <arpa/inet.h> /* For socklen_t */
+#include <string.h> /* For memcpy, strlen etc. */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* For close(), various types and constants */
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h> /* For fcntl() */
+#endif
+#include <netdb.h> /* For gethostbyname() */
+#endif /* CYGWIN */
+
+#include <glib.h>
+#include <errno.h> /* For errno and Unix error codes */
+#include <stdlib.h> /* For exit() and atoi() */
+#include <stdio.h> /* For perror() */
+
+#include "error.h"
+#include "network.h"
+#include "nls.h"
+
+/* Maximum sizes (in bytes) of read and write buffers - connections should
+ be dropped if either buffer is filled */
+#define MAXREADBUF (32768)
+#define MAXWRITEBUF (65536)
+
+#ifdef CYGWIN
+
+void StartNetworking() {
+ WSADATA wsaData;
+ if (WSAStartup(MAKEWORD(1,0),&wsaData)!=0) {
+ g_warning(_("Cannot initialise WinSock!"));
+ exit(1);
+ }
+}
+
+void StopNetworking() {
+ WSACleanup();
+}
+
+void SetReuse(SOCKET sock) {
+ BOOL i=TRUE;
+ if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&i,sizeof(i))==-1) {
+ perror("setsockopt"); exit(1);
+ }
+}
+
+void SetBlocking(SOCKET sock,gboolean blocking) {
+ unsigned long param;
+ param = blocking ? 0 : 1;
+ ioctlsocket(sock,FIONBIO,¶m);
+}
+
+#else
+
+void StartNetworking() {}
+void StopNetworking() {}
+
+void SetReuse(int sock) {
+ int i=1;
+ if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==-1) {
+ perror("setsockopt"); exit(1);
+ }
+}
+
+void SetBlocking(int sock,gboolean blocking) {
+ fcntl(sock,F_SETFL,blocking ? 0 : O_NONBLOCK);
+}
+
+#endif /* CYGWIN */
+
+static gboolean FinishConnect(int fd,LastError *error);
+
+static void NetBufCallBack(NetworkBuffer *NetBuf) {
+ if (NetBuf && NetBuf->CallBack) {
+ (*NetBuf->CallBack)(NetBuf,!NetBuf->WaitConnect,
+ NetBuf->WriteBuf.DataPresent || NetBuf->WaitConnect);
+ }
+}
+
+static void NetBufCallBackStop(NetworkBuffer *NetBuf) {
+ if (NetBuf && NetBuf->CallBack) (*NetBuf->CallBack)(NetBuf,FALSE,FALSE);
+}
+
+void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar) {
+/* Initialises the passed network buffer, ready for use. Messages sent */
+/* or received on the buffered connection will be terminated by the */
+/* given character, and if they end in "StripChar" it will be removed */
+/* before the messages are sent or received. */
+ NetBuf->fd=-1;
+ NetBuf->InputTag=0;
+ NetBuf->CallBack=NULL;
+ NetBuf->CallBackData=NULL;
+ NetBuf->Terminator=Terminator;
+ NetBuf->StripChar=StripChar;
+ NetBuf->ReadBuf.Data=NetBuf->WriteBuf.Data=NULL;
+ NetBuf->ReadBuf.Length=NetBuf->WriteBuf.Length=0;
+ NetBuf->ReadBuf.DataPresent=NetBuf->WriteBuf.DataPresent=0;
+ NetBuf->WaitConnect=FALSE;
+ ClearError(&NetBuf->error);
+}
+
+void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
+ gpointer CallBackData) {
+ NetBufCallBackStop(NetBuf);
+ NetBuf->CallBack=CallBack;
+ NetBuf->CallBackData=CallBackData;
+ NetBufCallBack(NetBuf);
+}
+
+void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd) {
+/* Sets up the given network buffer to handle data being sent/received */
+/* through the given socket */
+ NetBuf->fd=fd;
+}
+
+gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf) {
+/* Returns TRUE if the pointer is to a valid network buffer, and it's */
+/* connected to an active socket. */
+ return (NetBuf && NetBuf->fd>=0);
+}
+
+gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
+ unsigned RemotePort) {
+ ShutdownNetworkBuffer(NetBuf);
+ if (StartConnect(&NetBuf->fd,RemoteHost,RemotePort,TRUE,&NetBuf->error)) {
+ NetBuf->WaitConnect=TRUE;
+
+/* Notify the owner if necessary to check for the connection completing */
+ NetBufCallBack(NetBuf);
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+void ShutdownNetworkBuffer(NetworkBuffer *NetBuf) {
+/* Frees the network buffer's data structures (leaving it in the */
+/* 'initialised' state) and closes the accompanying socket. */
+
+ NetBufCallBackStop(NetBuf);
+
+ if (NetBuf->fd>=0) CloseSocket(NetBuf->fd);
+
+ g_free(NetBuf->ReadBuf.Data);
+ g_free(NetBuf->WriteBuf.Data);
+
+ InitNetworkBuffer(NetBuf,NetBuf->Terminator,NetBuf->StripChar);
+}
+
+void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf,fd_set *readfds,
+ fd_set *writefds,fd_set *errorfds,int *MaxSock) {
+/* Updates the sets of read and write file descriptors to monitor */
+/* input to/output from the given network buffer. MaxSock is updated */
+/* with the highest-numbered file descriptor (plus 1) for use in a */
+/* later select() call. */
+ if (!NetBuf || NetBuf->fd<=0) return;
+ FD_SET(NetBuf->fd,readfds);
+ if (errorfds) FD_SET(NetBuf->fd,errorfds);
+ if (NetBuf->fd >= *MaxSock) *MaxSock=NetBuf->fd+1;
+ if (NetBuf->WriteBuf.DataPresent || NetBuf->WaitConnect) {
+ FD_SET(NetBuf->fd,writefds);
+ }
+}
+
+static gboolean DoNetworkBufferStuff(NetworkBuffer *NetBuf,gboolean ReadReady,
+ gboolean WriteReady,gboolean ErrorReady,
+ gboolean *ReadOK,gboolean *WriteOK,
+ gboolean *ErrorOK) {
+/* Reads and writes data if the network connection is ready. Sets the */
+/* various OK variables to TRUE if no errors occurred in the relevant */
+/* operations, and returns TRUE if data was read and is waiting for */
+/* processing. */
+ gboolean DataWaiting=FALSE,ConnectDone=FALSE;
+ gboolean retval;
+ *ReadOK=*WriteOK=*ErrorOK=TRUE;
+
+ if (ErrorReady) *ErrorOK=FALSE;
+ else if (NetBuf->WaitConnect) {
+ if (WriteReady) {
+ retval=FinishConnect(NetBuf->fd,&NetBuf->error);
+ ConnectDone=TRUE;
+ NetBuf->WaitConnect=FALSE;
+
+ if (!retval) {
+ *WriteOK=FALSE;
+ }
+ }
+ } else {
+ if (WriteReady) *WriteOK=WriteDataToWire(NetBuf);
+
+ if (ReadReady) {
+ *ReadOK=ReadDataFromWire(NetBuf);
+ if (NetBuf->ReadBuf.DataPresent>0) DataWaiting=TRUE;
+ }
+ }
+
+ if (!(*ErrorOK && *WriteOK && *ReadOK)) {
+/* We don't want to check the socket any more */
+ NetBufCallBackStop(NetBuf);
+/* If there were errors, then the socket is now useless - so close it */
+ CloseSocket(NetBuf->fd);
+ NetBuf->fd=-1;
+ } else if (ConnectDone) {
+/* If we just connected, then no need to listen for write-ready status
+ any more */
+ NetBufCallBack(NetBuf);
+ } else if (WriteReady && NetBuf->WriteBuf.DataPresent==0) {
+/* If we wrote out everything, then tell the owner so that the socket no
+ longer needs to be checked for write-ready status */
+ NetBufCallBack(NetBuf);
+ }
+
+ return DataWaiting;
+}
+
+gboolean RespondToSelect(NetworkBuffer *NetBuf,fd_set *readfds,
+ fd_set *writefds,fd_set *errorfds,
+ gboolean *DoneOK) {
+/* Responds to a select() call by reading/writing data as necessary. */
+/* If any data were read, TRUE is returned. "DoneOK" is set TRUE */
+/* unless a fatal error (i.e. the connection was broken) occurred. */
+ gboolean ReadOK,WriteOK,ErrorOK;
+ gboolean DataWaiting=FALSE;
+
+ *DoneOK=TRUE;
+ if (!NetBuf || NetBuf->fd<=0) return DataWaiting;
+ DataWaiting=DoNetworkBufferStuff(NetBuf,FD_ISSET(NetBuf->fd,readfds),
+ FD_ISSET(NetBuf->fd,writefds),
+ errorfds ? FD_ISSET(NetBuf->fd,errorfds) : FALSE,
+ &ReadOK,&WriteOK,&ErrorOK);
+ *DoneOK=(WriteOK && ErrorOK && ReadOK);
+ return DataWaiting;
+}
+
+gboolean NetBufHandleNetwork(NetworkBuffer *NetBuf,gboolean ReadReady,
+ gboolean WriteReady,gboolean *DoneOK) {
+ gboolean ReadOK,WriteOK,ErrorOK;
+ gboolean DataWaiting=FALSE;
+
+ *DoneOK=TRUE;
+ if (!NetBuf || NetBuf->fd<=0) return DataWaiting;
+
+ DataWaiting=DoNetworkBufferStuff(NetBuf,ReadReady,WriteReady,FALSE,
+ &ReadOK,&WriteOK,&ErrorOK);
+
+ *DoneOK=(WriteOK && ErrorOK && ReadOK);
+ return DataWaiting;
+}
+
+gint CountWaitingMessages(NetworkBuffer *NetBuf) {
+/* Returns the number of complete (terminated) messages waiting in the */
+/* given network buffer. This is the number of times that */
+/* GetWaitingMessage() can be safely called without it returning NULL. */
+ ConnBuf *conn;
+ gint i,msgs=0;
+
+ conn=&NetBuf->ReadBuf;
+
+ if (conn->Data) for (i=0;i<conn->DataPresent;i++) {
+ if (conn->Data[i]==NetBuf->Terminator) msgs++;
+ }
+ return msgs;
+}
+
+gchar *GetWaitingMessage(NetworkBuffer *NetBuf) {
+/* Reads a complete (terminated) message from the network buffer. The */
+/* message is removed from the buffer, and returned as a null-terminated */
+/* string (the network terminator is removed). If no complete message is */
+/* waiting, NULL is returned. The string is dynamically allocated, and */
+/* so must be g_free'd by the caller. */
+ ConnBuf *conn;
+ int MessageLen;
+ char *SepPt;
+ gchar *NewMessage;
+ conn=&NetBuf->ReadBuf;
+ if (!conn->Data || !conn->DataPresent) return NULL;
+ SepPt=memchr(conn->Data,NetBuf->Terminator,conn->DataPresent);
+ if (!SepPt) return NULL;
+ *SepPt='\0';
+ MessageLen=SepPt-conn->Data+1;
+ SepPt--;
+ if (NetBuf->StripChar && *SepPt==NetBuf->StripChar) *SepPt='\0';
+ NewMessage=g_new(gchar,MessageLen);
+ memcpy(NewMessage,conn->Data,MessageLen);
+ if (MessageLen<conn->DataPresent) {
+ memmove(&conn->Data[0],&conn->Data[MessageLen],
+ conn->DataPresent-MessageLen);
+ }
+ conn->DataPresent-=MessageLen;
+ return NewMessage;
+}
+
+gboolean ReadDataFromWire(NetworkBuffer *NetBuf) {
+/* Reads any waiting data on the given network buffer's TCP/IP connection */
+/* into the read buffer. Returns FALSE if the connection was closed, or */
+/* if the read buffer's maximum size was reached. */
+ ConnBuf *conn;
+ int CurrentPosition,BytesRead;
+ conn=&NetBuf->ReadBuf;
+ CurrentPosition=conn->DataPresent;
+ while(1) {
+ if (CurrentPosition>=conn->Length) {
+ if (conn->Length==MAXREADBUF) {
+ SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF);
+ return FALSE; /* drop connection */
+ }
+ if (conn->Length==0) conn->Length=256; else conn->Length*=2;
+ if (conn->Length>MAXREADBUF) conn->Length=MAXREADBUF;
+ conn->Data=g_realloc(conn->Data,conn->Length);
+ }
+ BytesRead=recv(NetBuf->fd,&conn->Data[CurrentPosition],
+ conn->Length-CurrentPosition,0);
+ if (BytesRead==SOCKET_ERROR) {
+#ifdef CYGWIN
+ int Error = WSAGetLastError();
+ if (Error==WSAEWOULDBLOCK) break;
+ else { SetError(&NetBuf->error,ET_WINSOCK,Error); return FALSE; }
+#else
+ if (errno==EAGAIN) break;
+ else if (errno!=EINTR) {
+ SetError(&NetBuf->error,ET_ERRNO,errno);
+ return FALSE;
+ }
+#endif
+ } else if (BytesRead==0) {
+ return FALSE;
+ } else {
+ CurrentPosition+=BytesRead;
+ conn->DataPresent=CurrentPosition;
+ }
+ }
+ return TRUE;
+}
+
+void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data) {
+/* Writes the null-terminated string "data" to the network buffer, ready */
+/* to be sent to the wire when the network connection becomes free. The */
+/* message is automatically terminated. Fails to write the message without */
+/* error if the buffer reaches its maximum size (although this error will */
+/* be detected when an attempt is made to write the buffer to the wire). */
+ int AddLength,NewLength;
+ ConnBuf *conn;
+ conn=&NetBuf->WriteBuf;
+ AddLength=strlen(data)+1;
+ NewLength=conn->DataPresent+AddLength;
+ if (NewLength > conn->Length) {
+ conn->Length*=2;
+ conn->Length=MAX(conn->Length,NewLength);
+ if (conn->Length > MAXWRITEBUF) conn->Length=MAXWRITEBUF;
+ if (NewLength > conn->Length) return;
+ conn->Data=g_realloc(conn->Data,conn->Length);
+ }
+ memcpy(&conn->Data[conn->DataPresent],data,AddLength);
+ conn->DataPresent=NewLength;
+ conn->Data[NewLength-1]=NetBuf->Terminator;
+
+/* If the buffer was empty before, we may need to tell the owner to check
+ the socket for write-ready status */
+ if (NewLength==AddLength) NetBufCallBack(NetBuf);
+}
+
+gboolean WriteDataToWire(NetworkBuffer *NetBuf) {
+/* Writes any waiting data in the network buffer to the wire. Returns */
+/* TRUE on success, or FALSE if the buffer's maximum length is */
+/* reached, or the remote end has closed the connection. */
+ ConnBuf *conn;
+ int CurrentPosition,BytesSent;
+ conn=&NetBuf->WriteBuf;
+ if (!conn->Data || !conn->DataPresent) return TRUE;
+ if (conn->Length==MAXWRITEBUF) {
+ SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF);
+ return FALSE;
+ }
+ CurrentPosition=0;
+ while (CurrentPosition<conn->DataPresent) {
+ BytesSent=send(NetBuf->fd,&conn->Data[CurrentPosition],
+ conn->DataPresent-CurrentPosition,0);
+ if (BytesSent==SOCKET_ERROR) {
+#ifdef CYGWIN
+ int Error=WSAGetLastError();
+ if (Error==WSAEWOULDBLOCK) break;
+ else { SetError(&NetBuf->error,ET_WINSOCK,Error); return FALSE; }
+#else
+ if (errno==EAGAIN) break;
+ else if (errno!=EINTR) {
+ SetError(&NetBuf->error,ET_ERRNO,errno);
+ return FALSE;
+ }
+#endif
+ } else {
+ CurrentPosition+=BytesSent;
+ }
+ }
+ if (CurrentPosition>0 && CurrentPosition<conn->DataPresent) {
+ memmove(&conn->Data[0],&conn->Data[CurrentPosition],
+ conn->DataPresent-CurrentPosition);
+ }
+ conn->DataPresent-=CurrentPosition;
+ return TRUE;
+}
+
+static void SendHttpRequest(HttpConnection *conn) {
+ GString *text;
+
+ conn->Tries++;
+ conn->StatusCode=0;
+ conn->Status=HS_CONNECTING;
+
+ text=g_string_new("");
+
+ if (conn->Redirect) {
+ g_string_sprintf(text,"%s %s HTTP/1.0",conn->Method,conn->Redirect);
+ g_free(conn->Redirect); conn->Redirect=NULL;
+ } else {
+ g_string_sprintf(text,"%s http://%s:%u%s HTTP/1.0",
+ conn->Method,conn->HostName,conn->Port,conn->Query);
+ }
+ QueueMessageForSend(&conn->NetBuf,text->str);
+
+ if (conn->Headers) QueueMessageForSend(&conn->NetBuf,conn->Headers);
+
+ g_string_sprintf(text,"User-Agent: dopewars/%s",VERSION);
+ QueueMessageForSend(&conn->NetBuf,text->str);
+
+/* Insert a blank line between headers and body */
+ QueueMessageForSend(&conn->NetBuf,"");
+
+ if (conn->Body) QueueMessageForSend(&conn->NetBuf,conn->Body);
+
+ g_string_free(text,TRUE);
+}
+
+static gboolean StartHttpConnect(HttpConnection *conn) {
+ gchar *ConnectHost;
+ unsigned ConnectPort;
+
+ if (conn->Proxy) {
+ ConnectHost=conn->Proxy; ConnectPort=conn->ProxyPort;
+ } else {
+ ConnectHost=conn->HostName; ConnectPort=conn->Port;
+ }
+
+ if (!StartNetworkBufferConnect(&conn->NetBuf,ConnectHost,ConnectPort)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean OpenHttpConnection(HttpConnection **connpt,gchar *HostName,
+ unsigned Port,gchar *Proxy,unsigned ProxyPort,
+ gchar *Method,gchar *Query,gchar *Headers,
+ gchar *Body) {
+ HttpConnection *conn;
+ g_assert(HostName && Method && Query && connpt);
+
+ conn=g_new0(HttpConnection,1);
+ InitNetworkBuffer(&conn->NetBuf,'\n','\r');
+ conn->HostName=g_strdup(HostName);
+ if (Proxy && Proxy[0]) conn->Proxy=g_strdup(Proxy);
+ conn->Method=g_strdup(Method);
+ conn->Query=g_strdup(Query);
+ if (Headers && Headers[0]) conn->Headers=g_strdup(Headers);
+ if (Body && Body[0]) conn->Body=g_strdup(Body);
+ conn->Port = Port;
+ conn->ProxyPort = ProxyPort;
+ *connpt = conn;
+
+ if (StartHttpConnect(conn)) {
+ SendHttpRequest(conn);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+void CloseHttpConnection(HttpConnection *conn) {
+ ShutdownNetworkBuffer(&conn->NetBuf);
+ g_free(conn->HostName);
+ g_free(conn->Proxy);
+ g_free(conn->Method);
+ g_free(conn->Query);
+ g_free(conn->Headers);
+ g_free(conn->Body);
+ g_free(conn->Redirect);
+ g_free(conn);
+}
+
+gboolean IsHttpError(HttpConnection *conn) {
+ return IsError(&conn->NetBuf.error);
+}
+
+gchar *ReadHttpResponse(HttpConnection *conn) {
+ gchar *msg,**split;
+
+ msg=GetWaitingMessage(&conn->NetBuf);
+ if (msg) switch(conn->Status) {
+ case HS_CONNECTING: /* OK, we should have the HTTP status line */
+ conn->Status=HS_READHEADERS;
+ split=g_strsplit(msg," ",2);
+ if (split[0] && split[1]) {
+ conn->StatusCode=atoi(split[1]);
+ g_print("HTTP status code %d returned\n",conn->StatusCode);
+ } else g_warning("Invalid HTTP status line %s",msg);
+ g_strfreev(split);
+ break;
+ case HS_READHEADERS:
+ if (msg[0]==0) conn->Status=HS_READSEPARATOR;
+ else {
+ split=g_strsplit(msg," ",1);
+ if (split[0] && split[1]) {
+ if (conn->StatusCode==302 && strcmp(split[0],"Location:")==0) {
+ g_print("Redirect to %s\n",split[1]);
+ g_free(conn->Redirect);
+ conn->Redirect = g_strdup(split[1]);
+ }
+/* g_print("Header %s (value %s) read\n",split[0],split[1]);*/
+ }
+ g_strfreev(split);
+ }
+ break;
+ case HS_READSEPARATOR:
+ conn->Status=HS_READBODY;
+ break;
+ case HS_READBODY: /* At present, we do nothing special with the body */
+ break;
+ }
+ return msg;
+}
+
+gboolean HandleHttpCompletion(HttpConnection *conn) {
+ NBCallBack CallBack;
+ gpointer CallBackData;
+ if (conn->Redirect) {
+ g_print("Following redirect\n");
+ CallBack=conn->NetBuf.CallBack;
+ CallBackData=conn->NetBuf.CallBackData;
+ ShutdownNetworkBuffer(&conn->NetBuf);
+ if (StartHttpConnect(conn)) {
+ SendHttpRequest(conn);
+ SetNetworkBufferCallBack(&conn->NetBuf,CallBack,CallBackData);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
+ gboolean NonBlocking,LastError *error) {
+ struct sockaddr_in ClientAddr;
+ struct hostent *he;
+
+ if ((he=gethostbyname(RemoteHost))==NULL) {
+#ifdef CYGWIN
+ if (error) SetError(error,ET_WINSOCK,WSAGetLastError());
+#else
+ if (error) SetError(error,ET_HERRNO,h_errno);
+#endif
+ return FALSE;
+ }
+ *fd=socket(AF_INET,SOCK_STREAM,0);
+ if (*fd==SOCKET_ERROR) {
+#ifdef CYGWIN
+ if (error) SetError(error,ET_WINSOCK,WSAGetLastError());
+#else
+ if (error) SetError(error,ET_ERRNO,errno);
+#endif
+ return FALSE;
+ }
+
+ ClientAddr.sin_family=AF_INET;
+ ClientAddr.sin_port=htons(RemotePort);
+ ClientAddr.sin_addr=*((struct in_addr *)he->h_addr);
+ memset(ClientAddr.sin_zero,0,sizeof(ClientAddr.sin_zero));
+
+ SetBlocking(*fd,!NonBlocking);
+
+ if (connect(*fd,(struct sockaddr *)&ClientAddr,
+ sizeof(struct sockaddr))==SOCKET_ERROR) {
+#ifdef CYGWIN
+ int errcode=WSAGetLastError();
+ if (errcode==WSAEWOULDBLOCK) return TRUE;
+ else if (error) SetError(error,ET_WINSOCK,errcode);
+#else
+ if (errno==EINPROGRESS) return TRUE;
+ else if (error) SetError(error,ET_ERRNO,errno);
+#endif
+ CloseSocket(*fd); *fd=-1;
+ return FALSE;
+ } else {
+ SetBlocking(*fd,FALSE); /* All connected sockets should be nonblocking */
+ }
+ return TRUE;
+}
+
+gboolean FinishConnect(int fd,LastError *error) {
+ int errcode;
+#ifdef CYGWIN
+ errcode = WSAGetLastError();
+ if (errcode==0) return TRUE;
+ else {
+ if (error) { SetError(error,ET_WINSOCK,errcode); }
+ return FALSE;
+ }
+#else
+#ifdef HAVE_SOCKLEN_T
+ socklen_t optlen;
+#else
+ int optlen;
+#endif
+
+ optlen=sizeof(errcode);
+ if (getsockopt(fd,SOL_SOCKET,SO_ERROR,&errcode,&optlen)==-1) {
+ errcode = errno;
+ }
+ if (errcode==0) return TRUE;
+ else {
+ if (error) { SetError(error,ET_ERRNO,errcode); }
+ return FALSE;
+ }
+#endif /* CYGWIN */
+}
+
+#endif /* NETWORKING */
(DIR) diff --git a/src/network.h b/src/network.h
t@@ -0,0 +1,152 @@
+/* network.h Header file for low-level networking routines */
+/* Copyright (C) 1998-2001 Ben Webb */
+/* Email: ben@bellatrix.pcl.ox.ac.uk */
+/* WWW: http://bellatrix.pcl.ox.ac.uk/~ben/dopewars/ */
+
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+
+#ifndef __NETWORK_H__
+#define __NETWORK_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Various includes necessary for select() calls */
+#include <sys/types.h>
+/* Be careful not to include both sys/time.h and time.h on those systems */
+/* which don't like it */
+#if TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+
+#include "error.h"
+
+#ifdef NETWORKING
+
+#ifndef SOCKET_ERROR
+#define SOCKET_ERROR -1
+#endif
+
+typedef struct _ConnBuf {
+ gchar *Data; /* bytes waiting to be read/written */
+ gint Length; /* allocated length of the "Data" buffer */
+ gint DataPresent; /* number of bytes currently in "Data" */
+} ConnBuf;
+
+typedef struct _NetworkBuffer NetworkBuffer;
+
+typedef void (*NBCallBack)(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
+
+/* Handles reading and writing messages from/to a network connection */
+struct _NetworkBuffer {
+ int fd; /* File descriptor of the socket */
+ gint InputTag; /* Identifier for gdk_input routines */
+ NBCallBack CallBack; /* Function called when the socket read- or
+ write-able status changes */
+ gpointer CallBackData; /* Data accessible to the callback function */
+ char Terminator; /* Character that separates messages */
+ char StripChar; /* Character that should be removed from messages */
+ ConnBuf ReadBuf; /* New data, waiting for the application */
+ ConnBuf WriteBuf; /* Data waiting to be written to the wire */
+ gboolean WaitConnect; /* TRUE if a non-blocking connect is in progress */
+ LastError error; /* Any error from the last operation */
+};
+
+/* Keeps track of the progress of an HTTP connection */
+typedef enum {
+ HS_CONNECTING, HS_READHEADERS, HS_READSEPARATOR, HS_READBODY
+} HttpStatus;
+
+/* A structure used to keep track of an HTTP connection */
+typedef struct _HttpConnection {
+ gchar *HostName; /* The machine on which the desired page resides */
+ unsigned Port; /* The port */
+ gchar *Proxy; /* If non-NULL, a web proxy to use */
+ unsigned ProxyPort; /* The port to use for talking to the proxy */
+ gchar *Method; /* e.g. GET, POST */
+ gchar *Query; /* e.g. the path of the desired webpage */
+ gchar *Headers; /* if non-NULL, e.g. Content-Type */
+ gchar *Body; /* if non-NULL, data to send */
+ gchar *Redirect; /* if non-NULL, a URL to redirect to */
+ NetworkBuffer NetBuf; /* The actual network connection itself */
+ gint Tries; /* Number of requests actually sent so far */
+ gint StatusCode; /* 0=no status yet, otherwise an HTTP status code */
+ HttpStatus Status;
+} HttpConnection;
+
+void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar);
+void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
+ gpointer CallBackData);
+gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf);
+void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd);
+gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
+ unsigned RemotePort);
+void ShutdownNetworkBuffer(NetworkBuffer *NetBuf);
+void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf,fd_set *readfds,
+ fd_set *writefds,fd_set *errorfds,int *MaxSock);
+gboolean RespondToSelect(NetworkBuffer *NetBuf,fd_set *readfds,
+ fd_set *writefds,fd_set *errorfds,
+ gboolean *DoneOK);
+gboolean NetBufHandleNetwork(NetworkBuffer *NetBuf,gboolean ReadReady,
+ gboolean WriteReady,gboolean *DoneOK);
+gboolean ReadDataFromWire(NetworkBuffer *NetBuf);
+gboolean WriteDataToWire(NetworkBuffer *NetBuf);
+void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data);
+gint CountWaitingMessages(NetworkBuffer *NetBuf);
+gchar *GetWaitingMessage(NetworkBuffer *NetBuf);
+
+gboolean OpenHttpConnection(HttpConnection **conn,gchar *HostName,
+ unsigned Port,gchar *Proxy,unsigned ProxyPort,
+ gchar *Method,gchar *Query,
+ gchar *Headers,gchar *Body);
+void CloseHttpConnection(HttpConnection *conn);
+gboolean IsHttpError(HttpConnection *conn);
+gchar *ReadHttpResponse(HttpConnection *conn);
+gboolean HandleHttpCompletion(HttpConnection *conn);
+
+gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
+ gboolean NonBlocking,LastError *error);
+void StartNetworking(void);
+void StopNetworking(void);
+
+#ifdef CYGWIN
+#define CloseSocket(sock) closesocket(sock)
+void SetReuse(SOCKET sock);
+void SetBlocking(SOCKET sock,gboolean blocking);
+#else
+#define CloseSocket(sock) close(sock)
+void SetReuse(int sock);
+void SetBlocking(int sock,gboolean blocking);
+#endif
+
+#endif /* NETWORKING */
+
+#endif /* __NETWORK_H__ */
(DIR) diff --git a/win32/filelist b/win32/filelist
t@@ -40,7 +40,9 @@ dopewars.exe
dopewars server.lnk
dopewars.exe
-s
-
+dopewars command line options.lnk
+dopewars.exe
+-h
dopewars help.lnk
index.html