package rule

import (
	"encoding/json"
	"fmt"
	"net"
	"testing"
	"time"

	"github.com/evilsocket/opensnitch/daemon/conman"
	"github.com/evilsocket/opensnitch/daemon/core"
	"github.com/evilsocket/opensnitch/daemon/netstat"
	"github.com/evilsocket/opensnitch/daemon/procmon"
)

var (
	defaultProcPath = "/usr/bin/opensnitchd"
	defaultProcArgs = "-rules-path /etc/opensnitchd/rules/"
	defaultDstHost  = "opensnitch.io"
	defaultDstPort  = uint(443)
	defaultDstIP    = "185.53.178.14"
	defaultUserID   = 666

	netEntry = &netstat.Entry{
		UserId: defaultUserID,
	}

	proc = &procmon.Process{
		ID:   12345,
		Path: defaultProcPath,
		Args: []string{"-rules-path", "/etc/opensnitchd/rules/"},
	}

	conn = &conman.Connection{
		Protocol: "TCP",
		SrcPort:  66666,
		SrcIP:    net.ParseIP("192.168.1.111"),
		DstIP:    net.ParseIP(defaultDstIP),
		DstPort:  defaultDstPort,
		DstHost:  defaultDstHost,
		Process:  proc,
		Entry:    netEntry,
	}
)

func compileListOperators(list *[]Operator, t *testing.T) {
	op := *list
	for i := 0; i < len(*list); i++ {
		if err := op[i].Compile(); err != nil {
			t.Error("NewOperator List, Compile() subitem error:", err)
		}
	}
}

func unmarshalListData(data string, t *testing.T) (op *[]Operator) {
	if err := json.Unmarshal([]byte(data), &op); err != nil {
		t.Error("Error unmarshalling list data:", err, data)
		return nil
	}
	return op
}

func restoreConnection() {
	conn.Process.Path = defaultProcPath
	conn.DstHost = defaultDstHost
	conn.DstPort = defaultDstPort
	conn.Entry.UserId = defaultUserID
}

