source: gsdl/trunk/runtime-src/src/w32server/netio.cpp@ 19210

Last change on this file since 19210 was 19210, checked in by davidb, 15 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
File size: 13.0 KB
Line 
1/**********************************************************************
2 *
3 * netio.cpp
4 * Copyright (C) 1996
5 *
6 * A component of the fnord webserver written by [email protected].
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 repository browser.