1335640Shselasky/*
2335640Shselasky * Copyright (c) 2008 CACE Technologies, Davis (California)
3335640Shselasky * All rights reserved.
4335640Shselasky *
5335640Shselasky * Redistribution and use in source and binary forms, with or without
6335640Shselasky * modification, are permitted provided that the following conditions
7335640Shselasky * are met:
8335640Shselasky *
9335640Shselasky * 1. Redistributions of source code must retain the above copyright
10335640Shselasky * notice, this list of conditions and the following disclaimer.
11335640Shselasky * 2. Redistributions in binary form must reproduce the above copyright
12335640Shselasky * notice, this list of conditions and the following disclaimer in the
13335640Shselasky * documentation and/or other materials provided with the distribution.
14335640Shselasky * 3. Neither the name of CACE Technologies nor the names of its
15335640Shselasky * contributors may be used to endorse or promote products derived from
16335640Shselasky * this software without specific prior written permission.
17335640Shselasky *
18335640Shselasky * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19335640Shselasky * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20335640Shselasky * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21335640Shselasky * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22335640Shselasky * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23335640Shselasky * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24335640Shselasky * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25335640Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26335640Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27335640Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28335640Shselasky * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29335640Shselasky *
30335640Shselasky */
31335640Shselasky
32335640Shselasky#ifdef HAVE_CONFIG_H
33335640Shselasky#include <config.h>
34335640Shselasky#endif
35335640Shselasky
36335640Shselasky#include <pcap.h>
37335640Shselasky#include <pcap-int.h>
38335640Shselasky
39335640Shselasky#include "pcap-tc.h"
40335640Shselasky
41335640Shselasky#include <malloc.h>
42335640Shselasky#include <memory.h>
43335640Shselasky#include <string.h>
44335640Shselasky#include <errno.h>
45335640Shselasky
46335640Shselasky#ifdef _WIN32
47335640Shselasky#include <tchar.h>
48335640Shselasky#endif
49335640Shselasky
50335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnQueryPortList)			(PTC_PORT *ppPorts, PULONG pLength);
51335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnFreePortList)			(TC_PORT *pPorts);
52335640Shselasky
53335640Shselaskytypedef PCHAR		(TC_CALLCONV *TcFcnStatusGetString)			(TC_STATUS status);
54335640Shselasky
55335640Shselaskytypedef PCHAR		(TC_CALLCONV *TcFcnPortGetName)				(TC_PORT port);
56335640Shselaskytypedef PCHAR		(TC_CALLCONV *TcFcnPortGetDescription)		(TC_PORT port);
57335640Shselasky
58335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnInstanceOpenByName)		(PCHAR name, PTC_INSTANCE pInstance);
59335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnInstanceClose)			(TC_INSTANCE instance);
60335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnInstanceSetFeature)		(TC_INSTANCE instance, ULONG feature, ULONG value);
61335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnInstanceQueryFeature)	(TC_INSTANCE instance, ULONG feature, PULONG pValue);
62335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnInstanceReceivePackets)	(TC_INSTANCE instance, PTC_PACKETS_BUFFER pBuffer);
63335640Shselaskytypedef HANDLE		(TC_CALLCONV *TcFcnInstanceGetReceiveWaitHandle) (TC_INSTANCE instance);
64335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnInstanceTransmitPackets)	(TC_INSTANCE instance, TC_PACKETS_BUFFER pBuffer);
65335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnInstanceQueryStatistics)	(TC_INSTANCE instance, PTC_STATISTICS pStatistics);
66335640Shselasky
67335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnPacketsBufferCreate)		(ULONG size, PTC_PACKETS_BUFFER pBuffer);
68335640Shselaskytypedef VOID		(TC_CALLCONV *TcFcnPacketsBufferDestroy)	(TC_PACKETS_BUFFER buffer);
69335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnPacketsBufferQueryNextPacket)(TC_PACKETS_BUFFER buffer, PTC_PACKET_HEADER pHeader, PVOID *ppData);
70335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnPacketsBufferCommitNextPacket)(TC_PACKETS_BUFFER buffer, PTC_PACKET_HEADER pHeader, PVOID pData);
71335640Shselasky
72335640Shselaskytypedef VOID		(TC_CALLCONV *TcFcnStatisticsDestroy)		(TC_STATISTICS statistics);
73335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnStatisticsUpdate)		(TC_STATISTICS statistics);
74335640Shselaskytypedef TC_STATUS	(TC_CALLCONV *TcFcnStatisticsQueryValue)	(TC_STATISTICS statistics, ULONG counterId, PULONGLONG pValue);
75335640Shselasky
76335640Shselaskytypedef enum LONG
77335640Shselasky{
78335640Shselasky	TC_API_UNLOADED = 0,
79335640Shselasky	TC_API_LOADED,
80335640Shselasky	TC_API_CANNOT_LOAD,
81335640Shselasky	TC_API_LOADING
82335640Shselasky}
83335640Shselasky	TC_API_LOAD_STATUS;
84335640Shselasky
85335640Shselasky
86335640Shselaskytypedef struct _TC_FUNCTIONS
87335640Shselasky{
88335640Shselasky	TC_API_LOAD_STATUS			LoadStatus;
89335640Shselasky#ifdef _WIN32
90335640Shselasky	HMODULE						hTcApiDllHandle;
91335640Shselasky#endif
92335640Shselasky	TcFcnQueryPortList			QueryPortList;
93335640Shselasky	TcFcnFreePortList			FreePortList;
94335640Shselasky	TcFcnStatusGetString		StatusGetString;
95335640Shselasky
96335640Shselasky	TcFcnPortGetName			PortGetName;
97335640Shselasky	TcFcnPortGetDescription		PortGetDescription;
98335640Shselasky
99335640Shselasky	TcFcnInstanceOpenByName		InstanceOpenByName;
100335640Shselasky	TcFcnInstanceClose			InstanceClose;
101335640Shselasky	TcFcnInstanceSetFeature		InstanceSetFeature;
102335640Shselasky	TcFcnInstanceQueryFeature	InstanceQueryFeature;
103335640Shselasky	TcFcnInstanceReceivePackets	InstanceReceivePackets;
104335640Shselasky#ifdef _WIN32
105335640Shselasky	TcFcnInstanceGetReceiveWaitHandle InstanceGetReceiveWaitHandle;
106335640Shselasky#endif
107335640Shselasky	TcFcnInstanceTransmitPackets InstanceTransmitPackets;
108335640Shselasky	TcFcnInstanceQueryStatistics InstanceQueryStatistics;
109335640Shselasky
110335640Shselasky	TcFcnPacketsBufferCreate	PacketsBufferCreate;
111335640Shselasky	TcFcnPacketsBufferDestroy	PacketsBufferDestroy;
112335640Shselasky	TcFcnPacketsBufferQueryNextPacket	PacketsBufferQueryNextPacket;
113335640Shselasky	TcFcnPacketsBufferCommitNextPacket  PacketsBufferCommitNextPacket;
114335640Shselasky
115335640Shselasky	TcFcnStatisticsDestroy		StatisticsDestroy;
116335640Shselasky	TcFcnStatisticsUpdate		StatisticsUpdate;
117335640Shselasky	TcFcnStatisticsQueryValue	StatisticsQueryValue;
118335640Shselasky}
119335640Shselasky	TC_FUNCTIONS;
120335640Shselasky
121335640Shselaskystatic pcap_if_t* TcCreatePcapIfFromPort(TC_PORT port);
122335640Shselaskystatic int TcSetDatalink(pcap_t *p, int dlt);
123335640Shselaskystatic int TcGetNonBlock(pcap_t *p);
124335640Shselaskystatic int TcSetNonBlock(pcap_t *p, int nonblock);
125335640Shselaskystatic void TcCleanup(pcap_t *p);
126356341Scystatic int TcInject(pcap_t *p, const void *buf, int size);
127335640Shselaskystatic int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
128335640Shselaskystatic int TcStats(pcap_t *p, struct pcap_stat *ps);
129335640Shselaskystatic int TcSetFilter(pcap_t *p, struct bpf_program *fp);
130335640Shselasky#ifdef _WIN32
131335640Shselaskystatic struct pcap_stat *TcStatsEx(pcap_t *p, int *pcap_stat_size);
132335640Shselaskystatic int TcSetBuff(pcap_t *p, int dim);
133335640Shselaskystatic int TcSetMode(pcap_t *p, int mode);
134335640Shselaskystatic int TcSetMinToCopy(pcap_t *p, int size);
135335640Shselaskystatic HANDLE TcGetReceiveWaitHandle(pcap_t *p);
136335640Shselaskystatic int TcOidGetRequest(pcap_t *p, bpf_u_int32 oid, void *data, size_t *lenp);
137335640Shselaskystatic int TcOidSetRequest(pcap_t *p, bpf_u_int32 oid, const void *data, size_t *lenp);
138335640Shselaskystatic u_int TcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync);
139335640Shselaskystatic int TcSetUserBuffer(pcap_t *p, int size);
140335640Shselaskystatic int TcLiveDump(pcap_t *p, char *filename, int maxsize, int maxpacks);
141335640Shselaskystatic int TcLiveDumpEnded(pcap_t *p, int sync);
142335640Shselaskystatic PAirpcapHandle TcGetAirPcapHandle(pcap_t *p);
143335640Shselasky#endif
144335640Shselasky
145335640Shselasky#ifdef _WIN32
146335640ShselaskyTC_FUNCTIONS g_TcFunctions =
147335640Shselasky{
148335640Shselasky	TC_API_UNLOADED, /* LoadStatus */
149335640Shselasky	NULL,  /* hTcApiDllHandle */
150335640Shselasky	NULL,  /* QueryPortList */
151335640Shselasky	NULL,  /* FreePortList */
152335640Shselasky	NULL,  /* StatusGetString */
153335640Shselasky	NULL,  /* PortGetName */
154335640Shselasky	NULL,  /* PortGetDescription */
155335640Shselasky	NULL,  /* InstanceOpenByName */
156335640Shselasky	NULL,  /* InstanceClose */
157335640Shselasky	NULL,  /* InstanceSetFeature */
158335640Shselasky	NULL,  /* InstanceQueryFeature */
159335640Shselasky	NULL,  /* InstanceReceivePackets */
160335640Shselasky	NULL,  /* InstanceGetReceiveWaitHandle */
161335640Shselasky	NULL,  /* InstanceTransmitPackets */
162335640Shselasky	NULL,  /* InstanceQueryStatistics */
163335640Shselasky	NULL,  /* PacketsBufferCreate */
164335640Shselasky	NULL,  /* PacketsBufferDestroy */
165335640Shselasky	NULL,  /* PacketsBufferQueryNextPacket */
166335640Shselasky	NULL,  /* PacketsBufferCommitNextPacket */
167335640Shselasky	NULL,  /* StatisticsDestroy */
168335640Shselasky	NULL,  /* StatisticsUpdate */
169335640Shselasky	NULL  /* StatisticsQueryValue */
170335640Shselasky};
171335640Shselasky#else
172335640ShselaskyTC_FUNCTIONS g_TcFunctions =
173335640Shselasky{
174335640Shselasky	TC_API_LOADED, /* LoadStatus */
175335640Shselasky	TcQueryPortList,
176335640Shselasky	TcFreePortList,
177335640Shselasky	TcStatusGetString,
178335640Shselasky	TcPortGetName,
179335640Shselasky	TcPortGetDescription,
180335640Shselasky	TcInstanceOpenByName,
181335640Shselasky	TcInstanceClose,
182335640Shselasky	TcInstanceSetFeature,
183335640Shselasky	TcInstanceQueryFeature,
184335640Shselasky	TcInstanceReceivePackets,
185335640Shselasky#ifdef _WIN32
186335640Shselasky	TcInstanceGetReceiveWaitHandle,
187335640Shselasky#endif
188335640Shselasky	TcInstanceTransmitPackets,
189335640Shselasky	TcInstanceQueryStatistics,
190335640Shselasky	TcPacketsBufferCreate,
191335640Shselasky	TcPacketsBufferDestroy,
192335640Shselasky	TcPacketsBufferQueryNextPacket,
193335640Shselasky	TcPacketsBufferCommitNextPacket,
194335640Shselasky	TcStatisticsDestroy,
195335640Shselasky	TcStatisticsUpdate,
196335640Shselasky	TcStatisticsQueryValue,
197335640Shselasky};
198335640Shselasky#endif
199335640Shselasky
200335640Shselasky#define MAX_TC_PACKET_SIZE	9500
201335640Shselasky
202335640Shselasky#pragma pack(push, 1)
203335640Shselasky
204335640Shselasky#define PPH_PH_FLAG_PADDING	((UCHAR)0x01)
205335640Shselasky#define PPH_PH_VERSION		((UCHAR)0x00)
206335640Shselasky
207335640Shselaskytypedef struct _PPI_PACKET_HEADER
208335640Shselasky{
209335640Shselasky	UCHAR	PphVersion;
210335640Shselasky	UCHAR	PphFlags;
211335640Shselasky	USHORT	PphLength;
212335640Shselasky	ULONG	PphDlt;
213335640Shselasky}
214335640Shselasky	PPI_PACKET_HEADER, *PPPI_PACKET_HEADER;
215335640Shselasky
216335640Shselaskytypedef struct _PPI_FIELD_HEADER
217335640Shselasky{
218335640Shselasky	USHORT PfhType;
219335640Shselasky	USHORT PfhLength;
220335640Shselasky}
221335640Shselasky	PPI_FIELD_HEADER, *PPPI_FIELD_HEADER;
222335640Shselasky
223335640Shselasky
224335640Shselasky#define		PPI_FIELD_TYPE_AGGREGATION_EXTENSION	((UCHAR)0x08)
225335640Shselasky
226335640Shselaskytypedef struct _PPI_FIELD_AGGREGATION_EXTENSION
227335640Shselasky{
228335640Shselasky	ULONG		InterfaceId;
229335640Shselasky}
230335640Shselasky	PPI_FIELD_AGGREGATION_EXTENSION, *PPPI_FIELD_AGGREGATION_EXTENSION;
231335640Shselasky
232335640Shselasky
233335640Shselasky#define		PPI_FIELD_TYPE_802_3_EXTENSION			((UCHAR)0x09)
234335640Shselasky
235335640Shselasky#define PPI_FLD_802_3_EXT_FLAG_FCS_PRESENT			((ULONG)0x00000001)
236335640Shselasky
237335640Shselaskytypedef struct _PPI_FIELD_802_3_EXTENSION
238335640Shselasky{
239335640Shselasky	ULONG		Flags;
240335640Shselasky	ULONG		Errors;
241335640Shselasky}
242335640Shselasky	PPI_FIELD_802_3_EXTENSION, *PPPI_FIELD_802_3_EXTENSION;
243335640Shselasky
244335640Shselaskytypedef struct _PPI_HEADER
245335640Shselasky{
246335640Shselasky	PPI_PACKET_HEADER PacketHeader;
247335640Shselasky	PPI_FIELD_HEADER  AggregationFieldHeader;
248335640Shselasky	PPI_FIELD_AGGREGATION_EXTENSION AggregationField;
249335640Shselasky	PPI_FIELD_HEADER  Dot3FieldHeader;
250335640Shselasky	PPI_FIELD_802_3_EXTENSION Dot3Field;
251335640Shselasky}
252335640Shselasky	PPI_HEADER, *PPPI_HEADER;
253335640Shselasky#pragma pack(pop)
254335640Shselasky
255335640Shselasky#ifdef _WIN32
256335640Shselasky//
257335640Shselasky// This wrapper around loadlibrary appends the system folder (usually c:\windows\system32)
258335640Shselasky// to the relative path of the DLL, so that the DLL is always loaded from an absolute path
259335640Shselasky// (It's no longer possible to load airpcap.dll from the application folder).
260335640Shselasky// This solves the DLL Hijacking issue discovered in August 2010
261335640Shselasky// http://blog.metasploit.com/2010/08/exploiting-dll-hijacking-flaws.html
262335640Shselasky//
263335640ShselaskyHMODULE LoadLibrarySafe(LPCTSTR lpFileName)
264335640Shselasky{
265335640Shselasky  TCHAR path[MAX_PATH];
266335640Shselasky  TCHAR fullFileName[MAX_PATH];
267335640Shselasky  UINT res;
268335640Shselasky  HMODULE hModule = NULL;
269335640Shselasky  do
270335640Shselasky  {
271335640Shselasky	res = GetSystemDirectory(path, MAX_PATH);
272335640Shselasky
273335640Shselasky	if (res == 0)
274335640Shselasky	{
275335640Shselasky		//
276335640Shselasky		// some bad failure occurred;
277335640Shselasky		//
278335640Shselasky		break;
279335640Shselasky	}
280335640Shselasky
281335640Shselasky	if (res > MAX_PATH)
282335640Shselasky	{
283335640Shselasky		//
284335640Shselasky		// the buffer was not big enough
285335640Shselasky		//
286335640Shselasky		SetLastError(ERROR_INSUFFICIENT_BUFFER);
287335640Shselasky		break;
288335640Shselasky	}
289335640Shselasky
290335640Shselasky	if (res + 1 + _tcslen(lpFileName) + 1 < MAX_PATH)
291335640Shselasky	{
292335640Shselasky		memcpy(fullFileName, path, res * sizeof(TCHAR));
293335640Shselasky		fullFileName[res] = _T('\\');
294335640Shselasky		memcpy(&fullFileName[res + 1], lpFileName, (_tcslen(lpFileName) + 1) * sizeof(TCHAR));
295335640Shselasky
296335640Shselasky		hModule = LoadLibrary(fullFileName);
297335640Shselasky	}
298335640Shselasky	else
299335640Shselasky	{
300335640Shselasky		SetLastError(ERROR_INSUFFICIENT_BUFFER);
301335640Shselasky	}
302335640Shselasky
303335640Shselasky  }while(FALSE);
304335640Shselasky
305335640Shselasky  return hModule;
306335640Shselasky}
307335640Shselasky
308335640Shselasky/*
309335640Shselasky * NOTE: this function should be called by the pcap functions that can theoretically
310335640Shselasky *       deal with the Tc library for the first time, namely listing the adapters and
311335640Shselasky *       opening one. All the other ones (close, read, write, set parameters) work
312335640Shselasky *       on an open instance of TC, so we do not care to call this function
313335640Shselasky */
314335640ShselaskyTC_API_LOAD_STATUS LoadTcFunctions(void)
315335640Shselasky{
316335640Shselasky	TC_API_LOAD_STATUS currentStatus;
317335640Shselasky
318335640Shselasky	do
319335640Shselasky	{
320335640Shselasky		currentStatus = InterlockedCompareExchange((LONG*)&g_TcFunctions.LoadStatus, TC_API_LOADING, TC_API_UNLOADED);
321335640Shselasky
322335640Shselasky		while(currentStatus == TC_API_LOADING)
323335640Shselasky		{
324335640Shselasky			currentStatus = InterlockedCompareExchange((LONG*)&g_TcFunctions.LoadStatus, TC_API_LOADING, TC_API_LOADING);
325335640Shselasky			Sleep(10);
326335640Shselasky		}
327335640Shselasky
328335640Shselasky		/*
329335640Shselasky		 * at this point we are either in the LOADED state, unloaded state (i.e. we are the ones loading everything)
330335640Shselasky		 * or in cannot load
331335640Shselasky		 */
332335640Shselasky		if(currentStatus  == TC_API_LOADED)
333335640Shselasky		{
334335640Shselasky			return TC_API_LOADED;
335335640Shselasky		}
336335640Shselasky
337335640Shselasky		if (currentStatus == TC_API_CANNOT_LOAD)
338335640Shselasky		{
339335640Shselasky			return TC_API_CANNOT_LOAD;
340335640Shselasky		}
341335640Shselasky
342335640Shselasky		currentStatus = TC_API_CANNOT_LOAD;
343335640Shselasky
344335640Shselasky		g_TcFunctions.hTcApiDllHandle = LoadLibrarySafe("TcApi.dll");
345335640Shselasky		if (g_TcFunctions.hTcApiDllHandle == NULL)	break;
346335640Shselasky
347335640Shselasky		g_TcFunctions.QueryPortList					= (TcFcnQueryPortList)			GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcQueryPortList");
348335640Shselasky		g_TcFunctions.FreePortList					= (TcFcnFreePortList)			GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcFreePortList");
349335640Shselasky
350335640Shselasky		g_TcFunctions.StatusGetString				= (TcFcnStatusGetString)		GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatusGetString");
351335640Shselasky
352335640Shselasky		g_TcFunctions.PortGetName					= (TcFcnPortGetName)			GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetName");
353335640Shselasky		g_TcFunctions.PortGetDescription			= (TcFcnPortGetDescription)		GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetDescription");
354335640Shselasky
355335640Shselasky		g_TcFunctions.InstanceOpenByName			= (TcFcnInstanceOpenByName)		GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceOpenByName");
356335640Shselasky		g_TcFunctions.InstanceClose					= (TcFcnInstanceClose)			GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceClose");
357335640Shselasky		g_TcFunctions.InstanceSetFeature			= (TcFcnInstanceSetFeature)		GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceSetFeature");
358335640Shselasky		g_TcFunctions.InstanceQueryFeature			= (TcFcnInstanceQueryFeature)	GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryFeature");
359335640Shselasky		g_TcFunctions.InstanceReceivePackets		= (TcFcnInstanceReceivePackets)	GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceReceivePackets");
360335640Shselasky		g_TcFunctions.InstanceGetReceiveWaitHandle	= (TcFcnInstanceGetReceiveWaitHandle)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceGetReceiveWaitHandle");
361335640Shselasky		g_TcFunctions.InstanceTransmitPackets		= (TcFcnInstanceTransmitPackets)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceTransmitPackets");
362335640Shselasky		g_TcFunctions.InstanceQueryStatistics		= (TcFcnInstanceQueryStatistics)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryStatistics");
363335640Shselasky
364335640Shselasky		g_TcFunctions.PacketsBufferCreate			= (TcFcnPacketsBufferCreate)	GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCreate");
365335640Shselasky		g_TcFunctions.PacketsBufferDestroy			= (TcFcnPacketsBufferDestroy)	GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferDestroy");
366335640Shselasky		g_TcFunctions.PacketsBufferQueryNextPacket	= (TcFcnPacketsBufferQueryNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferQueryNextPacket");
367335640Shselasky		g_TcFunctions.PacketsBufferCommitNextPacket	= (TcFcnPacketsBufferCommitNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCommitNextPacket");
368335640Shselasky
369335640Shselasky		g_TcFunctions.StatisticsDestroy				= (TcFcnStatisticsDestroy)		GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsDestroy");
370335640Shselasky		g_TcFunctions.StatisticsUpdate				= (TcFcnStatisticsUpdate)		GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsUpdate");
371335640Shselasky		g_TcFunctions.StatisticsQueryValue			= (TcFcnStatisticsQueryValue)	GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsQueryValue");
372335640Shselasky
373335640Shselasky		if (   g_TcFunctions.QueryPortList == NULL
374335640Shselasky			|| g_TcFunctions.FreePortList == NULL
375335640Shselasky			|| g_TcFunctions.StatusGetString == NULL
376335640Shselasky			|| g_TcFunctions.PortGetName == NULL
377335640Shselasky			|| g_TcFunctions.PortGetDescription == NULL
378335640Shselasky			|| g_TcFunctions.InstanceOpenByName == NULL
379335640Shselasky			|| g_TcFunctions.InstanceClose == NULL
380335640Shselasky			|| g_TcFunctions.InstanceSetFeature	 == NULL
381335640Shselasky			|| g_TcFunctions.InstanceQueryFeature == NULL
382335640Shselasky			|| g_TcFunctions.InstanceReceivePackets == NULL
383335640Shselasky			|| g_TcFunctions.InstanceGetReceiveWaitHandle == NULL
384335640Shselasky			|| g_TcFunctions.InstanceTransmitPackets == NULL
385335640Shselasky			|| g_TcFunctions.InstanceQueryStatistics == NULL
386335640Shselasky			|| g_TcFunctions.PacketsBufferCreate == NULL
387335640Shselasky			|| g_TcFunctions.PacketsBufferDestroy == NULL
388335640Shselasky			|| g_TcFunctions.PacketsBufferQueryNextPacket == NULL
389335640Shselasky			|| g_TcFunctions.PacketsBufferCommitNextPacket == NULL
390335640Shselasky			|| g_TcFunctions.StatisticsDestroy == NULL
391335640Shselasky			|| g_TcFunctions.StatisticsUpdate == NULL
392335640Shselasky			|| g_TcFunctions.StatisticsQueryValue == NULL
393335640Shselasky		)
394335640Shselasky		{
395335640Shselasky			break;
396335640Shselasky		}
397335640Shselasky
398335640Shselasky		/*
399335640Shselasky		 * everything got loaded, yay!!
400335640Shselasky		 */
401335640Shselasky		currentStatus = TC_API_LOADED;
402335640Shselasky	}while(FALSE);
403335640Shselasky
404335640Shselasky	if (currentStatus != TC_API_LOADED)
405335640Shselasky	{
406335640Shselasky		if (g_TcFunctions.hTcApiDllHandle != NULL)
407335640Shselasky		{
408335640Shselasky			FreeLibrary(g_TcFunctions.hTcApiDllHandle);
409335640Shselasky			g_TcFunctions.hTcApiDllHandle = NULL;
410335640Shselasky		}
411335640Shselasky	}
412335640Shselasky
413335640Shselasky	InterlockedExchange((LONG*)&g_TcFunctions.LoadStatus, currentStatus);
414335640Shselasky
415335640Shselasky	return currentStatus;
416335640Shselasky}
417335640Shselasky#else
418335640Shselasky// static linking
419335640ShselaskyTC_API_LOAD_STATUS LoadTcFunctions(void)
420335640Shselasky{
421335640Shselasky	return TC_API_LOADED;
422335640Shselasky}
423335640Shselasky#endif
424335640Shselasky
425335640Shselasky/*
426335640Shselasky * Private data for capturing on TurboCap devices.
427335640Shselasky */
428335640Shselaskystruct pcap_tc {
429335640Shselasky	TC_INSTANCE TcInstance;
430335640Shselasky	TC_PACKETS_BUFFER TcPacketsBuffer;
431335640Shselasky	ULONG TcAcceptedCount;
432335640Shselasky	u_char *PpiPacket;
433335640Shselasky};
434335640Shselasky
435335640Shselaskyint
436335640ShselaskyTcFindAllDevs(pcap_if_list_t *devlist, char *errbuf)
437335640Shselasky{
438335640Shselasky	TC_API_LOAD_STATUS loadStatus;
439335640Shselasky	ULONG numPorts;
440335640Shselasky	PTC_PORT pPorts = NULL;
441335640Shselasky	TC_STATUS status;
442335640Shselasky	int result = 0;
443356341Scy	pcap_if_t *dev;
444335640Shselasky	ULONG i;
445335640Shselasky
446335640Shselasky	do
447335640Shselasky	{
448335640Shselasky		loadStatus = LoadTcFunctions();
449335640Shselasky
450335640Shselasky		if (loadStatus != TC_API_LOADED)
451335640Shselasky		{
452335640Shselasky			result = 0;
453335640Shselasky			break;
454335640Shselasky		}
455335640Shselasky
456335640Shselasky		/*
457335640Shselasky		 * enumerate the ports, and add them to the list
458335640Shselasky		 */
459335640Shselasky		status = g_TcFunctions.QueryPortList(&pPorts, &numPorts);
460335640Shselasky
461335640Shselasky		if (status != TC_SUCCESS)
462335640Shselasky		{
463335640Shselasky			result = 0;
464335640Shselasky			break;
465335640Shselasky		}
466335640Shselasky
467335640Shselasky		for (i = 0; i < numPorts; i++)
468335640Shselasky		{
469335640Shselasky			/*
470335640Shselasky			 * transform the port into an entry in the list
471335640Shselasky			 */
472335640Shselasky			dev = TcCreatePcapIfFromPort(pPorts[i]);
473335640Shselasky
474335640Shselasky			if (dev != NULL)
475356341Scy				add_dev(devlist, dev->name, dev->flags, dev->description, errbuf);
476335640Shselasky		}
477335640Shselasky
478335640Shselasky		if (numPorts > 0)
479335640Shselasky		{
480335640Shselasky			/*
481335640Shselasky			 * ignore the result here
482335640Shselasky			 */
483335640Shselasky			status = g_TcFunctions.FreePortList(pPorts);
484335640Shselasky		}
485335640Shselasky
486335640Shselasky	}while(FALSE);
487335640Shselasky
488335640Shselasky	return result;
489335640Shselasky}
490335640Shselasky
491335640Shselaskystatic pcap_if_t* TcCreatePcapIfFromPort(TC_PORT port)
492335640Shselasky{
493335640Shselasky	CHAR *name;
494335640Shselasky	CHAR *description;
495335640Shselasky	pcap_if_t *newIf = NULL;
496335640Shselasky
497335640Shselasky	newIf = (pcap_if_t*)malloc(sizeof(*newIf));
498335640Shselasky	if (newIf == NULL)
499335640Shselasky	{
500335640Shselasky		return NULL;
501335640Shselasky	}
502335640Shselasky
503335640Shselasky	memset(newIf, 0, sizeof(*newIf));
504335640Shselasky
505335640Shselasky	name = g_TcFunctions.PortGetName(port);
506335640Shselasky	description = g_TcFunctions.PortGetDescription(port);
507335640Shselasky
508335640Shselasky	newIf->name = (char*)malloc(strlen(name) + 1);
509335640Shselasky	if (newIf->name == NULL)
510335640Shselasky	{
511335640Shselasky		free(newIf);
512335640Shselasky		return NULL;
513335640Shselasky	}
514335640Shselasky
515335640Shselasky	newIf->description = (char*)malloc(strlen(description) + 1);
516335640Shselasky	if (newIf->description == NULL)
517335640Shselasky	{
518335640Shselasky		free(newIf->name);
519335640Shselasky		free(newIf);
520335640Shselasky		return NULL;
521335640Shselasky	}
522335640Shselasky
523335640Shselasky	strcpy(newIf->name, name);
524335640Shselasky	strcpy(newIf->description, description);
525335640Shselasky
526335640Shselasky	newIf->addresses = NULL;
527335640Shselasky	newIf->next = NULL;
528335640Shselasky	newIf->flags = 0;
529335640Shselasky
530335640Shselasky	return newIf;
531335640Shselasky
532335640Shselasky}
533335640Shselasky
534335640Shselaskystatic int
535335640ShselaskyTcActivate(pcap_t *p)
536335640Shselasky{
537335640Shselasky	struct pcap_tc *pt = p->priv;
538335640Shselasky	TC_STATUS status;
539335640Shselasky	ULONG timeout;
540335640Shselasky	PPPI_HEADER pPpiHeader;
541335640Shselasky
542335640Shselasky	if (p->opt.rfmon)
543335640Shselasky	{
544335640Shselasky		/*
545335640Shselasky		 * No monitor mode on Tc cards; they're Ethernet
546335640Shselasky		 * capture adapters.
547335640Shselasky		 */
548335640Shselasky		return PCAP_ERROR_RFMON_NOTSUP;
549335640Shselasky	}
550335640Shselasky
551335640Shselasky	pt->PpiPacket = malloc(sizeof(PPI_HEADER) + MAX_TC_PACKET_SIZE);
552335640Shselasky
553335640Shselasky	if (pt->PpiPacket == NULL)
554335640Shselasky	{
555335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error allocating memory");
556335640Shselasky		return PCAP_ERROR;
557335640Shselasky	}
558335640Shselasky
559335640Shselasky	/*
560335640Shselasky	 * Turn a negative snapshot value (invalid), a snapshot value of
561335640Shselasky	 * 0 (unspecified), or a value bigger than the normal maximum
562335640Shselasky	 * value, into the maximum allowed value.
563335640Shselasky	 *
564335640Shselasky	 * If some application really *needs* a bigger snapshot
565335640Shselasky	 * length, we should just increase MAXIMUM_SNAPLEN.
566335640Shselasky	 */
567335640Shselasky	if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
568335640Shselasky		p->snapshot = MAXIMUM_SNAPLEN;
569335640Shselasky
570335640Shselasky	/*
571335640Shselasky	 * Initialize the PPI fixed fields
572335640Shselasky	 */
573335640Shselasky	pPpiHeader = (PPPI_HEADER)pt->PpiPacket;
574335640Shselasky	pPpiHeader->PacketHeader.PphDlt = DLT_EN10MB;
575335640Shselasky	pPpiHeader->PacketHeader.PphLength = sizeof(PPI_HEADER);
576335640Shselasky	pPpiHeader->PacketHeader.PphFlags = 0;
577335640Shselasky	pPpiHeader->PacketHeader.PphVersion = 0;
578335640Shselasky
579335640Shselasky	pPpiHeader->AggregationFieldHeader.PfhLength = sizeof(PPI_FIELD_AGGREGATION_EXTENSION);
580335640Shselasky	pPpiHeader->AggregationFieldHeader.PfhType = PPI_FIELD_TYPE_AGGREGATION_EXTENSION;
581335640Shselasky
582335640Shselasky	pPpiHeader->Dot3FieldHeader.PfhLength = sizeof(PPI_FIELD_802_3_EXTENSION);
583335640Shselasky	pPpiHeader->Dot3FieldHeader.PfhType = PPI_FIELD_TYPE_802_3_EXTENSION;
584335640Shselasky
585335640Shselasky	status = g_TcFunctions.InstanceOpenByName(p->opt.device, &pt->TcInstance);
586335640Shselasky
587335640Shselasky	if (status != TC_SUCCESS)
588335640Shselasky	{
589335640Shselasky		/* Adapter detected but we are not able to open it. Return failure. */
590335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error opening TurboCap adapter: %s", g_TcFunctions.StatusGetString(status));
591335640Shselasky		return PCAP_ERROR;
592335640Shselasky	}
593335640Shselasky
594335640Shselasky	p->linktype = DLT_EN10MB;
595335640Shselasky	p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
596335640Shselasky	/*
597335640Shselasky	 * If that fails, just leave the list empty.
598335640Shselasky	 */
599335640Shselasky	if (p->dlt_list != NULL) {
600335640Shselasky		p->dlt_list[0] = DLT_EN10MB;
601335640Shselasky		p->dlt_list[1] = DLT_PPI;
602335640Shselasky		p->dlt_count = 2;
603335640Shselasky	}
604335640Shselasky
605335640Shselasky	/*
606335640Shselasky	 * ignore promiscuous mode
607335640Shselasky	 * p->opt.promisc
608335640Shselasky	 */
609335640Shselasky
610335640Shselasky
611335640Shselasky	/*
612335640Shselasky	 * ignore all the buffer sizes
613335640Shselasky	 */
614335640Shselasky
615335640Shselasky	/*
616335640Shselasky	 * enable reception
617335640Shselasky	 */
618335640Shselasky	status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_RX_STATUS, 1);
619335640Shselasky
620335640Shselasky	if (status != TC_SUCCESS)
621335640Shselasky	{
622335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error enabling reception on a TurboCap instance: %s", g_TcFunctions.StatusGetString(status));
623335640Shselasky		goto bad;
624335640Shselasky	}
625335640Shselasky
626335640Shselasky	/*
627335640Shselasky	 * enable transmission
628335640Shselasky	 */
629335640Shselasky	status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_TX_STATUS, 1);
630335640Shselasky	/*
631335640Shselasky	 * Ignore the error here.
632335640Shselasky	 */
633335640Shselasky
634335640Shselasky	p->inject_op = TcInject;
635335640Shselasky	/*
636335640Shselasky	 * if the timeout is -1, it means immediate return, no timeout
637335640Shselasky	 * if the timeout is 0, it means INFINITE
638335640Shselasky	 */
639335640Shselasky
640335640Shselasky	if (p->opt.timeout == 0)
641335640Shselasky	{
642335640Shselasky		timeout = 0xFFFFFFFF;
643335640Shselasky	}
644335640Shselasky	else
645335640Shselasky	if (p->opt.timeout < 0)
646335640Shselasky	{
647335640Shselasky		/*
648335640Shselasky		 *  we insert a minimal timeout here
649335640Shselasky		 */
650335640Shselasky		timeout = 10;
651335640Shselasky	}
652335640Shselasky	else
653335640Shselasky	{
654335640Shselasky		timeout = p->opt.timeout;
655335640Shselasky	}
656335640Shselasky
657335640Shselasky	status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_READ_TIMEOUT, timeout);
658335640Shselasky
659335640Shselasky	if (status != TC_SUCCESS)
660335640Shselasky	{
661335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error setting the read timeout a TurboCap instance: %s", g_TcFunctions.StatusGetString(status));
662335640Shselasky		goto bad;
663335640Shselasky	}
664335640Shselasky
665335640Shselasky	p->read_op = TcRead;
666335640Shselasky	p->setfilter_op = TcSetFilter;
667335640Shselasky	p->setdirection_op = NULL;	/* Not implemented. */
668335640Shselasky	p->set_datalink_op = TcSetDatalink;
669335640Shselasky	p->getnonblock_op = TcGetNonBlock;
670335640Shselasky	p->setnonblock_op = TcSetNonBlock;
671335640Shselasky	p->stats_op = TcStats;
672335640Shselasky#ifdef _WIN32
673335640Shselasky	p->stats_ex_op = TcStatsEx;
674335640Shselasky	p->setbuff_op = TcSetBuff;
675335640Shselasky	p->setmode_op = TcSetMode;
676335640Shselasky	p->setmintocopy_op = TcSetMinToCopy;
677335640Shselasky	p->getevent_op = TcGetReceiveWaitHandle;
678335640Shselasky	p->oid_get_request_op = TcOidGetRequest;
679335640Shselasky	p->oid_set_request_op = TcOidSetRequest;
680335640Shselasky	p->sendqueue_transmit_op = TcSendqueueTransmit;
681335640Shselasky	p->setuserbuffer_op = TcSetUserBuffer;
682335640Shselasky	p->live_dump_op = TcLiveDump;
683335640Shselasky	p->live_dump_ended_op = TcLiveDumpEnded;
684335640Shselasky	p->get_airpcap_handle_op = TcGetAirPcapHandle;
685335640Shselasky#else
686335640Shselasky	p->selectable_fd = -1;
687335640Shselasky#endif
688335640Shselasky
689335640Shselasky	p->cleanup_op = TcCleanup;
690335640Shselasky
691335640Shselasky	return 0;
692335640Shselaskybad:
693335640Shselasky	TcCleanup(p);
694335640Shselasky	return PCAP_ERROR;
695335640Shselasky}
696335640Shselasky
697335640Shselaskypcap_t *
698335640ShselaskyTcCreate(const char *device, char *ebuf, int *is_ours)
699335640Shselasky{
700335640Shselasky	ULONG numPorts;
701335640Shselasky	PTC_PORT pPorts = NULL;
702335640Shselasky	TC_STATUS status;
703335640Shselasky	int is_tc;
704335640Shselasky	ULONG i;
705335640Shselasky	pcap_t *p;
706335640Shselasky
707335640Shselasky	if (LoadTcFunctions() != TC_API_LOADED)
708335640Shselasky	{
709335640Shselasky		/*
710335640Shselasky		 * XXX - report this as an error rather than as
711335640Shselasky		 * "not a TurboCap device"?
712335640Shselasky		 */
713335640Shselasky		*is_ours = 0;
714335640Shselasky		return NULL;
715335640Shselasky	}
716335640Shselasky
717335640Shselasky	/*
718335640Shselasky	 * enumerate the ports, and add them to the list
719335640Shselasky	 */
720335640Shselasky	status = g_TcFunctions.QueryPortList(&pPorts, &numPorts);
721335640Shselasky
722335640Shselasky	if (status != TC_SUCCESS)
723335640Shselasky	{
724335640Shselasky		/*
725335640Shselasky		 * XXX - report this as an error rather than as
726335640Shselasky		 * "not a TurboCap device"?
727335640Shselasky		 */
728335640Shselasky		*is_ours = 0;
729335640Shselasky		return NULL;
730335640Shselasky	}
731335640Shselasky
732335640Shselasky	is_tc = FALSE;
733335640Shselasky	for (i = 0; i < numPorts; i++)
734335640Shselasky	{
735335640Shselasky		if (strcmp(g_TcFunctions.PortGetName(pPorts[i]), device) == 0)
736335640Shselasky		{
737335640Shselasky			is_tc = TRUE;
738335640Shselasky			break;
739335640Shselasky		}
740335640Shselasky	}
741335640Shselasky
742335640Shselasky	if (numPorts > 0)
743335640Shselasky	{
744335640Shselasky		/*
745335640Shselasky		 * ignore the result here
746335640Shselasky		 */
747335640Shselasky		(void)g_TcFunctions.FreePortList(pPorts);
748335640Shselasky	}
749335640Shselasky
750335640Shselasky	if (!is_tc)
751335640Shselasky	{
752335640Shselasky		*is_ours = 0;
753335640Shselasky		return NULL;
754335640Shselasky	}
755335640Shselasky
756335640Shselasky	/* OK, it's probably ours. */
757335640Shselasky	*is_ours = 1;
758335640Shselasky
759335640Shselasky	p = pcap_create_common(ebuf, sizeof (struct pcap_tc));
760335640Shselasky	if (p == NULL)
761335640Shselasky		return NULL;
762335640Shselasky
763335640Shselasky	p->activate_op = TcActivate;
764335640Shselasky	/*
765335640Shselasky	 * Set these up front, so that, even if our client tries
766335640Shselasky	 * to set non-blocking mode before we're activated, or
767335640Shselasky	 * query the state of non-blocking mode, they get an error,
768335640Shselasky	 * rather than having the non-blocking mode option set
769335640Shselasky	 * for use later.
770335640Shselasky	 */
771335640Shselasky	p->getnonblock_op = TcGetNonBlock;
772335640Shselasky	p->setnonblock_op = TcSetNonBlock;
773335640Shselasky	return p;
774335640Shselasky}
775335640Shselasky
776335640Shselaskystatic int TcSetDatalink(pcap_t *p, int dlt)
777335640Shselasky{
778335640Shselasky	/*
779335640Shselasky	 * We don't have to do any work here; pcap_set_datalink() checks
780335640Shselasky	 * whether the value is in the list of DLT_ values we
781335640Shselasky	 * supplied, so we don't have to, and, if it is valid, sets
782335640Shselasky	 * p->linktype to the new value; we don't have to do anything
783335640Shselasky	 * in hardware, we just use what's in p->linktype.
784335640Shselasky	 *
785335640Shselasky	 * We do have to have a routine, however, so that pcap_set_datalink()
786335640Shselasky	 * doesn't think we don't support setting the link-layer header
787335640Shselasky	 * type at all.
788335640Shselasky	 */
789335640Shselasky	return 0;
790335640Shselasky}
791335640Shselasky
792335640Shselaskystatic int TcGetNonBlock(pcap_t *p)
793335640Shselasky{
794335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
795335640Shselasky	    "Non-blocking mode isn't supported for TurboCap ports");
796335640Shselasky	return -1;
797335640Shselasky}
798335640Shselasky
799335640Shselaskystatic int TcSetNonBlock(pcap_t *p, int nonblock)
800335640Shselasky{
801335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
802335640Shselasky	    "Non-blocking mode isn't supported for TurboCap ports");
803335640Shselasky	return -1;
804335640Shselasky}
805335640Shselasky
806335640Shselaskystatic void TcCleanup(pcap_t *p)
807335640Shselasky{
808335640Shselasky	struct pcap_tc *pt = p->priv;
809335640Shselasky
810335640Shselasky	if (pt->TcPacketsBuffer != NULL)
811335640Shselasky	{
812335640Shselasky		g_TcFunctions.PacketsBufferDestroy(pt->TcPacketsBuffer);
813335640Shselasky		pt->TcPacketsBuffer = NULL;
814335640Shselasky	}
815335640Shselasky	if (pt->TcInstance != NULL)
816335640Shselasky	{
817335640Shselasky		/*
818335640Shselasky		 * here we do not check for the error values
819335640Shselasky		 */
820335640Shselasky		g_TcFunctions.InstanceClose(pt->TcInstance);
821335640Shselasky		pt->TcInstance = NULL;
822335640Shselasky	}
823335640Shselasky
824335640Shselasky	if (pt->PpiPacket != NULL)
825335640Shselasky	{
826335640Shselasky		free(pt->PpiPacket);
827335640Shselasky		pt->PpiPacket = NULL;
828335640Shselasky	}
829335640Shselasky
830335640Shselasky	pcap_cleanup_live_common(p);
831335640Shselasky}
832335640Shselasky
833335640Shselasky/* Send a packet to the network */
834356341Scystatic int TcInject(pcap_t *p, const void *buf, int size)
835335640Shselasky{
836335640Shselasky	struct pcap_tc *pt = p->priv;
837335640Shselasky	TC_STATUS status;
838335640Shselasky	TC_PACKETS_BUFFER buffer;
839335640Shselasky	TC_PACKET_HEADER header;
840335640Shselasky
841335640Shselasky	if (size >= 0xFFFF)
842335640Shselasky	{
843335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: the TurboCap API does not support packets larger than 64k");
844335640Shselasky		return -1;
845335640Shselasky	}
846335640Shselasky
847335640Shselasky	status = g_TcFunctions.PacketsBufferCreate(sizeof(TC_PACKET_HEADER) + TC_ALIGN_USHORT_TO_64BIT((USHORT)size), &buffer);
848335640Shselasky
849335640Shselasky	if (status != TC_SUCCESS)
850335640Shselasky	{
851335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCreate failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
852335640Shselasky		return -1;
853335640Shselasky	}
854335640Shselasky
855335640Shselasky	/*
856335640Shselasky	 * we assume that the packet is without the checksum, as common with WinPcap
857335640Shselasky	 */
858335640Shselasky	memset(&header, 0, sizeof(header));
859335640Shselasky
860335640Shselasky	header.Length = (USHORT)size;
861335640Shselasky	header.CapturedLength = header.Length;
862335640Shselasky
863335640Shselasky	status = g_TcFunctions.PacketsBufferCommitNextPacket(buffer, &header, (PVOID)buf);
864335640Shselasky
865335640Shselasky	if (status == TC_SUCCESS)
866335640Shselasky	{
867335640Shselasky		status = g_TcFunctions.InstanceTransmitPackets(pt->TcInstance, buffer);
868335640Shselasky
869335640Shselasky		if (status != TC_SUCCESS)
870335640Shselasky		{
871335640Shselasky			pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcInstanceTransmitPackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
872335640Shselasky		}
873335640Shselasky	}
874335640Shselasky	else
875335640Shselasky	{
876335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCommitNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
877335640Shselasky	}
878335640Shselasky
879335640Shselasky	g_TcFunctions.PacketsBufferDestroy(buffer);
880335640Shselasky
881335640Shselasky	if (status != TC_SUCCESS)
882335640Shselasky	{
883335640Shselasky		return -1;
884335640Shselasky	}
885335640Shselasky	else
886335640Shselasky	{
887335640Shselasky		return 0;
888335640Shselasky	}
889335640Shselasky}
890335640Shselasky
891335640Shselaskystatic int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
892335640Shselasky{
893335640Shselasky	struct pcap_tc *pt = p->priv;
894335640Shselasky	TC_STATUS status;
895335640Shselasky	int n = 0;
896335640Shselasky
897335640Shselasky	/*
898335640Shselasky	 * Has "pcap_breakloop()" been called?
899335640Shselasky	 */
900335640Shselasky	if (p->break_loop)
901335640Shselasky	{
902335640Shselasky		/*
903335640Shselasky		 * Yes - clear the flag that indicates that it
904335640Shselasky		 * has, and return -2 to indicate that we were
905335640Shselasky		 * told to break out of the loop.
906335640Shselasky		 */
907335640Shselasky		p->break_loop = 0;
908335640Shselasky		return -2;
909335640Shselasky	}
910335640Shselasky
911335640Shselasky	if (pt->TcPacketsBuffer == NULL)
912335640Shselasky	{
913335640Shselasky		status = g_TcFunctions.InstanceReceivePackets(pt->TcInstance, &pt->TcPacketsBuffer);
914335640Shselasky		if (status != TC_SUCCESS)
915335640Shselasky		{
916335640Shselasky			pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcInstanceReceivePackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
917335640Shselasky			return -1;
918335640Shselasky		}
919335640Shselasky	}
920335640Shselasky
921335640Shselasky	while (TRUE)
922335640Shselasky	{
923335640Shselasky		struct pcap_pkthdr hdr;
924335640Shselasky		TC_PACKET_HEADER tcHeader;
925335640Shselasky		PVOID data;
926335640Shselasky		ULONG filterResult;
927335640Shselasky
928335640Shselasky		/*
929335640Shselasky		 * Has "pcap_breakloop()" been called?
930335640Shselasky		 * If so, return immediately - if we haven't read any
931335640Shselasky		 * packets, clear the flag and return -2 to indicate
932335640Shselasky		 * that we were told to break out of the loop, otherwise
933335640Shselasky		 * leave the flag set, so that the *next* call will break
934335640Shselasky		 * out of the loop without having read any packets, and
935335640Shselasky		 * return the number of packets we've processed so far.
936335640Shselasky		 */
937335640Shselasky		if (p->break_loop)
938335640Shselasky		{
939335640Shselasky			if (n == 0)
940335640Shselasky			{
941335640Shselasky				p->break_loop = 0;
942335640Shselasky				return -2;
943335640Shselasky			}
944335640Shselasky			else
945335640Shselasky			{
946335640Shselasky				return n;
947335640Shselasky			}
948335640Shselasky		}
949335640Shselasky
950335640Shselasky		if (pt->TcPacketsBuffer == NULL)
951335640Shselasky		{
952335640Shselasky			break;
953335640Shselasky		}
954335640Shselasky
955335640Shselasky		status = g_TcFunctions.PacketsBufferQueryNextPacket(pt->TcPacketsBuffer, &tcHeader, &data);
956335640Shselasky
957335640Shselasky		if (status == TC_ERROR_END_OF_BUFFER)
958335640Shselasky		{
959335640Shselasky			g_TcFunctions.PacketsBufferDestroy(pt->TcPacketsBuffer);
960335640Shselasky			pt->TcPacketsBuffer = NULL;
961335640Shselasky			break;
962335640Shselasky		}
963335640Shselasky
964335640Shselasky		if (status != TC_SUCCESS)
965335640Shselasky		{
966335640Shselasky			pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcPacketsBufferQueryNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
967335640Shselasky			return -1;
968335640Shselasky		}
969335640Shselasky
970335640Shselasky		/* No underlaying filtering system. We need to filter on our own */
971335640Shselasky		if (p->fcode.bf_insns)
972335640Shselasky		{
973335640Shselasky			filterResult = bpf_filter(p->fcode.bf_insns, data, tcHeader.Length, tcHeader.CapturedLength);
974335640Shselasky
975335640Shselasky			if (filterResult == 0)
976335640Shselasky			{
977335640Shselasky				continue;
978335640Shselasky			}
979335640Shselasky
980335640Shselasky			if (filterResult > tcHeader.CapturedLength)
981335640Shselasky			{
982335640Shselasky				filterResult = tcHeader.CapturedLength;
983335640Shselasky			}
984335640Shselasky		}
985335640Shselasky		else
986335640Shselasky		{
987335640Shselasky			filterResult = tcHeader.CapturedLength;
988335640Shselasky		}
989335640Shselasky
990335640Shselasky		pt->TcAcceptedCount ++;
991335640Shselasky
992335640Shselasky		hdr.ts.tv_sec = (bpf_u_int32)(tcHeader.Timestamp / (ULONGLONG)(1000  * 1000 * 1000));
993335640Shselasky		hdr.ts.tv_usec = (bpf_u_int32)((tcHeader.Timestamp % (ULONGLONG)(1000  * 1000 * 1000)) / 1000);
994335640Shselasky
995335640Shselasky		if (p->linktype == DLT_EN10MB)
996335640Shselasky		{
997335640Shselasky			hdr.caplen = filterResult;
998335640Shselasky			hdr.len = tcHeader.Length;
999335640Shselasky			(*callback)(user, &hdr, data);
1000335640Shselasky		}
1001335640Shselasky		else
1002335640Shselasky		{
1003335640Shselasky			PPPI_HEADER pPpiHeader = (PPPI_HEADER)pt->PpiPacket;
1004335640Shselasky			PVOID data2 = pPpiHeader + 1;
1005335640Shselasky
1006335640Shselasky			pPpiHeader->AggregationField.InterfaceId = TC_PH_FLAGS_RX_PORT_ID(tcHeader.Flags);
1007335640Shselasky			pPpiHeader->Dot3Field.Errors = tcHeader.Errors;
1008335640Shselasky			if (tcHeader.Flags & TC_PH_FLAGS_CHECKSUM)
1009335640Shselasky			{
1010335640Shselasky				pPpiHeader->Dot3Field.Flags = PPI_FLD_802_3_EXT_FLAG_FCS_PRESENT;
1011335640Shselasky			}
1012335640Shselasky			else
1013335640Shselasky			{
1014335640Shselasky				pPpiHeader->Dot3Field.Flags = 0;
1015335640Shselasky			}
1016335640Shselasky
1017335640Shselasky			if (filterResult <= MAX_TC_PACKET_SIZE)
1018335640Shselasky			{
1019335640Shselasky				memcpy(data2, data, filterResult);
1020335640Shselasky				hdr.caplen = sizeof(PPI_HEADER) + filterResult;
1021335640Shselasky				hdr.len = sizeof(PPI_HEADER) + tcHeader.Length;
1022335640Shselasky			}
1023335640Shselasky			else
1024335640Shselasky			{
1025335640Shselasky				memcpy(data2, data, MAX_TC_PACKET_SIZE);
1026335640Shselasky				hdr.caplen = sizeof(PPI_HEADER) + MAX_TC_PACKET_SIZE;
1027335640Shselasky				hdr.len = sizeof(PPI_HEADER) + tcHeader.Length;
1028335640Shselasky			}
1029335640Shselasky
1030335640Shselasky			(*callback)(user, &hdr, pt->PpiPacket);
1031335640Shselasky
1032335640Shselasky		}
1033335640Shselasky
1034335640Shselasky		if (++n >= cnt && cnt > 0)
1035335640Shselasky		{
1036335640Shselasky			return n;
1037335640Shselasky		}
1038335640Shselasky	}
1039335640Shselasky
1040335640Shselasky	return n;
1041335640Shselasky}
1042335640Shselasky
1043335640Shselaskystatic int
1044335640ShselaskyTcStats(pcap_t *p, struct pcap_stat *ps)
1045335640Shselasky{
1046335640Shselasky	struct pcap_tc *pt = p->priv;
1047335640Shselasky	TC_STATISTICS statistics;
1048335640Shselasky	TC_STATUS status;
1049335640Shselasky	ULONGLONG counter;
1050335640Shselasky	struct pcap_stat s;
1051335640Shselasky
1052335640Shselasky	status = g_TcFunctions.InstanceQueryStatistics(pt->TcInstance, &statistics);
1053335640Shselasky
1054335640Shselasky	if (status != TC_SUCCESS)
1055335640Shselasky	{
1056335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
1057335640Shselasky		return -1;
1058335640Shselasky	}
1059335640Shselasky
1060335640Shselasky	memset(&s, 0, sizeof(s));
1061335640Shselasky
1062335640Shselasky	status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter);
1063335640Shselasky	if (status != TC_SUCCESS)
1064335640Shselasky	{
1065335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
1066335640Shselasky		return -1;
1067335640Shselasky	}
1068335640Shselasky	if (counter <= (ULONGLONG)0xFFFFFFFF)
1069335640Shselasky	{
1070335640Shselasky		s.ps_recv = (ULONG)counter;
1071335640Shselasky	}
1072335640Shselasky	else
1073335640Shselasky	{
1074335640Shselasky		s.ps_recv = 0xFFFFFFFF;
1075335640Shselasky	}
1076335640Shselasky
1077335640Shselasky	status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter);
1078335640Shselasky	if (status != TC_SUCCESS)
1079335640Shselasky	{
1080335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
1081335640Shselasky		return -1;
1082335640Shselasky	}
1083335640Shselasky	if (counter <= (ULONGLONG)0xFFFFFFFF)
1084335640Shselasky	{
1085335640Shselasky		s.ps_ifdrop = (ULONG)counter;
1086335640Shselasky		s.ps_drop = (ULONG)counter;
1087335640Shselasky	}
1088335640Shselasky	else
1089335640Shselasky	{
1090335640Shselasky		s.ps_ifdrop = 0xFFFFFFFF;
1091335640Shselasky		s.ps_drop = 0xFFFFFFFF;
1092335640Shselasky	}
1093335640Shselasky
1094335640Shselasky#if defined(_WIN32) && defined(ENABLE_REMOTE)
1095335640Shselasky	s.ps_capt = pt->TcAcceptedCount;
1096335640Shselasky#endif
1097335640Shselasky	*ps = s;
1098335640Shselasky
1099335640Shselasky	return 0;
1100335640Shselasky}
1101335640Shselasky
1102335640Shselasky
1103335640Shselasky/*
1104335640Shselasky * We filter at user level, since the kernel driver does't process the packets
1105335640Shselasky */
1106335640Shselaskystatic int
1107335640ShselaskyTcSetFilter(pcap_t *p, struct bpf_program *fp)
1108335640Shselasky{
1109335640Shselasky	if(!fp)
1110335640Shselasky	{
1111335640Shselasky		strncpy(p->errbuf, "setfilter: No filter specified", sizeof(p->errbuf));
1112335640Shselasky		return -1;
1113335640Shselasky	}
1114335640Shselasky
1115335640Shselasky	/* Install a user level filter */
1116335640Shselasky	if (install_bpf_program(p, fp) < 0)
1117335640Shselasky	{
1118335640Shselasky		return -1;
1119335640Shselasky	}
1120335640Shselasky
1121335640Shselasky	return 0;
1122335640Shselasky}
1123335640Shselasky
1124335640Shselasky#ifdef _WIN32
1125335640Shselaskystatic struct pcap_stat *
1126335640ShselaskyTcStatsEx(pcap_t *p, int *pcap_stat_size)
1127335640Shselasky{
1128335640Shselasky	struct pcap_tc *pt = p->priv;
1129335640Shselasky	TC_STATISTICS statistics;
1130335640Shselasky	TC_STATUS status;
1131335640Shselasky	ULONGLONG counter;
1132335640Shselasky
1133335640Shselasky	*pcap_stat_size = sizeof (p->stat);
1134335640Shselasky
1135335640Shselasky	status = g_TcFunctions.InstanceQueryStatistics(pt->TcInstance, &statistics);
1136335640Shselasky
1137335640Shselasky	if (status != TC_SUCCESS)
1138335640Shselasky	{
1139335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
1140335640Shselasky		return NULL;
1141335640Shselasky	}
1142335640Shselasky
1143335640Shselasky	memset(&p->stat, 0, sizeof(p->stat));
1144335640Shselasky
1145335640Shselasky	status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter);
1146335640Shselasky	if (status != TC_SUCCESS)
1147335640Shselasky	{
1148335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
1149335640Shselasky		return NULL;
1150335640Shselasky	}
1151335640Shselasky	if (counter <= (ULONGLONG)0xFFFFFFFF)
1152335640Shselasky	{
1153335640Shselasky		p->stat.ps_recv = (ULONG)counter;
1154335640Shselasky	}
1155335640Shselasky	else
1156335640Shselasky	{
1157335640Shselasky		p->stat.ps_recv = 0xFFFFFFFF;
1158335640Shselasky	}
1159335640Shselasky
1160335640Shselasky	status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter);
1161335640Shselasky	if (status != TC_SUCCESS)
1162335640Shselasky	{
1163335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
1164335640Shselasky		return NULL;
1165335640Shselasky	}
1166335640Shselasky	if (counter <= (ULONGLONG)0xFFFFFFFF)
1167335640Shselasky	{
1168335640Shselasky		p->stat.ps_ifdrop = (ULONG)counter;
1169335640Shselasky		p->stat.ps_drop = (ULONG)counter;
1170335640Shselasky	}
1171335640Shselasky	else
1172335640Shselasky	{
1173335640Shselasky		p->stat.ps_ifdrop = 0xFFFFFFFF;
1174335640Shselasky		p->stat.ps_drop = 0xFFFFFFFF;
1175335640Shselasky	}
1176335640Shselasky
1177335640Shselasky#if defined(_WIN32) && defined(ENABLE_REMOTE)
1178335640Shselasky	p->stat.ps_capt = pt->TcAcceptedCount;
1179335640Shselasky#endif
1180335640Shselasky
1181335640Shselasky	return &p->stat;
1182335640Shselasky}
1183335640Shselasky
1184335640Shselasky/* Set the dimension of the kernel-level capture buffer */
1185335640Shselaskystatic int
1186335640ShselaskyTcSetBuff(pcap_t *p, int dim)
1187335640Shselasky{
1188335640Shselasky	/*
1189335640Shselasky	 * XXX turbocap has an internal way of managing buffers.
1190335640Shselasky	 * And at the moment it's not configurable, so we just
1191335640Shselasky	 * silently ignore the request to set the buffer.
1192335640Shselasky	 */
1193335640Shselasky	return 0;
1194335640Shselasky}
1195335640Shselasky
1196335640Shselaskystatic int
1197335640ShselaskyTcSetMode(pcap_t *p, int mode)
1198335640Shselasky{
1199335640Shselasky	if (mode != MODE_CAPT)
1200335640Shselasky	{
1201335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mode %u not supported by TurboCap devices. TurboCap only supports capture.", mode);
1202335640Shselasky		return -1;
1203335640Shselasky	}
1204335640Shselasky
1205335640Shselasky	return 0;
1206335640Shselasky}
1207335640Shselasky
1208335640Shselaskystatic int
1209335640ShselaskyTcSetMinToCopy(pcap_t *p, int size)
1210335640Shselasky{
1211335640Shselasky	struct pcap_tc *pt = p->priv;
1212335640Shselasky	TC_STATUS status;
1213335640Shselasky
1214335640Shselasky	if (size < 0)
1215335640Shselasky	{
1216335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mintocopy cannot be less than 0.");
1217335640Shselasky		return -1;
1218335640Shselasky	}
1219335640Shselasky
1220335640Shselasky	status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_MINTOCOPY, (ULONG)size);
1221335640Shselasky
1222335640Shselasky	if (status != TC_SUCCESS)
1223335640Shselasky	{
1224335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error setting the mintocopy: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
1225335640Shselasky	}
1226335640Shselasky
1227335640Shselasky	return 0;
1228335640Shselasky}
1229335640Shselasky
1230335640Shselaskystatic HANDLE
1231335640ShselaskyTcGetReceiveWaitHandle(pcap_t *p)
1232335640Shselasky{
1233335640Shselasky	struct pcap_tc *pt = p->priv;
1234335640Shselasky
1235335640Shselasky	return g_TcFunctions.InstanceGetReceiveWaitHandle(pt->TcInstance);
1236335640Shselasky}
1237335640Shselasky
1238335640Shselaskystatic int
1239335640ShselaskyTcOidGetRequest(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_)
1240335640Shselasky{
1241335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
1242335640Shselasky	    "An OID get request cannot be performed on a TurboCap device");
1243335640Shselasky	return PCAP_ERROR;
1244335640Shselasky}
1245335640Shselasky
1246335640Shselaskystatic int
1247335640ShselaskyTcOidSetRequest(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_,
1248335640Shselasky    size_t *lenp _U_)
1249335640Shselasky{
1250335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
1251335640Shselasky	    "An OID set request cannot be performed on a TurboCap device");
1252335640Shselasky	return PCAP_ERROR;
1253335640Shselasky}
1254335640Shselasky
1255335640Shselaskystatic u_int
1256335640ShselaskyTcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_)
1257335640Shselasky{
1258335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
1259335640Shselasky	    "Packets cannot be bulk transmitted on a TurboCap device");
1260335640Shselasky	return 0;
1261335640Shselasky}
1262335640Shselasky
1263335640Shselaskystatic int
1264335640ShselaskyTcSetUserBuffer(pcap_t *p, int size _U_)
1265335640Shselasky{
1266335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
1267335640Shselasky	    "The user buffer cannot be set on a TurboCap device");
1268335640Shselasky	return -1;
1269335640Shselasky}
1270335640Shselasky
1271335640Shselaskystatic int
1272335640ShselaskyTcLiveDump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_)
1273335640Shselasky{
1274335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
1275335640Shselasky	    "Live packet dumping cannot be performed on a TurboCap device");
1276335640Shselasky	return -1;
1277335640Shselasky}
1278335640Shselasky
1279335640Shselaskystatic int
1280335640ShselaskyTcLiveDumpEnded(pcap_t *p, int sync _U_)
1281335640Shselasky{
1282335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
1283335640Shselasky	    "Live packet dumping cannot be performed on a TurboCap device");
1284335640Shselasky	return -1;
1285335640Shselasky}
1286335640Shselasky
1287335640Shselaskystatic PAirpcapHandle
1288335640ShselaskyTcGetAirPcapHandle(pcap_t *p _U_)
1289335640Shselasky{
1290335640Shselasky	return NULL;
1291335640Shselasky}
1292335640Shselasky#endif
1293