func TestNewOperatorSimple(t *testing.T) {
	t.Log("Test NewOperator() simple")
	var list []Operator

	opSimple, err := NewOperator(Simple, false, OpTrue, "", list)
	if err != nil {
		t.Error("NewOperator simple.err should be nil: ", err)
		t.Fail()
	}
	if err = opSimple.Compile(); err != nil {
		t.Fail()
	}
	if opSimple.Match(nil, false) == false {
		t.Error("Test NewOperator() simple.case-insensitive doesn't match")
		t.Fail()
	}

	t.Run("Operator Simple proc.id", func(t *testing.T) {
		// proc.id not sensitive
		opSimple, err = NewOperator(Simple, false, OpProcessID, "12345", list)
		if err != nil {
			t.Error("NewOperator simple.case-insensitive.proc.id err should be nil: ", err)
			t.Fail()
		}
		if err = opSimple.Compile(); err != nil {
			t.Error("NewOperator simple.case-insensitive.proc.id Compile() err:", err)
			t.Fail()
		}
		if opSimple.Match(conn, false) == false {
			t.Error("Test NewOperator() simple proc.id doesn't match")
			t.Fail()
		}
	})

	opSimple, err = NewOperator(Simple, false, OpProcessPath, defaultProcPath, list)
	t.Run("Operator Simple proc.path case-insensitive", func(t *testing.T) {
		// proc path not sensitive
		if err != nil {
			t.Error("NewOperator simple proc.path err should be nil: ", err)
			t.Fail()
		}
		if err = opSimple.Compile(); err != nil {
			t.Error("NewOperator simple.case-insensitive.proc.path Compile() err:", err)
			t.Fail()
		}
		if opSimple.Match(conn, false) == false {
			t.Error("Test NewOperator() simple proc.path doesn't match")
			t.Fail()
		}
	})

	t.Run("Operator Simple proc.path sensitive", func(t *testing.T) {
		// proc path sensitive
		opSimple.Sensitive = true
		conn.Process.Path = "/usr/bin/OpenSnitchd"
		if opSimple.Match(conn, false) == true {
			t.Error("Test NewOperator() simple proc.path sensitive match")
			t.Fail()
		}
	})

	opSimple, err = NewOperator(Simple, false, OpDstHost, defaultDstHost, list)
	t.Run("Operator Simple con.dstHost case-insensitive", func(t *testing.T) {
		// proc dst host not sensitive
		if err != nil {
			t.Error("NewOperator simple proc.path err should be nil: ", err)
			t.Fail()
		}
		if err = opSimple.Compile(); err != nil {
			t.Error("NewOperator simple.case-insensitive.dstHost Compile() err:", err)
			t.Fail()
		}
		if opSimple.Match(conn, false) == false {
			t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't match")
			t.Fail()
		}
	})

	t.Run("Operator Simple con.dstHost case-insensitive different host", func(t *testing.T) {
		conn.DstHost = "www.opensnitch.io"
		if opSimple.Match(conn, false) == true {
			t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't MATCH")
			t.Fail()
		}
	})

	t.Run("Operator Simple con.dstHost sensitive", func(t *testing.T) {
		// proc dst host sensitive
		opSimple, err = NewOperator(Simple, true, OpDstHost, "OpEnsNitCh.io", list)
		if err != nil {
			t.Error("NewOperator simple.dstHost.sensitive err should be nil: ", err)
			t.Fail()
		}
		if err = opSimple.Compile(); err != nil {
			t.Error("NewOperator simple.dstHost.sensitive Compile() err:", err)
			t.Fail()
		}
		conn.DstHost = "OpEnsNitCh.io"
		if opSimple.Match(conn, false) == false {
			t.Error("Test NewOperator() simple.dstHost.sensitive doesn't match")
			t.Fail()
		}
	})

	t.Run("Operator Simple proc.args case-insensitive", func(t *testing.T) {
		// proc args case-insensitive
		opSimple, err = NewOperator(Simple, false, OpProcessCmd, defaultProcArgs, list)
		if err != nil {
			t.Error("NewOperator simple proc.args err should be nil: ", err)
			t.Fail()
		}
		if err = opSimple.Compile(); err != nil {
			t.Error("NewOperator simple proc.args Compile() err: ", err)
			t.Fail()
		}
		if opSimple.Match(conn, false) == false {
			t.Error("Test NewOperator() simple proc.args doesn't match")
			t.Fail()
		}
	})

	t.Run("Operator Simple con.dstIp case-insensitive", func(t *testing.T) {
		// proc dstIp case-insensitive
		opSimple, err = NewOperator(Simple, false, OpDstIP, defaultDstIP, list)
		if err != nil {
			t.Error("NewOperator simple conn.dstip.err should be nil: ", err)
			t.Fail()
		}
		if err = opSimple.Compile(); err != nil {
			t.Error("NewOperator simple con.dstIp Compile() err: ", err)
			t.Fail()
		}
		if opSimple.Match(conn, false) == false {
			t.Error("Test NewOperator() simple conn.dstip doesn't match")
			t.Fail()
		}
	})

	t.Run("Operator Simple UserId case-insensitive", func(t *testing.T) {
		// conn.uid case-insensitive
		opSimple, err = NewOperator(Simple, false, OpUserID, fmt.Sprint(defaultUserID), list)
		if err != nil {
			t.Error("NewOperator simple conn.userid.err should be nil: ", err)
			t.Fail()
		}
		if err = opSimple.Compile(); err != nil {
			t.Error("NewOperator simple UserId Compile() err: ", err)
			t.Fail()
		}
		if opSimple.Match(conn, false) == false {
			t.Error("Test NewOperator() simple conn.userid doesn't match")
			t.Fail()
		}
	})

	restoreConnection()
}

