886 lines
24 KiB
Go
886 lines
24 KiB
Go
// Copyright 2012 Google, Inc. All rights reserved.
|
|
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree.
|
|
|
|
package pcap
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
)
|
|
|
|
var pcapLoaded = false
|
|
|
|
const npcapPath = "\\Npcap"
|
|
|
|
func initDllPath(kernel32 syscall.Handle) {
|
|
setDllDirectory, err := syscall.GetProcAddress(kernel32, "SetDllDirectoryA")
|
|
if err != nil {
|
|
// we can't do anything since SetDllDirectoryA is missing - fall back to use first wpcap.dll we encounter
|
|
return
|
|
}
|
|
getSystemDirectory, err := syscall.GetProcAddress(kernel32, "GetSystemDirectoryA")
|
|
if err != nil {
|
|
// we can't do anything since SetDllDirectoryA is missing - fall back to use first wpcap.dll we encounter
|
|
return
|
|
}
|
|
buf := make([]byte, 4096)
|
|
r, _, _ := syscall.Syscall(getSystemDirectory, 2, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
|
|
if r == 0 || r > 4096-uintptr(len(npcapPath))-1 {
|
|
// we can't do anything since SetDllDirectoryA is missing - fall back to use first wpcap.dll we encounter
|
|
return
|
|
}
|
|
copy(buf[r:], npcapPath)
|
|
_, _, _ = syscall.Syscall(setDllDirectory, 1, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
|
|
// ignore errors here - we just fallback to load wpcap.dll from default locations
|
|
}
|
|
|
|
// loadedDllPath will hold the full pathname of the loaded wpcap.dll after init if possible
|
|
var loadedDllPath = "wpcap.dll"
|
|
|
|
func initLoadedDllPath(kernel32 syscall.Handle) {
|
|
getModuleFileName, err := syscall.GetProcAddress(kernel32, "GetModuleFileNameA")
|
|
if err != nil {
|
|
// we can't get the filename of the loaded module in this case - just leave default of wpcap.dll
|
|
return
|
|
}
|
|
buf := make([]byte, 4096)
|
|
r, _, _ := syscall.Syscall(getModuleFileName, 3, uintptr(wpcapHandle), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
|
|
if r == 0 {
|
|
// we can't get the filename of the loaded module in this case - just leave default of wpcap.dll
|
|
return
|
|
}
|
|
loadedDllPath = string(buf[:int(r)])
|
|
}
|
|
|
|
func mustLoad(fun string) uintptr {
|
|
addr, err := syscall.GetProcAddress(wpcapHandle, fun)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Couldn't load function %s from %s", fun, loadedDllPath))
|
|
}
|
|
return addr
|
|
}
|
|
|
|
func mightLoad(fun string) uintptr {
|
|
addr, err := syscall.GetProcAddress(wpcapHandle, fun)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return addr
|
|
}
|
|
|
|
func byteSliceToString(bval []byte) string {
|
|
for i := range bval {
|
|
if bval[i] == 0 {
|
|
return string(bval[:i])
|
|
}
|
|
}
|
|
return string(bval[:])
|
|
}
|
|
|
|
// bytePtrToString returns a string copied from pointer to a null terminated byte array
|
|
// WARNING: ONLY SAFE WITH IF r POINTS TO C MEMORY!
|
|
// govet will complain about this function for the reason stated above
|
|
func bytePtrToString(r uintptr) string {
|
|
if r == 0 {
|
|
return ""
|
|
}
|
|
bval := (*[1 << 30]byte)(unsafe.Pointer(r))
|
|
return byteSliceToString(bval[:])
|
|
}
|
|
|
|
var wpcapHandle syscall.Handle
|
|
var msvcrtHandle syscall.Handle
|
|
var (
|
|
callocPtr,
|
|
pcapStrerrorPtr,
|
|
pcapStatustostrPtr,
|
|
pcapOpenLivePtr,
|
|
pcapOpenOfflinePtr,
|
|
pcapClosePtr,
|
|
pcapGeterrPtr,
|
|
pcapStatsPtr,
|
|
pcapCompilePtr,
|
|
pcapFreecodePtr,
|
|
pcapLookupnetPtr,
|
|
pcapOfflineFilterPtr,
|
|
pcapSetfilterPtr,
|
|
pcapListDatalinksPtr,
|
|
pcapFreeDatalinksPtr,
|
|
pcapDatalinkValToNamePtr,
|
|
pcapDatalinkValToDescriptionPtr,
|
|
pcapOpenDeadPtr,
|
|
pcapNextExPtr,
|
|
pcapDatalinkPtr,
|
|
pcapSetDatalinkPtr,
|
|
pcapDatalinkNameToValPtr,
|
|
pcapLibVersionPtr,
|
|
pcapFreealldevsPtr,
|
|
pcapFindalldevsPtr,
|
|
pcapSendpacketPtr,
|
|
pcapSetdirectionPtr,
|
|
pcapSnapshotPtr,
|
|
pcapTstampTypeValToNamePtr,
|
|
pcapTstampTypeNameToValPtr,
|
|
pcapListTstampTypesPtr,
|
|
pcapFreeTstampTypesPtr,
|
|
pcapSetTstampTypePtr,
|
|
pcapGetTstampPrecisionPtr,
|
|
pcapSetTstampPrecisionPtr,
|
|
pcapOpenOfflineWithTstampPrecisionPtr,
|
|
pcapHOpenOfflineWithTstampPrecisionPtr,
|
|
pcapActivatePtr,
|
|
pcapCreatePtr,
|
|
pcapSetSnaplenPtr,
|
|
pcapSetPromiscPtr,
|
|
pcapSetTimeoutPtr,
|
|
pcapCanSetRfmonPtr,
|
|
pcapSetRfmonPtr,
|
|
pcapSetBufferSizePtr,
|
|
pcapSetImmediateModePtr,
|
|
pcapHopenOfflinePtr uintptr
|
|
)
|
|
|
|
func init() {
|
|
LoadWinPCAP()
|
|
}
|
|
|
|
// LoadWinPCAP attempts to dynamically load the wpcap DLL and resolve necessary functions
|
|
func LoadWinPCAP() error {
|
|
if pcapLoaded {
|
|
return nil
|
|
}
|
|
|
|
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't load kernel32.dll")
|
|
}
|
|
defer syscall.FreeLibrary(kernel32)
|
|
|
|
initDllPath(kernel32)
|
|
|
|
wpcapHandle, err = syscall.LoadLibrary("wpcap.dll")
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't load wpcap.dll")
|
|
}
|
|
initLoadedDllPath(kernel32)
|
|
msvcrtHandle, err = syscall.LoadLibrary("msvcrt.dll")
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't load msvcrt.dll")
|
|
}
|
|
callocPtr, err = syscall.GetProcAddress(msvcrtHandle, "calloc")
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't get calloc function")
|
|
}
|
|
|
|
pcapStrerrorPtr = mustLoad("pcap_strerror")
|
|
pcapStatustostrPtr = mightLoad("pcap_statustostr") // not available on winpcap
|
|
pcapOpenLivePtr = mustLoad("pcap_open_live")
|
|
pcapOpenOfflinePtr = mustLoad("pcap_open_offline")
|
|
pcapClosePtr = mustLoad("pcap_close")
|
|
pcapGeterrPtr = mustLoad("pcap_geterr")
|
|
pcapStatsPtr = mustLoad("pcap_stats")
|
|
pcapCompilePtr = mustLoad("pcap_compile")
|
|
pcapFreecodePtr = mustLoad("pcap_freecode")
|
|
pcapLookupnetPtr = mustLoad("pcap_lookupnet")
|
|
pcapOfflineFilterPtr = mustLoad("pcap_offline_filter")
|
|
pcapSetfilterPtr = mustLoad("pcap_setfilter")
|
|
pcapListDatalinksPtr = mustLoad("pcap_list_datalinks")
|
|
pcapFreeDatalinksPtr = mustLoad("pcap_free_datalinks")
|
|
pcapDatalinkValToNamePtr = mustLoad("pcap_datalink_val_to_name")
|
|
pcapDatalinkValToDescriptionPtr = mustLoad("pcap_datalink_val_to_description")
|
|
pcapOpenDeadPtr = mustLoad("pcap_open_dead")
|
|
pcapNextExPtr = mustLoad("pcap_next_ex")
|
|
pcapDatalinkPtr = mustLoad("pcap_datalink")
|
|
pcapSetDatalinkPtr = mustLoad("pcap_set_datalink")
|
|
pcapDatalinkNameToValPtr = mustLoad("pcap_datalink_name_to_val")
|
|
pcapLibVersionPtr = mustLoad("pcap_lib_version")
|
|
pcapFreealldevsPtr = mustLoad("pcap_freealldevs")
|
|
pcapFindalldevsPtr = mustLoad("pcap_findalldevs")
|
|
pcapSendpacketPtr = mustLoad("pcap_sendpacket")
|
|
pcapSetdirectionPtr = mustLoad("pcap_setdirection")
|
|
pcapSnapshotPtr = mustLoad("pcap_snapshot")
|
|
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
|
|
pcapTstampTypeValToNamePtr = mightLoad("pcap_tstamp_type_val_to_name")
|
|
pcapTstampTypeNameToValPtr = mightLoad("pcap_tstamp_type_name_to_val")
|
|
pcapListTstampTypesPtr = mightLoad("pcap_list_tstamp_types")
|
|
pcapFreeTstampTypesPtr = mightLoad("pcap_free_tstamp_types")
|
|
pcapSetTstampTypePtr = mightLoad("pcap_set_tstamp_type")
|
|
pcapGetTstampPrecisionPtr = mightLoad("pcap_get_tstamp_precision")
|
|
pcapSetTstampPrecisionPtr = mightLoad("pcap_set_tstamp_precision")
|
|
pcapOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_open_offline_with_tstamp_precision")
|
|
pcapHOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_hopen_offline_with_tstamp_precision")
|
|
pcapActivatePtr = mustLoad("pcap_activate")
|
|
pcapCreatePtr = mustLoad("pcap_create")
|
|
pcapSetSnaplenPtr = mustLoad("pcap_set_snaplen")
|
|
pcapSetPromiscPtr = mustLoad("pcap_set_promisc")
|
|
pcapSetTimeoutPtr = mustLoad("pcap_set_timeout")
|
|
//winpcap does not support rfmon
|
|
pcapCanSetRfmonPtr = mightLoad("pcap_can_set_rfmon")
|
|
pcapSetRfmonPtr = mightLoad("pcap_set_rfmon")
|
|
pcapSetBufferSizePtr = mustLoad("pcap_set_buffer_size")
|
|
//libpcap <1.5 does not have pcap_set_immediate_mode
|
|
pcapSetImmediateModePtr = mightLoad("pcap_set_immediate_mode")
|
|
pcapHopenOfflinePtr = mustLoad("pcap_hopen_offline")
|
|
|
|
pcapLoaded = true
|
|
return nil
|
|
}
|
|
|
|
func (h *pcapPkthdr) getSec() int64 {
|
|
return int64(h.Ts.Sec)
|
|
}
|
|
|
|
func (h *pcapPkthdr) getUsec() int64 {
|
|
return int64(h.Ts.Usec)
|
|
}
|
|
|
|
func (h *pcapPkthdr) getLen() int {
|
|
return int(h.Len)
|
|
}
|
|
|
|
func (h *pcapPkthdr) getCaplen() int {
|
|
return int(h.Caplen)
|
|
}
|
|
|
|
func statusError(status pcapCint) error {
|
|
var ret uintptr
|
|
if pcapStatustostrPtr == 0 {
|
|
ret, _, _ = syscall.Syscall(pcapStrerrorPtr, 1, uintptr(status), 0, 0)
|
|
} else {
|
|
ret, _, _ = syscall.Syscall(pcapStatustostrPtr, 1, uintptr(status), 0, 0)
|
|
}
|
|
return errors.New(bytePtrToString(ret))
|
|
}
|
|
|
|
func pcapGetTstampPrecision(cptr pcapTPtr) int {
|
|
if pcapGetTstampPrecisionPtr == 0 {
|
|
return pcapTstampPrecisionMicro
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapGetTstampPrecisionPtr, 1, uintptr(cptr), 0, 0)
|
|
return int(pcapCint(ret))
|
|
}
|
|
|
|
func pcapSetTstampPrecision(cptr pcapTPtr, precision int) error {
|
|
if pcapSetTstampPrecisionPtr == 0 {
|
|
return errors.New("Not supported")
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapSetTstampPrecisionPtr, 2, uintptr(cptr), uintptr(precision), 0)
|
|
if pcapCint(ret) < 0 {
|
|
return errors.New("Not supported")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func pcapOpenLive(device string, snaplen int, pro int, timeout int) (*Handle, error) {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := make([]byte, errorBufferSize)
|
|
dev, err := syscall.BytePtrFromString(device)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cptr, _, _ := syscall.Syscall6(pcapOpenLivePtr, 5, uintptr(unsafe.Pointer(dev)), uintptr(snaplen), uintptr(pro), uintptr(timeout), uintptr(unsafe.Pointer(&buf[0])), 0)
|
|
|
|
if cptr == 0 {
|
|
return nil, errors.New(byteSliceToString(buf))
|
|
}
|
|
return &Handle{cptr: pcapTPtr(cptr)}, nil
|
|
}
|
|
|
|
func openOffline(file string) (handle *Handle, err error) {
|
|
err = LoadWinPCAP()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := make([]byte, errorBufferSize)
|
|
f, err := syscall.BytePtrFromString(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var cptr uintptr
|
|
if pcapOpenOfflineWithTstampPrecisionPtr == 0 {
|
|
cptr, _, _ = syscall.Syscall(pcapOpenOfflinePtr, 2, uintptr(unsafe.Pointer(f)), uintptr(unsafe.Pointer(&buf[0])), 0)
|
|
} else {
|
|
cptr, _, _ = syscall.Syscall(pcapOpenOfflineWithTstampPrecisionPtr, 3, uintptr(unsafe.Pointer(f)), uintptr(pcapTstampPrecisionNano), uintptr(unsafe.Pointer(&buf[0])))
|
|
}
|
|
|
|
if cptr == 0 {
|
|
return nil, errors.New(byteSliceToString(buf))
|
|
}
|
|
|
|
h := &Handle{cptr: pcapTPtr(cptr)}
|
|
return h, nil
|
|
}
|
|
|
|
func (p *Handle) pcapClose() {
|
|
if p.cptr != 0 {
|
|
_, _, _ = syscall.Syscall(pcapClosePtr, 1, uintptr(p.cptr), 0, 0)
|
|
}
|
|
p.cptr = 0
|
|
}
|
|
|
|
func (p *Handle) pcapGeterr() error {
|
|
ret, _, _ := syscall.Syscall(pcapGeterrPtr, 1, uintptr(p.cptr), 0, 0)
|
|
return errors.New(bytePtrToString(ret))
|
|
}
|
|
|
|
func (p *Handle) pcapStats() (*Stats, error) {
|
|
var cstats pcapStats
|
|
ret, _, _ := syscall.Syscall(pcapStatsPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&cstats)), 0)
|
|
if pcapCint(ret) < 0 {
|
|
return nil, p.pcapGeterr()
|
|
}
|
|
return &Stats{
|
|
PacketsReceived: int(cstats.Recv),
|
|
PacketsDropped: int(cstats.Drop),
|
|
PacketsIfDropped: int(cstats.Ifdrop),
|
|
}, nil
|
|
}
|
|
|
|
// for libpcap < 1.8 pcap_compile is NOT thread-safe, so protect it.
|
|
var pcapCompileMu sync.Mutex
|
|
|
|
func (p *Handle) pcapCompile(expr string, maskp uint32) (pcapBpfProgram, error) {
|
|
var bpf pcapBpfProgram
|
|
cexpr, err := syscall.BytePtrFromString(expr)
|
|
if err != nil {
|
|
return pcapBpfProgram{}, err
|
|
}
|
|
pcapCompileMu.Lock()
|
|
defer pcapCompileMu.Unlock()
|
|
res, _, _ := syscall.Syscall6(pcapCompilePtr, 5, uintptr(p.cptr), uintptr(unsafe.Pointer(&bpf)), uintptr(unsafe.Pointer(cexpr)), uintptr(1), uintptr(maskp), 0)
|
|
if pcapCint(res) < 0 {
|
|
return bpf, p.pcapGeterr()
|
|
}
|
|
return bpf, nil
|
|
}
|
|
|
|
func (p pcapBpfProgram) free() {
|
|
_, _, _ = syscall.Syscall(pcapFreecodePtr, 1, uintptr(unsafe.Pointer(&p)), 0, 0)
|
|
}
|
|
|
|
func (p pcapBpfProgram) toBPFInstruction() []BPFInstruction {
|
|
bpfInsn := (*[bpfInstructionBufferSize]pcapBpfInstruction)(unsafe.Pointer(p.Insns))[0:p.Len:p.Len]
|
|
bpfInstruction := make([]BPFInstruction, len(bpfInsn), len(bpfInsn))
|
|
|
|
for i, v := range bpfInsn {
|
|
bpfInstruction[i].Code = v.Code
|
|
bpfInstruction[i].Jt = v.Jt
|
|
bpfInstruction[i].Jf = v.Jf
|
|
bpfInstruction[i].K = v.K
|
|
}
|
|
return bpfInstruction
|
|
}
|
|
|
|
func pcapBpfProgramFromInstructions(bpfInstructions []BPFInstruction) pcapBpfProgram {
|
|
var bpf pcapBpfProgram
|
|
bpf.Len = uint32(len(bpfInstructions))
|
|
cbpfInsns, _, _ := syscall.Syscall(callocPtr, 2, uintptr(len(bpfInstructions)), uintptr(unsafe.Sizeof(bpfInstructions[0])), 0)
|
|
gbpfInsns := (*[bpfInstructionBufferSize]pcapBpfInstruction)(unsafe.Pointer(cbpfInsns))
|
|
|
|
for i, v := range bpfInstructions {
|
|
gbpfInsns[i].Code = v.Code
|
|
gbpfInsns[i].Jt = v.Jt
|
|
gbpfInsns[i].Jf = v.Jf
|
|
gbpfInsns[i].K = v.K
|
|
}
|
|
|
|
bpf.Insns = (*pcapBpfInstruction)(unsafe.Pointer(cbpfInsns))
|
|
return bpf
|
|
}
|
|
|
|
func pcapLookupnet(device string) (netp, maskp uint32, err error) {
|
|
err = LoadWinPCAP()
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
buf := make([]byte, errorBufferSize)
|
|
dev, err := syscall.BytePtrFromString(device)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
e, _, _ := syscall.Syscall6(pcapLookupnetPtr, 4, uintptr(unsafe.Pointer(dev)), uintptr(unsafe.Pointer(&netp)), uintptr(unsafe.Pointer(&maskp)), uintptr(unsafe.Pointer(&buf[0])), 0, 0)
|
|
if pcapCint(e) < 0 {
|
|
return 0, 0, errors.New(byteSliceToString(buf))
|
|
}
|
|
return
|
|
}
|
|
|
|
func (b *BPF) pcapOfflineFilter(ci gopacket.CaptureInfo, data []byte) bool {
|
|
var hdr pcapPkthdr
|
|
hdr.Ts.Sec = int32(ci.Timestamp.Unix())
|
|
hdr.Ts.Usec = int32(ci.Timestamp.Nanosecond() / 1000)
|
|
hdr.Caplen = uint32(len(data)) // Trust actual length over ci.Length.
|
|
hdr.Len = uint32(ci.Length)
|
|
e, _, _ := syscall.Syscall(pcapOfflineFilterPtr, 3, uintptr(unsafe.Pointer(&b.bpf.bpf)), uintptr(unsafe.Pointer(&hdr)), uintptr(unsafe.Pointer(&data[0])))
|
|
return e != 0
|
|
}
|
|
|
|
func (p *Handle) pcapSetfilter(bpf pcapBpfProgram) error {
|
|
e, _, _ := syscall.Syscall(pcapSetfilterPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&bpf)), 0)
|
|
if pcapCint(e) < 0 {
|
|
return p.pcapGeterr()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Handle) pcapListDatalinks() (datalinks []Datalink, err error) {
|
|
var dltbuf *pcapCint
|
|
ret, _, _ := syscall.Syscall(pcapListDatalinksPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&dltbuf)), 0)
|
|
|
|
n := int(pcapCint(ret))
|
|
|
|
if n < 0 {
|
|
return nil, p.pcapGeterr()
|
|
}
|
|
defer syscall.Syscall(pcapFreeDatalinksPtr, 1, uintptr(unsafe.Pointer(dltbuf)), 0, 0)
|
|
|
|
datalinks = make([]Datalink, n)
|
|
|
|
dltArray := (*[1 << 28]pcapCint)(unsafe.Pointer(dltbuf))
|
|
|
|
for i := 0; i < n; i++ {
|
|
datalinks[i].Name = pcapDatalinkValToName(int((*dltArray)[i]))
|
|
datalinks[i].Description = pcapDatalinkValToDescription(int((*dltArray)[i]))
|
|
}
|
|
|
|
return datalinks, nil
|
|
}
|
|
|
|
func pcapOpenDead(linkType layers.LinkType, captureLength int) (*Handle, error) {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cptr, _, _ := syscall.Syscall(pcapOpenDeadPtr, 2, uintptr(linkType), uintptr(captureLength), 0)
|
|
if cptr == 0 {
|
|
return nil, errors.New("error opening dead capture")
|
|
}
|
|
|
|
return &Handle{cptr: pcapTPtr(cptr)}, nil
|
|
}
|
|
|
|
func (p *Handle) pcapNextPacketEx() NextError {
|
|
r, _, _ := syscall.Syscall(pcapNextExPtr, 3, uintptr(p.cptr), uintptr(unsafe.Pointer(&p.pkthdr)), uintptr(unsafe.Pointer(&p.bufptr)))
|
|
ret := pcapCint(r)
|
|
// According to https://github.com/the-tcpdump-group/libpcap/blob/1131a7c26c6f4d4772e4a2beeaf7212f4dea74ac/pcap.c#L398-L406 ,
|
|
// the return value of pcap_next_ex could be greater than 1 for success.
|
|
// Let's just make it 1 if it comes bigger than 1.
|
|
if ret > 1 {
|
|
ret = 1
|
|
}
|
|
return NextError(ret)
|
|
}
|
|
|
|
func (p *Handle) pcapDatalink() layers.LinkType {
|
|
ret, _, _ := syscall.Syscall(pcapDatalinkPtr, 1, uintptr(p.cptr), 0, 0)
|
|
return layers.LinkType(ret)
|
|
}
|
|
|
|
func (p *Handle) pcapSetDatalink(dlt layers.LinkType) error {
|
|
ret, _, _ := syscall.Syscall(pcapSetDatalinkPtr, 2, uintptr(p.cptr), uintptr(dlt), 0)
|
|
if pcapCint(ret) < 0 {
|
|
return p.pcapGeterr()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func pcapDatalinkValToName(dlt int) string {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapDatalinkValToNamePtr, 1, uintptr(dlt), 0, 0)
|
|
return bytePtrToString(ret)
|
|
}
|
|
|
|
func pcapDatalinkValToDescription(dlt int) string {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapDatalinkValToDescriptionPtr, 1, uintptr(dlt), 0, 0)
|
|
return bytePtrToString(ret)
|
|
}
|
|
|
|
func pcapDatalinkNameToVal(name string) int {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
cptr, err := syscall.BytePtrFromString(name)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapDatalinkNameToValPtr, 1, uintptr(unsafe.Pointer(cptr)), 0, 0)
|
|
return int(pcapCint(ret))
|
|
}
|
|
|
|
func pcapLibVersion() string {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapLibVersionPtr, 0, 0, 0, 0)
|
|
return bytePtrToString(ret)
|
|
}
|
|
|
|
func (p *Handle) isOpen() bool {
|
|
return p.cptr != 0
|
|
}
|
|
|
|
type pcapDevices struct {
|
|
all, cur *pcapIf
|
|
}
|
|
|
|
func (p pcapDevices) free() {
|
|
syscall.Syscall(pcapFreealldevsPtr, 1, uintptr(unsafe.Pointer(p.all)), 0, 0)
|
|
}
|
|
|
|
func (p *pcapDevices) next() bool {
|
|
if p.cur == nil {
|
|
p.cur = p.all
|
|
if p.cur == nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
if p.cur.Next == nil {
|
|
return false
|
|
}
|
|
p.cur = p.cur.Next
|
|
return true
|
|
}
|
|
|
|
func (p pcapDevices) name() string {
|
|
return bytePtrToString(uintptr(unsafe.Pointer(p.cur.Name)))
|
|
}
|
|
|
|
func (p pcapDevices) description() string {
|
|
return bytePtrToString(uintptr(unsafe.Pointer(p.cur.Description)))
|
|
}
|
|
|
|
func (p pcapDevices) flags() uint32 {
|
|
return p.cur.Flags
|
|
}
|
|
|
|
type pcapAddresses struct {
|
|
all, cur *pcapAddr
|
|
}
|
|
|
|
func (p *pcapAddresses) next() bool {
|
|
if p.cur == nil {
|
|
p.cur = p.all
|
|
if p.cur == nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
if p.cur.Next == nil {
|
|
return false
|
|
}
|
|
p.cur = p.cur.Next
|
|
return true
|
|
}
|
|
|
|
func (p pcapAddresses) addr() *syscall.RawSockaddr {
|
|
return p.cur.Addr
|
|
}
|
|
|
|
func (p pcapAddresses) netmask() *syscall.RawSockaddr {
|
|
return p.cur.Netmask
|
|
}
|
|
|
|
func (p pcapAddresses) broadaddr() *syscall.RawSockaddr {
|
|
return p.cur.Broadaddr
|
|
}
|
|
|
|
func (p pcapAddresses) dstaddr() *syscall.RawSockaddr {
|
|
return p.cur.Dstaddr
|
|
}
|
|
|
|
func (p pcapDevices) addresses() pcapAddresses {
|
|
return pcapAddresses{all: p.cur.Addresses}
|
|
}
|
|
|
|
func pcapFindAllDevs() (pcapDevices, error) {
|
|
var alldevsp pcapDevices
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
return alldevsp, err
|
|
}
|
|
|
|
buf := make([]byte, errorBufferSize)
|
|
|
|
ret, _, _ := syscall.Syscall(pcapFindalldevsPtr, 2, uintptr(unsafe.Pointer(&alldevsp.all)), uintptr(unsafe.Pointer(&buf[0])), 0)
|
|
|
|
if pcapCint(ret) < 0 {
|
|
return pcapDevices{}, errors.New(byteSliceToString(buf))
|
|
}
|
|
return alldevsp, nil
|
|
}
|
|
|
|
func (p *Handle) pcapSendpacket(data []byte) error {
|
|
ret, _, _ := syscall.Syscall(pcapSendpacketPtr, 3, uintptr(p.cptr), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)))
|
|
if pcapCint(ret) < 0 {
|
|
return p.pcapGeterr()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Handle) pcapSetdirection(direction Direction) error {
|
|
status, _, _ := syscall.Syscall(pcapSetdirectionPtr, 2, uintptr(p.cptr), uintptr(direction), 0)
|
|
if pcapCint(status) < 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Handle) pcapSnapshot() int {
|
|
ret, _, _ := syscall.Syscall(pcapSnapshotPtr, 1, uintptr(p.cptr), 0, 0)
|
|
return int(pcapCint(ret))
|
|
}
|
|
|
|
func (t TimestampSource) pcapTstampTypeValToName() string {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
|
|
if pcapTstampTypeValToNamePtr == 0 {
|
|
return "pcap timestamp types not supported"
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapTstampTypeValToNamePtr, 1, uintptr(t), 0, 0)
|
|
return bytePtrToString(ret)
|
|
}
|
|
|
|
func pcapTstampTypeNameToVal(s string) (TimestampSource, error) {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
|
|
if pcapTstampTypeNameToValPtr == 0 {
|
|
return 0, statusError(pcapCint(pcapError))
|
|
}
|
|
cs, err := syscall.BytePtrFromString(s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
ret, _, _ := syscall.Syscall(pcapTstampTypeNameToValPtr, 1, uintptr(unsafe.Pointer(cs)), 0, 0)
|
|
t := pcapCint(ret)
|
|
if t < 0 {
|
|
return 0, statusError(pcapCint(t))
|
|
}
|
|
return TimestampSource(t), nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapGeterr() error {
|
|
ret, _, _ := syscall.Syscall(pcapGeterrPtr, 1, uintptr(p.cptr), 0, 0)
|
|
return errors.New(bytePtrToString(ret))
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapActivate() (*Handle, activateError) {
|
|
r, _, _ := syscall.Syscall(pcapActivatePtr, 1, uintptr(p.cptr), 0, 0)
|
|
ret := activateError(pcapCint(r))
|
|
if ret != aeNoError {
|
|
return nil, ret
|
|
}
|
|
h := &Handle{
|
|
cptr: p.cptr,
|
|
}
|
|
p.cptr = 0
|
|
return h, ret
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapClose() {
|
|
if p.cptr != 0 {
|
|
_, _, _ = syscall.Syscall(pcapClosePtr, 1, uintptr(p.cptr), 0, 0)
|
|
}
|
|
p.cptr = 0
|
|
}
|
|
|
|
func pcapCreate(device string) (*InactiveHandle, error) {
|
|
err := LoadWinPCAP()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := make([]byte, errorBufferSize)
|
|
dev, err := syscall.BytePtrFromString(device)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cptr, _, _ := syscall.Syscall(pcapCreatePtr, 2, uintptr(unsafe.Pointer(dev)), uintptr(unsafe.Pointer(&buf[0])), 0)
|
|
if cptr == 0 {
|
|
return nil, errors.New(byteSliceToString(buf))
|
|
}
|
|
return &InactiveHandle{cptr: pcapTPtr(cptr)}, nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapSetSnaplen(snaplen int) error {
|
|
status, _, _ := syscall.Syscall(pcapSetSnaplenPtr, 2, uintptr(p.cptr), uintptr(snaplen), 0)
|
|
if pcapCint(status) < 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapSetPromisc(promisc bool) error {
|
|
var pro uintptr
|
|
if promisc {
|
|
pro = 1
|
|
}
|
|
status, _, _ := syscall.Syscall(pcapSetPromiscPtr, 2, uintptr(p.cptr), pro, 0)
|
|
if pcapCint(status) < 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapSetTimeout(timeout time.Duration) error {
|
|
status, _, _ := syscall.Syscall(pcapSetTimeoutPtr, 2, uintptr(p.cptr), uintptr(timeoutMillis(timeout)), 0)
|
|
|
|
if pcapCint(status) < 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapListTstampTypes() (out []TimestampSource) {
|
|
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
|
|
if pcapListTstampTypesPtr == 0 {
|
|
return
|
|
}
|
|
var types *pcapCint
|
|
ret, _, _ := syscall.Syscall(pcapListTstampTypesPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&types)), 0)
|
|
n := int(pcapCint(ret))
|
|
if n < 0 {
|
|
return // public interface doesn't have error :(
|
|
}
|
|
defer syscall.Syscall(pcapFreeTstampTypesPtr, 1, uintptr(unsafe.Pointer(types)), 0, 0)
|
|
typesArray := (*[1 << 28]pcapCint)(unsafe.Pointer(types))
|
|
for i := 0; i < n; i++ {
|
|
out = append(out, TimestampSource((*typesArray)[i]))
|
|
}
|
|
return
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapSetTstampType(t TimestampSource) error {
|
|
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
|
|
if pcapSetTstampTypePtr == 0 {
|
|
return statusError(pcapError)
|
|
}
|
|
status, _, _ := syscall.Syscall(pcapSetTstampTypePtr, 2, uintptr(p.cptr), uintptr(t), 0)
|
|
if pcapCint(status) < 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapSetRfmon(monitor bool) error {
|
|
//winpcap does not support rfmon
|
|
if pcapCanSetRfmonPtr == 0 {
|
|
return CannotSetRFMon
|
|
}
|
|
var mon uintptr
|
|
if monitor {
|
|
mon = 1
|
|
}
|
|
canset, _, _ := syscall.Syscall(pcapCanSetRfmonPtr, 1, uintptr(p.cptr), 0, 0)
|
|
switch canset {
|
|
case 0:
|
|
return CannotSetRFMon
|
|
case 1:
|
|
// success
|
|
default:
|
|
return statusError(pcapCint(canset))
|
|
}
|
|
status, _, _ := syscall.Syscall(pcapSetRfmonPtr, 2, uintptr(p.cptr), mon, 0)
|
|
if status != 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapSetBufferSize(bufferSize int) error {
|
|
status, _, _ := syscall.Syscall(pcapSetBufferSizePtr, 2, uintptr(p.cptr), uintptr(bufferSize), 0)
|
|
if pcapCint(status) < 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *InactiveHandle) pcapSetImmediateMode(mode bool) error {
|
|
//libpcap <1.5 does not have pcap_set_immediate_mode
|
|
if pcapSetImmediateModePtr == 0 {
|
|
return statusError(pcapError)
|
|
}
|
|
var md uintptr
|
|
if mode {
|
|
md = 1
|
|
}
|
|
status, _, _ := syscall.Syscall(pcapSetImmediateModePtr, 2, uintptr(p.cptr), md, 0)
|
|
if pcapCint(status) < 0 {
|
|
return statusError(pcapCint(status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Handle) setNonBlocking() error {
|
|
// do nothing
|
|
return nil
|
|
}
|
|
|
|
// waitForPacket waits for a packet or for the timeout to expire.
|
|
func (p *Handle) waitForPacket() {
|
|
// can't use select() so instead just switch goroutines
|
|
runtime.Gosched()
|
|
}
|
|
|
|
// openOfflineFile returns contents of input file as a *Handle.
|
|
func openOfflineFile(file *os.File) (handle *Handle, err error) {
|
|
err = LoadWinPCAP()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := make([]byte, errorBufferSize)
|
|
cf := file.Fd()
|
|
|
|
var cptr uintptr
|
|
if pcapOpenOfflineWithTstampPrecisionPtr == 0 {
|
|
cptr, _, _ = syscall.Syscall(pcapHopenOfflinePtr, 2, cf, uintptr(unsafe.Pointer(&buf[0])), 0)
|
|
} else {
|
|
cptr, _, _ = syscall.Syscall(pcapHOpenOfflineWithTstampPrecisionPtr, 3, cf, uintptr(pcapTstampPrecisionNano), uintptr(unsafe.Pointer(&buf[0])))
|
|
}
|
|
|
|
if cptr == 0 {
|
|
return nil, errors.New(byteSliceToString(buf))
|
|
}
|
|
return &Handle{cptr: pcapTPtr(cptr)}, nil
|
|
}
|