// 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/gopacket/gopacket"
	"github.com/gopacket/gopacket/layers"
	"golang.org/x/sys/windows"
)

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 := windows.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 := windows.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 windows.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)

	if haveSearch, _ := syscall.GetProcAddress(kernel32, "AddDllDirectory"); haveSearch != 0 {
		// if AddDllDirectory is present, we can use LOAD_LIBRARY_* stuff with LoadLibraryEx to avoid wpcap.dll hijacking
		// see: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
		const LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400
		const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
		wpcapHandle, err = windows.LoadLibraryEx("wpcap.dll", 0, LOAD_LIBRARY_SEARCH_USER_DIRS|LOAD_LIBRARY_SEARCH_SYSTEM32)
		if err != nil {
			return fmt.Errorf("couldn't load wpcap.dll")
		}
	} else {
		// otherwise fall back to load it with the unsafe search cause by SetDllDirectory
		wpcapHandle, err = windows.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
}