func TestNewOperatorNetwork(t *testing.T) {
	t.Log("Test NewOperator() network")
	var dummyList []Operator

	opSimple, err := NewOperator(Network, false, OpDstNetwork, "185.53.178.14/24", dummyList)
	if err != nil {
		t.Error("NewOperator network.err should be nil: ", err)
		t.Fail()
	}
	if err = opSimple.Compile(); err != nil {
		t.Fail()
	}
	if opSimple.Match(conn, false) == false {
		t.Error("Test NewOperator() network doesn't match")
		t.Fail()
	}

	opSimple, err = NewOperator(Network, false, OpDstNetwork, "8.8.8.8/24", dummyList)
	if err != nil {
		t.Error("NewOperator network.err should be nil: ", err)
		t.Fail()
	}
	if err = opSimple.Compile(); err != nil {
		t.Fail()
	}
	if opSimple.Match(conn, false) == true {
		t.Error("Test NewOperator() network doesn't match:", conn.DstIP)
		t.Fail()
	}

	restoreConnection()
}

func TestNewOperatorRegexp(t *testing.T) {
	t.Log("Test NewOperator() regexp")
	var dummyList []Operator

	opRE, err := NewOperator(Regexp, false, OpProto, "^TCP$", dummyList)
	if err != nil {
		t.Error("NewOperator regexp.err should be nil: ", err)
		t.Fail()
	}
	if err = opRE.Compile(); err != nil {
		t.Fail()
	}
	if opRE.Match(conn, false) == false {
		t.Error("Test NewOperator() regexp doesn't match")
		t.Fail()
	}

	restoreConnection()
}

func TestNewOperatorInvalidRegexp(t *testing.T) {
	t.Log("Test NewOperator() invalid regexp")
	var dummyList []Operator

	opRE, err := NewOperator(Regexp, false, OpProto, "^TC(P$", dummyList)
	if err != nil {
		t.Error("NewOperator regexp.err should be nil: ", err)
		t.Fail()
	}
	if err = opRE.Compile(); err == nil {
		t.Error("NewOperator() invalid regexp. It should fail: ", err)
		t.Fail()
	}

	restoreConnection()
}

func TestNewOperatorRegexpSensitive(t *testing.T) {
	t.Log("Test NewOperator() regexp sensitive")
	var dummyList []Operator

	var sensitive Sensitive
	sensitive = true

	conn.Process.Path = "/tmp/cUrL"

	opRE, err := NewOperator(Regexp, sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList)
	if err != nil {
		t.Error("NewOperator regexp.case-sensitive.err should be nil: ", err)
		t.Fail()
	}
	if err = opRE.Compile(); err != nil {
		t.Fail()
	}
	if opRE.Match(conn, false) == false {
		t.Error("Test NewOperator() RE sensitive doesn't match:", conn.Process.Path)
		t.Fail()
	}

	t.Run("Operator regexp proc.path case-sensitive", func(t *testing.T) {
		conn.Process.Path = "/tmp/curl"
		if opRE.Match(conn, false) == true {
			t.Error("Test NewOperator() RE sensitive match:", conn.Process.Path)
			t.Fail()
		}
	})

	opRE, err = NewOperator(Regexp, !sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList)
	if err != nil {
		t.Error("NewOperator regexp.case-insensitive.err should be nil: ", err)
		t.Fail()
	}
	if err = opRE.Compile(); err != nil {
		t.Fail()
	}
	if opRE.Match(conn, false) == false {
		t.Error("Test NewOperator() RE not sensitive match:", conn.Process.Path)
		t.Fail()
	}

	restoreConnection()
}

