Răsfoiți Sursa

Portted split.c from polipo

Eugene Fryntov 11 ani în urmă
părinte
comite
78ee9c008c
2 a modificat fișierele cu 115 adăugiri și 0 ștergeri
  1. 91 0
      psiphon/splitTunnel.go
  2. 24 0
      psiphon/utils.go

+ 91 - 0
psiphon/splitTunnel.go

@@ -0,0 +1,91 @@
+package psiphon
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/binary"
+	"net"
+	"sort"
+	"strings"
+)
+
+// SplitTunnelDirector determines whether a destination should be
+// accessed through a tunnel or accessed directly.
+type SplitTunnelDirector struct {
+	localNetworks []*net.IPNet
+}
+
+// NewSplitTunnelDirector creates a new SplitTunnelDirector, initializing
+// it with routes data which maps out the ranges of IP addresses which should
+// be excluded from tunneling. dnsServerAddress is used when a hostname must
+// be resolved prior to making a determination. dnsDialConfig is used when
+// making the connection to dnsServerAddress.
+func NewSplitTunnelDirector(routesData []byte) (director *SplitTunnelDirector, err error) {
+
+	// TODO: implementation
+	dir := &SplitTunnelDirector{}
+	dir.initRoutesData(routesData)
+	return dir, nil
+}
+
+func (director *SplitTunnelDirector) initRoutesData(routesData []byte) {
+	scanner := bufio.NewScanner(bytes.NewReader(routesData))
+	scanner.Split(bufio.ScanLines)
+	for scanner.Scan() {
+		s := strings.Split(scanner.Text(), "\t")
+		if len(s) != 2 {
+			continue
+		}
+
+		nwIP := ParseIPv4(s[0])
+		nwMask := ParseIPv4Mask(s[1])
+
+		if nwIP == nil || nwMask == nil {
+			continue
+		}
+
+		director.localNetworks = append(director.localNetworks, &net.IPNet{IP: nwIP.Mask(nwMask), Mask: nwMask})
+		// sort and remove duplicates from our networks array
+		// so we could run binary search against it
+		sort.Sort(NetworkSorter(director.localNetworks))
+		director.removeDuplicates()
+	}
+}
+
+// Adapted from
+// http://openmymind.net/2011/7/15/Learning-Go-By-Benchmarking-Set-Implementation/
+func (director *SplitTunnelDirector) removeDuplicates() {
+	length := len(director.localNetworks) - 1
+	for i := 0; i < length; i++ {
+		for j := i + 1; j <= length; j++ {
+			if director.localNetworks[i].IP.Equal(director.localNetworks[j].IP) {
+				director.localNetworks[j] = director.localNetworks[length]
+				director.localNetworks = director.localNetworks[0:length]
+				length--
+				j--
+			}
+		}
+	}
+}
+
+func (director *SplitTunnelDirector) isLocalAddress(addr net.IP) bool {
+	length := len(director.localNetworks)
+	addrValue := binary.BigEndian.Uint32(addr.To4())
+	idx := sort.Search(length, func(i int) bool {
+		nwValue := binary.BigEndian.Uint32(director.localNetworks[i].IP)
+		return nwValue > addrValue
+	})
+
+	return idx > 0 && director.localNetworks[idx-1].IP.Equal(addr.Mask(director.localNetworks[idx-1].Mask))
+}
+
+type NetworkSorter []*net.IPNet
+
+func (ns NetworkSorter) Len() int      { return len(ns) }
+func (ns NetworkSorter) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] }
+func (ns NetworkSorter) Less(i, j int) bool {
+	nwa := binary.BigEndian.Uint32(ns[i].IP)
+	nwb := binary.BigEndian.Uint32(ns[j].IP)
+	return nwa < nwb
+}
+

+ 24 - 0
psiphon/utils.go

@@ -161,3 +161,27 @@ func IsAddressInUseError(err error) bool {
 	}
 	return false
 }
+
+// ParseIPv4Mask parses IPv4 string of form a.b.c.d and returns 4 bytes long net.IP
+func ParseIPv4(s string) net.IP {
+	ip := net.ParseIP(s)
+	if ip == nil {
+		return nil
+	}
+	return ip.To4()
+}
+
+// ParseIPv4Mask parses netmask in form of an IPv4 string, i.e. 240.0.0.0
+func ParseIPv4Mask(s string) net.IPMask {
+	ip := ParseIPv4(s)
+	if ip == nil {
+		return nil
+	}
+	mask := net.IPMask(ip)
+
+	//verify if mask value is valid
+	if bits, size := mask.Size(); bits == 0 || size == 0 {
+		return nil
+	}
+	return mask
+}