2018-05-10 01:18:19 +02:00
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ccan/err/err.h>
|
|
|
|
#include <ccan/io/io.h>
|
2018-05-10 01:18:23 +02:00
|
|
|
#include <ccan/rbuf/rbuf.h>
|
|
|
|
#include <ccan/read_write_all/read_write_all.h>
|
2018-05-10 01:18:19 +02:00
|
|
|
#include <ccan/str/hex/hex.h>
|
2018-05-10 01:18:23 +02:00
|
|
|
#include <ccan/tal/grab_file/grab_file.h>
|
2018-05-10 01:18:19 +02:00
|
|
|
#include <ccan/tal/str/str.h>
|
|
|
|
#include <common/type_to_string.h>
|
|
|
|
#include <common/utils.h>
|
|
|
|
#include <common/wireaddr.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2018-05-10 01:18:23 +02:00
|
|
|
#include <lightningd/log.h>
|
2018-05-10 01:18:23 +02:00
|
|
|
#include <lightningd/tor.h>
|
2018-05-10 01:18:19 +02:00
|
|
|
#include <netdb.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <wire/wire.h>
|
|
|
|
|
|
|
|
#define MAX_TOR_COOKIE_LEN 32
|
|
|
|
#define MAX_TOR_SERVICE_READBUFFER_LEN 255
|
|
|
|
#define MAX_TOR_ONION_V2_ADDR_LEN 16
|
|
|
|
#define MAX_TOR_ONION_V3_ADDR_LEN 56
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
static void *buf_resize(void *buf, size_t len)
|
2018-05-10 01:18:19 +02:00
|
|
|
{
|
2018-05-10 01:18:23 +02:00
|
|
|
tal_resize(&buf, len);
|
|
|
|
return buf;
|
2018-05-10 01:18:19 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
static void tor_send_cmd(struct lightningd *ld,
|
|
|
|
struct rbuf *rbuf, const char *cmd)
|
2018-05-10 01:18:19 +02:00
|
|
|
{
|
2018-05-10 01:18:23 +02:00
|
|
|
log_io(ld->log, LOG_IO_OUT, "torcontrol", cmd, strlen(cmd));
|
|
|
|
if (!write_all(rbuf->fd, cmd, strlen(cmd)))
|
|
|
|
err(1, "Writing '%s' to Tor socket", cmd);
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
log_io(ld->log, LOG_IO_OUT, "torcontrol", "\r\n", 2);
|
|
|
|
if (!write_all(rbuf->fd, "\r\n", 2))
|
|
|
|
err(1, "Writing CRLF to Tor socket");
|
2018-05-10 01:18:19 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
static char *tor_response_line(struct lightningd *ld, struct rbuf *rbuf)
|
2018-05-10 01:18:19 +02:00
|
|
|
{
|
2018-05-10 01:18:23 +02:00
|
|
|
char *line;
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
while ((line = rbuf_read_str(rbuf, '\n', buf_resize)) != NULL) {
|
|
|
|
log_io(ld->log, LOG_IO_IN, "torcontrol", line, strlen(line));
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
/* Weird response */
|
|
|
|
if (!strstarts(line, "250"))
|
|
|
|
errx(1, "Tor returned '%s'", line);
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
/* Last line */
|
|
|
|
if (strstarts(line, "250 "))
|
|
|
|
break;
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
return line + 4;
|
|
|
|
}
|
|
|
|
return NULL;
|
2018-05-10 01:18:19 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
static void discard_remaining_response(struct lightningd *ld, struct rbuf *rbuf)
|
2018-05-10 01:18:19 +02:00
|
|
|
{
|
2018-05-10 01:18:23 +02:00
|
|
|
while (tor_response_line(ld, rbuf));
|
2018-05-10 01:18:19 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
static void make_onion(struct lightningd *ld, struct rbuf *rbuf)
|
2018-05-10 01:18:19 +02:00
|
|
|
{
|
2018-05-10 01:18:23 +02:00
|
|
|
char *line;
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
//V3 tor after 3.3.3.aplha FIXME: TODO SAIBATO
|
|
|
|
//sprintf((char *)reach->buffer,"ADD_ONION NEW:ED25519-V3 Port=9735,127.0.0.1:9735\r\n");
|
|
|
|
tor_send_cmd(ld, rbuf,
|
|
|
|
tal_fmt(tmpctx, "ADD_ONION NEW:RSA1024 Port=%d,127.0.0.1:%d Flags=DiscardPK,Detach",
|
|
|
|
ld->portnum, ld->portnum));
|
|
|
|
|
|
|
|
while ((line = tor_response_line(ld, rbuf)) != NULL) {
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
if (!strstarts(line, "ServiceID="))
|
|
|
|
continue;
|
|
|
|
line += strlen("ServiceID=");
|
|
|
|
/* Strip the trailing CR */
|
|
|
|
if (strchr(line, '\r'))
|
|
|
|
*strchr(line, '\r') = '\0';
|
|
|
|
|
|
|
|
n = tal_count(ld->proposed_wireaddr);
|
|
|
|
tal_resize(&ld->proposed_wireaddr, n + 1);
|
|
|
|
tal_resize(&ld->proposed_listen_announce, n + 1);
|
|
|
|
parse_wireaddr_internal(tal_fmt(tmpctx, "%s.onion", line),
|
|
|
|
&ld->proposed_wireaddr[n],
|
2018-05-10 01:18:24 +02:00
|
|
|
ld->portnum, false, false, NULL);
|
2018-05-10 01:18:23 +02:00
|
|
|
ld->proposed_listen_announce[n] = ADDR_ANNOUNCE;
|
|
|
|
discard_remaining_response(ld, rbuf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errx(1, "Tor didn't give us a ServiceID");
|
2018-05-10 01:18:19 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
/* https://gitweb.torproject.org/torspec.git/tree/control-spec.txt:
|
|
|
|
*
|
|
|
|
* MidReplyLine = StatusCode "-" ReplyLine
|
|
|
|
* DataReplyLine = StatusCode "+" ReplyLine CmdData
|
|
|
|
* EndReplyLine = StatusCode SP ReplyLine
|
|
|
|
* ReplyLine = [ReplyText] CRLF
|
|
|
|
* ReplyText = XXXX
|
|
|
|
* StatusCode = 3DIGIT
|
|
|
|
*/
|
|
|
|
static void negotiate_auth(struct lightningd *ld, struct rbuf *rbuf)
|
2018-05-10 01:18:19 +02:00
|
|
|
{
|
2018-05-10 01:18:23 +02:00
|
|
|
char *line;
|
2018-05-10 01:18:24 +02:00
|
|
|
char *cookiefile = NULL;
|
|
|
|
int cookiefileerrno;
|
2018-05-10 01:18:23 +02:00
|
|
|
|
|
|
|
tor_send_cmd(ld, rbuf, "PROTOCOLINFO 1");
|
|
|
|
|
|
|
|
while ((line = tor_response_line(ld, rbuf)) != NULL) {
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
if (!strstarts(line, "AUTH METHODS="))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (strstr(line, "NULL")) {
|
|
|
|
discard_remaining_response(ld, rbuf);
|
|
|
|
tor_send_cmd(ld, rbuf, "AUTHENTICATE");
|
|
|
|
discard_remaining_response(ld, rbuf);
|
|
|
|
return;
|
|
|
|
} else if (strstr(line, "HASHEDPASSWORD")
|
|
|
|
&& strlen(ld->tor_service_password)) {
|
|
|
|
discard_remaining_response(ld, rbuf);
|
|
|
|
tor_send_cmd(ld, rbuf,
|
|
|
|
tal_fmt(tmpctx, "AUTHENTICATE \"%s\"",
|
|
|
|
ld->tor_service_password));
|
|
|
|
discard_remaining_response(ld, rbuf);
|
|
|
|
return;
|
|
|
|
} else if ((p = strstr(line, "COOKIEFILE=\"")) != NULL) {
|
|
|
|
char *contents, *end;
|
|
|
|
|
|
|
|
p += strlen("COOKIEFILE=\"");
|
|
|
|
end = strstr(p, "\"");
|
|
|
|
if (!end)
|
|
|
|
errx(1, "Tor protocolinfo bad line '%s'", line);
|
|
|
|
*end = '\0';
|
|
|
|
|
2018-05-10 01:18:24 +02:00
|
|
|
/* If we can't access this, try other methods */
|
|
|
|
cookiefile = tal_strdup(tmpctx, p);
|
2018-05-10 01:18:23 +02:00
|
|
|
contents = grab_file(tmpctx, p);
|
2018-05-10 01:18:24 +02:00
|
|
|
if (!contents) {
|
|
|
|
cookiefileerrno = errno;
|
|
|
|
fprintf(stderr, "No cookies for me!\n");
|
|
|
|
continue;
|
|
|
|
}
|
2018-05-10 01:18:23 +02:00
|
|
|
discard_remaining_response(ld, rbuf);
|
|
|
|
tor_send_cmd(ld, rbuf,
|
|
|
|
tal_fmt(tmpctx, "AUTHENTICATE %s",
|
|
|
|
tal_hexstr(tmpctx,
|
|
|
|
contents,
|
|
|
|
tal_len(contents)-1)));
|
|
|
|
discard_remaining_response(ld, rbuf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-05-10 01:18:24 +02:00
|
|
|
|
|
|
|
/* Now report if we tried cookie file and it failed */
|
|
|
|
if (cookiefile) {
|
|
|
|
errno = cookiefileerrno;
|
|
|
|
err(1, "Could not open Tor cookie file '%s'", cookiefile);
|
|
|
|
}
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
errx(1, "Tor protocolinfo did not give auth");
|
2018-05-10 01:18:19 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
void tor_init(struct lightningd *ld)
|
2018-05-10 01:18:19 +02:00
|
|
|
{
|
2018-05-10 01:18:23 +02:00
|
|
|
int fd;
|
|
|
|
struct addrinfo *ai_tor;
|
|
|
|
struct rbuf rbuf;
|
|
|
|
char *buffer;
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
if (!ld->config.tor_enable_auto_hidden_service)
|
|
|
|
return;
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
/* FIXME: Need better way to convert wireaddr to addrinfo... */
|
2018-05-10 01:18:23 +02:00
|
|
|
if (getaddrinfo(fmt_wireaddr_without_port(ld, ld->tor_serviceaddr),
|
|
|
|
tal_fmt(tmpctx, "%d", ld->tor_serviceaddr->port), NULL,
|
2018-05-10 01:18:23 +02:00
|
|
|
&ai_tor) != 0)
|
|
|
|
errx(1, "getaddrinfo failed for Tor service");
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
fd = socket(ai_tor->ai_family, SOCK_STREAM, 0);
|
|
|
|
if (fd < 0)
|
|
|
|
err(1, "Creating stream socket for Tor");
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
if (connect(fd, ai_tor->ai_addr, ai_tor->ai_addrlen) != 0)
|
|
|
|
err(1, "Connecting stream socket to Tor service");
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
buffer = tal_arr(tmpctx, char, rbuf_good_size(fd));
|
|
|
|
rbuf_init(&rbuf, fd, buffer, tal_len(buffer));
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
negotiate_auth(ld, &rbuf);
|
|
|
|
make_onion(ld, &rbuf);
|
2018-05-10 01:18:19 +02:00
|
|
|
|
2018-05-10 01:18:23 +02:00
|
|
|
/*on the other hand we can stay connected until ln finish to keep onion alive and then vanish */
|
|
|
|
//because when we run with Detach flag as we now do every start of LN creates a new addr while the old
|
|
|
|
//stays valid until reboot this might not be desired so we can also drop Detach and use the
|
|
|
|
//read_partial to keep it open until LN drops
|
|
|
|
//FIXME: SAIBATO we might not want to close this conn
|
|
|
|
close(fd);
|
2018-05-10 01:18:19 +02:00
|
|
|
}
|