func TestNewOperatorList(t *testing.T) {
	t.Log("Test NewOperator() List")
	var list []Operator
	listData := `[{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]`

	// simple list
	opList, err := NewOperator(List, false, OpProto, listData, list)
	t.Run("Operator List simple case-insensitive", func(t *testing.T) {
		if err != nil {
			t.Error("NewOperator list.regexp.err should be nil: ", err)
			t.Fail()
		}
		if err = opList.Compile(); err != nil {
			t.Fail()
		}
		opList.List = *unmarshalListData(opList.Data, t)
		compileListOperators(&opList.List, t)
		if opList.Match(conn, false) == false {
			t.Error("Test NewOperator() list simple doesn't match")
			t.Fail()
		}
	})

	t.Run("Operator List regexp case-insensitive", func(t *testing.T) {
		// list with regexp, case-insensitive
		listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/bin/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]`
		opList.List = *unmarshalListData(listData, t)
		compileListOperators(&opList.List, t)
		if err = opList.Compile(); err != nil {
			t.Fail()
		}
		if opList.Match(conn, false) == false {
			t.Error("Test NewOperator() list regexp doesn't match")
			t.Fail()
		}
	})

	t.Run("Operator List regexp case-sensitive", func(t *testing.T) {
		// list with regexp, case-sensitive
		// "data": "^/usr/BiN/.*" must match conn.Process.Path (sensitive)
		listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/BiN/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]`
		opList.List = *unmarshalListData(listData, t)
		compileListOperators(&opList.List, t)
		conn.Process.Path = "/usr/BiN/opensnitchd"
		opList.Sensitive = true
		if err = opList.Compile(); err != nil {
			t.Fail()
		}
		if opList.Match(conn, false) == false {
			t.Error("Test NewOperator() list.regexp.sensitive doesn't match:", conn.Process.Path)
			t.Fail()
		}
	})

	t.Run("Operator List regexp case-insensitive 2", func(t *testing.T) {
		// "data": "^/usr/BiN/.*" must not match conn.Process.Path (insensitive)
		opList.Sensitive = false
		conn.Process.Path = "/USR/BiN/opensnitchd"
		if err = opList.Compile(); err != nil {
			t.Fail()
		}
		if opList.Match(conn, false) == false {
			t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path)
			t.Fail()
		}
	})

	t.Run("Operator List regexp case-insensitive 3", func(t *testing.T) {
		// "data": "^/usr/BiN/.*" must match conn.Process.Path (insensitive)
		opList.Sensitive = false
		conn.Process.Path = "/USR/bin/opensnitchd"
		if err = opList.Compile(); err != nil {
			t.Fail()
		}
		if opList.Match(conn, false) == false {
			t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path)
			t.Fail()
		}
	})

	restoreConnection()
}

func TestNewOperatorListsSimple(t *testing.T) {
	t.Log("Test NewOperator() Lists simple")
	var dummyList []Operator

	opLists, err := NewOperator(Lists, false, OpDomainsLists, "testdata/lists/domains/", dummyList)
	if err != nil {
		t.Error("NewOperator Lists, shouldn't be nil: ", err)
		t.Fail()
	}
	if err = opLists.Compile(); err != nil {
		t.Error("NewOperator Lists, Compile() error:", err)
	}
	time.Sleep(time.Second)
	t.Log("testing Lists, DstHost:", conn.DstHost)
	// The list contains 4 lines, 1 is a comment and there's a domain duplicated.
	// We should only load lines that start with 0.0.0.0 or 127.0.0.1
	if len(opLists.lists) != 2 {
		t.Error("NewOperator Lists, number of domains error:", opLists.lists, len(opLists.lists))
	}
	if opLists.Match(conn, false) == false {
		t.Error("Test NewOperator() lists doesn't match")
	}

	opLists.StopMonitoringLists()
	time.Sleep(time.Second)
	opLists.Lock()
	if len(opLists.lists) != 0 {
		t.Error("NewOperator Lists, number should be 0 after stop:", opLists.lists, len(opLists.lists))
	}
	opLists.Unlock()

	restoreConnection()
}

