root/main/trunk/greenstone2/runtime-src/src/w32server/netio.cpp @ 24411

Revision 19210, 13.0 KB (checked in by davidb, 11 years ago)

Local Library Server can now either be configured to connects from anywhere or be restricted to only allow local connections. The option is controlled by setting 'externalaccess' to 1 or 0 appropriately. A widget for this is provided under File/Settings

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1/**********************************************************************
2 *
3 * netio.cpp
4 * Copyright (C) 1996
5 *
6 * A component of the fnord webserver written by bmorin@wpi.edu.
7 *
8 * Altered for use with the Greenstone digital library software by the
9 * New Zealand Digital Library Project at the University of Waikato,
10 * New Zealand.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 *********************************************************************/
27
28#include <windows.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <string.h>
32#include <memory.h>
33#pragma hdrstop
34#include "netio.h"
35#include "locate.h"
36#include "d_winsock.h"
37#include "settings.h"
38
39//Private Variables and Declarations
40
41//Max string size for a host name
42#define MAXHOSTNAME 100
43
44//Timeout for waiting between "packets" in seconds
45#define NETIO_CONN_TIMEOUT 20 //3 sec  was 180 @@@@@ WJR
46
47#define HTTP_DNS_MSG WM_USER + 1
48int dns_msg_received = 0;
49int dns_msg_error = 0;
50
51DWORD LocalIPNumber;
52char LocalName[MAXHOSTNAME];
53
54//Private Function Declarations
55DWORD GetHostID();
56
57// returns 0 on success,
58// WSASYSNOTREADY, WSAVERNOTSUPPORTED, or WSAEINVAL on failure
59int InitNetIO() {
60  WSADATA Data;
61 
62  // Load Winsock
63  int err = d_WSAStartup(MAKEWORD(1, 1), &Data);
64 
65  // get local IP number
66  if (err == 0) LocalIPNumber = GetHostID();
67  else LocalIPNumber = INADDR_ANY;
68 
69  // set the Local Name to "" for now
70  LocalName[0] = 0;
71 
72  return err;
73}
74
75
76void CleanUpNetIO() {
77  //Experimental, heard this will better clean up, especialy if a call is
78  //blocking....
79  d_WSAUnhookBlockingHook();
80
81  //Clean up Winsock
82  d_WSACleanup();
83}
84
85
86void ResetNetIO() {
87  //Figure out the local name on first querry, set to "" until then...
88  LocalName[0] = 0;
89}
90
91
92DWORD GetLocalIP() {
93  return LocalIPNumber;
94}
95
96long __stdcall DNSWindowProc(HWND hWnd,UINT wMsg, WPARAM wParam, LPARAM lParam) {
97  if (wMsg == HTTP_DNS_MSG) {
98    dns_msg_received = 1;
99    dns_msg_error = WSAGETASYNCERROR(lParam);
100    return 0;
101  }
102 
103  return DefWindowProc(hWnd, wMsg, wParam, lParam);
104}
105
106char *GetLocalName(HINSTANCE hInstance) {
107  // static in case it is written to after the function has finished
108  // (I did not error checking on WSACancelAsyncRequest)
109  static char buf[MAXGETHOSTSTRUCT];
110   
111  if (LocalName[0] == 0) {
112    hostent *DNSResult = NULL;
113    in_addr LocalInAddr;
114   
115    // if we failed to get the local IP number
116    // use the loop-back device
117    if (LocalIPNumber == INADDR_ANY) {
118      strcpy(LocalName, "127.0.0.1"); // loop-back device
119      return LocalName;
120    }
121   
122    //Convert the number to an in_addr struct
123    LocalInAddr.s_addr = LocalIPNumber;
124   
125    // if we fail to find the domain name we will
126    // still want the IP address
127    strcpy(LocalName, d_inet_ntoa(LocalInAddr));
128   
129    // make sure they actually passed in an instance handle
130    if (hInstance == NULL) return LocalName;
131   
132    // do a async domain name lookup so that we
133    // can control the timeout
134   
135    // create a window class and window to handle the async messages
136    WNDCLASS wc; HWND hwndDNS;
137   
138    wc.style = 0;
139    wc.lpfnWndProc = DNSWindowProc;
140    wc.cbClsExtra = 0;
141    wc.cbWndExtra = 0;
142    wc.hInstance = hInstance;
143    wc.hIcon = NULL;
144    wc.hCursor = NULL;
145    wc.hbrBackground = NULL;
146    wc.lpszMenuName = NULL;
147    wc.lpszClassName = "GSDL DNS Window";
148    if (!RegisterClass(&wc)) return LocalName;
149   
150    hwndDNS = CreateWindow("GSDL DNS Window", "",
151               WS_OVERLAPPEDWINDOW, 0, 0, 100, 100,
152               NULL, NULL, hInstance, NULL);
153    if (!hwndDNS) return LocalName;
154   
155    // process all messages currently on the queue
156    MSG Message;
157    while (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {
158      TranslateMessage(&Message); /* translate keyboard messages */
159      DispatchMessage(&Message);  /* return control to Windows NT */
160    }
161   
162    //Do a async DNS lookup on the IP number
163    dns_msg_received = 0;
164    dns_msg_error = 0;
165    HANDLE asyncGetHostReq = d_WSAAsyncGetHostByAddr(
166                             hwndDNS, HTTP_DNS_MSG, (char *)&(LocalInAddr),
167                             4, PF_INET, buf, MAXGETHOSTSTRUCT);
168   
169    if (asyncGetHostReq != NULL) {
170      // wait 5 seconds for the request to complete
171      int now = GetTickCount();
172      while ((DiffTickCounts(now, GetTickCount()) < 5000) && !dns_msg_received) {
173    if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {
174      TranslateMessage(&Message); /* translate keyboard messages */
175      DispatchMessage(&Message);  /* return control to Windows NT */
176    } else {
177      Sleep(1);
178    }
179      }
180      if (!dns_msg_received)
181    d_WSACancelAsyncRequest(asyncGetHostReq);
182    }
183   
184    DestroyWindow(hwndDNS);
185   
186    if (dns_msg_received && (dns_msg_error == 0)) {
187      //Worked, use the primary name
188      strcpy(LocalName, ((hostent *)(buf))->h_name);
189      //Convert it to lower case for cosmedic reasons
190      CharLower(LocalName);
191    }
192  }
193  return LocalName;
194}
195
196
197// returns 0 on success, and a WSA error message on failure.
198// possible error messages include:
199// WSANOTINITIALISED, WSAENETDOWN, WSAEAFNOSUPPORT, WSAEINPROGRESS, WSAEMFILE,
200// WSAENOBUFS, WSAEPROTONOSUPPORT, WSAEPROTOTYPE, WSAESOCKTNOSUPPORT,
201// WSAEADDRINUSE, WSAEINVAL, WSAEISCONN, WSAENOTSOCK, WSAEOPNOTSUPP
202
203#define MAXBINDCOUNT 4096
204int CreateListeningSocket(int &PortNum, HWND MsgWindow,
205              WORD SocketMsg, SOCKET &ServerSocket) {
206  int err = 0;
207  SOCKADDR_IN ServerSockAddr;
208  int bind_port, bind_count;
209 
210  //Create the Server Socket
211  ServerSocket = d_socket(AF_INET, SOCK_STREAM, 0);
212  if (ServerSocket == INVALID_SOCKET) return d_WSAGetLastError();
213 
214  bind_port = PortNum;
215  for (bind_count=0; bind_count < MAXBINDCOUNT; ++bind_count) {
216    // Set up the Server Socket Address
217    memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); //Needed?
218    ServerSockAddr.sin_port = d_htons( (WORD) bind_port);
219    ServerSockAddr.sin_family = AF_INET;
220    if (gsdl_external_access) {
221      ServerSockAddr.sin_addr.s_addr = d_htonl(INADDR_ANY);
222    }
223    else {
224      ServerSockAddr.sin_addr.s_addr = d_htonl(INADDR_LOOPBACK);
225    }
226
227
228    // Try to bind the socket with the address
229    if (d_bind(ServerSocket, (LPSOCKADDR) &ServerSockAddr,
230           sizeof(ServerSockAddr)) != SOCKET_ERROR) {
231      PortNum = bind_port;
232      break;
233    }
234   
235    // make sure it failed to bind because it was
236    // already bound
237    err = d_WSAGetLastError ();
238    if (err != WSAEADDRINUSE) return err;
239   
240    // Or choose another port number to try
241    if (bind_port == 80) bind_port = IPPORT_RESERVED+1;
242    else if (bind_count == 0) bind_port = 80;
243    else ++bind_port;
244  }
245 
246  // return an error in we couldn't find a valid port
247  if (bind_count == MAXBINDCOUNT) return WSAEADDRINUSE;
248 
249  // Start listening for connections
250  if (d_listen(ServerSocket, SOMAXCONN) == SOCKET_ERROR)
251    return d_WSAGetLastError ();   
252 
253  // Set up event for new connections
254  if (d_WSAAsyncSelect(ServerSocket, MsgWindow, SocketMsg, FD_ACCEPT) == SOCKET_ERROR)
255    return d_WSAGetLastError ();
256 
257  return 0;
258}
259
260
261int AnswerListeningSocket(SOCKET ServerSocket, SOCKET &ClientSocket,
262              SOCKADDR_IN &ClientSockAddr, int AddrLen) {
263  if (d_WSAIsBlocking()) {
264    log_message("rejected connect due blocking\n");
265    return -1;
266  }
267 
268  ClientSocket = d_accept(ServerSocket, (LPSOCKADDR) &ClientSockAddr, &AddrLen);
269  if (ClientSocket == INVALID_SOCKET) {
270    log_message("accept failed - connection lost\n");
271    return -1;
272  }
273 
274  log_message("accept success - connection made\n");
275  return 0;
276}
277
278void DestroyListeningSocket(SOCKET &ServerSocket, HWND MsgWindow) {
279  //Remove any message notification
280  d_WSAAsyncSelect(ServerSocket, MsgWindow, 0, 0);
281 
282  //Close the socket
283  CloseSocket(ServerSocket);
284}
285
286void CloseSocket(SOCKET &TargetSocket) {
287  if (TargetSocket != INVALID_SOCKET) {
288    //Since we're closing the socket, there's not much I can do about errors
289    //now so I'm not gonna bother checking...
290   
291    //Shutdown both ends, assume we have all data...
292    d_shutdown(TargetSocket, 2);
293   
294    d_closesocket(TargetSocket);
295   
296    //Make sure we can't use the old handle again...
297    TargetSocket = INVALID_SOCKET;
298  }
299}
300
301int GetData(SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize,
302        int ThreadNum) {
303 
304  int NumRecv;
305  int Error;
306  struct timeval Timeout;
307  fd_set SocketSet;
308 
309  //Set up a socket set structure with just ClientSocket for  select(..)
310  FD_ZERO(&SocketSet);
311  FD_SET(ClientSocket, &SocketSet);
312 
313  //set timeout
314  Timeout.tv_sec = NETIO_CONN_TIMEOUT;
315  Timeout.tv_usec = 0;
316 
317  do {
318    NumRecv = d_recv(ClientSocket, (char *) IOBuffer, IOBufferSize, 0);
319    if (NumRecv == 0) {
320      //Lost connect
321      return -1;
322     
323    } else if (NumRecv == SOCKET_ERROR) {
324      Error = d_WSAGetLastError();
325      if (Error == WSAEWOULDBLOCK) {
326    NumRecv = 0;
327    //Wait for socket to be readable
328    if (d_select(0, &SocketSet, NULL, NULL, &Timeout) != 1) {
329      //Timeout
330      return -1;
331    }
332   
333      } else {
334    //Assume connection terminated
335    return -1;
336      }
337    }
338  } while(NumRecv == 0);
339  return NumRecv;
340}
341
342int GetLine(text_t &OutStr, SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize,
343        int &BufferIndex, int &DataInBuffer, int ThreadNum) {
344
345  OutStr.clear();
346  char CurChar;
347 
348  do {
349    if (BufferIndex == DataInBuffer) { //Need more data
350      DataInBuffer = GetData(ClientSocket, IOBuffer, IOBufferSize, ThreadNum);
351      if (DataInBuffer == -1) {
352    //Lost connect
353    return -1;
354      }
355      BufferIndex = 0;
356    }
357    CurChar = IOBuffer[BufferIndex];
358    ++BufferIndex;
359    if ((CurChar != 10) && (CurChar != 13))  {
360      OutStr.push_back(CurChar);
361    }
362  } while (CurChar != 10);
363 
364  return 0;
365}
366
367int SendData(SOCKET ClientSocket, BYTE *SendBuffer, int NumToSend,
368         int ThreadNum) {
369 
370  int NumSent = 0;
371  int Error;
372  struct timeval Timeout;
373  fd_set SocketSet;
374 
375  char NumSentStr[50];
376  itoa(NumToSend, NumSentStr, 10);
377 
378  //Set up a socket set structure with just ClientSocket for  select(..)
379  FD_ZERO(&SocketSet);
380  FD_SET(ClientSocket, &SocketSet);
381  //set timeout
382  Timeout.tv_sec = NETIO_CONN_TIMEOUT;
383  Timeout.tv_usec = 0;
384 
385  while (NumToSend > 0) {
386    NumSent = d_send(ClientSocket, (char *) SendBuffer + NumSent, NumToSend, 0);
387    if (NumSent == 0) {
388      //Lost connect
389      return -1;
390    }
391    else if (NumSent == SOCKET_ERROR) {
392      Error = d_WSAGetLastError();
393      if (Error == WSAEWOULDBLOCK) {
394    NumSent = 0;
395    if (d_select(0, NULL, &SocketSet, NULL, &Timeout) != 1) {
396      //Timeout
397      return -1;
398    }
399      }
400      else {
401    //Lost Connection
402    return -1;
403      }
404    }
405    NumToSend -= NumSent;
406  }
407  return 0;
408}
409
410
411/******************************************************************************/
412//Private Functions
413/******************************************************************************/
414/*-----------------------------------------------------------
415 * Function: GetHostID()
416 *
417 * Description:
418 *  Get the Local IP address using the following algorithm:
419 *    - get local hostname with gethostname()
420 *    - attempt to resolve local hostname with gethostbyname()
421 *    if that fails:
422 *    - get a UDP socket
423 *    - connect UDP socket to arbitrary address and port
424 *    - use getsockname() to get local address
425 *
426 * Notes: Copyright by Bob Quinn, 1995, taken from his Winsock library
427 *        was removed from its original module unmodified
428 */
429DWORD GetHostID () {
430  char szLclHost [MAXHOSTNAME];
431  LPHOSTENT lpstHostent;
432  SOCKADDR_IN stLclAddr;
433  SOCKADDR_IN stRmtAddr;
434  int nAddrSize = sizeof(SOCKADDR);
435  SOCKET hSock;
436  int nRet;
437 
438  /* Init local address (to zero) */
439  stLclAddr.sin_addr.s_addr = INADDR_ANY;
440 
441  /* Get the local hostname */
442  nRet = d_gethostname(szLclHost, MAXHOSTNAME);
443  if (nRet != SOCKET_ERROR) {
444    /* Resolve hostname for local address */
445    lpstHostent = d_gethostbyname((LPSTR)szLclHost);
446    if (lpstHostent)
447      stLclAddr.sin_addr.s_addr = *((u_long FAR*) (lpstHostent->h_addr));
448  }
449 
450  /* If still not resolved, then try second strategy */
451  if (stLclAddr.sin_addr.s_addr == INADDR_ANY) {
452    /* Get a UDP socket */
453    hSock = d_socket(AF_INET, SOCK_DGRAM, 0);
454    if (hSock != INVALID_SOCKET)  {
455      /* Connect to arbitrary port and address (NOT loopback) */
456      stRmtAddr.sin_family = AF_INET;
457      stRmtAddr.sin_port   = d_htons(IPPORT_ECHO);
458      stRmtAddr.sin_addr.s_addr = d_inet_addr("128.127.50.1");
459      nRet = d_connect(hSock,
460               (LPSOCKADDR)&stRmtAddr,
461               sizeof(SOCKADDR));
462      if (nRet != SOCKET_ERROR) {
463    /* Get local address */
464    d_getsockname(hSock,
465              (LPSOCKADDR)&stLclAddr,
466              (int FAR*)&nAddrSize);
467      }
468      d_closesocket(hSock);   /* we're done with the socket */
469    }
470  }
471  return (stLclAddr.sin_addr.s_addr);
472}
Note: See TracBrowser for help on using the browser.