mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
commit
909ba573ea
@ -25,6 +25,10 @@ transaction](https://github.com/lightningnetwork/lnd/pull/6730).
|
|||||||
* [The macaroon key store implementation was refactored to be more generally
|
* [The macaroon key store implementation was refactored to be more generally
|
||||||
usable](https://github.com/lightningnetwork/lnd/pull/6509).
|
usable](https://github.com/lightningnetwork/lnd/pull/6509).
|
||||||
|
|
||||||
|
* [Fixed a bug where cookie authentication with Tor would fail if the cookie
|
||||||
|
path contained spaces](https://github.com/lightningnetwork/lnd/pull/6829).
|
||||||
|
Now it parses Tor control port messages correctly.
|
||||||
|
|
||||||
## `lncli`
|
## `lncli`
|
||||||
* [Add an `insecure` flag to skip tls auth as well as a `metadata` string slice
|
* [Add an `insecure` flag to skip tls auth as well as a `metadata` string slice
|
||||||
flag](https://github.com/lightningnetwork/lnd/pull/6818) that allows the
|
flag](https://github.com/lightningnetwork/lnd/pull/6818) that allows the
|
||||||
@ -41,6 +45,7 @@ transaction](https://github.com/lightningnetwork/lnd/pull/6730).
|
|||||||
# Contributors (Alphabetical Order)
|
# Contributors (Alphabetical Order)
|
||||||
|
|
||||||
* Carla Kirk-Cohen
|
* Carla Kirk-Cohen
|
||||||
|
* cutiful
|
||||||
* Daniel McNally
|
* Daniel McNally
|
||||||
* Elle Mouton
|
* Elle Mouton
|
||||||
* ErikEk
|
* ErikEk
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -77,6 +78,17 @@ var (
|
|||||||
// errTCNotStarted is used when we require the tor controller to be
|
// errTCNotStarted is used when we require the tor controller to be
|
||||||
// not stopped while it is.
|
// not stopped while it is.
|
||||||
errTCStopped = errors.New("tor controller must not be stopped")
|
errTCStopped = errors.New("tor controller must not be stopped")
|
||||||
|
|
||||||
|
// replyFieldRegexp is the regular expression used to find fields in a
|
||||||
|
// reply. Parameters within a reply should be of the form KEY=VALUE or
|
||||||
|
// KEY="VALUE", where quoted values might contain spaces, newlines and
|
||||||
|
// quoted pairs. If the parameter doesn't contain "=", then we can
|
||||||
|
// assume it doesn't provide any relevant information that isn't already
|
||||||
|
// known. Read more on this topic:
|
||||||
|
// https://gitweb.torproject.org/torspec.git/tree/control-spec.txt#n188
|
||||||
|
replyFieldRegexp = regexp.MustCompile(
|
||||||
|
`[^" \r\n]+=(?:"(?:[^"\\]|\\[\0-\x7F])*"|[^" \r\n]*)`,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Controller is an implementation of the Tor Control protocol. This is used in
|
// Controller is an implementation of the Tor Control protocol. This is used in
|
||||||
@ -367,29 +379,51 @@ func (c *Controller) readResponse(expected int) (int, string, error) {
|
|||||||
return code, reply, nil
|
return code, reply, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unescapeValue removes escape codes from the value in the Tor reply. A
|
||||||
|
// backslash followed by any character represents that character, so we remove
|
||||||
|
// any backslash not preceded by another backslash.
|
||||||
|
func unescapeValue(value string) string {
|
||||||
|
newString := ""
|
||||||
|
justRemovedBackslash := false
|
||||||
|
|
||||||
|
for _, char := range value {
|
||||||
|
if char == '\\' && !justRemovedBackslash {
|
||||||
|
justRemovedBackslash = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newString += string(char)
|
||||||
|
justRemovedBackslash = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return newString
|
||||||
|
}
|
||||||
|
|
||||||
// parseTorReply parses the reply from the Tor server after receiving a command
|
// parseTorReply parses the reply from the Tor server after receiving a command
|
||||||
// from a controller. This will parse the relevant reply parameters into a map
|
// from a controller. This will parse the relevant reply parameters into a map
|
||||||
// of keys and values.
|
// of keys and values.
|
||||||
func parseTorReply(reply string) map[string]string {
|
func parseTorReply(reply string) map[string]string {
|
||||||
params := make(map[string]string)
|
params := make(map[string]string)
|
||||||
|
|
||||||
// Replies can either span single or multiple lines, so we'll default
|
// Find all fields of a reply. The -1 indicates that we want this to
|
||||||
// to stripping whitespace and newlines in order to retrieve the
|
// find all instances of the regexp.
|
||||||
// individual contents of it. The -1 indicates that we want this to span
|
contents := replyFieldRegexp.FindAllString(reply, -1)
|
||||||
// across all instances of a newline.
|
|
||||||
contents := strings.Split(strings.Replace(reply, "\n", " ", -1), " ")
|
|
||||||
for _, content := range contents {
|
for _, content := range contents {
|
||||||
// Each parameter within the reply should be of the form
|
// Each parameter within the reply should be of the form
|
||||||
// "KEY=VALUE". If the parameter doesn't contain "=", then we
|
// KEY=VALUE or KEY="VALUE".
|
||||||
// can assume it does not provide any other relevant information
|
|
||||||
// already known.
|
|
||||||
keyValue := strings.SplitN(content, "=", 2)
|
keyValue := strings.SplitN(content, "=", 2)
|
||||||
if len(keyValue) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
key := keyValue[0]
|
key := keyValue[0]
|
||||||
value := keyValue[1]
|
value := keyValue[1]
|
||||||
|
|
||||||
|
// Quoted strings need extra processing.
|
||||||
|
if strings.HasPrefix(value, `"`) {
|
||||||
|
// Remove quotes around the value.
|
||||||
|
value = value[1 : len(value)-1]
|
||||||
|
|
||||||
|
// Unescape the value.
|
||||||
|
value = unescapeValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
params[key] = value
|
params[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,3 +359,64 @@ func TestReconnectSucceed(t *testing.T) {
|
|||||||
// Check that the connection has been updated.
|
// Check that the connection has been updated.
|
||||||
require.NotEqual(t, proxy.clientConn, c.conn)
|
require.NotEqual(t, proxy.clientConn, c.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestParseTorReply tests that Tor replies are parsed correctly.
|
||||||
|
func TestParseTorReply(t *testing.T) {
|
||||||
|
testCase := []struct {
|
||||||
|
reply string
|
||||||
|
expectedParams map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// Test a regular reply.
|
||||||
|
reply: `VERSION Tor="0.4.7.8"`,
|
||||||
|
expectedParams: map[string]string{
|
||||||
|
"Tor": "0.4.7.8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Test a reply with multiple values, one of them
|
||||||
|
// containing spaces.
|
||||||
|
reply: `AUTH METHODS=COOKIE,SAFECOOKIE,HASHEDPASSWORD` +
|
||||||
|
` COOKIEFILE="/path/with/spaces/Tor Browser/c` +
|
||||||
|
`ontrol_auth_cookie"`,
|
||||||
|
expectedParams: map[string]string{
|
||||||
|
"METHODS": "COOKIE,SAFECOOKIE,HASHEDPASSWORD",
|
||||||
|
"COOKIEFILE": "/path/with/spaces/Tor Browser/" +
|
||||||
|
"control_auth_cookie",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Test a multiline reply.
|
||||||
|
reply: "ServiceID=id\r\nOK",
|
||||||
|
expectedParams: map[string]string{"ServiceID": "id"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Test a reply with invalid parameters.
|
||||||
|
reply: "AUTH =invalid",
|
||||||
|
expectedParams: map[string]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Test escaping arbitrary characters.
|
||||||
|
reply: `PARAM="esca\ped \"doub\lequotes\""`,
|
||||||
|
expectedParams: map[string]string{
|
||||||
|
`PARAM`: `escaped "doublequotes"`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Test escaping backslashes. Each single backslash
|
||||||
|
// should be removed, each double backslash replaced
|
||||||
|
// with a single one. Note that the single backslash
|
||||||
|
// before the space escapes the space character, so
|
||||||
|
// there's two spaces in a row.
|
||||||
|
reply: `PARAM="escaped \\ \ \\\\"`,
|
||||||
|
expectedParams: map[string]string{
|
||||||
|
`PARAM`: `escaped \ \\`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCase {
|
||||||
|
params := parseTorReply(tc.reply)
|
||||||
|
require.Equal(t, tc.expectedParams, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user