func TestNewOperatorListsIPs(t *testing.T) {
	t.Log("Test NewOperator() Lists domains_regexp")

	var subOp *Operator
	var list []Operator
	listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.ips", "data": "testdata/lists/ips/", "sensitive": false}]`

	opLists, err := NewOperator(List, false, OpList, listData, list)
	if err != nil {
		t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
		t.Fail()
	}
	if err := opLists.Compile(); err != nil {
		t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
	}
	opLists.List = *unmarshalListData(opLists.Data, t)
	for i := 0; i < len(opLists.List); i++ {
		if err := opLists.List[i].Compile(); err != nil {
			t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
		}
		if opLists.List[i].Type == Lists {
			subOp = &opLists.List[i]
		}
	}

	time.Sleep(time.Second)
	if opLists.Match(conn, false) == false {
		t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
	}

	subOp.Lock()
	listslen := len(subOp.lists)
	subOp.Unlock()
	if listslen != 2 {
		t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
	}

	//t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
	if opLists.Match(conn, false) == false {
		// we don't care about if it matches, we're testing race conditions
		t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
	}

	subOp.StopMonitoringLists()
	time.Sleep(time.Second)
	subOp.Lock()
	if len(subOp.lists) != 0 {
		t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
	}
	subOp.Unlock()

	restoreConnection()
}

func TestNewOperatorListsNETs(t *testing.T) {
	t.Log("Test NewOperator() Lists domains_regexp")

	var subOp *Operator
	var list []Operator
	listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.nets", "data": "testdata/lists/nets/", "sensitive": false}]`

	opLists, err := NewOperator(List, false, OpList, listData, list)
	if err != nil {
		t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
		t.Fail()
	}
	if err := opLists.Compile(); err != nil {
		t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
	}
	opLists.List = *unmarshalListData(opLists.Data, t)
	for i := 0; i < len(opLists.List); i++ {
		if err := opLists.List[i].Compile(); err != nil {
			t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
		}
		if opLists.List[i].Type == Lists {
			subOp = &opLists.List[i]
		}
	}

	time.Sleep(time.Second)
	if opLists.Match(conn, false) == false {
		t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
	}

	subOp.Lock()
	listslen := len(subOp.lists)
	subOp.Unlock()
	if listslen != 2 {
		t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
	}

	//t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
	if opLists.Match(conn, false) == false {
		// we don't care about if it matches, we're testing race conditions
		t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
	}

	subOp.StopMonitoringLists()
	time.Sleep(time.Second)
	subOp.Lock()
	if len(subOp.lists) != 0 {
		t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
	}
	subOp.Unlock()

	restoreConnection()
}

func TestNewOperatorListsComplex(t *testing.T) {
	t.Log("Test NewOperator() Lists complex")
	var subOp *Operator
	var list []Operator
	listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains", "data": "testdata/lists/domains/", "sensitive": false}]`

	opLists, err := NewOperator(List, false, OpList, listData, list)
	if err != nil {
		t.Error("NewOperator Lists complex, shouldn't be nil: ", err)
		t.Fail()
	}
	if err := opLists.Compile(); err != nil {
		t.Error("NewOperator Lists complex, Compile() error:", err)
	}
	opLists.List = *unmarshalListData(opLists.Data, t)
	for i := 0; i < len(opLists.List); i++ {
		if err := opLists.List[i].Compile(); err != nil {
			t.Error("NewOperator Lists complex, Compile() subitem error:", err)
		}
		if opLists.List[i].Type == Lists {
			subOp = &opLists.List[i]
		}
	}
	time.Sleep(time.Second)
	subOp.Lock()
	if len(subOp.lists) != 2 {
		t.Error("NewOperator Lists complex, number of domains error:", subOp.lists)
	}
	subOp.Unlock()
	if opLists.Match(conn, false) == false {
		t.Error("Test NewOperator() Lists complex, doesn't match")
	}

	subOp.StopMonitoringLists()
	time.Sleep(time.Second)
	subOp.Lock()
	if len(subOp.lists) != 0 {
		t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
	}
	subOp.Unlock()

	restoreConnection()
}

