0
|
1 /*
|
|
2 * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved.
|
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 *
|
|
5 * This code is free software; you can redistribute it and/or modify it
|
|
6 * under the terms of the GNU General Public License version 2 only, as
|
|
7 * published by the Free Software Foundation.
|
|
8 *
|
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
12 * version 2 for more details (a copy is included in the LICENSE file that
|
|
13 * accompanied this code).
|
|
14 *
|
|
15 * You should have received a copy of the GNU General Public License version
|
|
16 * 2 along with this work; if not, write to the Free Software Foundation,
|
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18 *
|
|
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or
|
|
21 * have any questions.
|
|
22 *
|
|
23 */
|
|
24
|
|
25 // A Simple Windows Debug Server.
|
|
26 //
|
|
27 // This software provides a socket-based debug server which uses
|
|
28 // mostly ASCII protocols to communicate with its clients. Since the
|
|
29 // Windows security model is largely based around being able to run
|
|
30 // programs on the machine, this server only accepts connections
|
|
31 // coming from localhost.
|
|
32 //
|
|
33 // When run as a service (under Windows NT), this software provides
|
|
34 // clients the ability to attach to and detach from processes without
|
|
35 // killing those processes. Ordinarily this is forbidden by the
|
|
36 // Windows debugging APIs (although more recent debugging environments
|
|
37 // from Microsoft seem to have circumvented this restriction, perhaps
|
|
38 // in a different way). This is achieved by forking a persistent
|
|
39 // subprocess for each debugging session which remains alive as long
|
|
40 // as the target process is.
|
|
41 //
|
|
42 // At this point the client can read information out of the target
|
|
43 // process's address space. Future work includes exposing more
|
|
44 // functionality like writing to the remote address space and
|
|
45 // suspending and resuming threads.
|
|
46
|
|
47 #include <iostream>
|
|
48 #include <vector>
|
|
49 #include <stdlib.h>
|
|
50 // Must come before everything else
|
|
51 #include <winsock2.h>
|
|
52 #include <assert.h>
|
|
53 #include "Dispatcher.hpp"
|
|
54 #include "Handler.hpp"
|
|
55 #include "initWinsock.hpp"
|
|
56 #include "ioUtils.hpp"
|
|
57 #include "isNT4.hpp"
|
|
58 #include "Message.hpp"
|
|
59 #include "nt4internals.hpp"
|
|
60 #include "ports.h"
|
|
61 #include "procList.hpp"
|
|
62 #include "serverLists.hpp"
|
|
63 #include "Reaper.hpp"
|
|
64
|
|
65 // Uncomment the #define below to get messages on stderr
|
|
66 // #define DEBUGGING
|
|
67
|
|
68 using namespace std;
|
|
69
|
|
70 static ChildList childList;
|
|
71 static ClientList clientList;
|
|
72 static Reaper* reaper = NULL;
|
|
73
|
|
74 // Needed prototypes
|
|
75 void shutdownChild(ChildInfo* childInfo);
|
|
76 void detachClient(ClientInfo* clientInfo);
|
|
77 void shutdownClient(ClientInfo* clientInfo);
|
|
78
|
|
79 char *
|
|
80 longToDotFormat(long addr)
|
|
81 {
|
|
82 char *temp_s = new char[20];
|
|
83
|
|
84 sprintf(temp_s, "%d.%d.%d.%d", ((addr & 0xff000000) >> 24),
|
|
85 ((addr & 0x00ff0000) >> 16), ((addr & 0x0000ff00) >> 8),
|
|
86 (addr & 0x000000ff));
|
|
87
|
|
88 return temp_s;
|
|
89 }
|
|
90
|
|
91 // NOTE that we do this query every time. It is a bad idea to cache IP
|
|
92 // addresses. For example, we might be hosted on a machine using DHCP
|
|
93 // and the connection addresses might change over time. (Yes, this
|
|
94 // actually happened.)
|
|
95 bool
|
|
96 isConnectionOkay(ULONG connAddr) {
|
|
97 if (connAddr == INADDR_LOOPBACK) {
|
|
98 return true;
|
|
99 }
|
|
100
|
|
101 const int MAXNAME = 1024;
|
|
102 char myname[MAXNAME];
|
|
103 gethostname(myname, MAXNAME);
|
|
104 struct hostent* myInfo = gethostbyname(myname);
|
|
105 if (myInfo == NULL) {
|
|
106 #ifdef DEBUGGING
|
|
107 cerr << "My host information was null" << endl;
|
|
108 #endif
|
|
109 } else {
|
|
110 // Run down the list of IP addresses for myself
|
|
111 assert(myInfo->h_length == sizeof(ULONG));
|
|
112 #ifdef DEBUGGING
|
|
113 cerr << "My known IP addresses: " << endl;
|
|
114 #endif
|
|
115 for (char** pp = myInfo->h_addr_list; *pp != NULL; pp++) {
|
|
116 char* p = *pp;
|
|
117 ULONG altAddr = ntohl(*((ULONG*) p));
|
|
118 #ifdef DEBUGGING
|
|
119 char* name = longToDotFormat(altAddr);
|
|
120 cerr << name << endl;
|
|
121 delete[] name;
|
|
122 #endif
|
|
123 if (altAddr == connAddr) {
|
|
124 #ifdef DEBUGGING
|
|
125 cerr << "FOUND" << endl;
|
|
126 #endif
|
|
127 return true;
|
|
128 }
|
|
129 }
|
|
130 #ifdef DEBUGGING
|
|
131 cerr << "Done." << endl;
|
|
132 #endif
|
|
133 }
|
|
134
|
|
135 return false;
|
|
136 }
|
|
137
|
|
138 SOCKET
|
|
139 setupListeningSocket(short port) {
|
|
140 SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
|
|
141 if (listening == INVALID_SOCKET) {
|
|
142 cerr << "Error creating listening socket" << endl;
|
|
143 exit(1);
|
|
144 }
|
|
145
|
|
146 int reuseAddress = 1;
|
|
147 if (setsockopt(listening, SOL_SOCKET, SO_REUSEADDR,
|
|
148 (char *)&reuseAddress, sizeof(reuseAddress)) == -1) {
|
|
149 cerr << "Error reusing address" << endl;
|
|
150 exit(1);
|
|
151 }
|
|
152
|
|
153 struct sockaddr_in serverInfo;
|
|
154
|
|
155 memset((char *)&serverInfo, 0, sizeof(serverInfo));
|
|
156 serverInfo.sin_addr.s_addr = INADDR_ANY;
|
|
157 serverInfo.sin_family = AF_INET;
|
|
158 serverInfo.sin_port = htons(port);
|
|
159
|
|
160 if (bind(listening, (struct sockaddr *) &serverInfo, sizeof(serverInfo)) < 0) {
|
|
161 cerr << "Error binding socket" << endl;
|
|
162 exit(1);
|
|
163 }
|
|
164
|
|
165 if (listen(listening, 5) < 0) {
|
|
166 cerr << "Error listening" << endl;
|
|
167 exit(1);
|
|
168 }
|
|
169
|
|
170 return listening;
|
|
171 }
|
|
172
|
|
173 /** Accepts a connection from the given listening socket, but only if
|
|
174 the connection came from localhost. Returns INVALID_SOCKET if the
|
|
175 connection came from any other IP address or if an error occurred
|
|
176 during the call to accept(). */
|
|
177 SOCKET
|
|
178 acceptFromLocalhost(SOCKET listening) {
|
|
179 struct sockaddr_in peerAddr;
|
|
180 int peerAddrLen = sizeof(peerAddr);
|
|
181 SOCKET fd = accept(listening, (sockaddr*) &peerAddr, &peerAddrLen);
|
|
182 if (fd == INVALID_SOCKET) {
|
|
183 return fd;
|
|
184 }
|
|
185
|
|
186 if (!isConnectionOkay(ntohl(peerAddr.sin_addr.s_addr))) {
|
|
187 // Reject connections from other machines for security purposes.
|
|
188 // The Windows security model seems to assume one user per
|
|
189 // machine, and that security is compromised if another user is
|
|
190 // able to run executables on the given host. (If these
|
|
191 // assumptions are not strict enough, we will have to change
|
|
192 // this.)
|
|
193 shutdown(fd, SD_BOTH);
|
|
194 closesocket(fd);
|
|
195 return INVALID_SOCKET;
|
|
196 }
|
|
197
|
|
198 // Disable TCP buffering on all sockets. We send small amounts of
|
|
199 // data back and forth and don't want buffering.
|
|
200 int buffer_val = 1;
|
|
201 if (setsockopt(fd, IPPROTO_IP, TCP_NODELAY,
|
|
202 (char *) &buffer_val, sizeof(buffer_val)) < 0) {
|
|
203 shutdown(fd, SD_BOTH);
|
|
204 closesocket(fd);
|
|
205 }
|
|
206
|
|
207 return fd;
|
|
208 }
|
|
209
|
|
210 void
|
|
211 reapCB(void* arg) {
|
|
212 ChildInfo* info = (ChildInfo*) arg;
|
|
213 ListsLocker ll;
|
|
214 DWORD pid = info->getPid();
|
|
215 shutdownChild(info);
|
|
216 #ifdef DEBUGGING
|
|
217 cerr << "Reaped child for process " << pid << endl;
|
|
218 #endif
|
|
219 }
|
|
220
|
|
221 /** Starts a child process with stdin and stdout redirected to pipes,
|
|
222 handles to which are returned. auxHandle1 and auxHandle2 should be
|
|
223 closed as well when the child process exits. Returns false if
|
|
224 process creation failed. */
|
|
225 bool
|
|
226 startChildProcess(DWORD pidToDebug,
|
|
227 DWORD childStdinBufSize,
|
|
228 DWORD childStdoutBufSize,
|
|
229 LPHANDLE childProcessHandle,
|
|
230 LPHANDLE writeToStdinHandle,
|
|
231 LPHANDLE readFromStdoutHandle,
|
|
232 LPHANDLE auxHandle1,
|
|
233 LPHANDLE auxHandle2) {
|
|
234 // Code adapted from Microsoft example
|
|
235 // "Creating a Child Process with Redirected Input and Output"
|
|
236
|
|
237 SECURITY_ATTRIBUTES saAttr;
|
|
238 BOOL fSuccess;
|
|
239
|
|
240 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
|
|
241 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
|
|
242 hSaveStdin, hSaveStdout;
|
|
243
|
|
244 // Set the bInheritHandle flag so pipe handles are inherited.
|
|
245 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
246 saAttr.bInheritHandle = TRUE;
|
|
247 saAttr.lpSecurityDescriptor = NULL;
|
|
248
|
|
249 // The steps for redirecting child process's STDOUT:
|
|
250 // 1. Save current STDOUT, to be restored later.
|
|
251 // 2. Create anonymous pipe to be STDOUT for child process.
|
|
252 // 3. Set STDOUT of the parent process to be write handle to
|
|
253 // the pipe, so it is inherited by the child process.
|
|
254 // 4. Create a noninheritable duplicate of the read handle and
|
|
255 // close the inheritable read handle.
|
|
256
|
|
257 // Save the handle to the current STDOUT.
|
|
258 hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
259 // Create a pipe for the child process's STDOUT.
|
|
260 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, childStdoutBufSize)) {
|
|
261 return false;
|
|
262 }
|
|
263 // Set a write handle to the pipe to be STDOUT.
|
|
264 if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) {
|
|
265 return false;
|
|
266 }
|
|
267 // Create noninheritable read handle and close the inheritable read
|
|
268 // handle.
|
|
269 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
|
|
270 GetCurrentProcess(), &hChildStdoutRdDup,
|
|
271 0, FALSE,
|
|
272 DUPLICATE_SAME_ACCESS);
|
|
273 if( !fSuccess ) {
|
|
274 return false;
|
|
275 }
|
|
276 CloseHandle(hChildStdoutRd);
|
|
277
|
|
278 // The steps for redirecting child process's STDIN:
|
|
279 // 1. Save current STDIN, to be restored later.
|
|
280 // 2. Create anonymous pipe to be STDIN for child process.
|
|
281 // 3. Set STDIN of the parent to be the read handle to the
|
|
282 // pipe, so it is inherited by the child process.
|
|
283 // 4. Create a noninheritable duplicate of the write handle,
|
|
284 // and close the inheritable write handle.
|
|
285 // Save the handle to the current STDIN.
|
|
286 hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
|
|
287 // Create a pipe for the child process's STDIN.
|
|
288 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, childStdinBufSize)) {
|
|
289 return false;
|
|
290 }
|
|
291 // Set a read handle to the pipe to be STDIN.
|
|
292 if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) {
|
|
293 return false;
|
|
294 }
|
|
295 // Duplicate the write handle to the pipe so it is not inherited.
|
|
296 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
|
|
297 GetCurrentProcess(), &hChildStdinWrDup, 0,
|
|
298 FALSE, // not inherited
|
|
299 DUPLICATE_SAME_ACCESS);
|
|
300 if (! fSuccess) {
|
|
301 return false;
|
|
302 }
|
|
303 CloseHandle(hChildStdinWr);
|
|
304
|
|
305 // Create the child process
|
|
306 char cmdLine[256];
|
|
307 sprintf(cmdLine, "SwDbgSub.exe %u", pidToDebug);
|
|
308 PROCESS_INFORMATION procInfo;
|
|
309 STARTUPINFO startInfo;
|
|
310 memset((char*) &startInfo, 0, sizeof(startInfo));
|
|
311 startInfo.cb = sizeof(startInfo);
|
|
312 BOOL res = CreateProcess(NULL,
|
|
313 cmdLine,
|
|
314 NULL,
|
|
315 NULL,
|
|
316 TRUE, // inherit handles: important
|
|
317 0,
|
|
318 NULL,
|
|
319 NULL,
|
|
320 &startInfo,
|
|
321 &procInfo);
|
|
322 if (!res) {
|
|
323 return false;
|
|
324 }
|
|
325 // After process creation, restore the saved STDIN and STDOUT.
|
|
326 if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) {
|
|
327 return false;
|
|
328 }
|
|
329 if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
|
|
330 return false;
|
|
331 }
|
|
332
|
|
333 // hChildStdinWrDup can be used to write to the child's stdin
|
|
334 // hChildStdoutRdDup can be used to read from the child's stdout
|
|
335
|
|
336 // NOTE: example code closes hChildStdoutWr before reading from
|
|
337 // hChildStdoutRdDup. "Close the write end of the pipe before
|
|
338 // reading from the read end of the pipe"??? Looks like this is
|
|
339 // example-specific.
|
|
340
|
|
341 // Set up return arguments
|
|
342 // hChildStdoutRd and hChildStdinWr are already closed at this point
|
|
343 *childProcessHandle = procInfo.hProcess;
|
|
344 *writeToStdinHandle = hChildStdinWrDup;
|
|
345 *readFromStdoutHandle = hChildStdoutRdDup;
|
|
346 *auxHandle1 = hChildStdinRd;
|
|
347 *auxHandle2 = hChildStdoutWr;
|
|
348 return true;
|
|
349 }
|
|
350
|
|
351 /** Clears the event and writes the message to the child process */
|
|
352 bool
|
|
353 sendMessage(ChildInfo* child, Message* message) {
|
|
354 DWORD numBytesWritten;
|
|
355 if (!WriteFile(child->getWriteToStdinHandle(),
|
|
356 message, sizeof(Message), &numBytesWritten, NULL)) {
|
|
357 return false;
|
|
358 }
|
|
359 if (numBytesWritten != sizeof(Message)) {
|
|
360 return false;
|
|
361 }
|
|
362 // Follow up "poke" messages with the raw data
|
|
363 if (message->type == Message::POKE) {
|
|
364 if (!WriteFile(child->getWriteToStdinHandle(),
|
|
365 message->pokeArg.data, message->pokeArg.numBytes, &numBytesWritten, NULL)) {
|
|
366 return false;
|
|
367 }
|
|
368 if (numBytesWritten != message->pokeArg.numBytes) {
|
|
369 return false;
|
|
370 }
|
|
371 }
|
|
372 return true;
|
|
373 }
|
|
374
|
|
375 /** Copies data from child's stdout to the client's IOBuf and sends it
|
|
376 along */
|
|
377 bool
|
|
378 forwardReplyToClient(ChildInfo* child, ClientInfo* client) {
|
|
379 DWORD total = 0;
|
|
380 IOBuf::FillState ret;
|
|
381
|
|
382 do {
|
|
383 DWORD temp;
|
|
384 ret = client->getIOBuf()->fillFromFileHandle(child->getReadFromStdoutHandle(),
|
|
385 &temp);
|
|
386 if (ret == IOBuf::DONE || ret == IOBuf::MORE_DATA_PENDING) {
|
|
387 if (!client->getIOBuf()->flush()) {
|
|
388 #ifdef DEBUGGING
|
|
389 cerr << "Forward failed because flush failed" << endl;
|
|
390 #endif
|
|
391 return false;
|
|
392 }
|
|
393 total += temp;
|
|
394 }
|
|
395 } while (ret == IOBuf::MORE_DATA_PENDING);
|
|
396
|
|
397 return (ret == IOBuf::FAILED) ? false : true;
|
|
398 }
|
|
399
|
|
400 //----------------------------------------------------------------------
|
|
401 // Server Handler
|
|
402 //
|
|
403
|
|
404 class ServerHandler : public Handler {
|
|
405 public:
|
|
406 ServerHandler();
|
|
407
|
|
408 // Starts up in Unicode mode by default
|
|
409 bool getASCII();
|
|
410
|
|
411 void setIOBuf(IOBuf* ioBuf);
|
|
412
|
|
413 void procList(char* arg);
|
|
414
|
|
415 // Must be called before calling one of the routines below
|
|
416 void setClientInfo(ClientInfo* info);
|
|
417
|
|
418 // Indicates to outer loop that exit was called or that an error
|
|
419 // occurred and that the client exited.
|
|
420 bool exited();
|
|
421 // Clears this state
|
|
422 void clearExited();
|
|
423
|
|
424 void ascii(char* arg);
|
|
425 void unicode(char* arg);
|
|
426 void attach(char* arg);
|
|
427 void detach(char* arg);
|
|
428 void libInfo(char* arg);
|
|
429 void peek(char* arg);
|
|
430 void poke(char* arg);
|
|
431 void threadList(char* arg);
|
|
432 void dupHandle(char* arg);
|
|
433 void closeHandle(char* arg);
|
|
434 void getContext(char* arg);
|
|
435 void setContext(char* arg);
|
|
436 void selectorEntry(char* arg);
|
|
437 void suspend(char* arg);
|
|
438 void resume(char* arg);
|
|
439 void pollEvent(char* arg);
|
|
440 void continueEvent(char* arg);
|
|
441 void exit(char* arg);
|
|
442
|
|
443 // This is pretty gross. Needed to make the target process know
|
|
444 // about clients that have disconnected unexpectedly while attached.
|
|
445 friend void shutdownClient(ClientInfo*);
|
|
446 private:
|
|
447 // Writes: charSize <space> numChars <space> <binary string>
|
|
448 // Handles both ASCII and UNICODE modes
|
|
449 void writeString(USHORT len, WCHAR* str);
|
|
450
|
|
451 // Handles only ASCII mode
|
|
452 void writeString(USHORT len, char* str);
|
|
453
|
|
454 ClientInfo* clientInfo;
|
|
455 IOBuf* ioBuf;
|
|
456 bool _exited;
|
|
457 bool _ascii;
|
|
458 };
|
|
459
|
|
460 static ServerHandler* handler;
|
|
461
|
|
462 ServerHandler::ServerHandler() {
|
|
463 _exited = false;
|
|
464 _ascii = false;
|
|
465 ioBuf = NULL;
|
|
466 }
|
|
467
|
|
468 bool
|
|
469 ServerHandler::getASCII() {
|
|
470 return _ascii;
|
|
471 }
|
|
472
|
|
473 void
|
|
474 ServerHandler::setIOBuf(IOBuf* buf) {
|
|
475 ioBuf = buf;
|
|
476 }
|
|
477
|
|
478 void
|
|
479 ServerHandler::setClientInfo(ClientInfo* info) {
|
|
480 clientInfo = info;
|
|
481 }
|
|
482
|
|
483 bool
|
|
484 ServerHandler::exited() {
|
|
485 return _exited;
|
|
486 }
|
|
487
|
|
488 void
|
|
489 ServerHandler::clearExited() {
|
|
490 _exited = false;
|
|
491 }
|
|
492
|
|
493 void
|
|
494 ServerHandler::ascii(char* arg) {
|
|
495 _ascii = true;
|
|
496 }
|
|
497
|
|
498 void
|
|
499 ServerHandler::unicode(char* arg) {
|
|
500 _ascii = false;
|
|
501 }
|
|
502
|
|
503 void
|
|
504 ServerHandler::procList(char* arg) {
|
|
505 #ifdef DEBUGGING
|
|
506 cerr << "proclist" << endl;
|
|
507 #endif
|
|
508
|
|
509 ProcEntryList processes;
|
|
510 ::procList(processes);
|
|
511
|
|
512 ioBuf->writeInt(processes.size());
|
|
513
|
|
514 for (ProcEntryList::iterator iter = processes.begin();
|
|
515 iter != processes.end(); iter++) {
|
|
516 ProcEntry& entry = *iter;
|
|
517 ioBuf->writeSpace();
|
|
518 ioBuf->writeUnsignedInt(entry.getPid());
|
|
519 ioBuf->writeSpace();
|
|
520 writeString(entry.getNameLength(), entry.getName());
|
|
521 }
|
|
522
|
|
523 ioBuf->writeEOL();
|
|
524 ioBuf->flush();
|
|
525 }
|
|
526
|
|
527 void
|
|
528 ServerHandler::attach(char* arg) {
|
|
529 // If the client is already attached to a process, fail.
|
|
530 if (clientInfo->getTarget() != NULL) {
|
|
531 ioBuf->writeBoolAsInt(false);
|
|
532 ioBuf->writeEOL();
|
|
533 ioBuf->flush();
|
|
534 return;
|
|
535 }
|
|
536
|
|
537 // Try to get pid
|
|
538 DWORD pid;
|
|
539 if (!scanUnsignedLong(&arg, &pid)) {
|
|
540 ioBuf->writeBoolAsInt(false);
|
|
541 ioBuf->writeEOL();
|
|
542 ioBuf->flush();
|
|
543 return;
|
|
544 }
|
|
545
|
|
546 // See whether this pid is already forked
|
|
547 ListsLocker ll;
|
|
548 ChildInfo* childInfo = childList.getChildByPid(pid);
|
|
549 if (childInfo != NULL) {
|
|
550 // If this child already has a client, return false
|
|
551 if (childInfo->getClient() != NULL) {
|
|
552 ioBuf->writeBoolAsInt(false);
|
|
553 ioBuf->writeEOL();
|
|
554 ioBuf->flush();
|
|
555 return;
|
|
556 }
|
|
557
|
|
558 // Otherwise, can associate this client with this child process
|
|
559 childInfo->setClient(clientInfo);
|
|
560 clientInfo->setTarget(childInfo);
|
|
561
|
|
562 // Tell the child we are attaching so it can suspend the target
|
|
563 // process
|
|
564 Message msg;
|
|
565 msg.type = Message::ATTACH;
|
|
566 sendMessage(childInfo, &msg);
|
|
567
|
|
568 ioBuf->writeBoolAsInt(true);
|
|
569 ioBuf->writeEOL();
|
|
570 ioBuf->flush();
|
|
571 return;
|
|
572 } else {
|
|
573 // Have to fork a new child subprocess
|
|
574 HANDLE childProcessHandle;
|
|
575 HANDLE writeToStdinHandle;
|
|
576 HANDLE readFromStdoutHandle;
|
|
577 HANDLE auxHandle1;
|
|
578 HANDLE auxHandle2;
|
|
579 if (!startChildProcess(pid,
|
|
580 32768,
|
|
581 131072,
|
|
582 &childProcessHandle,
|
|
583 &writeToStdinHandle,
|
|
584 &readFromStdoutHandle,
|
|
585 &auxHandle1,
|
|
586 &auxHandle2)) {
|
|
587 ioBuf->writeBoolAsInt(false);
|
|
588 ioBuf->writeEOL();
|
|
589 ioBuf->flush();
|
|
590 return;
|
|
591 }
|
|
592
|
|
593 // See whether the child succeeded in attaching to the process
|
|
594 char res;
|
|
595 DWORD numRead;
|
|
596 if (!ReadFile(readFromStdoutHandle,
|
|
597 &res,
|
|
598 sizeof(char),
|
|
599 &numRead,
|
|
600 NULL)) {
|
|
601 ioBuf->writeBoolAsInt(false);
|
|
602 ioBuf->writeEOL();
|
|
603 ioBuf->flush();
|
|
604 return;
|
|
605 }
|
|
606
|
|
607 if (!res) {
|
|
608 ioBuf->writeBoolAsInt(false);
|
|
609 ioBuf->writeEOL();
|
|
610 ioBuf->flush();
|
|
611 return;
|
|
612 }
|
|
613
|
|
614 // OK, success.
|
|
615 childInfo = new ChildInfo(pid, childProcessHandle,
|
|
616 writeToStdinHandle, readFromStdoutHandle,
|
|
617 auxHandle1, auxHandle2);
|
|
618 childList.addChild(childInfo);
|
|
619 reaper->registerProcess(childProcessHandle, childInfo);
|
|
620 // Associate this client with this child process
|
|
621 childInfo->setClient(clientInfo);
|
|
622 clientInfo->setTarget(childInfo);
|
|
623
|
|
624 // Tell the child process to actually suspend the target process
|
|
625 Message msg;
|
|
626 msg.type = Message::ATTACH;
|
|
627 sendMessage(childInfo, &msg);
|
|
628
|
|
629 // Write result to client
|
|
630 ioBuf->writeBoolAsInt(true);
|
|
631 ioBuf->writeEOL();
|
|
632 ioBuf->flush();
|
|
633 return;
|
|
634 }
|
|
635 }
|
|
636
|
|
637 void
|
|
638 ServerHandler::detach(char* arg) {
|
|
639 // If the client is not attached, fail.
|
|
640 if (clientInfo->getTarget() == NULL) {
|
|
641 ioBuf->writeBoolAsInt(false);
|
|
642 ioBuf->writeEOL();
|
|
643 ioBuf->flush();
|
|
644 return;
|
|
645 }
|
|
646
|
|
647 detachClient(clientInfo);
|
|
648
|
|
649 ioBuf->writeBoolAsInt(true);
|
|
650 ioBuf->writeEOL();
|
|
651 ioBuf->flush();
|
|
652 }
|
|
653
|
|
654 void
|
|
655 ServerHandler::libInfo(char* arg) {
|
|
656 ListsLocker ll;
|
|
657 ChildInfo* child = clientInfo->getTarget();
|
|
658 if (child == NULL) {
|
|
659 ioBuf->writeInt(0);
|
|
660 ioBuf->writeEOL();
|
|
661 ioBuf->flush();
|
|
662 return;
|
|
663 }
|
|
664
|
|
665 // Send message to child
|
|
666 Message msg;
|
|
667 msg.type = Message::LIBINFO;
|
|
668 sendMessage(child, &msg);
|
|
669
|
|
670 // Forward reply to client
|
|
671 forwardReplyToClient(child, clientInfo);
|
|
672 }
|
|
673
|
|
674 void
|
|
675 ServerHandler::peek(char* arg) {
|
|
676 ListsLocker ll;
|
|
677 ChildInfo* child = clientInfo->getTarget();
|
|
678 if (child == NULL) {
|
|
679 ioBuf->writeString("B");
|
|
680 ioBuf->writeBinChar(0);
|
|
681 ioBuf->flush();
|
|
682 return;
|
|
683 }
|
|
684
|
|
685 // Try to get address
|
|
686 DWORD address;
|
|
687 if (!scanAddress(&arg, &address)) {
|
|
688 ioBuf->writeString("B");
|
|
689 ioBuf->writeBinChar(0);
|
|
690 ioBuf->flush();
|
|
691 return;
|
|
692 }
|
|
693
|
|
694 // Try to get number of bytes
|
|
695 DWORD numBytes;
|
|
696 if (!scanUnsignedLong(&arg, &numBytes)) {
|
|
697 ioBuf->writeString("B");
|
|
698 ioBuf->writeBinChar(0);
|
|
699 ioBuf->flush();
|
|
700 return;
|
|
701 }
|
|
702
|
|
703 // Send message to child
|
|
704 Message msg;
|
|
705 msg.type = Message::PEEK;
|
|
706 msg.peekArg.address = address;
|
|
707 msg.peekArg.numBytes = numBytes;
|
|
708 sendMessage(child, &msg);
|
|
709
|
|
710 // Forward reply to client
|
|
711 forwardReplyToClient(child, clientInfo);
|
|
712 }
|
|
713
|
|
714 void
|
|
715 ServerHandler::poke(char* arg) {
|
|
716 #ifdef DEBUGGING
|
|
717 cerr << "ServerHandler::poke" << endl;
|
|
718 #endif
|
|
719 ListsLocker ll;
|
|
720 ChildInfo* child = clientInfo->getTarget();
|
|
721 if (child == NULL) {
|
|
722 ioBuf->writeBoolAsInt(false);
|
|
723 ioBuf->flush();
|
|
724 return;
|
|
725 }
|
|
726
|
|
727 // Try to get address
|
|
728 DWORD address;
|
|
729 if (!scanAddress(&arg, &address)) {
|
|
730 ioBuf->writeBoolAsInt(false);
|
|
731 ioBuf->flush();
|
|
732 return;
|
|
733 }
|
|
734
|
|
735 // Try to get number of bytes
|
|
736 if (!scanAndSkipBinEscapeChar(&arg)) {
|
|
737 ioBuf->writeBoolAsInt(false);
|
|
738 ioBuf->flush();
|
|
739 return;
|
|
740 }
|
|
741 DWORD numBytes;
|
|
742 if (!scanBinUnsignedLong(&arg, &numBytes)) {
|
|
743 ioBuf->writeBoolAsInt(false);
|
|
744 ioBuf->flush();
|
|
745 return;
|
|
746 }
|
|
747
|
|
748 // Raw data is now in "arg"
|
|
749 // Send message to child
|
|
750 Message msg;
|
|
751 msg.type = Message::POKE;
|
|
752 msg.pokeArg.address = address;
|
|
753 msg.pokeArg.numBytes = numBytes;
|
|
754 msg.pokeArg.data = arg;
|
|
755 sendMessage(child, &msg);
|
|
756
|
|
757 // Forward reply to client
|
|
758 forwardReplyToClient(child, clientInfo);
|
|
759 }
|
|
760
|
|
761 void
|
|
762 ServerHandler::threadList(char* arg) {
|
|
763 ListsLocker ll;
|
|
764 ChildInfo* child = clientInfo->getTarget();
|
|
765 if (child == NULL) {
|
|
766 ioBuf->writeBoolAsInt(false);
|
|
767 ioBuf->flush();
|
|
768 return;
|
|
769 }
|
|
770
|
|
771 // Send message to child
|
|
772 Message msg;
|
|
773 msg.type = Message::THREADLIST;
|
|
774 sendMessage(child, &msg);
|
|
775
|
|
776 // Forward reply to client
|
|
777 forwardReplyToClient(child, clientInfo);
|
|
778 }
|
|
779
|
|
780 void
|
|
781 ServerHandler::dupHandle(char* arg) {
|
|
782 ListsLocker ll;
|
|
783 ChildInfo* child = clientInfo->getTarget();
|
|
784 if (child == NULL) {
|
|
785 ioBuf->writeBoolAsInt(false);
|
|
786 ioBuf->flush();
|
|
787 return;
|
|
788 }
|
|
789
|
|
790 // Try to get handle
|
|
791 DWORD address;
|
|
792 if (!scanAddress(&arg, &address)) {
|
|
793 ioBuf->writeBoolAsInt(false);
|
|
794 ioBuf->flush();
|
|
795 }
|
|
796
|
|
797 // Send message to child
|
|
798 Message msg;
|
|
799 msg.type = Message::DUPHANDLE;
|
|
800 msg.handleArg.handle = (HANDLE) address;
|
|
801 sendMessage(child, &msg);
|
|
802
|
|
803 // Forward reply to client
|
|
804 forwardReplyToClient(child, clientInfo);
|
|
805 }
|
|
806
|
|
807 void
|
|
808 ServerHandler::closeHandle(char* arg) {
|
|
809 ListsLocker ll;
|
|
810 ChildInfo* child = clientInfo->getTarget();
|
|
811 if (child == NULL) {
|
|
812 return;
|
|
813 }
|
|
814
|
|
815 // Try to get handle
|
|
816 DWORD address;
|
|
817 if (!scanAddress(&arg, &address)) {
|
|
818 return;
|
|
819 }
|
|
820
|
|
821 // Send message to child
|
|
822 Message msg;
|
|
823 msg.type = Message::CLOSEHANDLE;
|
|
824 msg.handleArg.handle = (HANDLE) address;
|
|
825 sendMessage(child, &msg);
|
|
826
|
|
827 // No reply
|
|
828 }
|
|
829
|
|
830 void
|
|
831 ServerHandler::getContext(char* arg) {
|
|
832 ListsLocker ll;
|
|
833 ChildInfo* child = clientInfo->getTarget();
|
|
834 if (child == NULL) {
|
|
835 ioBuf->writeBoolAsInt(false);
|
|
836 ioBuf->flush();
|
|
837 return;
|
|
838 }
|
|
839
|
|
840 // Try to get handle
|
|
841 DWORD address;
|
|
842 if (!scanAddress(&arg, &address)) {
|
|
843 ioBuf->writeBoolAsInt(false);
|
|
844 ioBuf->flush();
|
|
845 return;
|
|
846 }
|
|
847
|
|
848 // Send message to child
|
|
849 Message msg;
|
|
850 msg.type = Message::GETCONTEXT;
|
|
851 msg.handleArg.handle = (HANDLE) address;
|
|
852 sendMessage(child, &msg);
|
|
853
|
|
854 // Forward reply to client
|
|
855 forwardReplyToClient(child, clientInfo);
|
|
856 }
|
|
857
|
|
858 void
|
|
859 ServerHandler::setContext(char* arg) {
|
|
860 ListsLocker ll;
|
|
861 ChildInfo* child = clientInfo->getTarget();
|
|
862 if (child == NULL) {
|
|
863 ioBuf->writeBoolAsInt(false);
|
|
864 ioBuf->flush();
|
|
865 return;
|
|
866 }
|
|
867
|
|
868 // Try to get handle
|
|
869 DWORD address;
|
|
870 if (!scanAddress(&arg, &address)) {
|
|
871 ioBuf->writeBoolAsInt(false);
|
|
872 ioBuf->flush();
|
|
873 return;
|
|
874 }
|
|
875
|
|
876 // Try to get context
|
|
877 DWORD regs[NUM_REGS_IN_CONTEXT];
|
|
878 for (int i = 0; i < NUM_REGS_IN_CONTEXT; i++) {
|
|
879 if (!scanAddress(&arg, ®s[i])) {
|
|
880 ioBuf->writeBoolAsInt(false);
|
|
881 ioBuf->flush();
|
|
882 return;
|
|
883 }
|
|
884 }
|
|
885
|
|
886 // Send message to child
|
|
887 Message msg;
|
|
888 msg.type = Message::SETCONTEXT;
|
|
889 msg.setContextArg.handle = (HANDLE) address;
|
|
890 msg.setContextArg.Eax = regs[0];
|
|
891 msg.setContextArg.Ebx = regs[1];
|
|
892 msg.setContextArg.Ecx = regs[2];
|
|
893 msg.setContextArg.Edx = regs[3];
|
|
894 msg.setContextArg.Esi = regs[4];
|
|
895 msg.setContextArg.Edi = regs[5];
|
|
896 msg.setContextArg.Ebp = regs[6];
|
|
897 msg.setContextArg.Esp = regs[7];
|
|
898 msg.setContextArg.Eip = regs[8];
|
|
899 msg.setContextArg.Ds = regs[9];
|
|
900 msg.setContextArg.Es = regs[10];
|
|
901 msg.setContextArg.Fs = regs[11];
|
|
902 msg.setContextArg.Gs = regs[12];
|
|
903 msg.setContextArg.Cs = regs[13];
|
|
904 msg.setContextArg.Ss = regs[14];
|
|
905 msg.setContextArg.EFlags = regs[15];
|
|
906 msg.setContextArg.Dr0 = regs[16];
|
|
907 msg.setContextArg.Dr1 = regs[17];
|
|
908 msg.setContextArg.Dr2 = regs[18];
|
|
909 msg.setContextArg.Dr3 = regs[19];
|
|
910 msg.setContextArg.Dr6 = regs[20];
|
|
911 msg.setContextArg.Dr7 = regs[21];
|
|
912 sendMessage(child, &msg);
|
|
913
|
|
914 // Forward reply to client
|
|
915 forwardReplyToClient(child, clientInfo);
|
|
916 }
|
|
917
|
|
918 void
|
|
919 ServerHandler::selectorEntry(char* arg) {
|
|
920 ListsLocker ll;
|
|
921 ChildInfo* child = clientInfo->getTarget();
|
|
922 if (child == NULL) {
|
|
923 ioBuf->writeBoolAsInt(false);
|
|
924 ioBuf->flush();
|
|
925 return;
|
|
926 }
|
|
927
|
|
928 // Try to get thread handle
|
|
929 DWORD address;
|
|
930 if (!scanAddress(&arg, &address)) {
|
|
931 ioBuf->writeBoolAsInt(false);
|
|
932 ioBuf->flush();
|
|
933 return;
|
|
934 }
|
|
935
|
|
936 // Try to get selector
|
|
937 DWORD selector;
|
|
938 if (!scanUnsignedLong(&arg, &selector)) {
|
|
939 ioBuf->writeBoolAsInt(false);
|
|
940 ioBuf->flush();
|
|
941 return;
|
|
942 }
|
|
943
|
|
944 // Send message to child
|
|
945 Message msg;
|
|
946 msg.type = Message::SELECTORENTRY;
|
|
947 msg.selectorArg.handle = (HANDLE) address;
|
|
948 msg.selectorArg.selector = selector;
|
|
949 sendMessage(child, &msg);
|
|
950
|
|
951 // Forward reply to client
|
|
952 forwardReplyToClient(child, clientInfo);
|
|
953 }
|
|
954
|
|
955 void
|
|
956 ServerHandler::suspend(char* arg) {
|
|
957 ListsLocker ll;
|
|
958 ChildInfo* child = clientInfo->getTarget();
|
|
959 if (child == NULL) {
|
|
960 return;
|
|
961 }
|
|
962
|
|
963 // Send message to child
|
|
964 Message msg;
|
|
965 msg.type = Message::SUSPEND;
|
|
966 sendMessage(child, &msg);
|
|
967
|
|
968 // No reply
|
|
969 }
|
|
970
|
|
971 void
|
|
972 ServerHandler::resume(char* arg) {
|
|
973 ListsLocker ll;
|
|
974 ChildInfo* child = clientInfo->getTarget();
|
|
975 if (child == NULL) {
|
|
976 return;
|
|
977 }
|
|
978
|
|
979 // Send message to child
|
|
980 Message msg;
|
|
981 msg.type = Message::RESUME;
|
|
982 sendMessage(child, &msg);
|
|
983
|
|
984 // No reply
|
|
985 }
|
|
986
|
|
987 void
|
|
988 ServerHandler::pollEvent(char* arg) {
|
|
989 ListsLocker ll;
|
|
990 ChildInfo* child = clientInfo->getTarget();
|
|
991 if (child == NULL) {
|
|
992 ioBuf->writeBoolAsInt(false);
|
|
993 ioBuf->flush();
|
|
994 return;
|
|
995 }
|
|
996
|
|
997 // Send message to child
|
|
998 Message msg;
|
|
999 msg.type = Message::POLLEVENT;
|
|
1000 sendMessage(child, &msg);
|
|
1001
|
|
1002 // Forward reply to client
|
|
1003 forwardReplyToClient(child, clientInfo);
|
|
1004 }
|
|
1005
|
|
1006 void
|
|
1007 ServerHandler::continueEvent(char* arg) {
|
|
1008 ListsLocker ll;
|
|
1009 ChildInfo* child = clientInfo->getTarget();
|
|
1010 if (child == NULL) {
|
|
1011 ioBuf->writeBoolAsInt(false);
|
|
1012 ioBuf->flush();
|
|
1013 return;
|
|
1014 }
|
|
1015
|
|
1016 // Try to get bool arg
|
|
1017 int passEventToClient;
|
|
1018 if (!scanInt(&arg, &passEventToClient)) {
|
|
1019 ioBuf->writeBoolAsInt(false);
|
|
1020 ioBuf->flush();
|
|
1021 return;
|
|
1022 }
|
|
1023
|
|
1024 // Send message to child
|
|
1025 Message msg;
|
|
1026 msg.type = Message::CONTINUEEVENT;
|
|
1027 msg.boolArg.val = ((passEventToClient != 0) ? true : false);
|
|
1028 sendMessage(child, &msg);
|
|
1029
|
|
1030 // Forward reply to client
|
|
1031 forwardReplyToClient(child, clientInfo);
|
|
1032 }
|
|
1033
|
|
1034 void
|
|
1035 ServerHandler::exit(char* arg) {
|
|
1036 shutdownClient(clientInfo);
|
|
1037 _exited = true;
|
|
1038 }
|
|
1039
|
|
1040 void
|
|
1041 ServerHandler::writeString(USHORT len, WCHAR* str) {
|
|
1042 if (_ascii) {
|
|
1043 char* cStr = new char[len + 1];
|
|
1044 sprintf(cStr, "%.*ls", len, str);
|
|
1045 writeString(len, cStr);
|
|
1046 delete[] cStr;
|
|
1047 } else {
|
|
1048 ioBuf->writeInt(sizeof(unsigned short));
|
|
1049 ioBuf->writeSpace();
|
|
1050 ioBuf->writeInt(len);
|
|
1051 ioBuf->writeSpace();
|
|
1052 for (int i = 0; i < len; i++) {
|
|
1053 ioBuf->writeBinUnsignedShort(str[i]);
|
|
1054 }
|
|
1055 }
|
|
1056 }
|
|
1057
|
|
1058 void
|
|
1059 ServerHandler::writeString(USHORT len, char* str) {
|
|
1060 ioBuf->writeInt(1);
|
|
1061 ioBuf->writeSpace();
|
|
1062 ioBuf->writeInt(len);
|
|
1063 ioBuf->writeSpace();
|
|
1064 ioBuf->writeString(str);
|
|
1065 }
|
|
1066
|
|
1067 //
|
|
1068 //----------------------------------------------------------------------
|
|
1069
|
|
1070 //----------------------------------------------------------------------
|
|
1071 // Shutdown routines
|
|
1072 //
|
|
1073
|
|
1074 void
|
|
1075 shutdownChild(ChildInfo* childInfo) {
|
|
1076 childList.removeChild(childInfo);
|
|
1077 childInfo->closeAll();
|
|
1078 if (childInfo->getClient() != NULL) {
|
|
1079 shutdownClient(childInfo->getClient());
|
|
1080 }
|
|
1081 delete childInfo;
|
|
1082 }
|
|
1083
|
|
1084 void
|
|
1085 detachClient(ClientInfo* info) {
|
|
1086 ListsLocker ll;
|
|
1087 // May have been dissociated while not under cover of lock
|
|
1088 if (info->getTarget() == NULL) {
|
|
1089 return;
|
|
1090 }
|
|
1091
|
|
1092 // Tell the child that we have detached to let the target process
|
|
1093 // continue running
|
|
1094 Message msg;
|
|
1095 msg.type = Message::DETACH;
|
|
1096 sendMessage(info->getTarget(), &msg);
|
|
1097
|
|
1098 // Dissociate the client and the target
|
|
1099 info->getTarget()->setClient(NULL);
|
|
1100 info->setTarget(NULL);
|
|
1101 }
|
|
1102
|
|
1103 void
|
|
1104 shutdownClient(ClientInfo* clientInfo) {
|
|
1105 #ifdef DEBUGGING
|
|
1106 cerr << "Shutting down client" << endl;
|
|
1107 #endif
|
|
1108
|
|
1109 // If we're connected, inform the target process that we're
|
|
1110 // disconnecting
|
|
1111 detachClient(clientInfo);
|
|
1112
|
|
1113 // Remove this client from the list and delete it
|
|
1114 clientList.removeClient(clientInfo);
|
|
1115 if (clientInfo->getTarget() != NULL) {
|
|
1116 clientInfo->getTarget()->setClient(NULL);
|
|
1117 }
|
|
1118 clientInfo->closeAll();
|
|
1119 delete clientInfo;
|
|
1120 }
|
|
1121
|
|
1122 //
|
|
1123 //----------------------------------------------------------------------
|
|
1124
|
|
1125
|
|
1126 /** Main dispatcher for client commands. NOTE: do not refer to this
|
|
1127 clientInfo data structure after calling this routine, as it may be
|
|
1128 deleted internally. */
|
|
1129 void
|
|
1130 readAndDispatch(ClientInfo* clientInfo) {
|
|
1131 IOBuf::ReadLineResult res;
|
|
1132 IOBuf* ioBuf = clientInfo->getIOBuf();
|
|
1133 unsigned long howMany;
|
|
1134 ioctlsocket(clientInfo->getDataSocket(), FIONREAD, &howMany);
|
|
1135 if (howMany == 0) {
|
|
1136 // Client closed down.
|
|
1137 shutdownClient(clientInfo);
|
|
1138 return;
|
|
1139 }
|
|
1140 // Read and process as much data as possible
|
|
1141 do {
|
|
1142 res = ioBuf->tryReadLine();
|
|
1143 if (res == IOBuf::RL_ERROR) {
|
|
1144 #ifdef DEBUGGING
|
|
1145 cerr << "Error while reading line" << endl;
|
|
1146 #endif
|
|
1147 shutdownClient(clientInfo);
|
|
1148 return;
|
|
1149 } else if (res == IOBuf::RL_GOT_DATA) {
|
|
1150 #ifdef DEBUGGING
|
|
1151 cerr << "Got data: \"" << ioBuf->getLine() << "\"" << endl;
|
|
1152 #endif
|
|
1153 handler->setIOBuf(ioBuf);
|
|
1154 handler->setClientInfo(clientInfo);
|
|
1155 handler->clearExited();
|
|
1156 Dispatcher::dispatch(ioBuf->getLine(), handler);
|
|
1157 }
|
|
1158 } while (res == IOBuf::RL_GOT_DATA && (!handler->exited()));
|
|
1159 #ifdef DEBUGGING
|
|
1160 cerr << "Exiting readAndDispatch" << endl;
|
|
1161 #endif
|
|
1162 }
|
|
1163
|
|
1164 int
|
|
1165 main(int argc, char **argv)
|
|
1166 {
|
|
1167 initWinsock();
|
|
1168
|
|
1169 if (isNT4()) {
|
|
1170 loadPSAPIDLL(); // Will exit if not present
|
|
1171 }
|
|
1172
|
|
1173 SOCKET clientListeningSock = setupListeningSocket(CLIENT_PORT);
|
|
1174
|
|
1175 handler = new ServerHandler();
|
|
1176 Lists::init();
|
|
1177
|
|
1178 reaper = new Reaper(&reapCB);
|
|
1179 if (!reaper->start()) {
|
|
1180 exit(1);
|
|
1181 }
|
|
1182
|
|
1183 while (true) {
|
|
1184 // Select on all sockets:
|
|
1185 // - client listening socket
|
|
1186 // - sockets for all client connections
|
|
1187
|
|
1188 // When one of the client connections closes, close its socket
|
|
1189 // handles.
|
|
1190
|
|
1191 fd_set set;
|
|
1192 SOCKET maxSock = 0;
|
|
1193
|
|
1194 // Set up fd_set
|
|
1195 {
|
|
1196 int i;
|
|
1197 FD_ZERO(&set);
|
|
1198 FD_SET(clientListeningSock, &set);
|
|
1199 if (clientListeningSock > maxSock) {
|
|
1200 maxSock = clientListeningSock;
|
|
1201 }
|
|
1202 for (i = 0; i < clientList.size(); i++) {
|
|
1203 ClientInfo* info = clientList.get(i);
|
|
1204 if (info->getDataSocket() > maxSock) {
|
|
1205 maxSock = info->getDataSocket();
|
|
1206 }
|
|
1207 FD_SET(info->getDataSocket(), &set);
|
|
1208 }
|
|
1209 }
|
|
1210 struct timeval timeout;
|
|
1211 timeout.tv_sec = 300; // 5 minutes
|
|
1212 timeout.tv_usec = 0;
|
|
1213 int res = select(maxSock, &set, NULL, NULL, &timeout);
|
|
1214 if (res > 0) {
|
|
1215
|
|
1216 ////////////////
|
|
1217 // New client //
|
|
1218 ////////////////
|
|
1219 if (FD_ISSET(clientListeningSock, &set)) {
|
|
1220 SOCKET fd = acceptFromLocalhost(clientListeningSock);
|
|
1221 if (fd != INVALID_SOCKET) {
|
|
1222 // Create new client information object
|
|
1223 ClientInfo* info = new ClientInfo(fd);
|
|
1224 // Add to list of clients
|
|
1225 clientList.addClient(info);
|
|
1226 #ifdef DEBUGGING
|
|
1227 cerr << "New client" << endl;
|
|
1228 #endif
|
|
1229 }
|
|
1230 }
|
|
1231
|
|
1232 ///////////////////////////
|
|
1233 // Commands from clients //
|
|
1234 ///////////////////////////
|
|
1235 ClientInfo* clientInfo;
|
|
1236 if (clientList.isAnyDataSocketSet(&set, &clientInfo)) {
|
|
1237 readAndDispatch(clientInfo);
|
|
1238 }
|
|
1239 } else if (res < 0) {
|
|
1240 // Looks like one of the clients was killed. Try to figure out which one.
|
|
1241 bool found = false;
|
|
1242 fd_set set;
|
|
1243 struct timeval timeout;
|
|
1244 timeout.tv_sec = 0;
|
|
1245 timeout.tv_usec = 0;
|
|
1246 for (int i = 0; i < clientList.size(); i++) {
|
|
1247 ClientInfo* info = clientList.get(i);
|
|
1248 FD_ZERO(&set);
|
|
1249 FD_SET(info->getDataSocket(), &set);
|
|
1250 if (select(1 + info->getDataSocket(), &set, NULL, NULL, &timeout) < 0) {
|
|
1251 found = true;
|
|
1252 clientList.removeClient(info);
|
|
1253 info->closeAll();
|
|
1254 delete info;
|
|
1255 break;
|
|
1256 }
|
|
1257 }
|
|
1258 if (!found) {
|
|
1259 // This indicates trouble -- one of our listening sockets died.
|
|
1260 exit(1);
|
|
1261 }
|
|
1262 }
|
|
1263 }
|
|
1264
|
|
1265 return 0;
|
|
1266 }
|