/**************************************************************************** * [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); }