diff --git a/channeld/Makefile b/channeld/Makefile index d0a20f987..7769e638f 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -33,6 +33,7 @@ ALL_GEN_HEADERS += $(LIGHTNINGD_CHANNEL_HEADERS_GEN) # Common source we use. CHANNELD_COMMON_OBJS := \ + common/base32.o \ common/bip32.o \ common/channel_config.o \ common/crypto_state.o \ diff --git a/closingd/Makefile b/closingd/Makefile index 2e4e93191..44b7e7b4a 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -42,6 +42,7 @@ $(LIGHTNINGD_CLOSING_OBJS): $(LIGHTNINGD_HEADERS) # Common source we use. CLOSINGD_COMMON_OBJS := \ + common/base32.o \ common/close_tx.o \ common/crypto_state.o \ common/crypto_sync.o \ diff --git a/common/Makefile b/common/Makefile index be000c92e..7f996c2d1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,4 +1,5 @@ COMMON_SRC_NOGEN := \ + common/base32.c \ common/bech32.c \ common/bech32_util.c \ common/bip32.c \ @@ -41,6 +42,7 @@ COMMON_SRC_NOGEN := \ common/timeout.c \ common/type_to_string.c \ common/utils.c \ + common/tor.c \ common/utxo.c \ common/version.c \ common/wallet_tx.c \ diff --git a/common/base32.c b/common/base32.c new file mode 100644 index 000000000..7530aa798 --- /dev/null +++ b/common/base32.c @@ -0,0 +1,63 @@ +#include +#include + +/* This is a rework of what i found on the Net about base32 + * + * so Orum (shallot) and Markus Gutschke (Google.inc) should be mentioned here + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define BASE32DATA "abcdefghijklmnopqrstuvwxyz234567" + +char *b32_encode(char *dst, u8 * src, u8 ver) +{ + u16 byte = 0, poff = 0; + for (; byte < ((ver == 2) ? 16 : 56); poff += 5) { + if (poff > 7) { + poff -= 8; + src++; + } + dst[byte++] = + BASE32DATA[(htobe16(*(u16 *) src) >> (11 - poff)) & (u16) + 0x001F]; + } + dst[byte] = 0; + return dst; +} + +//FIXME quiknditry + +void b32_decode(u8 * dst, u8 * src, u8 ver) +{ + int rem = 0; + int i; + u8 *p = src; + int buf; + u8 ch; + for (i = 0; i < ((ver == 2) ? 16 : 56); p++) { + ch = *p; + buf <<= 5; + if ((ch >= 'a' && ch <= 'z')) { + ch = (ch & 0x1F) - 1; + } else if (ch != '.') { + ch -= '2' - 0x1A; + } else return; + buf = buf | ch; + rem = rem + 5; + if (rem >= 8) { + dst[i++] = buf >> (rem - 8); + rem -= 8; + } + } +} diff --git a/common/base32.h b/common/base32.h new file mode 100644 index 000000000..39456aedf --- /dev/null +++ b/common/base32.h @@ -0,0 +1,10 @@ +#ifndef LIGHTNING_COMMON_BASE32_H +#define LIGHTNING_COMMON_BASE32_H +#include "config.h" +#include + + +char *b32_encode(char *dst, u8 * src, u8 ver); +void b32_decode(u8 * dst, u8 * src, u8 ver); + +#endif /* LIGHTNING_COMMON_BASE32_H */ diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index 0773e3a3c..ea7e4b2df 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -1,3 +1,4 @@ +#include "../common/base32.c" #include "../common/wireaddr.c" #include @@ -102,6 +103,18 @@ int main(void) assert(parse_wireaddr("[::ffff:127.0.0.1]:49150", &addr, 1, NULL)); assert(addr.port == 49150); + + assert(parse_wireaddr("4ruvswpqec5i2gogopxl4vm5bruzknbvbylov2awbo4rxiq4cimdldad.onion:49150", &addr, 1, NULL)); + assert(addr.port == 49150); + + assert(parse_wireaddr("4ruvswpqec5i2gogopxl4vm5bruzknbvbylov2awbo4rxiq4cimdldad.onion", &addr, 1, NULL)); + assert(addr.port == 1); + + assert(parse_wireaddr("odpzvneidqdf5hdq.onion:49150", &addr, 1, NULL)); + assert(addr.port == 49150); + + assert(parse_wireaddr("odpzvneidqdf5hdq.onion.onion", &addr, 1, NULL)); + assert(addr.port == 1); tal_free(tmpctx); return 0; } diff --git a/common/tor.c b/common/tor.c new file mode 100644 index 000000000..7a293f536 --- /dev/null +++ b/common/tor.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +static bool return_from_service_call; + +struct tor_service_reaching { + struct lightningd *ld; + u8 buffer[MAX_TOR_SERVICE_READBUFFER_LEN]; + char *cookie[MAX_TOR_COOKIE_LEN]; + u8 *p; + bool noauth; + size_t hlen; +}; + +static struct io_plan *io_tor_connect_close(struct io_conn *conn) +{ + err(1, "Cannot create TOR service address"); + return_from_service_call = true; + return io_close(conn); +} + +static struct io_plan *io_tor_connect_create_onion_finished(struct io_conn + *conn, struct + tor_service_reaching + *reach) +{ + char *temp_char; + + if (reach->hlen == MAX_TOR_ONION_V2_ADDR_LEN) { + size_t n = tal_count(reach->ld->proposed_wireaddr); + tal_resize(&reach->ld->proposed_wireaddr, n + 1); + tal_resize(&reach->ld->proposed_listen_announce, n+1); + reach->ld->proposed_listen_announce[n] = ADDR_ANNOUNCE; + temp_char = tal_fmt(tmpctx, "%.56s.onion", reach->buffer); + parse_wireaddr_internal(temp_char, + &reach->ld->proposed_wireaddr[n], + reach->ld->portnum, false, NULL); + return_from_service_call = true; + return io_close(conn); + } + /*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 + //return io_read_partial(conn, reach->p, 1 ,&reach->hlen, io_tor_connect_create_onion_finished, reach); + return io_tor_connect_close(conn); +} + +static struct io_plan *io_tor_connect_after_create_onion(struct io_conn *conn, struct + tor_service_reaching + *reach) +{ + reach->p = reach->p + reach->hlen; + + if (!strstr((char *)reach->buffer, "ServiceID=")) { + if (reach->hlen == 0) + return io_tor_connect_close(conn); + + return io_read_partial(conn, reach->p, 1, &reach->hlen, + io_tor_connect_after_create_onion, + reach); + } else { + memset(reach->buffer, 0, sizeof(reach->buffer)); + return io_read_partial(conn, reach->buffer, + MAX_TOR_ONION_V2_ADDR_LEN, &reach->hlen, + io_tor_connect_create_onion_finished, + reach); + } +} + +//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"); + +static struct io_plan *io_tor_connect_make_onion(struct io_conn *conn, struct tor_service_reaching + *reach) +{ + if (strstr((char *)reach->buffer, "250 OK") == NULL) + return io_tor_connect_close(conn); + + sprintf((char *)reach->buffer, + "ADD_ONION NEW:RSA1024 Port=%d,127.0.0.1:%d Flags=DiscardPK,Detach\r\n", + reach->ld->portnum, reach->ld->portnum); + + reach->hlen = strlen((char *)reach->buffer); + reach->p = reach->buffer; + return io_write(conn, reach->buffer, reach->hlen, + io_tor_connect_after_create_onion, reach); +} + +static struct io_plan *io_tor_connect_after_authenticate(struct io_conn *conn, struct + tor_service_reaching + *reach) +{ + return io_read(conn, reach->buffer, 7, io_tor_connect_make_onion, + reach); +} + +static struct io_plan *io_tor_connect_authenticate(struct io_conn *conn, struct + tor_service_reaching + *reach) +{ + sprintf((char *)reach->buffer, "AUTHENTICATE %s\r\n", + (char *)reach->cookie); + + if (reach->noauth) + sprintf((char *)reach->buffer, "AUTHENTICATE\r\n"); + + reach->hlen = strlen((char *)reach->buffer); + + return io_write(conn, reach->buffer, reach->hlen, + io_tor_connect_after_authenticate, reach); + +} + +static struct io_plan *io_tor_connect_after_answer_pi(struct io_conn *conn, struct + tor_service_reaching + *reach) +{ + char *p = tal(reach, char); + char *p2 = tal(reach, char); + + u8 *buf = tal_arrz(reach, u8, MAX_TOR_COOKIE_LEN); + + reach->noauth = false; + + if (strstr((char *)reach->buffer, "NULL")) + reach->noauth = true; + else if (strstr((char *)reach->buffer, "HASHEDPASSWORD") + && (strlen(reach->ld->tor_service_password))) { + reach->noauth = false; + sprintf((char *)reach->cookie, "\"%s\"", + reach->ld->tor_service_password); + } else if ((p = strstr((char *)reach->buffer, "COOKIEFILE="))) { + assert(strlen(p) > 12); + p2 = strstr((char *)(p + 12), "\""); + assert(p2 != NULL); + *(char *)(p + (strlen(p) - strlen(p2))) = 0; + + int fd = open((char *)(p + 12), O_RDONLY); + if (fd < 0) + return io_tor_connect_close(conn); + if (!read(fd, buf, MAX_TOR_COOKIE_LEN)) { + close(fd); + return io_tor_connect_close(conn); + } else + close(fd); + + hex_encode(buf, 32, (char *)reach->cookie, 80); + reach->noauth = false; + } else + return io_tor_connect_close(conn); + + return io_tor_connect_authenticate(conn, reach); + +} + +static struct io_plan *io_tor_connect_after_protocolinfo(struct io_conn *conn, struct + tor_service_reaching + *reach) +{ + + memset(reach->buffer, 0, MAX_TOR_SERVICE_READBUFFER_LEN); + return io_read_partial(conn, reach->buffer, + MAX_TOR_SERVICE_READBUFFER_LEN - 1, &reach->hlen, + &io_tor_connect_after_answer_pi, reach); +} + +static struct io_plan *io_tor_connect_after_resp_to_connect(struct io_conn + *conn, struct + tor_service_reaching + *reach) +{ + + sprintf((char *)reach->buffer, "PROTOCOLINFO 1\r\n"); + reach->hlen = strlen((char *)reach->buffer); + + return io_write(conn, reach->buffer, reach->hlen, + io_tor_connect_after_protocolinfo, reach); +} + +static struct io_plan *tor_connect_finish(struct io_conn *conn, + struct tor_service_reaching *reach) +{ + return io_tor_connect_after_resp_to_connect(conn, reach); +} + +static struct io_plan *tor_conn_init(struct io_conn *conn, + struct lightningd *ld) +{ + struct addrinfo *ai_tor = tal(ld, struct addrinfo); + struct tor_service_reaching *reach = + tal(ld, struct tor_service_reaching); + + reach->ld = ld; + + getaddrinfo(fmt_wireaddr_without_port(ld, ld->tor_serviceaddrs), + tal_fmt(ld, "%d", ld->tor_serviceaddrs->port), NULL, + &ai_tor); + + return io_connect(conn, ai_tor, &tor_connect_finish, reach); +} + +bool create_tor_hidden_service_conn(struct lightningd * ld) +{ + int fd; + struct io_conn *conn; + + return_from_service_call = false; + + fd = socket(AF_INET, SOCK_STREAM, 0); + conn = io_new_conn(NULL, fd, &tor_conn_init, ld); + if (!conn) { + return_from_service_call = true; + err(1, "Cannot create new TOR connection"); + } + return true; +} + +bool do_we_use_tor_addr(const struct wireaddr * wireaddr) +{ + for (int i = 0; i < tal_count(wireaddr); i++) { + if ((wireaddr[i].type == ADDR_TYPE_TOR_V2) + || (wireaddr[i].type == ADDR_TYPE_TOR_V3)) + return true; + } + return false; +} + +bool check_return_from_service_call(void) +{ + return return_from_service_call; +} diff --git a/common/tor.h b/common/tor.h new file mode 100644 index 000000000..318c18329 --- /dev/null +++ b/common/tor.h @@ -0,0 +1,14 @@ +#ifndef LIGHTNING_COMMON_TOR_H +#define LIGHTNING_COMMON_TOR_H +#include "config.h" +#include +#include +#include +#include +#include + +bool check_return_from_service_call(void); +bool parse_tor_wireaddr(const char *arg,u8 *ip_ld,u16 *port_ld); +bool create_tor_hidden_service_conn(struct lightningd *); +bool do_we_use_tor_addr(const struct wireaddr *wireaddrs); +#endif /* LIGHTNING_COMMON_TOR_H */ diff --git a/common/wireaddr.c b/common/wireaddr.c index f76660901..4e799a80a 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -1,14 +1,20 @@ #include #include #include +#include #include +#include #include +#include #include #include #include +#include +#include #include #include #include +#include #include /* Returns false if we didn't parse it, and *cursor == NULL if malformed. */ @@ -23,6 +29,12 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) case ADDR_TYPE_IPV6: addr->addrlen = 16; break; + case ADDR_TYPE_TOR_V2: + addr->addrlen = TOR_V2_ADDRLEN; + break; + case ADDR_TYPE_TOR_V3: + addr->addrlen = TOR_V3_ADDRLEN; + break; default: return false; } @@ -96,7 +108,7 @@ bool fromwire_wireaddr_internal(const u8 **cursor, size_t *max, char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a) { - char addrstr[INET6_ADDRSTRLEN]; + char addrstr[FQDN_ADDRLEN]; char *ret, *hex; switch (a->type) { @@ -108,6 +120,12 @@ char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a) if (!inet_ntop(AF_INET6, a->addr, addrstr, INET6_ADDRSTRLEN)) return "Unprintable-ipv6-address"; return tal_fmt(ctx, "[%s]:%u", addrstr, a->port); + case ADDR_TYPE_TOR_V2: + return tal_fmt(ctx, "%s.onion:%u", + b32_encode(addrstr, (u8 *) a->addr, 2), a->port); + case ADDR_TYPE_TOR_V3: + return tal_fmt(ctx, "%s.onion:%u", + b32_encode(addrstr, (u8 *) a->addr, 3), a->port); case ADDR_TYPE_PADDING: break; } @@ -170,6 +188,8 @@ bool wireaddr_is_wildcard(const struct wireaddr *addr) case ADDR_TYPE_IPV4: return memeqzero(addr->addr, addr->addrlen); case ADDR_TYPE_PADDING: + case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V3: return false; } abort(); @@ -190,6 +210,36 @@ char *fmt_wireaddr_internal(const tal_t *ctx, } REGISTER_TYPE_TO_STRING(wireaddr_internal, fmt_wireaddr_internal); +char *fmt_wireaddr_without_port(const tal_t * ctx, const struct wireaddr *a) +{ + char addrstr[FQDN_ADDRLEN]; + char *ret, *hex; + + switch (a->type) { + case ADDR_TYPE_IPV4: + if (!inet_ntop(AF_INET, a->addr, addrstr, INET_ADDRSTRLEN)) + return "Unprintable-ipv4-address"; + return tal_fmt(ctx, "%s", addrstr); + case ADDR_TYPE_IPV6: + if (!inet_ntop(AF_INET6, a->addr, addrstr, INET6_ADDRSTRLEN)) + return "Unprintable-ipv6-address"; + return tal_fmt(ctx, "[%s]", addrstr); + case ADDR_TYPE_TOR_V2: + return tal_fmt(ctx, "%.16s.onion", + b32_encode(addrstr, (u8 *) a->addr, 2)); + case ADDR_TYPE_TOR_V3: + return tal_fmt(ctx, "%.56s.onion", + b32_encode(addrstr, (u8 *) a->addr, 3)); + case ADDR_TYPE_PADDING: + break; + } + + hex = tal_hexstr(ctx, a->addr, a->addrlen); + ret = tal_fmt(ctx, "Unknown type %u %s", a->type, hex); + tal_free(hex); + return ret; +} + /* Valid forms: * * [anything]: @@ -244,8 +294,28 @@ bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname, struct addrinfo *addrinfo; struct addrinfo hints; int gai_err; + u8 tor_dec_bytes[TOR_V3_ADDRLEN]; bool res = false; + /* Don't do lookup on onion addresses. */ + if (strends(hostname, ".onion")) { + if (strlen(hostname) < 25) { //FIXME bool is_V2_or_V3_TOR(addr); + addr->type = ADDR_TYPE_TOR_V2; + addr->addrlen = TOR_V2_ADDRLEN; + addr->port = port; + b32_decode((u8 *) tor_dec_bytes, (u8 *)hostname, 2); + memcpy(&addr->addr, tor_dec_bytes, addr->addrlen); + return true; + } else { + addr->type = ADDR_TYPE_TOR_V3; + addr->addrlen = TOR_V3_ADDRLEN; + addr->port = port; + b32_decode((u8 *) tor_dec_bytes, (u8 *)hostname, 3); + memcpy(&addr->addr, tor_dec_bytes, addr->addrlen); + return true; + } + } + memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; diff --git a/common/wireaddr.h b/common/wireaddr.h index cfffcfb3c..2248f533c 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -30,18 +31,23 @@ struct sockaddr_un; * where `checksum = sha3(".onion checksum" | pubkey || version)[:2]` */ +#define TOR_V2_ADDRLEN 12 +#define TOR_V3_ADDRLEN 37 +#define FQDN_ADDRLEN 255 + enum wire_addr_type { ADDR_TYPE_PADDING = 0, ADDR_TYPE_IPV4 = 1, ADDR_TYPE_IPV6 = 2, + ADDR_TYPE_TOR_V2 = 3, + ADDR_TYPE_TOR_V3 = 4 }; -/* FIXME(cdecker) Extend this once we have defined how TOR addresses - * should look like */ +/* Structure now fit for tor support */ struct wireaddr { enum wire_addr_type type; u8 addrlen; - u8 addr[16]; + u8 addr[TOR_V3_ADDRLEN]; //or FQDN_ADDRLEN ? u16 port; }; @@ -64,6 +70,7 @@ void towire_addr_listen_announce(u8 **pptr, enum addr_listen_announce ala); bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 port, const char **err_msg); char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a); +char *fmt_wireaddr_without_port(const tal_t *ctx, const struct wireaddr *a); bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname, const u16 port, const char **err_msg); diff --git a/doc/TOR.md b/doc/TOR.md new file mode 100644 index 000000000..c5b69191f --- /dev/null +++ b/doc/TOR.md @@ -0,0 +1,146 @@ +HOWTO USE TOR WITH C-LIGHTNING + +what do we support + +1 c-lightning has a public IP address and no TOR hidden service address, + but can connect to an onion address via a TOR socks 5 proxy. + +2 c-lightning has a public IP address and a fixed TOR hidden service address that is persistent + so that external users can connect to this node. + +3 c-lightning has a public IP address and not fixed TOR service address that (changes at each restart + and that vanish at restart of tor) + so that external users can connect to this node by TOR and IP + +4 c-lightning has no public IP address, but has a fixed TOR hidden service address that is persistent + so that external users can connect to this node. + +5 c-lightning has no public IP address, and has no fixed TOR hidden service address + (changes at each restart and vanish at restart of tor) to make it harder to track this node. + +6 c-lightning has a public IP address and a fixed TOR V3 service address and a TOR V2 service address + that (changes at each restart and that vanish at restart of tor) + so that external users can connect to this node by TOR V2 and V3 and IP + +7 c-lightning has nop public IP address and a fixed TOR V3 service address and fixed TOR V2 service address + a 3rd V2 address that (changes at each restart and that vanish at restart of tor) + so that external users can connect to this node by TOR V2 and V3 and a random V2 until next tor release then also (V3 randomly) + +8 c-lightning has a public IP address and no TOR hidden service address, + but can connect to any V4/6 ip address via a IPV4/6 socks 5 proxy. + + +to use tor you have to have tor installed an running. + +i.e. +sudo apt install tor +/etc/init.d/tor start + +if new to tor you might not change the default setting +# The safe default with minimal harassment (See tor FAQ) +ExitPolicy reject *:* # no exits allowed + +this does not effect c-ln connect listen etc. +it will only prevent that you become a full exitpoint +Only enable this if you are sure about the implications. + + +if you want an auto service created +edit the torconfig file /etc/tor/torrc + +set +ControlPort 9051 +CookieAuthentication 1 +CookieAuthFileGroupReadable 1 + +or create a password with + +cmdline +tor --hash-password yourepassword + +this returns an line like +16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F + +put this in the /etc/tor/torrc file + +i.e. +HashedControlPassword 16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F + +save +and +/etc/init.d/tor restart + +then you can use c-lightning with following options + +--tor-service-password=yourpassword to access the tor service at 9051 + +--proxy=127.0.0.1:9050 : set the Tor proxy to use + +or the password for the service if cookiefile is not accessable + +--tor-auto-listen true : try to generate an temp V2 onion addr + +NOTE if --tor-proxy set all traffic will be rooted over the proxy +if --addr is not specified only the auto generated onion addr will be used for your node. + +you can also set a fixed onion addr by option +--addr=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion (V2 or V3 is allowed) + +this addr can be created by + +HiddenServiceDir /var/lib/tor/bitcoin-service_v2/ +HiddenServiceVersion 2 +HiddenServicePort 8333 127.0.0.1:8333 + + +HiddenServiceDir /var/lib/tor/other_hidden_service_v3/ +HiddenServiceVersion 3 +HiddenServicePort 9735 127.0.0.1:9735 + +in /etc/tor/torrc + +the addr for +the --addr option + +you find after /etc/init.d/tor restart + +i.e. +in /var/lib/tor/other_hidden_service_v3/hostname + + +to see your onion addr use +cli/lightning-cli getinfo + +some examples: + +sudo lightningd/lightningd --network=testnet --addr=127.0.0.1 --port=1234 +--proxy=127.0.0.1:9050 --tor-auto-listen true --tor-service=127.0.0.1:9051 + +this will try to generate an V2 auto hidden-service by reading the tor cookie and +also create local ipaddr at port 1234 +so the node is accessableby connect peerid xxxxxxxxxxxxxxxx.onion 1234 +or local by connect ID 127.0.0.1 1234 + +lightningd/lightningd --network=testnet --port=1234 +--proxy=127.0.0.1:9050 --tor-service-password testpassword --tor-auto-listen true --tor-service=127.0.0.1:9051 + +this will try to generate an V2 auto temp hidden-service addr by using the password to access tor service api +so the node accessable by connect peerid xxxxxxxxxxxxxxxxxxx.onion 1234 + + +lightningd/lightningd --network=testnet --port=1234 +--proxy=127.0.0.1:9050 --addr=xxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion --port 1234 + +this will use the hidden-service set by /etc/tor/torrc and use the hidden service +so the node is accessable by connect peerid xxxxxxxxxxxxxxxxxxxxxxxx.onion 1234 +or +lightningd/lightningd --network=testnet --port=1234 +--proxy=127.0.0.1:9050 --addr=xxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion --port 1234 +this will use the hidden-service set by /etc/tor/torrc and use the hidden service +so the node is only accessable by connect peerid xxxxxxxxxxxxxxxxxxxxxxxonion 1234 + +for connects you can use +i.e cli/lightning-cli connect peerID xxxxxxxxxxxxxxxxxxxxxxx.onion 1234 + + + diff --git a/gossipd/Makefile b/gossipd/Makefile index adecacc0a..c9e894c64 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -37,6 +37,7 @@ LIGHTNINGD_HEADERS_GEN += $(LIGHTNINGD_GOSSIP_HEADERS) # Common source we use. GOSSIPD_COMMON_OBJS := \ + common/base32.o \ common/bech32.o \ common/bech32_util.o \ common/bip32.o \ @@ -55,6 +56,7 @@ GOSSIPD_COMMON_OBJS := \ common/status_wire.o \ common/subdaemon.o \ common/timeout.o \ + common/tor.o \ common/type_to_string.o \ common/utils.o \ common/utxo.o \ diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 090e9f196..7c1e24a10 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,176 @@ #include #include +#define GOSSIP_MAX_REACH_ATTEMPTS 10 + +#define SOCKS_NOAUTH 0 +#define SOCKS_ERROR 0xff +#define SOCKS_CONNECT 1 +#define SOCKS_TYP_IPV4 1 +#define SOCKS_DOMAIN 3 +#define SOCKS_TYP_IPV6 4 +#define SOCKS_V5 5 + +#define MAX_SIZE_OF_SOCKS5_REQ_OR_RESP 255 +#define SIZE_OF_RESPONSE 4 +#define SIZE_OF_REQUEST 3 +#define SIZE_OF_IPV4_RESPONSE 6 +#define SIZE_OF_IPV6_RESPONSE 18 +#define SOCK_REQ_METH_LEN 3 +#define SOCK_REQ_V5_LEN 5 +#define SOCK_REQ_V5_HEADER_LEN 7 + +/* some crufts can not forward ipv6*/ +#undef BIND_FIRST_TO_IPV6 + +struct reaching_socks { + + u8 buffer[MAX_SIZE_OF_SOCKS5_REQ_OR_RESP]; + size_t hlen; + in_port_t port; + char *host; + struct reaching *reach; +}; + +static struct io_plan *connect_finish(struct io_conn *, + struct reaching_socks *); + +static struct io_plan *connect_finish2(struct io_conn *, + struct reaching_socks *); + +static struct io_plan *connect_out(struct io_conn *, struct reaching_socks *); + +static struct io_plan *io_tor_connect_after_req_to_connect(struct io_conn *, + struct reaching_socks + *); +static struct io_plan *io_tor_connect_after_req_host(struct io_conn *, + struct reaching_socks *); + +static struct io_plan *io_tor_connect_do_req(struct io_conn *, + struct reaching_socks *); + +static struct io_plan *connect_out(struct io_conn *, struct reaching_socks *); + +static struct io_plan *io_tor_connect_do_req(struct io_conn *, + struct reaching_socks *); + +static struct io_plan *io_tor_connect_after_resp_to_connect(struct io_conn + *conn, + struct + reaching_socks *); + +static struct io_plan *io_tor_connect(struct io_conn *, struct reaching *); + +static struct io_plan *io_tor_connect_after_resp_to_connect(struct io_conn + *conn, + struct + reaching_socks + *reach) +{ + if (reach->buffer[1] == SOCKS_ERROR) { + status_trace("Connected out for %s error", reach->host); + return io_close(conn); + } + /* make the V5 request */ + reach->hlen = strlen(reach->host); + reach->buffer[0] = SOCKS_V5; + reach->buffer[1] = SOCKS_CONNECT; + reach->buffer[2] = 0; + reach->buffer[3] = SOCKS_DOMAIN; + reach->buffer[4] = reach->hlen; + + memcpy(reach->buffer + SOCK_REQ_V5_LEN, reach->host, reach->hlen); + memcpy(reach->buffer + SOCK_REQ_V5_LEN + strlen(reach->host), + &(reach->port), sizeof reach->port); + + return io_write(conn, reach->buffer, + SOCK_REQ_V5_HEADER_LEN + reach->hlen, + io_tor_connect_after_req_host, reach); +} + +static struct io_plan *io_tor_connect_after_req_to_connect(struct io_conn *conn, + struct reaching_socks + *reach) +{ + + return io_read(conn, reach->buffer, 2, + &io_tor_connect_after_resp_to_connect, reach); +} + +static struct io_plan *io_tor_connect_do_req(struct io_conn *conn, + struct reaching_socks *reach) +{ + /* make the init request */ + reach->buffer[0] = SOCKS_V5; + reach->buffer[1] = 1; + reach->buffer[2] = SOCKS_NOAUTH; + + return io_write(conn, reach->buffer, SOCK_REQ_METH_LEN, + &io_tor_connect_after_req_to_connect, reach); +} + +static struct io_plan *connection_out(struct io_conn *conn, + struct reaching *reach); + +static struct io_plan *connect_finish2(struct io_conn *conn, + struct reaching_socks *reach) +{ + status_trace("Now try LN connect out for host %s", reach->host); + return connection_out(conn, reach->reach); +} + +static struct io_plan *connect_finish(struct io_conn *conn, + struct reaching_socks *reach) +{ + + if ( reach->buffer[1] == '\0') { + if ( reach->buffer[3] == SOCKS_TYP_IPV6) { + return io_read(conn, + (reach->buffer + SIZE_OF_RESPONSE - + SIZE_OF_IPV4_RESPONSE), + SIZE_OF_IPV6_RESPONSE - + SIZE_OF_RESPONSE - SIZE_OF_IPV4_RESPONSE, + &connect_finish2, reach); + + } else if ( reach->buffer[3] == SOCKS_TYP_IPV4) { + status_trace("Now try LN connect out for host %s", + reach->host); + return connection_out(conn, reach->reach); + } else { + status_trace + ("Tor connect out for host %s error invalid type return ", + reach->host); + return io_close(conn); + } + } else { + status_trace("Tor connect out for host %s error: %x ", + reach->host, reach->buffer[1]); + return io_close(conn); + } +} + +static struct io_plan *connect_out(struct io_conn *conn, + struct reaching_socks *reach) +{ + return io_read(conn, reach->buffer, + SIZE_OF_IPV4_RESPONSE + SIZE_OF_RESPONSE, + &connect_finish, reach); + +} + +/* called when TOR responds */ +static struct io_plan *io_tor_connect_after_req_host(struct io_conn *conn, + struct reaching_socks + *reach) +{ + if (reach->buffer[0] == '0') { + status_trace("Connected out over tor for %s failed", + reach->host); + return io_close(conn); + } + return connect_out(conn, reach); +} + #define HSM_FD 3 #define INITIAL_WAIT_SECONDS 1 @@ -142,6 +313,9 @@ struct daemon { /* Automatically reconnect. */ bool reconnect; + + struct wireaddr *tor_proxyaddrs; + bool use_tor_proxy_always; }; /* Peers we're trying to reach. */ @@ -230,6 +404,49 @@ static bool send_peer_with_fds(struct peer *peer, const u8 *msg); static void wake_pkt_out(struct peer *peer); static void retry_important(struct important_peerid *imp); +// called when we want to connect to TOR SOCKS5 +static struct io_plan *io_tor_connect(struct io_conn *conn, + struct reaching *reach) +{ + struct addrinfo *ai_tor = tal(reach, struct addrinfo); + char *port_addr = tal(reach, char); + struct io_plan *plan; + struct reaching_socks *reach_tor = tal(reach, struct reaching_socks); + + assert(reach->addr.itype == ADDR_INTERNAL_WIREADDR); + reach_tor->port = htons(reach->addr.u.wireaddr.port); + port_addr = tal_fmt(reach, "%u", reach->daemon->tor_proxyaddrs->port); + getaddrinfo((char *) + fmt_wireaddr_without_port(tmpctx, + reach->daemon->tor_proxyaddrs), + port_addr, NULL, &ai_tor); + status_trace("Tor proxyaddr : %s", + fmt_wireaddr(reach, reach->daemon->tor_proxyaddrs)); + reach_tor->host = tal_strdup(reach, ""); + + if ((reach->addr.u.wireaddr.type) == ADDR_TYPE_TOR_V3) + reach_tor->host = + tal_fmt(reach, "%.62s", + fmt_wireaddr_without_port(tmpctx, &reach->addr.u.wireaddr)); + else if ((reach->addr.u.wireaddr.type) == ADDR_TYPE_TOR_V2) + reach_tor->host = + tal_fmt(reach, "%.22s", + fmt_wireaddr_without_port(tmpctx, &reach->addr.u.wireaddr)); + else if ((reach->addr.u.wireaddr.type) == ADDR_TYPE_IPV4) + reach_tor->host = + tal_fmt(reach, "%s", + fmt_wireaddr_without_port(tmpctx, &reach->addr.u.wireaddr)); + else if ((reach->addr.u.wireaddr.type) == ADDR_TYPE_IPV6) + reach_tor->host = + tal_fmt(reach, "%s", + fmt_wireaddr_without_port(tmpctx, &reach->addr.u.wireaddr)); + reach_tor->reach = reach; + + plan = io_connect(conn, ai_tor, &io_tor_connect_do_req, reach_tor); + + return plan; +} + static void destroy_peer(struct peer *peer) { struct important_peerid *imp; @@ -1605,6 +1822,8 @@ static bool handle_wireaddr_listen(struct daemon *daemon, } return false; case ADDR_TYPE_PADDING: + case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V3: break; } status_failed(STATUS_FAIL_INTERNAL_ERROR, @@ -1730,6 +1949,7 @@ static struct io_plan *gossip_init(struct daemon_conn *master, struct bitcoin_blkid chain_hash; u32 update_channel_interval; bool dev_allow_localhost; + daemon->tor_proxyaddrs = tal_arrz(daemon, struct wireaddr, 1); if (!fromwire_gossipctl_init( daemon, msg, &daemon->broadcast_interval, &chain_hash, @@ -1737,6 +1957,7 @@ static struct io_plan *gossip_init(struct daemon_conn *master, &daemon->localfeatures, &daemon->proposed_wireaddr, &daemon->proposed_listen_announce, daemon->rgb, daemon->alias, &update_channel_interval, &daemon->reconnect, + daemon->tor_proxyaddrs, &daemon->use_tor_proxy_always, &dev_allow_localhost)) { master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } @@ -1876,6 +2097,7 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr_un sun; + bool use_tor = false; /* FIXME: make generic */ ai.ai_flags = 0; @@ -1909,12 +2131,26 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) ai.ai_addrlen = sizeof(sin6); ai.ai_addr = (struct sockaddr *)&sin6; break; + case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V3: + use_tor = true; + break; case ADDR_TYPE_PADDING: /* Shouldn't happen. */ return io_close(conn); } + + if (!use_tor && reach->daemon->tor_proxyaddrs->port > 0) { + /* We dont use tor proxy if we only have ip */ + if (reach->daemon->use_tor_proxy_always + || do_we_use_tor_addr(reach->daemon->announcable)) + use_tor = true; + } } + io_set_finish(conn, connect_failed, reach); + if (use_tor) + return io_tor_connect(conn, reach); return io_connect(conn, &ai, connection_out, reach); } @@ -2046,6 +2282,8 @@ static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, "Can't reach ALLPROTO"); case ADDR_INTERNAL_WIREADDR: switch (a->addr.u.wireaddr.type) { + case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V3: case ADDR_TYPE_IPV4: af = AF_INET; break; diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index d7023b924..4524a3c96 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -18,6 +18,8 @@ gossipctl_init,,rgb,3*u8 gossipctl_init,,alias,32*u8 gossipctl_init,,update_channel_interval,u32 gossipctl_init,,reconnect,bool +gossipctl_init,,tor_proxyaddrs,struct wireaddr +gossipctl_init,,use_tor_proxy_always,bool gossipctl_init,,dev_allow_localhost,bool # Activate the gossip daemon, so others can connect. diff --git a/gossipd/netaddress.c b/gossipd/netaddress.c index 7d3d4b41f..7b695b8c7 100644 --- a/gossipd/netaddress.c +++ b/gossipd/netaddress.c @@ -259,7 +259,10 @@ bool guess_address(struct wireaddr *addr) memcpy(addr->addr, &sin6.sin6_addr, addr->addrlen); return ret; } + case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V3: case ADDR_TYPE_PADDING: + status_broken("Cannot guess address type %u", addr->type); break; } abort(); diff --git a/lightningd/Makefile b/lightningd/Makefile index d925c4c0d..44691a011 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -14,6 +14,7 @@ default: lightningd-all # Common source we use. LIGHTNINGD_COMMON_OBJS := \ + common/base32.o \ common/bech32.o \ common/bech32_util.o \ common/bip32.o \ @@ -43,6 +44,7 @@ LIGHTNINGD_COMMON_OBJS := \ common/type_to_string.o \ common/utils.o \ common/utxo.o \ + common/tor.o \ common/version.o \ common/wallet_tx.o \ common/wire_error.o \ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index ba895748d..6e5dff9c7 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -209,8 +209,9 @@ void gossip_init(struct lightningd *ld) if (!ld->gossip) err(1, "Could not subdaemon gossip"); - /* If no addr specified, hand wildcard to gossipd */ - if (tal_count(wireaddrs) == 0 && ld->autolisten) { + /* If no addr (not even Tor auto) specified, hand wildcard to gossipd */ + if (tal_count(wireaddrs) == 0 && ld->autolisten + && !ld->config.tor_enable_auto_hidden_service) { wireaddrs = tal_arrz(tmpctx, struct wireaddr_internal, 1); listen_announce = tal_arr(tmpctx, enum addr_listen_announce, 1); wireaddrs->itype = ADDR_INTERNAL_ALLPROTO; @@ -225,6 +226,7 @@ void gossip_init(struct lightningd *ld) get_offered_local_features(tmpctx), wireaddrs, listen_announce, ld->rgb, ld->alias, ld->config.channel_update_interval, ld->reconnect, + ld->tor_proxyaddrs, ld->use_tor_proxy_always, allow_localhost); subd_send_msg(ld->gossip, msg); } diff --git a/lightningd/json.c b/lightningd/json.c index b401ca00a..88cadf2e9 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -139,6 +139,14 @@ void json_add_address(struct json_result *response, const char *fieldname, json_add_string(response, "type", "ipv6"); json_add_string(response, "address", addrstr); json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_TOR_V2) { + json_add_string(response, "type", "torv2"); + json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); + json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_TOR_V3) { + json_add_string(response, "type", "torv3"); + json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); + json_add_num(response, "port", addr->port); } json_object_end(response); } diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 5a297563b..ddbbc3662 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -83,7 +84,10 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->pidfile = NULL; ld->ini_autocleaninvoice_cycle = 0; ld->ini_autocleaninvoice_expiredby = 86400; - + ld->tor_service_password = tal_arrz(ld, char, 32); + ld->tor_proxyaddrs = tal_arrz(ld, struct wireaddr,1); + ld->tor_serviceaddrs = tal_arrz(ld, struct wireaddr,1); + ld->use_tor_proxy_always = false; return ld; } @@ -312,6 +316,13 @@ int main(int argc, char *argv[]) /* Ignore SIGPIPE: we look at our write return values*/ signal(SIGPIPE, SIG_IGN); + /* tor support */ + if (ld->config.tor_enable_auto_hidden_service) { + create_tor_hidden_service_conn(ld); + while (!check_return_from_service_call()) + io_loop(NULL, NULL); + } + /* Make sure we can reach other daemons, and versions match. */ test_daemons(ld); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index d711a58b6..afb3259bb 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -76,6 +76,12 @@ struct config { /* Number of blocks to rescan from the current head, or absolute * blockheight if rescan >= 500'000 */ s32 rescan; + + /* tor support */ + bool tor_enable_auto_hidden_service; + + /* ipv6 bind disable */ + bool no_ipv6_bind; }; struct lightningd { @@ -195,6 +201,12 @@ struct lightningd { /* Things we've marked as not leaking. */ const void **notleaks; #endif /* DEVELOPER */ + + /* tor support */ + struct wireaddr *tor_proxyaddrs; + struct wireaddr *tor_serviceaddrs; + char *tor_service_password; + bool use_tor_proxy_always; }; const struct chainparams *get_chainparams(const struct lightningd *ld); diff --git a/lightningd/options.c b/lightningd/options.c index 0f83e1cb1..f877775af 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -295,6 +296,30 @@ static char *opt_set_offline(struct lightningd *ld) return NULL; } +static char *opt_add_torproxy_addr(const char *arg, struct lightningd *ld) +{ + + if (!parse_wireaddr(arg, ld->tor_proxyaddrs,9050,NULL)) { + return tal_fmt(NULL, "Unable to parse Tor proxy address '%s'", arg); + } + return NULL; +} + +static char *opt_add_tor_service_addr(const char *arg, struct lightningd *ld) +{ + + if (!parse_wireaddr(arg, ld->tor_serviceaddrs,9051,NULL)) { + return tal_fmt(NULL, "Unable to parse Tor service address '%s'", arg); + } + return NULL; +} + +static char *opt_add_tor_service_password(const char *arg, struct lightningd *ld) +{ + ld->tor_service_password = tal_fmt(ld, "%.30s", arg); + return NULL; +} + static void config_register_opts(struct lightningd *ld) { opt_register_noarg("--daemon", opt_set_bool, &ld->daemon, @@ -367,7 +392,7 @@ static void config_register_opts(struct lightningd *ld) "Set an IP address (v4 or v6) to listen on, but not announce"); opt_register_arg("--announce-addr", opt_add_announce_addr, NULL, ld, - "Set an IP address (v4 or v6) to announce, but not listen on"); + "Set an IP address (v4 or v6) or .onion v2/v3 to announce, but not listen on"); opt_register_noarg("--offline", opt_set_offline, ld, "Start in offline-mode (do not automatically reconnect and do not accept incoming connections)"); @@ -398,6 +423,16 @@ static void config_register_opts(struct lightningd *ld) opt_set_u64, opt_show_u64, &ld->ini_autocleaninvoice_cycle, "If expired invoice autoclean enabled, invoices that have expired for at least this given seconds are cleaned"); + opt_register_arg("--proxy", opt_add_torproxy_addr, NULL, + ld,"Set a socks v5 proxy IP address and port"); + opt_register_arg("--tor-service",opt_add_tor_service_addr, NULL, + ld,"Set a tor service api IP address and port"); + opt_register_arg("--tor-service-password", opt_add_tor_service_password, NULL, + ld,"Set a Tor hidden service password"); + opt_register_arg("--tor-auto-listen", opt_set_bool_arg, opt_show_bool, + &ld->config.tor_enable_auto_hidden_service , "Generate and use a temp auto hidden-service and show the onion address"); + opt_register_arg("--always-use-tor-proxy", opt_set_bool_arg, opt_show_bool, + &ld->use_tor_proxy_always , "Use the Tor proxy always"); } #if DEVELOPER @@ -474,6 +509,9 @@ static const struct config testnet_config = { /* Rescan 5 hours of blocks on testnet, it's reorg happy */ .rescan = 30, + + /* tor support */ + .tor_enable_auto_hidden_service = false }; /* aka. "Dude, where's my coins?" */ @@ -538,6 +576,9 @@ static const struct config mainnet_config = { /* Rescan 2.5 hours of blocks on startup, it's not so reorg happy */ .rescan = 15, + + + .tor_enable_auto_hidden_service = false }; static void check_config(struct lightningd *ld) @@ -937,6 +978,12 @@ static void add_config(struct lightningd *ld, ld->proposed_listen_announce, ADDR_ANNOUNCE); return; + } else if (opt->cb_arg == (void *)opt_add_torproxy_addr) { + answer = fmt_wireaddr(name0, ld->tor_proxyaddrs); + } else if (opt->cb_arg == (void *)opt_add_tor_service_addr) { + answer = fmt_wireaddr(name0, ld->tor_serviceaddrs); + } else if (opt->cb_arg == (void *)opt_add_tor_service_password) { + answer = tal_fmt(name0, "%s", ld->tor_service_password); #if DEVELOPER } else if (strstarts(name, "dev-")) { /* Ignore dev settings */ diff --git a/lightningd/test/run-find_my_path.c b/lightningd/test/run-find_my_path.c index 5deec9119..a1956bd3f 100644 --- a/lightningd/test/run-find_my_path.c +++ b/lightningd/test/run-find_my_path.c @@ -1,6 +1,8 @@ #define main unused_main int unused_main(int argc, char *argv[]); - +#include "../../common/base32.c" +#include "../../common/wireaddr.c" +#include "../../common/tor.c" #include "../lightningd.c" /* AUTOGENERATED MOCKS START */ diff --git a/openingd/Makefile b/openingd/Makefile index d4842f6bc..27883c375 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -35,6 +35,7 @@ LIGHTNINGD_HEADERS_NOGEN += $(LIGHTNINGD_OPENING_HEADERS_NOGEN) # Common source we use. OPENINGD_COMMON_OBJS := \ + common/base32.o \ common/bip32.o \ common/channel_config.o \ common/crypto_state.o \ diff --git a/wallet/test/Makefile b/wallet/test/Makefile index 3f7c43b6f..8dc3afb08 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -3,6 +3,7 @@ WALLET_TEST_OBJS := $(WALLET_TEST_SRC:.c=.o) WALLET_TEST_PROGRAMS := $(WALLET_TEST_OBJS:.o=) WALLET_TEST_COMMON_OBJS := \ + common/base32.o \ common/htlc_state.o \ common/type_to_string.o \ common/memleak.o \