diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..0ec0fb7 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,63 @@ +// utils.go - A grab bag of useful utilitiy functions. +// +// To the extent possible under law, Yawning Angel waived all copyright +// and related or neighboring rights to bulb, using the creative +// commons "cc0" public domain dedication. See LICENSE or +// for full details. + +// Package utils implements useful utilities for dealing with Tor and it's +// control port. +package utils + +import ( + "net" + "net/url" + "strconv" +) + +// ParseControlPortString parses a string representation of a control port +// address into a network/address string pair suitable for use with "dial". +// +// Valid string representations are: +// * tcp://address:port +// * unix://path +// * port (Translates to tcp://127.0.0.1:port) +func ParseControlPortString(raw string) (network, addr string, err error) { + // Try parsing it as a naked port. + if _, err = strconv.ParseUint(raw, 10, 16); err == nil { + raw = "tcp://127.0.0.1:" + raw + } + + // Ok, parse/validate the URI. + uri, err := url.Parse(raw) + if err != nil { + return "", "", err + } + if uri.Opaque != "" || uri.RawQuery != "" || uri.Fragment != "" { + return "", "", net.InvalidAddrError("uri has Opaque/Query/Fragment") + } + switch uri.Scheme { + case "tcp": + if uri.Path != "" { + return "", "", net.InvalidAddrError("tcp uri has a path") + } + tcpAddr, err := net.ResolveTCPAddr(uri.Scheme, uri.Host) + if err != nil { + return "", "", err + } + if tcpAddr.Port == 0 { + return "", "", net.InvalidAddrError("tcp uri is missing a port") + } + return uri.Scheme, uri.Host, nil + case "unix": + if uri.Host != "" { + return "", "", net.InvalidAddrError("unix uri has a host") + } + _, err := net.ResolveUnixAddr(uri.Scheme, uri.Path) + if err != nil { + return "", "", err + } + return uri.Scheme, uri.Path, nil + } + return "", "", net.InvalidAddrError("unknown scheme: " + uri.Scheme) +}