// // Finger Version 3.1, a Windows Sockets Finger Client // // Copyright 1992, 1993 Network Research Corporation // // Permission to use, modify, and distribute this software and its // documentation for any purpose and without fee is hereby granted, provided // that the above copyright notice appears in all copies and that both // that copyright notice and this permission notice appear in supporting // documentation. NRC makes no claims as to the suitability of this software // for any purpose. // // Module NETWRKM uses Windows Sockets asynchronous (message based) calls to // query a remote finger server for a list of currently logged in users. // Module FINGER initiates the operation by calling FingerStart(), and // NETWRKM signals completion by calling FingerFinish(). NETWRKM uses DSPLIST // functions to send the retrieved data to the FINGER user interface module. // // 04/01/92 Lee Murach Created. // 11/30/92 Lee Murach Adapted for WS 1.1 WSAAsyncSelect() // 12/02/92 Lee Murach Restructured for Finger 3.0 integrated release // 01/15/92 Lee Murach Change FD_READ to FD_READ | FD_CLOSE // 03/01/93 Lee Murach Added WSAEWOULDBLOCK check to DoConnect() // 03/25/93 Lee Murach Added per-user finger support. // #include #include #include #include #include "finger.h" // messages for windows sockets returns #define WM_SERVICE (WM_USER + 1) #define WM_HOSTRESOLVED (WM_USER + 2) #define WM_CONNECTED (WM_USER + 3) #define WM_OKTORECV (WM_USER + 4) #define NETWINDOW "NetWindow" LONG FAR APIENTRY NetWndProc(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam); LONG DoResolveHost(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam); LONG DoConnect(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam); LONG DoQuery(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam); LONG DoRetrieval(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam); void LoadEntBuf(IPA ipa); DECODEWORD netMsgs[] = // network window messages & handlers { WM_SERVICE, DoResolveHost, WM_HOSTRESOLVED, DoConnect, WM_CONNECTED, DoQuery, WM_OKTORECV, DoRetrieval }; SOCKET Sock; // connects to remote server's socket int Port; // hold port for finger service char EntBuf[MAXGETHOSTSTRUCT]; // buf for service & resolve returns HWND hNetWnd; // network window handle // // InitNetApp -- registers window class for the network module's invisible // child window. This is called only for the first instance of finger. // VOID InitNetApp(VOID) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = NetWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInst; wndclass.hIcon = 0; wndclass.hCursor = 0; wndclass.hbrBackground = 0; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = NETWINDOW; RegisterClass(&wndclass); } // // InitNetInst -- initializes the network module by creating an invisible // child of the main finger window which will be used to receive Windows // Sockets notification messages. The window will be automatically // destroyed when the main window exits, so no finalization is required. // VOID InitNetInst(HWND hWnd) { hNetWnd = CreateWindow( NETWINDOW, "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWnd, 0, hInst, NULL); } // // NetWndProc -- callback function for network window. The network window // is a child window created especially to receive windows sockets network // messages. The function decodes and routes to appropriate message handler. // LONG FAR APIENTRY NetWndProc(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam) { int i; for (i = 0; i < dim(netMsgs); i++) { if (wMsg == netMsgs[i].Code) return((*netMsgs[i].Fxn)(hWnd, wMsg, wParam, lParam)); } return(DefWindowProc(hWnd, wMsg, wParam, lParam)); } // // FingerStart -- called by FINGER module to initiate a conversation with // the remote finger server. We start by resolving the finger tcp service // to a port number. Windows Sockets WSAAsync routines signal completion // by posting messages, which are dispatched to appropriate handlers. // VOID FingerStart(VOID) { if (WSAAsyncGetServByName(hNetWnd, WM_SERVICE, "finger", "tcp", EntBuf, sizeof(EntBuf)) == -1) { ReportFingerErr(FE_NOPORT); FingerFinish(FE_ERROR); } } // // DoResolveHost -- resolves host specifier to an IP address. Since we // allow a "dotted decimal" IP address to be entered in lieu of a DNS host // name, we check for this syntax before assuming a DNS name. // LONG DoResolveHost(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam) { IPA ipa; if (WSAGETSELECTERROR(lParam)) { ReportFingerErr(FE_NOPORT); // cannot locate finger service FingerFinish(FE_ERROR); return(FALSE); } Port = ((SERVENT *)EntBuf)->s_port; // we're going to reuse the buffer // if host specifier is dotted decimal ip address, resolve right here if ((ipa = INET_ADDR(szHostName)) != INADDR_NONE) { LoadEntBuf(ipa); PostMessage(hNetWnd, WM_HOSTRESOLVED, 0, 0); return(FALSE); } // assume specifier is DNS host name if (WSAAsyncGetHostByName(hNetWnd, WM_HOSTRESOLVED, szHostName, EntBuf, sizeof(EntBuf)) == -1) { ReportFingerErr(FE_NOHOST); FingerFinish(FE_ERROR); } return(FALSE); } // // LoadEntBuf -- loads the EntBuf (sufficiently) with a HOSTENT and // referenced IPA. This is so we can return IPAs in the same // manner as a WSAAsync call. // void LoadEntBuf(IPA ipa) { LPHOSTENT phe = (LPHOSTENT) EntBuf; LPPIPA ppipa = (LPPIPA) (EntBuf + sizeof(HOSTENT)); LPIPA pipa = (LPIPA) (EntBuf + sizeof(HOSTENT) + sizeof(LPPIPA)); memset(phe, 0, sizeof(HOSTENT)); phe->h_addr_list = (char FAR * FAR *) ppipa; *ppipa = pipa; *pipa = ipa; } // // DoConnect -- allocates a socket and connects to remote finger server. // LONG DoConnect(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam) { if (WSAGETSELECTERROR(lParam)) { ReportFingerErr(FE_NOHOST); // could not resolve host name FingerFinish(FE_ERROR); return(FALSE); } if ((Sock = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { SOCKADDR_IN server; u_long block = FALSE; HOSTENT *phe = (HOSTENT *) EntBuf; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = Port; server.sin_addr = *((IN_ADDR FAR *) *phe->h_addr_list); // post message when connect is established WSAAsyncSelect(Sock, hNetWnd, WM_CONNECTED, FD_CONNECT); if ( connect(Sock, (SOCKADDR *)&server, sizeof(server)) < 0 && WSAGetLastError() != WSAEWOULDBLOCK) { ReportFingerErr(FE_NOCONN); FingerFinish(FE_ERROR); } } else { ReportFingerErr(FE_NOSOCK); FingerFinish(FE_ERROR); } return(FALSE); } // // DoQuery -- sends a query for all currently logged in users to remote // server. // LONG DoQuery(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam) { char msg[MAXUSER+3]; int msglen; if (WSAGETSELECTERROR(lParam)) { ReportFingerErr(FE_NOCONN); // could not connect to server FingerFinish(FE_ERROR); return(FALSE); } // post message when data is available for read WSAAsyncSelect(Sock, hNetWnd, WM_OKTORECV, FD_READ | FD_CLOSE); strcpy(msg, szUser); strcat(msg, "\r\n"); msglen = strlen(msg); if (send(Sock, msg, msglen, 0) != msglen) { ReportFingerErr(FE_NOSEND); FingerFinish(FE_ERROR); return(FALSE); } OpenDisplayList(); // new display list will contain received data return(FALSE); } // // DoRetrieval -- fetches ascii text from remote finger server, and builds // display list until the end of the text stream. // LONG DoRetrieval(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam) { static char buf[500]; int nchars, err = 0; /* receives data not to exceed buf size & reenables notification of more data pending */ if ((nchars = recv(Sock, (char FAR *)&buf, sizeof(buf), 0)) > 0) { PushChars(buf, nchars); // adds character to display list return(FALSE); } CloseDisplayList(); // close list if error or end-of-data closesocket(Sock); // don't need socket anymore if (nchars < 0) { FreeDisplayList(); ReportFingerErr(FE_NORECV); // error during receive err = FE_ERROR; } FingerFinish(err); // signal end-of-finger return(FALSE); }