/* This file was written by Loris Degioanni, and is part of Kismet Kismet 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. Kismet 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 Kismet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #ifdef SYS_CYGWIN #include #include #include #include #define HANDLE2FD_INTERNAL #include "cygwin_utils.h" // // There are two main parameters that can be used to tune the WinPcap performance: // mintocopy and the read event timeout. // // Mintocopy is the minimum amount of data in the kernel buffer that causes the read event to // be set by the driver. A small mintocopy means good responisiveness but high CPU load. A big // mintocopy forces bigger kernel buffering, at the cost of low responsiveness // // The read event timeout can be used to check the availability of data once in a while. When the // timeout expires, the application will unblock and perform a read, even if the driver doesn't // have mintocopy bytes in the buffer. // // Using the timeout prevents kismet from sitting forver before processing the packets when traffic // is low, but can cause empty reads. Therefore, we set it to a large enough interval that the // performace hit is neglibile. // #define THREAD_WAIT_INTERVAL 500 Handle2Fd::Handle2Fd() { NHandles = 1; ThreadAlive = 1; PipeSignalled = 0; WaitThreadHandle = NULL; FirstFdSet = 1; InitializeCriticalSection(&PipeCs); } Handle2Fd::~Handle2Fd() { // Kill the thread and wait until he's returned ThreadAlive = 0; SetEvent(WinHandles[0]); WaitForSingleObject(WaitThreadHandle, INFINITE); } // Set the pipe fd so that it unblocks select void Handle2Fd::SetPipe() { int val; EnterCriticalSection(&PipeCs); if (!PipeSignalled) { write(PipeFds[1], &val, sizeof(val)); fdatasync(PipeFds[1]); PipeSignalled = 1; } LeaveCriticalSection(&PipeCs); } // Reset the pipe fd so that it blocks select void Handle2Fd::ResetPipe() { int val; EnterCriticalSection(&PipeCs); // First, write something to be sure the read will not block write(PipeFds[1], &val, sizeof(val)); fdatasync(PipeFds[1]); // Second, we drain the pipe while(read(PipeFds[0], ResetBuf, sizeof(ResetBuf)) == sizeof(ResetBuf)); // Third, we clear the signalled flag PipeSignalled = 0; LeaveCriticalSection(&PipeCs); } // This thread handles asynchronously waiting on the Windows events. // It signals the pipe if one or more events are set. DWORD WINAPI Handle2Fd::WaitThread(LPVOID lpParameter) { DWORD WaitRes; Handle2Fd* This = (Handle2Fd*)lpParameter; while (This->ThreadAlive) { WaitRes = WaitForMultipleObjects(This->NHandles, This->WinHandles, FALSE, THREAD_WAIT_INTERVAL); // Event number 0 is the service event used to kill the thread if (WaitRes != WAIT_OBJECT_0) { ResetEvent(This->ReadEvent); This->SetPipe(); WaitForSingleObject(This->ReadEvent, INFINITE); } } return 1; } // Mark a signal as read void Handle2Fd::Signalread() { SetEvent(ReadEvent); } // Activate this instance of the Handle2Fd class. // This involves creating the pipe, the service event and the support thread int Handle2Fd::Activate() { // Create the pipe if (pipe(PipeFds) != 0) { return -1; } // The fd stars in non-signaled state ResetPipe(); // Create the event for pipe control, and put it in our list WinHandles[0] = CreateEvent(NULL, TRUE, FALSE, NULL); if (!WinHandles[0]) { close(PipeFds[0]); close(PipeFds[1]); return -1; } // Create the event that will syncronize us with the read loop ReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!ReadEvent) { close(PipeFds[0]); close(PipeFds[1]); CloseHandle(WinHandles[0]); return -1; } // Start the thread that does the handle checking if ((WaitThreadHandle = CreateThread( NULL, 0, Handle2Fd::WaitThread, this, 0, NULL)) == NULL) { close(PipeFds[0]); close(PipeFds[1]); CloseHandle(WinHandles[0]); CloseHandle(ReadEvent); return -1; } return 1; } // The pipe exported by the Handle2Fd class requires manual reset. void Handle2Fd::Reset() { ResetPipe(); } // Add a new handle to the class int Handle2Fd::AddHandle(HANDLE h) { // If the thread is running, we don't accept new handles. This reduces the syncronization requirements if (!WaitThreadHandle) { if (NHandles < sizeof(WinHandles) / sizeof(WinHandles[0]) - 1) { WinHandles[NHandles++] = h; return 1; } } return -1; } // Get the pipe file descriptor. int Handle2Fd::GetFd() { return PipeFds[0]; } // Kismet-like MergeSet function unsigned int Handle2Fd::MergeSet(fd_set *set, unsigned int max) { Reset(); // Manual reset if (!FD_ISSET(GetFd(), set)) { FD_SET(PipeFds[0], set); if (FirstFdSet) { max++; FirstFdSet = 0; } } return max; } // Nonzero if the HandleNumber event is set int Handle2Fd::IsEventSet(unsigned int HandleNumber) { if (WaitForSingleObject(WinHandles[HandleNumber + 1], 0) == WAIT_OBJECT_0) { return 1; } else { return 0; } } #endif