/********************************************************************** * * netio.cpp * Copyright (C) 1996 * * A component of the fnord webserver written by bmorin@wpi.edu. * * Altered for use with the Greenstone digital library software by the * New Zealand Digital Library Project at the University of Waikato, * New Zealand. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * *********************************************************************/ #include #include #include #include #include #pragma hdrstop #include "netio.h" #include "locate.h" #include "d_winsock.h" #include "settings.h" //Private Variables and Declarations //Max string size for a host name #define MAXHOSTNAME 100 //Timeout for waiting between "packets" in seconds #define NETIO_CONN_TIMEOUT 20 //3 sec was 180 @@@@@ WJR #define HTTP_DNS_MSG WM_USER + 1 int dns_msg_received = 0; int dns_msg_error = 0; DWORD LocalIPNumber; char LocalName[MAXHOSTNAME]; //Private Function Declarations DWORD GetHostID(); // returns 0 on success, // WSASYSNOTREADY, WSAVERNOTSUPPORTED, or WSAEINVAL on failure int InitNetIO() { WSADATA Data; // Load Winsock int err = d_WSAStartup(MAKEWORD(1, 1), &Data); // get local IP number if (err == 0) LocalIPNumber = GetHostID(); else LocalIPNumber = INADDR_ANY; // set the Local Name to "" for now LocalName[0] = 0; return err; } void CleanUpNetIO() { //Experimental, heard this will better clean up, especialy if a call is //blocking.... d_WSAUnhookBlockingHook(); //Clean up Winsock d_WSACleanup(); } void ResetNetIO() { //Figure out the local name on first querry, set to "" until then... LocalName[0] = 0; } DWORD GetLocalIP() { return LocalIPNumber; } LRESULT __stdcall DNSWindowProc(HWND hWnd,UINT wMsg, WPARAM wParam, LPARAM lParam) { if (wMsg == HTTP_DNS_MSG) { dns_msg_received = 1; dns_msg_error = WSAGETASYNCERROR(lParam); return 0; } return DefWindowProc(hWnd, wMsg, wParam, lParam); } char *GetLocalName(HINSTANCE hInstance) { // static in case it is written to after the function has finished // (I did not error checking on WSACancelAsyncRequest) static char buf[MAXGETHOSTSTRUCT]; if (LocalName[0] == 0) { hostent *DNSResult = NULL; in_addr LocalInAddr; // if we failed to get the local IP number // use the loop-back device if (LocalIPNumber == INADDR_ANY) { strcpy(LocalName, "127.0.0.1"); // loop-back device return LocalName; } //Convert the number to an in_addr struct LocalInAddr.s_addr = LocalIPNumber; // if we fail to find the domain name we will // still want the IP address strcpy(LocalName, d_inet_ntoa(LocalInAddr)); // make sure they actually passed in an instance handle if (hInstance == NULL) return LocalName; // do a async domain name lookup so that we // can control the timeout // create a window class and window to handle the async messages WNDCLASS wc; HWND hwndDNS; wc.style = 0; wc.lpfnWndProc = DNSWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "GSDL DNS Window"; if (!RegisterClass(&wc)) return LocalName; hwndDNS = CreateWindow("GSDL DNS Window", "", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, hInstance, NULL); if (!hwndDNS) return LocalName; // process all messages currently on the queue MSG Message; while (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&Message); /* translate keyboard messages */ DispatchMessage(&Message); /* return control to Windows NT */ } //Do a async DNS lookup on the IP number dns_msg_received = 0; dns_msg_error = 0; HANDLE asyncGetHostReq = d_WSAAsyncGetHostByAddr( hwndDNS, HTTP_DNS_MSG, (char *)&(LocalInAddr), 4, PF_INET, buf, MAXGETHOSTSTRUCT); if (asyncGetHostReq != NULL) { // wait 5 seconds for the request to complete int now = GetTickCount(); while ((DiffTickCounts(now, GetTickCount()) < 5000) && !dns_msg_received) { if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&Message); /* translate keyboard messages */ DispatchMessage(&Message); /* return control to Windows NT */ } else { Sleep(1); } } if (!dns_msg_received) d_WSACancelAsyncRequest(asyncGetHostReq); } DestroyWindow(hwndDNS); if (dns_msg_received && (dns_msg_error == 0)) { //Worked, use the primary name strcpy(LocalName, ((hostent *)(buf))->h_name); //Convert it to lower case for cosmedic reasons CharLower(LocalName); } } return LocalName; } // returns 0 on success, and a WSA error message on failure. // possible error messages include: // WSANOTINITIALISED, WSAENETDOWN, WSAEAFNOSUPPORT, WSAEINPROGRESS, WSAEMFILE, // WSAENOBUFS, WSAEPROTONOSUPPORT, WSAEPROTOTYPE, WSAESOCKTNOSUPPORT, // WSAEADDRINUSE, WSAEINVAL, WSAEISCONN, WSAENOTSOCK, WSAEOPNOTSUPP #define MAXBINDCOUNT 4096 int CreateListeningSocket(int &PortNum, HWND MsgWindow, WORD SocketMsg, SOCKET &ServerSocket) { int err = 0; SOCKADDR_IN ServerSockAddr; int bind_port, bind_count; //Create the Server Socket ServerSocket = d_socket(AF_INET, SOCK_STREAM, 0); if (ServerSocket == INVALID_SOCKET) return d_WSAGetLastError(); bind_port = PortNum; for (bind_count=0; bind_count < MAXBINDCOUNT; ++bind_count) { // Set up the Server Socket Address memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); //Needed? ServerSockAddr.sin_port = d_htons( (WORD) bind_port); ServerSockAddr.sin_family = AF_INET; if (gsdl_external_access) { ServerSockAddr.sin_addr.s_addr = d_htonl(INADDR_ANY); } else { ServerSockAddr.sin_addr.s_addr = d_htonl(INADDR_LOOPBACK); } // Try to bind the socket with the address if (d_bind(ServerSocket, (LPSOCKADDR) &ServerSockAddr, sizeof(ServerSockAddr)) != SOCKET_ERROR) { PortNum = bind_port; break; } // make sure it failed to bind because it was // already bound err = d_WSAGetLastError (); if (err != WSAEADDRINUSE) return err; // Or choose another port number to try if (bind_port == 80) bind_port = IPPORT_RESERVED+1; else if (bind_count == 0) bind_port = 80; else ++bind_port; } // return an error in we couldn't find a valid port if (bind_count == MAXBINDCOUNT) return WSAEADDRINUSE; // Start listening for connections if (d_listen(ServerSocket, SOMAXCONN) == SOCKET_ERROR) return d_WSAGetLastError (); // Set up event for new connections if (d_WSAAsyncSelect(ServerSocket, MsgWindow, SocketMsg, FD_ACCEPT) == SOCKET_ERROR) return d_WSAGetLastError (); return 0; } int AnswerListeningSocket(SOCKET ServerSocket, SOCKET &ClientSocket, SOCKADDR_IN &ClientSockAddr, int AddrLen) { if (d_WSAIsBlocking()) { log_message("rejected connect due blocking\n"); return -1; } ClientSocket = d_accept(ServerSocket, (LPSOCKADDR) &ClientSockAddr, &AddrLen); if (ClientSocket == INVALID_SOCKET) { log_message("accept failed - connection lost\n"); return -1; } log_message("accept success - connection made\n"); return 0; } void DestroyListeningSocket(SOCKET &ServerSocket, HWND MsgWindow) { //Remove any message notification d_WSAAsyncSelect(ServerSocket, MsgWindow, 0, 0); //Close the socket CloseSocket(ServerSocket); } void CloseSocket(SOCKET &TargetSocket) { if (TargetSocket != INVALID_SOCKET) { //Since we're closing the socket, there's not much I can do about errors //now so I'm not gonna bother checking... //Shutdown both ends, assume we have all data... d_shutdown(TargetSocket, 2); d_closesocket(TargetSocket); //Make sure we can't use the old handle again... TargetSocket = INVALID_SOCKET; } } int GetData(SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize, int ThreadNum) { int NumRecv; int Error; struct timeval Timeout; fd_set SocketSet; //Set up a socket set structure with just ClientSocket for select(..) FD_ZERO(&SocketSet); FD_SET(ClientSocket, &SocketSet); //set timeout Timeout.tv_sec = NETIO_CONN_TIMEOUT; Timeout.tv_usec = 0; do { NumRecv = d_recv(ClientSocket, (char *) IOBuffer, IOBufferSize, 0); if (NumRecv == 0) { //Lost connect return -1; } else if (NumRecv == SOCKET_ERROR) { Error = d_WSAGetLastError(); if (Error == WSAEWOULDBLOCK) { NumRecv = 0; //Wait for socket to be readable if (d_select(0, &SocketSet, NULL, NULL, &Timeout) != 1) { //Timeout return -1; } } else { //Assume connection terminated return -1; } } } while(NumRecv == 0); return NumRecv; } int GetLine(text_t &OutStr, SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize, int &BufferIndex, int &DataInBuffer, int ThreadNum) { OutStr.clear(); char CurChar; do { if (BufferIndex == DataInBuffer) { //Need more data DataInBuffer = GetData(ClientSocket, IOBuffer, IOBufferSize, ThreadNum); if (DataInBuffer == -1) { //Lost connect return -1; } BufferIndex = 0; } CurChar = IOBuffer[BufferIndex]; ++BufferIndex; if ((CurChar != 10) && (CurChar != 13)) { OutStr.push_back(CurChar); } } while (CurChar != 10); return 0; } int SendData(SOCKET ClientSocket, BYTE *SendBuffer, int NumToSend, int ThreadNum) { int NumSent = 0; int Error; struct timeval Timeout; fd_set SocketSet; char NumSentStr[50]; itoa(NumToSend, NumSentStr, 10); //Set up a socket set structure with just ClientSocket for select(..) FD_ZERO(&SocketSet); FD_SET(ClientSocket, &SocketSet); //set timeout Timeout.tv_sec = NETIO_CONN_TIMEOUT; Timeout.tv_usec = 0; while (NumToSend > 0) { NumSent = d_send(ClientSocket, (char *) SendBuffer + NumSent, NumToSend, 0); if (NumSent == 0) { //Lost connect return -1; } else if (NumSent == SOCKET_ERROR) { Error = d_WSAGetLastError(); if (Error == WSAEWOULDBLOCK) { NumSent = 0; if (d_select(0, NULL, &SocketSet, NULL, &Timeout) != 1) { //Timeout return -1; } } else { //Lost Connection return -1; } } NumToSend -= NumSent; } return 0; } /******************************************************************************/ //Private Functions /******************************************************************************/ /*----------------------------------------------------------- * Function: GetHostID() * * Description: * Get the Local IP address using the following algorithm: * - get local hostname with gethostname() * - attempt to resolve local hostname with gethostbyname() * if that fails: * - get a UDP socket * - connect UDP socket to arbitrary address and port * - use getsockname() to get local address * * Notes: Copyright by Bob Quinn, 1995, taken from his Winsock library * was removed from its original module unmodified */ DWORD GetHostID () { char szLclHost [MAXHOSTNAME]; LPHOSTENT lpstHostent; SOCKADDR_IN stLclAddr; SOCKADDR_IN stRmtAddr; int nAddrSize = sizeof(SOCKADDR); SOCKET hSock; int nRet; /* Init local address (to zero) */ stLclAddr.sin_addr.s_addr = INADDR_ANY; /* Get the local hostname */ nRet = d_gethostname(szLclHost, MAXHOSTNAME); if (nRet != SOCKET_ERROR) { /* Resolve hostname for local address */ lpstHostent = d_gethostbyname((LPSTR)szLclHost); if (lpstHostent) stLclAddr.sin_addr.s_addr = *((u_long FAR*) (lpstHostent->h_addr)); } /* If still not resolved, then try second strategy */ if (stLclAddr.sin_addr.s_addr == INADDR_ANY) { /* Get a UDP socket */ hSock = d_socket(AF_INET, SOCK_DGRAM, 0); if (hSock != INVALID_SOCKET) { /* Connect to arbitrary port and address (NOT loopback) */ stRmtAddr.sin_family = AF_INET; stRmtAddr.sin_port = d_htons(IPPORT_ECHO); stRmtAddr.sin_addr.s_addr = d_inet_addr("128.127.50.1"); nRet = d_connect(hSock, (LPSOCKADDR)&stRmtAddr, sizeof(SOCKADDR)); if (nRet != SOCKET_ERROR) { /* Get local address */ d_getsockname(hSock, (LPSOCKADDR)&stLclAddr, (int FAR*)&nAddrSize); } d_closesocket(hSock); /* we're done with the socket */ } } return (stLclAddr.sin_addr.s_addr); }