1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
|
/*
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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#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
|