Browse Source

Initial commit for a tested and working code.

Eliezer Croitoru 2 years ago
commit
f1cfa788f7
4 changed files with 432 additions and 0 deletions
  1. 21 0
      README.md
  2. 140 0
      peers.go
  3. 266 0
      peersarray.go
  4. 5 0
      peersfile.txt

File diff suppressed because it is too large
+ 21 - 0
README.md


+ 140 - 0
peers.go

@@ -0,0 +1,140 @@
+package drblpeer
+
+import (
+	"github.com/asaskevich/govalidator"
+	"github.com/bogdanovich/dns_resolver"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	//	"fmt"
+)
+
+type DrblClient struct {
+	Peername    string
+	Path        string
+	Port        int
+	Weight      int64
+	Protocol    string
+	BlResponses []string
+	Resolver    *dns_resolver.DnsResolver
+	Client      *http.Client
+}
+
+func New(peerName, protocol, path string, port int, weight int64, bladdr []string) *DrblClient {
+	return &DrblClient{peerName,
+		path,
+		port,
+		weight,
+		protocol,
+		bladdr,
+		dns_resolver.New([]string{peerName}),
+		&http.Client{},
+	}
+}
+
+// In case of i/o timeout the resolver should be set to retry 3 times
+/*
+  instance.Resolver.RetryTimes = 0
+*/
+
+// return: found, allow\deny, admin\nonadmin, error)
+func (instance *DrblClient) Check(hostname string) (bool, bool, bool, string, error) {
+	found := false
+	admin := false
+	allow := true
+	key := ""
+
+	switch {
+	case instance.Protocol == "http" || instance.Protocol == "https":
+		testurl, _ := url.Parse(instance.Protocol + "://" + instance.Peername + ":" + strconv.Itoa(instance.Port) + instance.Path)
+		testurlVals := url.Values{}
+		testurlVals.Set("host", hostname)
+		testurl.RawQuery = testurlVals.Encode()
+
+		request, err := http.NewRequest("HEAD", testurl.String(), nil)
+		//request.SetBasicAuth(*user, *pass)
+
+		resp, err := instance.Client.Do(request)
+		if err != nil {
+			return found, allow, admin, key, err
+		}
+
+		if resp.Header.Get("X-Admin-Vote") == "YES" {
+			admin = true
+			found = true
+		}
+		if resp.Header.Get("X-Vote") == "BLOCK" {
+			found = true
+			allow = false
+		}
+
+		// For cases which debug is required you can use this key to see the BL VALUE
+		//resp.Header.Get("X-Rate")
+
+		key = resp.Header.Get("X-Rate-Key")
+
+	case instance.Protocol == "dns":
+		if len(hostname) > 1 {
+			ip, err := instance.Resolver.LookupHost(hostname)
+			if err != nil {
+				/* to debug use this:
+				//fmt.Println(instance, "Got error on lookup for", hostname)
+				*/
+				return found, false, admin, key, err
+			}
+
+			//	I could have added a loop over loop to verify that each host from the results
+			//	is not from the blacklisting results but it's not that important
+
+			if err == nil && len(ip) > 0 {
+				for _, block := range instance.BlResponses {
+					if ip[0].String() == block {
+						found = true
+						allow = false
+						break
+					}
+				}
+			}
+		}
+
+	case instance.Protocol == "dnsrbl":
+		if govalidator.IsIPv4(hostname) {
+			hostname = ReverseTheDomain(hostname)
+		}
+		if len(hostname) > 1 {
+			ip, err := instance.Resolver.LookupHost(hostname + "." + instance.Peername)
+			if err != nil {
+				return found, false, admin, key, err
+			}
+
+			if err == nil && len(ip) > 0 {
+				for _, block := range instance.BlResponses {
+					if ip[0].String() == block {
+						found = true
+						allow = false
+						break
+					}
+				}
+			}
+		}
+	}
+	return found, allow, admin, key, nil
+}
+
+// We need to implement a peer object which can be either DNS or HTTP,
+// the url of the host can be:
+// - host\ip:port
+// - type: dns, dnsrbl, http
+
+// Should have an interace\function: checkBl(host)(found, allowed, key)
+
+func ReverseTheDomain(orig string) string {
+	var c []string = strings.Split(orig, ".")
+
+	for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 {
+		c[i], c[j] = c[j], c[i]
+	}
+
+	return strings.Join(c, ".")
+}

+ 266 - 0
peersarray.go

