tOops! Network and error modules added. ;) (Plus, link to "dopewars -h" added t… | |
git clone git://src.adamsgaard.dk/vaccinewars | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 2d06fc42ffd5356336f0c442bffa716cdf1e5980 | |
parent 75487908600e5379fe2f8412948355e8df683006 | |
Author: Ben Webb <[email protected]> | |
Date: Mon, 1 Oct 2001 18:14:27 +0000 | |
Oops! Network and error modules added. ;) (Plus, link to "dopewars -h" added to… | |
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(-) | |
--- | |
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: [email protected] */ | |
+/* 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); | |
+} | |
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: [email protected] */ | |
+/* 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__ */ | |
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: [email protected] */ | |
+/* 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 */ | |
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: [email protected] */ | |
+/* 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__ */ | |
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 | |