diff --git a/src/mole/cmd/mole/forwarder.go b/src/mole/cmd/mole/forwarder.go index 5b97312..3c8d0e5 100644 --- a/src/mole/cmd/mole/forwarder.go +++ b/src/mole/cmd/mole/forwarder.go @@ -34,7 +34,7 @@ func startForwarder(dialer Dialer) chan<- conf.ForwardLine { fwdChan := make(chan conf.ForwardLine) go func() { for line := range fwdChan { - for i := 0; i <= line.Repeat; i++ { + for i := 0; i < len(line.Src.Ports); i++ { src := line.SrcString(i) dst := line.DstString(i) diff --git a/src/mole/cmd/mole/interfaces.go b/src/mole/cmd/mole/interfaces.go index 2946784..915743b 100644 --- a/src/mole/cmd/mole/interfaces.go +++ b/src/mole/cmd/mole/interfaces.go @@ -13,7 +13,7 @@ import ( ) var errNoLoopbackFound = errors.New("no loopback interface found") -var keepAddressRe = regexp.MustCompile(`^127\.0\.0\.([0-9]|[0-2][0-9]|3[0-1])$`) +var keepAddressRe = regexp.MustCompile(`^(127\.0\.0\.([0-9]|[0-2][0-9]|3[0-1])|::1)$`) func loInterface() string { intfs, err := net.Interfaces() @@ -56,6 +56,9 @@ func missingAddresses(cfg *conf.Config) []string { var missing []string for _, ip := range wanted { + if ip[0] == '[' { + ip = ip[1 : len(ip)-1] + } if !curMap[ip] { missing = append(missing, ip) } @@ -69,6 +72,9 @@ func extraneousAddresses(cfg *conf.Config) []string { added := cfg.SourceAddresses() addedMap := make(map[string]bool) for _, ip := range added { + if ip[0] == '[' { + ip = ip[1 : len(ip)-1] + } addedMap[ip] = true } diff --git a/src/mole/cmd/mole/platform_unix.go b/src/mole/cmd/mole/platform_unix.go index fa4dfc4..4a07091 100644 --- a/src/mole/cmd/mole/platform_unix.go +++ b/src/mole/cmd/mole/platform_unix.go @@ -53,7 +53,7 @@ func addToHostsFile(tag string, domain string, cfg *conf.Config) { if domain != "" { name = name + "." + domain } - ip := fwd.Lines[0].SrcIP + ip := fwd.Lines[0].Src.Addr.String() entries = append(entries, hosts.Entry{IP: ip, Names: []string{name}}) } diff --git a/src/mole/cmd/mole/shell.go b/src/mole/cmd/mole/shell.go index 7b6769a..b9808a4 100644 --- a/src/mole/cmd/mole/shell.go +++ b/src/mole/cmd/mole/shell.go @@ -2,11 +2,12 @@ package main import ( "fmt" + "github.com/sbinet/liner" + "io" "mole/ansi" "mole/conf" "mole/table" - "github.com/sbinet/liner" - "io" + "net" "os" "os/signal" "strconv" @@ -148,11 +149,17 @@ func shell(fwdChan chan<- conf.ForwardLine, cfg *conf.Config, dialer Dialer) { warnln(err) break } + srcpa := conf.Addrports{ + Addr: net.ParseIP(src[0]), + Ports: []int{srcp}, + } + dstpa := conf.Addrports{ + Addr: net.ParseIP(dst[0]), + Ports: []int{dstp}, + } fwd := conf.ForwardLine{ - SrcIP: src[0], - SrcPort: srcp, - DstIP: dst[0], - DstPort: dstp, + Src: srcpa, + Dst: dstpa, } okln("add", fwd) fwdChan <- fwd @@ -218,7 +225,7 @@ func testForwards(dialer Dialer, cfg *conf.Config) <-chan forwardTest { go func(fwd conf.Forward) { nlines := 0 for _, line := range fwd.Lines { - nlines += line.Repeat + 1 + nlines += len(line.Src.Ports) } res := forwardTest{name: fwd.Name} @@ -229,7 +236,7 @@ func testForwards(dialer Dialer, cfg *conf.Config) <-chan forwardTest { j := 0 for _, line := range fwd.Lines { - for i := 0; i <= line.Repeat; i++ { + for i := 0; i < len(line.Src.Ports); i++ { // Do each line in parallell go func(line conf.ForwardLine, i, j int) { outstanding <- true @@ -267,7 +274,7 @@ func testLineIndex(dialer Dialer, line conf.ForwardLine, i int) <-chan error { }() go func() { - debugln("test", line.DstString(i)) + debugln("test, Src:", line.SrcString(i), " Dst:", line.DstString(i)) conn, err := dialer.Dial("tcp", line.DstString(i)) if err == nil && conn != nil { conn.Close() diff --git a/src/mole/conf/config.go b/src/mole/conf/config.go index ce99be8..3ae55ee 100644 --- a/src/mole/conf/config.go +++ b/src/mole/conf/config.go @@ -3,6 +3,7 @@ package conf import ( "fmt" "io" + "net" "sort" "mole/ini" @@ -66,42 +67,54 @@ type Forward struct { Comments []string } +// Addr is a composite type of IPAddr and TCP ports +type Addrports struct { + Addr net.IP + Ports []int +} + // ForwardLine is a specific port or range or ports to forward type ForwardLine struct { - SrcIP string - SrcPort int - DstIP string - DstPort int - Repeat int + Src Addrports + Dst Addrports } // SrcString returns the source IP address and port as a string formatted for // use with Dial() and similar. func (line ForwardLine) SrcString(i int) string { - if i > line.Repeat { + if i >= len(line.Src.Ports) { panic("index > repeat") } - return fmt.Sprintf("%s:%d", line.SrcIP, line.SrcPort+i) + if line.Src.Addr.To4() != nil { + return fmt.Sprintf("%s:%d", line.Src.Addr.String(), line.Src.Ports[i]) + } else { + return fmt.Sprintf("[%s]:%d", line.Src.Addr.String(), line.Src.Ports[i]) + } } // DstString returns the destination IP address and port as a string formatted for // use with Dial() and similar. func (line ForwardLine) DstString(i int) string { - if i > line.Repeat { + //if i > line.Repeat { + if i >= len(line.Dst.Ports) { panic("index > repeat") } - return fmt.Sprintf("%s:%d", line.DstIP, line.DstPort+i) + if line.Dst.Addr.To4() != nil { + return fmt.Sprintf("%s:%d", line.Dst.Addr.String(), line.Dst.Ports[i]) + } else { + return fmt.Sprintf("[%s]:%d", line.Dst.Addr.String(), line.Dst.Ports[i]) + } } // String returns a human readable representation of the port forward. func (line ForwardLine) String() string { - if line.Repeat == 0 { - src := fmt.Sprintf("%s:%d", line.SrcIP, line.SrcPort) - dst := fmt.Sprintf("%s:%d", line.DstIP, line.DstPort) + if len(line.Src.Ports) == 1 { + src := fmt.Sprintf("%s:%d", line.Src.Addr.String(), line.Src.Ports[0]) + dst := fmt.Sprintf("%s:%d", line.Dst.Addr.String(), line.Dst.Ports[0]) return fmt.Sprintf("%s -> %s", src, dst) } - src := fmt.Sprintf("%s:%d-%d", line.SrcIP, line.SrcPort, line.SrcPort+line.Repeat) - dst := fmt.Sprintf("%s:%d-%d", line.DstIP, line.DstPort, line.DstPort+line.Repeat) + src := fmt.Sprintf("%s:%d-%d", line.Src.Addr.String(), line.Src.Ports[0], line.Src.Ports[len(line.Src.Ports)-1]) + dst := fmt.Sprintf("%s:%d-%d", line.Dst.Addr.String(), line.Dst.Ports[0], line.Dst.Ports[len(line.Src.Ports)-1]) return fmt.Sprintf("%s -> %s", src, dst) } @@ -117,7 +130,7 @@ func (c *Config) SourceAddresses() []string { addrMap := make(map[string]bool) for _, fwd := range c.Forwards { for _, line := range fwd.Lines { - addrMap[line.SrcIP] = true + addrMap[line.Src.Addr.String()] = true } } @@ -137,11 +150,13 @@ func (c *Config) Remap() { port := 10000 for fi := range c.Forwards { for li := range c.Forwards[fi].Lines { - if c.Forwards[fi].Lines[li].SrcIP != "127.0.0.1" { + if c.Forwards[fi].Lines[li].Src.Addr.String() != "127.0.0.1" && c.Forwards[fi].Lines[li].Src.Addr.String() != "[::1]" { // BUG: Need to keep track of used ports and not try to use them twice. - c.Forwards[fi].Lines[li].SrcIP = "127.0.0.1" - c.Forwards[fi].Lines[li].SrcPort = port - port += c.Forwards[fi].Lines[li].Repeat + 1 + c.Forwards[fi].Lines[li].Src.Addr = net.ParseIP("127.0.0.1") + for sp := range c.Forwards[fi].Lines[li].Src.Ports { + c.Forwards[fi].Lines[li].Src.Ports[sp] = port + port += 1 + } } } } diff --git a/src/mole/conf/config_test.go b/src/mole/conf/config_test.go index 608c4fd..e42b93f 100644 --- a/src/mole/conf/config_test.go +++ b/src/mole/conf/config_test.go @@ -161,7 +161,7 @@ func TestHosts(t *testing.T) { func TestForwards(t *testing.T) { cfg, _ := loadFile("test/valid-forwards.ini") - if l := len(cfg.Forwards); l != 2 { + if l := len(cfg.Forwards); l != 5 { t.Errorf("Incorrect len(Forwards) %d", l) } @@ -171,37 +171,37 @@ func TestForwards(t *testing.T) { } l1 := f.Lines[0] - if l1.SrcIP != "127.0.0.1" { - t.Errorf("Incorrect SrcIP %q", l1.SrcIP) + if l1.Src.Addr.String() != "127.0.0.1" { + t.Errorf("Incorrect SrcIP %q", l1.Src.Addr.String()) } - if l1.SrcPort != 42000 { - t.Errorf("Incorrect SrcPort %d", l1.SrcPort) + if l1.Src.Ports[0] != 42000 { + t.Errorf("Incorrect SrcPort %d", l1.Src.Ports[0]) } - if l1.DstIP != "192.168.173.10" { - t.Errorf("Incorrect DstIP %q", l1.DstIP) + if l1.Dst.Addr.String() != "192.168.173.10" { + t.Errorf("Incorrect DstIP %q", l1.Dst.Addr.String()) } - if l1.DstPort != 42000 { - t.Errorf("Incorrect DstPort %d", l1.DstPort) + if l1.Dst.Ports[0] != 42000 { + t.Errorf("Incorrect DstPort %d", l1.Dst.Ports[0]) } - if l1.Repeat != 2 { - t.Errorf("Incorrect Repeat %d", l1.Repeat) + if len(l1.Src.Ports) != 3 || len(l1.Dst.Ports) != 3 { + t.Errorf("Incorrect port range %d", l1.Src.Ports) } l2 := f.Lines[1] - if l2.SrcIP != "127.0.0.1" { - t.Errorf("Incorrect l2 SrcIP %q", l2.SrcIP) + if l2.Src.Addr.String() != "127.0.0.1" { + t.Errorf("Incorrect l2 SrcIP %q", l2.Src.Addr.String()) } - if l2.SrcPort != 8443 { - t.Errorf("Incorrect l2 SrcPort %d", l2.SrcPort) + if l2.Src.Ports[0] != 8443 { + t.Errorf("Incorrect l2 SrcPort %d", l2.Src.Ports[0]) } - if l2.DstIP != "192.168.173.10" { - t.Errorf("Incorrect l2 DstIP %q", l2.DstIP) + if l2.Dst.Addr.String() != "192.168.173.10" { + t.Errorf("Incorrect l2 DstIP %q", l2.Dst.Addr.String()) } - if l2.DstPort != 443 { - t.Errorf("Incorrect l2 DstPort %d", l2.DstPort) + if l2.Dst.Ports[0] != 443 { + t.Errorf("Incorrect l2 DstPort %d", l2.Dst.Ports[0]) } - if l2.Repeat != 0 { - t.Errorf("Incorrect l2 Repeat %d", l2.Repeat) + if len(l2.Src.Ports) != 1 || len(l2.Dst.Ports) != 1 { + t.Errorf("Incorrect l2 port range %d", l2.Src.Ports) } f = cfg.Forwards[1] diff --git a/src/mole/conf/parse.go b/src/mole/conf/parse.go index d3181c0..761f239 100644 --- a/src/mole/conf/parse.go +++ b/src/mole/conf/parse.go @@ -2,7 +2,7 @@ package conf import ( "fmt" - "regexp" + "net" "sort" "strconv" "strings" @@ -10,8 +10,6 @@ import ( "mole/ini" ) -var ipRe = regexp.MustCompile(`^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$`) - func parse(ic ini.Config) (cp *Config, err error) { c := Config{} c.General.Other = make(map[string]string) @@ -21,7 +19,6 @@ func parse(ic ini.Config) (cp *Config, err error) { for _, section := range ic.Sections() { options := ic.OptionMap(section) - if section == "general" { err := parseGeneral(&c, options) if err != nil { @@ -90,7 +87,7 @@ func parse(ic ini.Config) (cp *Config, err error) { for _, fwd := range c.Forwards { for _, line := range fwd.Lines { // Check for duplicate forwards - for i := 0; i <= line.Repeat; i++ { + for i := 0; i < len(line.Src.Ports); i++ { src := line.SrcString(i) if seenSources[src] { err = fmt.Errorf("duplicate forward source %q", src) @@ -100,8 +97,8 @@ func parse(ic ini.Config) (cp *Config, err error) { } // Check for privileged ports - if line.SrcPort < 1024 { - err = fmt.Errorf("privileged source port %d in forward source %q", line.SrcPort, line.SrcString(0)) + if line.Src.Ports[0] < 1024 { + err = fmt.Errorf("privileged source port %d in forward source %q", line.Src.Ports[0], line.SrcString(0)) return } } @@ -213,61 +210,82 @@ func parseForward(ic ini.Config, section string) (forw Forward, err error) { forw = Forward{Name: name} forw.Other = make(map[string]string) - var srcfs, dstfs, srcps []string - var srcport, dstport, repeat int + var srcps, dstps []string + var srcipstr, dstipstr, srcportsstr, dstportsstr string + var srcip, dstip net.IP + var srcport, dstport int + var srcports, dstports []int for k, v := range options { if k == "comment" { forw.Comments = append(forw.Comments, strings.Split(v, "\n")...) continue } - - srcfs = strings.SplitN(k, ":", 2) - if len(srcfs) != 2 || len(srcfs[0]) == 0 || len(srcfs[1]) == 0 { + srcipstr, srcportsstr, err = net.SplitHostPort(k) + if err != nil { err = fmt.Errorf("malformed forward source %q", k) return } - - if !ipRe.MatchString(srcfs[0]) { + srcip = net.ParseIP(srcipstr) + if srcip == nil { + err = fmt.Errorf("malformed forward source address %q", k) + return + } + if len(srcportsstr) == 0 { err = fmt.Errorf("malformed forward source %q", k) return } - srcps = strings.SplitN(srcfs[1], "-", 2) + srcps = strings.SplitN(srcportsstr, "-", 2) srcport, _ = strconv.Atoi(srcps[0]) if len(srcps) == 2 { ep, _ := strconv.Atoi(srcps[1]) - repeat = ep - srcport + srcports = MakeRangeArray(srcport, ep) } else { - repeat = 0 + srcports = []int{srcport} } - dstfs = strings.SplitN(v, ":", 2) + dstipstr, dstportsstr, err = net.SplitHostPort(v) + if err != nil { + dstipstr, _, err = net.SplitHostPort(v + ":0") + dstportsstr = srcportsstr + } + dstip = net.ParseIP(dstipstr) + if dstip == nil { + err = fmt.Errorf("malformed forward destination address %q", v) + return + } - if !ipRe.MatchString(dstfs[0]) { + if len(dstportsstr) == 0 { err = fmt.Errorf("malformed forward destination %q", v) return } - - if len(dstfs) == 2 { - if repeat > 0 { - err = fmt.Errorf("malformed forward destination %q (port range)", v) - return - } - if len(dstfs[0]) == 0 || len(dstfs[1]) == 0 { - err = fmt.Errorf("malformed forward destination %q", v) - return - } - dstport, _ = strconv.Atoi(dstfs[1]) + dstps = strings.SplitN(dstportsstr, "-", 2) + dstport, _ = strconv.Atoi(dstps[0]) + if len(dstps) == 2 { + ep, _ := strconv.Atoi(dstps[1]) + dstports = MakeRangeArray(dstport, ep) + } else if len(dstps) == 1 { + dstports = []int{dstport} } else { - dstport = srcport + dstports = srcports + } + if len(dstports) != len(srcports) { + err = fmt.Errorf("malformed forward, portranges not equally sized %q", v) + return + } + + src := Addrports{ + Addr: srcip, + Ports: srcports, + } + dst := Addrports{ + Addr: dstip, + Ports: dstports, } l := ForwardLine{ - SrcIP: srcfs[0], - SrcPort: srcport, - DstIP: dstfs[0], - DstPort: dstport, - Repeat: repeat, + Src: src, + Dst: dst, } forw.Lines = append(forw.Lines, l) @@ -276,3 +294,11 @@ func parseForward(ic ini.Config, section string) (forw Forward, err error) { forw.Comments = append(forw.Comments, ic.Comments(section)...) return } + +func MakeRangeArray(start, stop int) []int { + array := make([]int, stop-start+1) + for i := range array { + array[i] = start + i + } + return array +} diff --git a/src/mole/conf/test/inv-badfwd10.ini b/src/mole/conf/test/inv-badfwd10.ini new file mode 100644 index 0000000..177887c --- /dev/null +++ b/src/mole/conf/test/inv-badfwd10.ini @@ -0,0 +1,17 @@ +[general] +description = Operator (One) +author = Jakob Borg +version = 3.2 +main = tac1 + +[hosts.tac1] +addr = 172.16.32.32 +user = "mole1" +key = "test\nkey" + +[forwards.Residential] +1.2.3.4:12345 = 192.168.173.10:42002 +127.0.0.1:8443 = [2001:db8::1::]:443 + +[forwards.Corporate] +127.0.0.2:42000-42002 = 192.168.173.12 diff --git a/src/mole/conf/test/inv-badfwd7.ini b/src/mole/conf/test/inv-badfwd7.ini new file mode 100644 index 0000000..86e37f7 --- /dev/null +++ b/src/mole/conf/test/inv-badfwd7.ini @@ -0,0 +1,17 @@ +[general] +description = Operator (One) +author = Jakob Borg +version = 3.2 +main = tac1 + +[hosts.tac1] +addr = 172.16.32.32 +user = "mole1" +key = "test\nkey" + +[forwards.Residential] +[fc80::1::2]:12345 = 192.168.173.10:42002 +127.0.0.1:8443 = 192.168.173.10:443 + +[forwards.Corporate] +127.0.0.2:42000-42002 = 192.168.173.12 diff --git a/src/mole/conf/test/inv-badfwd8.ini b/src/mole/conf/test/inv-badfwd8.ini new file mode 100644 index 0000000..5317fb8 --- /dev/null +++ b/src/mole/conf/test/inv-badfwd8.ini @@ -0,0 +1,17 @@ +[general] +description = Operator (One) +author = Jakob Borg +version = 3.2 +main = tac1 + +[hosts.tac1] +addr = 172.16.32.32 +user = "mole1" +key = "test\nkey" + +[forwards.Residential] +1.2.3.4.5:12345 = 192.168.173.10:42002 +127.0.0.1:8443 = 192.168.173.10:443 + +[forwards.Corporate] +127.0.0.2:42000-42002 = 192.168.173.12:52000-52003 diff --git a/src/mole/conf/test/inv-badfwd9.ini b/src/mole/conf/test/inv-badfwd9.ini new file mode 100644 index 0000000..f651d5f --- /dev/null +++ b/src/mole/conf/test/inv-badfwd9.ini @@ -0,0 +1,17 @@ +[general] +description = Operator (One) +author = Jakob Borg +version = 3.2 +main = tac1 + +[hosts.tac1] +addr = 172.16.32.32 +user = "mole1" +key = "test\nkey" + +[forwards.Residential] +1.2.3.4:12345 = 192.168.173.10:42002 +127.0.0.1:8443 = 2001:db8::1:443 + +[forwards.Corporate] +127.0.0.2:42000-42002 = 192.168.173.12 diff --git a/src/mole/conf/test/valid-forwards.ini b/src/mole/conf/test/valid-forwards.ini index c396444..3597284 100644 --- a/src/mole/conf/test/valid-forwards.ini +++ b/src/mole/conf/test/valid-forwards.ini @@ -16,3 +16,16 @@ key = "test\nkey" [forwards.Corporate] 127.0.0.2:42000-42002 = 192.168.173.12 comment = yo + +[forwards.v6local] +[::1]:42000-42002 = 192.168.173.13:52000-52002 +[::1]:8443 = 192.168.173.13 + +[forwards.v6remote] +127.0.0.3:8443 = [2000:db8::1234] +127.0.0.3:8444 = [2000:db8::1234]:8443 +127.0.0.3:42000-42002 = [2000:db8::1234] + +[forwards.v6] +[fc00::1]:1234 = [2001:db8::1234]:8443 +[fc00::1]:1235 = [2001:db8::1234]