@@ -0,0 +1,266 @@
+package drblpeer
+
+import (
+//	"../watcher"
+	"fmt"
+	"github.com/asaskevich/govalidator"
+	"github.com/bogdanovich/dns_resolver"
+	"io/ioutil"
+	"net/http"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync/atomic"
+	"time"
+)
+
+var spacesAndTabs = regexp.MustCompile(`[\s\t]+`)
+
+type DrblPeers struct {
+	Peers     []DrblClient
+	HitWeight int64
+	Timeout   int
+	Debug     bool
+	/*
+		ReverseOrderDomLookup bool
+		ReverseOrderIpv4Lookup bool
+		Ipv6Lookup bool
+	*/
+}
+
+func NewPeerListFromFile(filename string, hitWeight int64, timeout int, debug bool) (*DrblPeers, error) {
+	//peerName, protocol, path string, port int, weight int64, bladdr []string 	) *DrblClient
+	// mainDrbPeer := drblpeer.New("199.85.126.20", "dns", "", 53, int64(128), []string{"156.154.175.216", "156.154.176.216"})
+	// drblPeers := *drblpeer.DrblPeers{[]DrblClient{}, int64(128), 30, true}
+
+	//var drblPeers drblpeer.DrblPeers{[]drblpeer.DrblClient{*mainDrbPeer}, int64(128), 30, true}
+
+	// Read whole file
+	// Split lines
+	// Walk on the lines
+	// If the line syntax is fine add it else move on
+	// Swap the drbl peers list
+	content, err := ioutil.ReadFile(filename)
+	if err != nil {
+		//fmt.Println(err)
+		return &DrblPeers{}, err
+
+	}
+	peersClient := make([]DrblClient, 0)
+	newlist := &DrblPeers{peersClient, hitWeight, timeout, debug}
+
+	lines := strings.Split(string(content), "\n")
+	for _, peerline := range lines {
+		if len(peerline) > 10 {
+			peer, err := NewPeerFromLine(peerline)
+			if err != nil {
+				if debug {
+					fmt.Println(err)
+				}
+				continue
+			} else {
+				newlist.Peers = append(newlist.Peers, *peer)
+			}
+		}
+	}
+	return newlist, nil
+}
+
+func NewPeerFromLine(peerline string) (*DrblClient, error) {
+	// Cleanup
+	peerline = spacesAndTabs.ReplaceAllString(peerline, " ")
+	/*
+		if strings.Contains(peerline,"\t") {
+			return &DrblClient{}, fmt.Errorf("tabs are not allowed in peerline +> %s", peerline)
+		}
+	*/
+	if len(peerline) > 10 {
+		lparts := strings.Split(peerline, " ")
+
+		if len(lparts) > 5 {
+			port, err := strconv.ParseUint(lparts[3], 10, 64) //port
+			if err != nil {
+				return &DrblClient{}, err
+			}
+			weight, err := strconv.ParseUint(lparts[4], 10, 64) //weight
+			if err != nil {
+				return &DrblClient{}, err
+			}
+			/*
+				switch {
+				case govalidator.IsIP(lparts[1]):
+					;;
+				case govalidator.IsDNSName(lparts[1]):
+					if lparts[0] != "http" {
+						addr, err := net.LookupHost(lparts[1])
+						if err != nil {
+								return &DrblClient{}, fmt.Errorf("%s hostname cannot be resolved", lparts[1])
+						}
+						// Choosing a static IP address
+						lparts[1] = addr[0]
+					}
+				default:
+					return &DrblClient{}, fmt.Errorf("%s is not a valid hostname or ip address", lparts[1])
+				}
+			*/
+			if !govalidator.IsHost(lparts[1]) {
+				return &DrblClient{}, fmt.Errorf("%s is not a valid hostname or ip address", lparts[1])
+			}
+			// http\dns\dnsbl ip\domain /path/vote/ port weigth bl ip's
+			// 0								1					2						3			4			5
+			switch {
+			case lparts[0] == "http" || lparts[0] == "https":
+				return &DrblClient{lparts[1],
+					lparts[2],
+					int(port),
+					int64(weight),
+					lparts[0],
+					[]string{},
+					dns_resolver.New([]string{lparts[1]}),
+					&http.Client{},
+				}, nil
+
+			case lparts[0] == "dns":
+				blIpAddr := make([]string, 1)
+				for _, addr := range lparts[5:] {
+					if govalidator.IsIPv4(addr) {
+						blIpAddr = append(blIpAddr, addr)
+					}
+				}
+				return &DrblClient{lparts[1],
+					lparts[2],
+					int(port),
+					int64(weight),
+					lparts[0],
+					blIpAddr,
+					dns_resolver.New([]string{lparts[1]}),
+					&http.Client{},
+				}, nil
+			case lparts[0] == "dnsbl":
+				blIpAddr := make([]string, 1)
+				for _, addr := range lparts[5:] {
+					if govalidator.IsIPv4(addr) {
+						blIpAddr = append(blIpAddr, addr)
+					}
+				}
+				return &DrblClient{lparts[1],
+					lparts[2],
+					int(port),
+					int64(weight),
+					lparts[0],
+					blIpAddr,
+					dns_resolver.New([]string{lparts[1]}),
+					&http.Client{},
+				}, nil
+			}
+		}
+		return &DrblClient{}, fmt.Errorf("drblpeer: malformed peerline %s", peerline)
+	} else {
+		return &DrblClient{}, fmt.Errorf("drblpeer: malformed peerline %s", peerline)
+	}
+}
+
+/*
+func PeerListWatcher(filename string, peersList *DrblPeers, debug bool) {
+	doneChan := make(chan bool)
+	newlist, err := NewPeerListFromFile(filename, peersList.HitWeight, peersList.Timeout, peersList.Debug)
+	if err != nil {
+		fmt.Println(err)
+		//DENY all unfortunate case
+	} else {
+		fmt.Println("no error on first parsing config file =>", filename)
+		peersList = newlist
+	}
+	for {
+		go func(doneChan chan bool) {
+			defer func() {
+				doneChan <- true
+			}()
+
+			err := watcher.WatchFile(filename)
+			if err != nil && debug {
+				fmt.Println("Error Watching config file =>", filename, "Error =>", err)
+			}
+			if debug {
+				fmt.Println("File", filename, "has been changed")
+			}
+		}(doneChan)
+
+		select {
+		case <-doneChan:
+			newlist, err := NewPeerListFromFile(filename, peersList.HitWeight, peersList.Timeout, peersList.Debug)
+			if err != nil {
+				//DENY all unfortunate case
+				fmt.Println("Error parsing config file =>", filename, "Error =>", err)
+			} else {
+				peersList = newlist
+			}
+		}
+	}
+}
+*/
+
+//Block and weight
+func (peersList *DrblPeers) Check(hostname string) (bool, int64) {
+	block := false
+
+	ch := make(chan int64)
+
+	for _, peer := range peersList.Peers {
+		go func(peer DrblClient) {
+			found, allowaccess, admin, key, err := peer.Check(hostname)
+			if err != nil {
+				if peersList.Debug {
+					fmt.Println("peer", peer.Peername, "had an error", err)
+				}
+				ch <- 0
+			}
+			if peersList.Debug {
+				fmt.Println("peer", peer.Peername, ", results =>", found, allowaccess, admin, key)
+			}
+
+			if found && !allowaccess {
+				ch <- peer.Weight
+			} else {
+				ch <- 0
+			}
+		}(peer)
+	}
+
+	localWeight := peersList.HitWeight
+	peersNumber := int64(len(peersList.Peers))
+	if peersList.Debug {
+		fmt.Println("Peers number", peersNumber)
+	}
+	if len(peersList.Peers) > 0 {
+		for {
+			if peersList.Debug {
+				fmt.Println("Peers number inside FOR", peersNumber)
+			}
+			if peersNumber <= 0 {
+				break
+			}
+			select {
+			case r := <-ch:
+				if peersList.Debug {
+					fmt.Println("Peers sent ", r)
+				}
+				atomic.AddInt64(&localWeight, -r)
+				atomic.AddInt64(&peersNumber, -1)
+			case <-time.After(time.Duration(peersList.Timeout) * time.Second):
+				// Return 0
+				break
+			}
+			if localWeight <= int64(0) {
+				block = true
+				return block, localWeight
+			}
+		}
+	}
+	if localWeight <= int64(0) {
+		block = true
+		return block, localWeight
+	}
+
+	return block, localWeight
+}

+ 5 - 0
peersfile.txt

@@ -0,0 +1,5 @@
+dns 199.85.126.20 / 53 128 156.154.175.216 156.154.176.216
+dns 199.85.127.20 / 53 128 156.154.175.216 156.154.176.216
+dns 208.67.222.123 / 53 128 146.112.61.104 146.112.61.105 146.112.61.106
+dns 208.67.220.123 / 53 128 146.112.61.104 146.112.61.105 146.112.61.106
+http ngtech.co.il /rbl/vote/ 80 128 0.0.0.0