diff options
| -rw-r--r-- | consensus.go | 63 | ||||
| -rw-r--r-- | consensus_test.go | 58 |
2 files changed, 109 insertions, 12 deletions
diff --git a/consensus.go b/consensus.go index 863ee61..f7b9f0a 100644 --- a/consensus.go +++ b/consensus.go @@ -11,6 +11,7 @@ import ( "io" "net" "os" + "regexp" "strconv" "strings" "time" @@ -38,6 +39,15 @@ type RouterFlags struct { V2Dir bool } +type RouterAddress struct { + IPv4Address net.IP + IPv4ORPort uint16 + IPv4DirPort uint16 + + IPv6Address net.IP + IPv6ORPort uint16 +} + type RouterStatus struct { // The single fields of an "r" line. @@ -45,9 +55,9 @@ type RouterStatus struct { Fingerprint Fingerprint Digest string Publication time.Time - Address net.IP - ORPort uint16 - DirPort uint16 + + // The IPv4 and IPv6 fields of "a" line + Address RouterAddress // The single fields of an "s" line. Flags RouterFlags @@ -87,12 +97,10 @@ type Consensus struct { // the status' string representation. func (s *RouterStatus) String() string { - return fmt.Sprintf("%s,%s,%s,%d,%d,%s,%s,%s", + return fmt.Sprintf("%s,%s,%+v,%s,%s,%s", s.Fingerprint, s.Nickname, s.Address, - s.ORPort, - s.DirPort, s.Flags, s.Publication.Format(time.RFC3339), strings.Replace(s.TorVersion, ",", "", -1)) @@ -233,6 +241,29 @@ func (a *Consensus) Intersect(b *Consensus) *Consensus { } // Implement the Stringer interface for pretty printing. +func (address RouterAddress) String() string { + var ipV4stringAddress []string + var ipV6stringAddress []string + + if address.IPv4Address != nil { + ipV4stringAddress = append(ipV4stringAddress, address.IPv4Address.String()) + } + ipV4stringAddress = append(ipV4stringAddress, fmt.Sprintf("%v", address.IPv4ORPort)) + ipV4stringAddress = append(ipV4stringAddress, fmt.Sprintf("%v", address.IPv4DirPort)) + ipV4Join := fmt.Sprintf(strings.Join(ipV4stringAddress, "|")) + + if address.IPv6Address == nil { + return ipV4Join + } + + ipV6stringAddress = append(ipV6stringAddress, address.IPv6Address.String()) + ipV6stringAddress = append(ipV6stringAddress, fmt.Sprintf("%v", address.IPv6ORPort)) + + ipV6Join := fmt.Sprintf(strings.Join(ipV6stringAddress, "|")) + return ipV4Join + "," + ipV6Join +} + +// Implement the Stringer interface for pretty printing. func (flags RouterFlags) String() string { var stringFlags []string @@ -313,6 +344,15 @@ func parseRouterFlags(flags []string) *RouterFlags { return routerFlags } +func parseIPv6AddressAndPort(addressAndPort string) (address net.IP, port uint16) { + var ipV6regex = regexp.MustCompile(`\[(.*?)\]`) + var ipV6portRegex = regexp.MustCompile(`\]:(.*)`) + address = net.ParseIP(ipV6regex.FindStringSubmatch(addressAndPort)[1]) + port = StringToPort(ipV6portRegex.FindStringSubmatch(addressAndPort)[1]) + + return address, port +} + // LazyParseRawStatus parses a raw router status (in string format) and returns // the router's fingerprint, a function which returns a RouterStatus, and an // error if there were any during parsing. Parsing of the given string is @@ -371,9 +411,12 @@ func ParseRawStatus(rawStatus string) (Fingerprint, GetStatus, error) { time, _ := time.Parse(publishedTimeLayout, strings.Join(words[4:6], " ")) status.Publication = time - status.Address = net.ParseIP(words[6]) - status.ORPort = StringToPort(words[7]) - status.DirPort = StringToPort(words[8]) + status.Address.IPv4Address = net.ParseIP(words[6]) + status.Address.IPv4ORPort = StringToPort(words[7]) + status.Address.IPv4DirPort = StringToPort(words[8]) + + case "a": + status.Address.IPv6Address, status.Address.IPv6ORPort = parseIPv6AddressAndPort(words[1]) case "s": status.Flags = *parseRouterFlags(words[1:]) @@ -527,7 +570,7 @@ func extractMetaInfo(r io.Reader, c *Consensus) error { // object filter. func (filter *ObjectFilter) MatchesRouterStatus(status *RouterStatus) bool { - if filter.HasIPAddr(status.Address) { + if filter.HasIPAddr(status.Address.IPv4Address) || (filter.HasIPAddr(status.Address.IPv6Address)) { return true } diff --git a/consensus_test.go b/consensus_test.go index efe356d..fdedc2a 100644 --- a/consensus_test.go +++ b/consensus_test.go @@ -102,7 +102,7 @@ func TestConsensusOperations(t *testing.T) { t.Error("Could not retrieve fingerprint which should be available.") } - if status.ORPort != 0 { + if status.Address.IPv4ORPort != 0 { t.Error("Field ORPort should be 0.") } @@ -123,6 +123,7 @@ func TestStatusParsing(t *testing.T) { func TestConsensusSetOperations(t *testing.T) { fingerprint0, getStatus0, err := ParseRawStatus(`r Karlstad0 m5TNC3uAV+ryG6fwI7ehyMqc5kU f1g9KQhgS0r6+H/7dzAJOpi6lG8 2014-12-08 06:57:54 193.11.166.194 9000 80 +a [2002:470:6e:80d::2]:22 s Fast Guard HSDir Running Stable V2Dir Valid v Tor 0.2.4.23 w Bandwidth=2670 @@ -132,6 +133,7 @@ p reject 1-65535`) } fingerprint1, getStatus1, err := ParseRawStatus(`r Karlstad1 zO8CqkVMCrD+GsaDBPbYxCIMGRI pR21zIq4gZQmZOj2FvRwNO5U+K0 2014-12-08 06:57:49 193.11.166.194 9001 0 +a [2a02:2430:3:2500::5fa3:1ef5]:9001 s Fast Guard Running Stable Valid v Tor 0.2.4.23 w Bandwidth=2290 @@ -141,6 +143,7 @@ p reject 1-65535`) } fingerprint2, getStatus2, err := ParseRawStatus(`r Karlstad2 e9hMtjhF4NYcHPqDkUobjJaEgrE eu8/9NajsgwD6+/vlObfyk2bZjo 2014-12-08 12:24:43 81.170.149.212 9001 0 +a [2a02:418:1007:b::48]:443 s Fast Running Stable Valid v Tor 0.2.3.25 w Bandwidth=778 @@ -153,7 +156,7 @@ p reject 1-65535`) t.Error("Unexpected fingerprint for router status.") } - if getStatus1().ORPort != 9001 { + if getStatus1().Address.IPv4ORPort != 9001 { t.Error("Unexpected ORPort.") } @@ -360,6 +363,57 @@ func TestConsensusToSlice(t *testing.T) { } } +func TestParseIPv6AddressAndPort(t *testing.T) { + + _, getStatus, err := ParseRawStatus(`r Karlstad0 m5TNC3uAV+ryG6fwI7ehyMqc5kU f1g9KQhgS0r6+H/7dzAJOpi6lG8 2014-12-08 06:57:54 193.11.166.194 9000 80 +a [2002:470:6e:80d::2]:22 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.2.4.23 +w Bandwidth=2670 +p reject 1-65535`) + if err != nil { + t.Error(err) + } + + if getStatus().Address.IPv6Address.String() != "2002:470:6e:80d::2" { + t.Error("Failes to Parse IPv6 Address correctly.") + } + + if getStatus().Address.IPv6ORPort != StringToPort("22") { + t.Error("Failes to Parse IPv6 Port correctly.") + } +} + +func TestPrintIPv6AddressAndPort(t *testing.T) { + + _, getStatus0, err := ParseRawStatus(`r Karlstad0 m5TNC3uAV+ryG6fwI7ehyMqc5kU f1g9KQhgS0r6+H/7dzAJOpi6lG8 2014-12-08 06:57:54 193.11.166.194 9000 80 +a [2002:470:6e:80d::2]:22 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.2.4.23 +w Bandwidth=2670 +p reject 1-65535`) + if err != nil { + t.Error(err) + } + + if getStatus0().Address.String() != "193.11.166.194|9000|80,2002:470:6e:80d::2|22" { + t.Error("Failed to pretty print IP addresses", getStatus0().Address.String()) + } + + _, getStatus1, err := ParseRawStatus(`r Karlstad0 m5TNC3uAV+ryG6fwI7ehyMqc5kU f1g9KQhgS0r6+H/7dzAJOpi6lG8 2014-12-08 06:57:54 193.11.166.194 9000 80 +s Fast Guard HSDir Running Stable V2Dir Valid +v Tor 0.2.4.23 +w Bandwidth=2670 +p reject 1-65535`) + if err != nil { + t.Error(err) + } + + if getStatus1().Address.String() != "193.11.166.194|9000|80" { + t.Error("Failed to pretty print IP addresses", getStatus1().Address.String()) + } +} + func TestParseRawConsensus(t *testing.T) { // Only run this test if the consensus file is there. if _, err := os.Stat(consensusFile); os.IsNotExist(err) { |
