/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | *
* -----------------------------------------------------------| \\._.// *
* SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) *
* -----------------------------------------------------------| ).:.( *
* SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} *
* -----------------------------------------------------------| / ' ' \ *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~*
* Scryn, Swordbearer, Rennard, Tricops, and Gorog. | *
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe. *
****************************************************************************/
// SmaugSocket.cpp : implementation file
#include "stdafx.h"
#include "SmaugWiz.h"
#include "smaug.h"
#include "SysData.h"
#include "objects.h"
#include "rooms.h"
#include "help.h"
#include "SmaugWizDoc.h"
#include "SmaugSocket.h"
#include "character.h"
#include "descriptor.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
BEGIN_MESSAGE_MAP(CSmaugSocketWnd, CWnd)
//{{AFX_MSG_MAP(CSmaugSocketWnd)
//}}AFX_MSG_MAP
ON_MESSAGE((WM_USER+502), OnHostNotify)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSmaugSocket
class CSmaugWizDoc;
CString GetLastErrorName ();
CSmaugSocket::CSmaugSocket (CSmaugWizDoc* const pDoc)
{
m_pDoc = pDoc;
m_pDes = NULL;
CreateSocketWindow ();
}
CSmaugSocket::~CSmaugSocket()
{
ASSERT (m_pSocketWnd);
ASSERT_VALID (m_pSocketWnd);
m_pSocketWnd->DestroyWindow ();
delete m_pSocketWnd;
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CSmaugSocket, CAsyncSocket)
//{{AFX_MSG_MAP(CSmaugSocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CSmaugSocket member functions
void CSmaugSocket::OnAccept (int nErrorCode)
{
CAsyncSocket::OnAccept (nErrorCode);
SocketAccept (nErrorCode);
}
void CSmaugSocket::OnReceive(int nErrorCode)
{
SocketRead (nErrorCode);
CAsyncSocket::OnReceive(nErrorCode);
}
void CSmaugSocket::OnSend(int nErrorCode)
{
SocketWrite (nErrorCode);
CAsyncSocket::OnSend(nErrorCode);
}
HANDLE CSmaugSocket::GetHostByAddr (const char* addr, int len, char* buf,
int buflen)
{
return WSAAsyncGetHostByAddr (m_pSocketWnd->m_hWnd,
WM_USER+502, addr, len, PF_INET, buf, buflen);
}
void CSmaugSocket::SocketAccept (int err)
{
CString Msg;
CSmaugSocket *pNewSock = NULL;
// Get a pending accept
pNewSock = new CSmaugSocket (gpDoc);
SOCKADDR_IN sAddr;
int len = sizeof (SOCKADDR_IN);
if (! Accept (*pNewSock, (SOCKADDR*) &sAddr, &len)) {
m_pDoc->LogString ("Couldn't accept connection", LOG_COMM);
delete pNewSock;
return;
}
// Construct a new descriptor.
CDescriptor *dnew;
if (! DFreeList.IsEmpty ()) {
dnew = (CDescriptor*) DFreeList.RemoveTail ();
dnew->Empty ();
}
else dnew = new CDescriptor;
pNewSock->m_pDes = dnew; // make it known to the new socket
dnew->m_pSocket = pNewSock;
dnew->m_Connected = CON_GET_NAME;
dnew->m_Outsize = 2000;
dnew->m_Idle = 0;
dnew->lines = 0;
dnew->scrlen = 24;
dnew->port = ntohs (sAddr.sin_port);
dnew->user = STRALLOC ("unknown");
dnew->m_pHost = NULL;
dnew->auth_fd = -1;
dnew->auth_inc = 0;
dnew->auth_state = 0;
dnew->newstate = 0;
dnew->m_PrevColor = 7;
dnew->m_pOutbuf = new char [dnew->m_Outsize];
dnew->m_HostAndName = new host_and_name_lookup;
strcpy (dnew->m_HostAndName->username, "<waiting>");
if (SysData.bNameResolving) {
// Request the name of the caller's machine from DNS
dnew->m_HostAndName->sin_addr = sAddr.sin_addr;
dnew->m_HostAndName->hRequestHandle =
pNewSock->GetHostByAddr ((char*) &dnew->m_HostAndName->sin_addr,
sizeof (dnew->m_HostAndName->sin_addr),
dnew->m_HostAndName->hostdata,
sizeof (dnew->m_HostAndName->hostdata));
}
// Note: Smaug had site ban stuff in here somewhere
#ifdef XXXX
CString PAddr;
UINT PPort;
if (! GetPeerName (PAddr, PPort)) {
Msg.Format ("SocketAccept:GetPeerName failure %d", GetLastError ());
m_pDoc->LogString (Msg);
dnew->m_pHost = str_dup ("");
} else {
Msg.Format ("Sock.sinaddr: %s", (const char*) PAddr);
m_pDoc->LogString (Msg);
// Request the name of the caller's machine from DNS
dnew->m_pHost = str_dup (Msg);
dnew->m_HostAndName->sin_addr = sock.sin_addr;
dnew->m_HostAndName->hRequestHandle =
WSAAsyncGetHostByAddr (hQryDlgBox, WM_NET_GETHOST,
(LPSTR) &dnew->m_HostAndName->sin_addr,
sizeof dnew->m_HostAndName->sin_addr, PF_INET,
dnew->m_HostAndName->hostdata,
sizeof dnew->m_HostAndName->hostdata);
// Look up the local name for this socket
if (getsockname (sSocket, (struct sockaddr*)
&dnew->m_HostAndName->us, &ulen) == SOCKET_ERROR) {
wsprintf (dnew->m_HostAndName->username,
" (getsockname)#%d", WSAGetLastError ());
goto failure;
}
// Create a socket to connect to the user's ident port
if ((dnew->m_HostAndName->sAuth =
socket (PF_INET, SOCK_STREAM, AF_UNSPEC)) == INVALID_SOCKET) {
wsprintf (dnew->m_HostAndName->username,
" (socket)#%d", WSAGetLastError ());
goto failure;
}
// Save the address of their side of the socket
dnew->m_HostAndName->them = sock;
// htons converts a u_short from host to network byte order.
// 113 is the auth socket
dnew->m_HostAndName->authsock.sin_port = htons (113);
// Use Internet protocol
dnew->m_HostAndName->authsock.sin_family = AF_INET;
// Look up the other end of our socket
dnew->m_HostAndName->authsock.sin_addr = sock.sin_addr;
WSAAsyncSelect (dnew->m_HostAndName->sAuth, hQryDlgBox,
WM_NET_AUTHCONNECT, FD_CONNECT|FD_READ|FD_WRITE);
if (connect (dnew->m_HostAndName->sAuth, (struct sockaddr*)
&dnew->m_HostAndName->authsock,
sizeof (dnew->m_HostAndName->authsock)) == SOCKET_ERROR) {
int nError = WSAGetLastError ();
if (nError != WSAEWOULDBLOCK) {
wsprintf (dnew->m_HostAndName->username,
" (connect)#%d", nError);
goto failure;
}
}
failure:
}
#endif
// Add descriptor to the list.
DList.AddHead (dnew);
// Update system counters
SysData.UpdateCounters (DList.GetCount ());
// OK now get messages from the new socket
pNewSock->SelectReadWrite ();
// Send the greeting.
CHelpData *pHelp = HelpList.Find ("GREETING", -2, 2);
if (pHelp)
dnew->WriteToBuffer (pHelp->GetText ());
// Here is where smaug has authorization stuff.
// Also number of players stuff.
// start_auth (dnew); /* Start username authorization */
}
void CSmaugSocket::SocketRead (int err)
{
ASSERT (m_pDes);
CDescriptor &Ds = *m_pDes;
Ds.m_bFcommand = FALSE;
if (Ds.m_pCharacter) then
Ds.m_pCharacter->SetTimer (0);
if (! Ds.ReadFromDescriptor ()) {
if (Ds.m_pCharacter) then
save_char_obj (Ds.m_pCharacter);
Ds.m_Outtop = 0;
RemoveCharacter (Ds);
}
}
void CSmaugSocket::SocketWrite (int err)
{
ASSERT (m_pDes);
CDescriptor &Ds = *m_pDes;
if (Ds.m_bFcommand || Ds.m_Outtop > 0) {
int iStart, nWrite;
for (iStart = 0; iStart < Ds.m_Outtop; iStart += nWrite) {
int nBlock = UMIN (Ds.m_Outtop - iStart, 4096);
if ((nWrite =
Send (Ds.m_pOutbuf + iStart, nBlock)) == SOCKET_ERROR) {
if (GetLastError () != WSAEWOULDBLOCK) {
m_pDoc->LogStringf (LOG_COMM, LEVEL_LOG,
"WriteToDescriptor:%s", NCCP GetLastErrorName ());
if (Ds.m_pCharacter != NULL) then
save_char_obj (Ds.m_pCharacter);
RemoveCharacter (Ds);
}
break;
}
SysData.AddSentBytes (nWrite);
}
// It might be better at this point to resend if we
// got EWOULDBLOCK, but we throw all data away...
Ds.m_Outtop = 0;
}
}
void CSmaugSocket::OnConnect(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
CAsyncSocket::OnConnect(nErrorCode);
}
LRESULT CSmaugSocket::GetHost (WPARAM wParam, LPARAM lParam)
{
ASSERT (m_pDes);
CString Msg;
if (! m_pDes) {
bug ("Get Host info for null descriptor");
return FALSE;
}
WORD wError = WSAGETASYNCERROR (lParam);
if (wError) {
char *p = " (Unknown Error)";
if (wError == WSAHOST_NOT_FOUND) then
p = " Host Not Found)";
else if (wError == WSATRY_AGAIN) then
p = " (Host not found, or Server Failure)";
else if (wError == WSANO_RECOVERY) then
p = " (Former, Refused, or Not Implemented)";
else if (wError == WSANO_DATA) then
p = " (Valid name, no data record)";
else if (wError == WSANO_DATA) then
p = " (No Address)";
Msg.Format ("%s %s", m_pDes->m_pHost, p);
delete [] m_pDes->m_pHost;
m_pDes->m_pHost = str_dup (Msg);
} else {
Msg.Format ("@%s", ((hostent*)
(m_pDes->m_HostAndName->hostdata))->h_name);
delete [] m_pDes->m_pHost;
m_pDes->m_pHost = str_dup (Msg);
}
return TRUE;
}
BOOL CSmaugSocket::SelectReadWrite ()
{
if (! AsyncSelect (FD_READ | FD_WRITE | FD_CLOSE)) {
CString Msg;
Msg.Format ("CSmaugSocket::SelectReadWrite: Select "
"failure %d on client socket.", GetLastError ());
m_pDoc->LogString (Msg, LOG_COMM);
return FALSE;
}
return TRUE;
}
void CSmaugSocket::CreateSocketWindow ()
{
m_pSocketWnd = new CSmaugSocketWnd (this);
m_pSocketWnd->m_hWnd = NULL;
if (!m_pSocketWnd->CreateEx(0, AfxRegisterWndClass(0),
_T("Socket Notification Sink"),
WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL))
{
TRACE0("Warning: unable to create SmaugSocket notify window!\n");
AfxThrowResourceException();
}
ASSERT(m_pSocketWnd->m_hWnd != NULL);
ASSERT(CWnd::FromHandlePermanent(m_pSocketWnd->m_hWnd) == m_pSocketWnd);
}
CString GetLastErrorName ()
{
CString s = "Unknown";
switch (GetLastError ()) {
case WSAEINTR:
s = "WSAEINTR";
break;
case WSAEBADF:
s = "WSAEBADF";
break;
case WSAEACCES:
s = "WSAEACCES";
break;
case WSAEFAULT:
s = "WSAEFAULT";
break;
case WSAEINVAL:
s = "WSAEINVAL";
break;
case WSAEMFILE:
s = "WSAEMFILE";
break;
case WSAEWOULDBLOCK:
s = "WSAEWOULDBLOCK";
break;
case WSAEINPROGRESS:
s = "WSAEINPROGRESS";
break;
case WSAEALREADY:
s = "WSAEALREADY";
break;
case WSAENOTSOCK:
s = "WSAENOTSOCK";
break;
case WSAEDESTADDRREQ:
s = "WSAEDESTADDRREQ";
break;
case WSAEMSGSIZE:
s = "WSAEMSGSIZE";
break;
case WSAEPROTOTYPE:
s = "WSAEPROTOTYPE";
break;
case WSAENOPROTOOPT:
s = "WSAENOPROTOOPT";
break;
case WSAEPROTONOSUPPORT:
s = "WSAEPROTONOSUPPORT";
break;
case WSAESOCKTNOSUPPORT:
s = "WSAESOCKTNOSUPPORT";
break;
case WSAEOPNOTSUPP:
s = "WSAEOPNOTSUPP";
break;
case WSAEPFNOSUPPORT:
s = "WSAEPFNOSUPPORT";
break;
case WSAEAFNOSUPPORT:
s = "WSAEAFNOSUPPORT";
break;
case WSAEADDRINUSE:
s = "WSAEADDRINUSE";
break;
case WSAEADDRNOTAVAIL:
s = "WSAEADDRNOTAVAIL";
break;
case WSAENETDOWN :
s = "WSAENETDOWN";
break;
case WSAENETUNREACH:
s = "WSAENETUNREACH";
break;
case WSAENETRESET:
s = "WSAENETRESET";
break;
case WSAECONNABORTED:
s = "WSAECONNABORTED";
break;
case WSAECONNRESET:
s = "WSAECONNRESET";
break;
case WSAENOBUFS:
s = "WSAENOBUFS";
break;
case WSAEISCONN:
s = "WSAEISCONN";
break;
case WSAENOTCONN:
s = "WSAENOTCONN";
break;
case WSAESHUTDOWN:
s = "WSAESHUTDOWN";
break;
case WSAETOOMANYREFS:
s = "WSAETOOMANYREFS";
break;
case WSAETIMEDOUT:
s = "WSAETIMEDOUT";
break;
case WSAECONNREFUSED:
s = "WSAECONNREFUSED";
break;
case WSAELOOP:
s = "WSAELOOP";
break;
case WSAENAMETOOLONG:
s = "WSAENAMETOOLONG";
break;
case WSAEHOSTDOWN:
s = "WSAEHOSTDOWN";
break;
case WSAEHOSTUNREACH:
s = "WSAEHOSTUNREACH";
break;
case WSAENOTEMPTY:
s = "WSAENOTEMPTY";
break;
case WSAEPROCLIM:
s = "WSAEPROCLIM";
break;
case WSAEUSERS:
s = "WSAEUSERS";
break;
case WSAEDQUOT:
s = "WSAEDQUOT";
break;
case WSAESTALE:
s = "WSAESTALE";
break;
case WSAEREMOTE:
s = "WSAEREMOTE";
break;
case WSAEDISCON:
s = "WSAEDISCON";
break;
case WSASYSNOTREADY:
s = "WSASYSNOTREADY";
break;
case WSAVERNOTSUPPORTED:
s = "WSAVERNOTSUPPORTED";
break;
case WSANOTINITIALISED:
s = "WSANOTINITIALISED";
break;
}
return s;
}
LRESULT CSmaugSocketWnd::OnHostNotify (WPARAM wParam, LPARAM lParam)
{
return m_pSock->GetHost (wParam, lParam);
}