func TestNewOperatorListsDomainsRegexp(t *testing.T) {
	t.Log("Test NewOperator() Lists domains_regexp")

	var subOp *Operator
	var list []Operator
	listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]`

	opLists, err := NewOperator(List, false, OpList, listData, list)
	if err != nil {
		t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
		t.Fail()
	}
	if err := opLists.Compile(); err != nil {
		t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
	}
	opLists.List = *unmarshalListData(opLists.Data, t)
	for i := 0; i < len(opLists.List); i++ {
		if err := opLists.List[i].Compile(); err != nil {
			t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
		}
		if opLists.List[i].Type == Lists {
			subOp = &opLists.List[i]
		}
	}

	time.Sleep(time.Second)
	if opLists.Match(conn, false) == false {
		t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
	}

	subOp.Lock()
	listslen := len(subOp.lists)
	subOp.Unlock()
	if listslen != 2 {
		t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
	}

	//t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
	if opLists.Match(conn, false) == false {
		// we don't care about if it matches, we're testing race conditions
		t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
	}

	subOp.StopMonitoringLists()
	time.Sleep(time.Second)
	subOp.Lock()
	if len(subOp.lists) != 0 {
		t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
	}
	subOp.Unlock()

	restoreConnection()
}

// Must be launched with -race to test that we don't cause leaks
// Race occured on operator.go:241 reListCmp().MathString()
// fixed here: 53419fe
func TestRaceNewOperatorListsDomainsRegexp(t *testing.T) {
	t.Log("Test NewOperator() Lists domains_regexp")

	var subOp *Operator
	var list []Operator
	listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]`

	opLists, err := NewOperator(List, false, OpList, listData, list)
	if err != nil {
		t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
		t.Fail()
	}
	if err := opLists.Compile(); err != nil {
		t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
	}
	opLists.List = *unmarshalListData(opLists.Data, t)
	for i := 0; i < len(opLists.List); i++ {
		if err := opLists.List[i].Compile(); err != nil {
			t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
		}
		if opLists.List[i].Type == Lists {
			subOp = &opLists.List[i]
		}
	}

	// touch domains list in background, to force a reload.
	go func() {
		touches := 1000
		for {
			if touches < 0 {
				break
			}
			core.Exec("/bin/touch", []string{"testdata/lists/regexp/domainsregexp.txt"})
			touches--
			time.Sleep(100 * time.Millisecond)
			//t.Log("touching:", touches)
		}
	}()

	time.Sleep(time.Second)

	subOp.Lock()
	listslen := len(subOp.lists)
	subOp.Unlock()
	if listslen != 2 {
		t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
	}

	tries := 10000
	for {
		if tries < 0 {
			break
		}
		//t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
		if opLists.Match(conn, false) == false {
			// we don't care about if it matches, we're testing race conditions
			t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
		}

		tries--
		time.Sleep(10 * time.Millisecond)
	}

	subOp.StopMonitoringLists()
	time.Sleep(time.Second)
	subOp.Lock()
	if len(subOp.lists) != 0 {
		t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
	}
	subOp.Unlock()

	restoreConnection()
}

func TestNewOperatorRegexpBareIpNoHostName(t *testing.T) {
	t.Log("Test NewOperator() regex bare IP (no host name)")
	var dummyList []Operator

	conn.DstHost = ""

	opRE, err := NewOperator(Regexp, true, OpDstHost, "^$", dummyList)
	if err != nil {
		t.Error("NewOperator regexp.case-sensitive.err should be nil: ", err)
		t.Fail()
	}
	if err = opRE.Compile(); err != nil {
		t.Fail()
	}
	if opRE.Match(conn, false) == false {
		t.Error("Test NewOperator() RE sensitive match:", conn.DstHost)
		t.Fail()
	}

	restoreConnection()
}

func TestNewOperatorSimpleBareIpNoHostName(t *testing.T) {
	t.Log("Test NewOperator() simple bare IP (no host name)")
	var dummyList []Operator

	conn.DstHost = ""

	opSimple, err := NewOperator(Simple, true, OpDstHost, "", dummyList)
	if err != nil {
		t.Error("NewOperator simple.case-sensitive.err should be nil: ", err)
		t.Fail()
	}
	if err = opSimple.Compile(); err != nil {
		t.Fail()
	}
	if opSimple.Match(conn, false) == false {
		t.Error("Test NewOperator() simple sensitive match:", conn.DstHost)
		t.Fail()
	}

	restoreConnection()
}
