Unverified Commit 5aff30df authored by meskio's avatar meskio 🏔️
Browse files

internal: parse extrainfo with '\,' and '\='

And skip the transport if one transport line fails to parse.

* Closes: #291
parent 8fd7d2d3
Loading
Loading
Loading
Loading
Loading
+42 −7
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ import (
	"net"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
@@ -33,6 +34,11 @@ const (
	RecordEndPrefix       = "-----END SIGNATURE-----"
)

var (
	uncommentedComma = regexp.MustCompile("[^\\\\],")
	uncommentedEqual = regexp.MustCompile("[^\\\\]=")
)

type flicker struct {
	speed     int
	flickered bool
@@ -441,7 +447,8 @@ func parseExtrainfoDoc(r io.Reader) (map[string]*resources.Bridge, error) {
			t.Fingerprint = b.Fingerprint
			err := populateTransportInfo(line, t)
			if err != nil {
				return nil, err
				log.Println(err)
				continue
			}
			b.AddTransport(t)
		}
@@ -503,13 +510,41 @@ func populateTransportInfo(transport string, t *resources.Transport) error {

	// We may be dealing with one or more key=value pairs.
	if len(words) > MinTransportWords {
		args := strings.Split(words[3], ",")
		for _, arg := range args {
			kv := strings.Split(arg, "=")
			if len(kv) != 2 {
				return fmt.Errorf("key:value pair in %q not separated by a '='", words[3])
		args := words[3]

		parseArg := func(start, end int) error {
			arg := args[start:end]
			arg = strings.ReplaceAll(arg, "\\,", ",")

			argIndexes := uncommentedEqual.FindAllStringIndex(arg, -1)
			if len(argIndexes) != 1 {
				return fmt.Errorf("transport arg has an unsupported format: %s", arg)
			}

			key := arg[0 : argIndexes[0][1]-1]
			key = strings.ReplaceAll(key, "\\=", "=")
			value := arg[argIndexes[0][1]:]
			value = strings.ReplaceAll(value, "\\=", "=")

			t.Parameters[key] = value
			return nil
		}
			t.Parameters[kv[0]] = kv[1]

		start := 0
		for _, index := range uncommentedComma.FindAllStringIndex(args, -1) {
			if len(index) != 2 {
				return fmt.Errorf("transport args has an unsupported format: %s", args)
			}
			err := parseArg(start, index[1]-1)
			if err != nil {
				return err
			}

			start = index[1]
		}
		err := parseArg(start, len(args))
		if err != nil {
			return err
		}
	}

+80 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
package internal

import (
	"reflect"
	"slices"
	"testing"

@@ -269,3 +270,82 @@ func TestBridgeWithPTnotAsVanilla(t *testing.T) {
		}
	}
}

func TestTransportInfo(t *testing.T) {
	type transportInfo struct {
		Type       string
		IPVer      core.IPVersion
		Address    string
		Port       uint16
		Parameters map[string]string
	}
	transportLines := map[string]transportInfo{
		"transport obfs4 139.55.50.64:39071 cert=ZZZZZZZZZZZ,iat-mode=0": transportInfo{
			Type:       "obfs4",
			IPVer:      core.IPv4,
			Address:    "139.55.50.64",
			Port:       39071,
			Parameters: map[string]string{"cert": "ZZZZZZZZZZZ", "iat-mode": "0"},
		},
		"transport obfs4 [2a0c:4d80:42:702::1]:27015 cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw,iat-mode=0": transportInfo{
			Type:       "obfs4",
			IPVer:      core.IPv6,
			Address:    "2a0c:4d80:42:702::1",
			Port:       27015,
			Parameters: map[string]string{"cert": "TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw", "iat-mode": "0"},
		},
		"transport webtunnel [2001:db8:d21c::2bc4]:443 url=https://example.com/something-random": transportInfo{
			Type:       "webtunnel",
			IPVer:      core.IPAny,
			Address:    "2001:db8:d21c::2bc4",
			Port:       443,
			Parameters: map[string]string{"url": "https://example.com/something-random"},
		},
		"transport obfs4 139.55.50.64:39071 cert=Z\\=Z\\=Z,iat-mode=0": transportInfo{
			Type:       "obfs4",
			IPVer:      core.IPv4,
			Address:    "139.55.50.64",
			Port:       39071,
			Parameters: map[string]string{"cert": "Z=Z=Z", "iat-mode": "0"},
		},
		"transport obfs4 139.55.50.64:39071 cert=Z\\,Z\\,Z,iat-mode=0": transportInfo{
			Type:       "obfs4",
			IPVer:      core.IPv4,
			Address:    "139.55.50.64",
			Port:       39071,
			Parameters: map[string]string{"cert": "Z,Z,Z", "iat-mode": "0"},
		},
		"transport obfs4 139.55.50.64:39071 cert=Z\\,\\=Z\\=Z\\,,ia\\,t-m\\=ode\\==0": transportInfo{
			Type:       "obfs4",
			IPVer:      core.IPv4,
			Address:    "139.55.50.64",
			Port:       39071,
			Parameters: map[string]string{"cert": "Z,=Z=Z,", "ia,t-m=ode=": "0"},
		},
	}

	for line, transport := range transportLines {
		parsedTransport := resources.NewTransport()
		err := populateTransportInfo(line, parsedTransport)
		if err != nil {
			t.Error("Error propulating transport info for ", line, err)
			continue
		}

		if !reflect.DeepEqual(parsedTransport.Parameters, transport.Parameters) {
			t.Error(line, "didn't produce the right parameters:", parsedTransport.Parameters)
		}
		if parsedTransport.Type() != transport.Type {
			t.Error(line, "didn't produce the right type:", parsedTransport.Type())
		}
		if parsedTransport.IPVer != transport.IPVer {
			t.Error(line, "didn't produce the right IPVer:", parsedTransport.IPVer)
		}
		if parsedTransport.Address.String() != transport.Address {
			t.Error(line, "didn't produce the right address:", parsedTransport.Address.String())
		}
		if parsedTransport.Port != transport.Port {
			t.Error(line, "didn't produce the right port:", parsedTransport.Port)
		}
	}
}