632 lines
14 KiB
C
632 lines
14 KiB
C
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <inttypes.h>
|
|
#include <netdb.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <secp256k1.h>
|
|
#include <secp256k1_ecdh.h>
|
|
#include <sodium/crypto_aead_chacha20poly1305.h>
|
|
#include <sodium/randombytes.h>
|
|
|
|
#include "handshake.h"
|
|
#include "error.h"
|
|
#include "crypto.h"
|
|
#include "endian.h"
|
|
#include "bigsize.h"
|
|
#include "compiler.h"
|
|
#include "lnsocket_internal.h"
|
|
#include "lnsocket.h"
|
|
|
|
#define array_len(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
#define MSGBUF_MEM (65536*2)
|
|
#define ERROR_MEM 4096
|
|
#define DEFAULT_TIMEOUT 3000
|
|
|
|
int push_error(struct lnsocket *lnsocket, const char *err);
|
|
|
|
static int char_to_hex(unsigned char *val, char c)
|
|
{
|
|
if (c >= '0' && c <= '9') {
|
|
*val = c - '0';
|
|
return 1;
|
|
}
|
|
if (c >= 'a' && c <= 'f') {
|
|
*val = c - 'a' + 10;
|
|
return 1;
|
|
}
|
|
if (c >= 'A' && c <= 'F') {
|
|
*val = c - 'A' + 10;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hex_decode(const char *str, size_t slen, void *buf, size_t bufsize)
|
|
{
|
|
unsigned char v1, v2;
|
|
unsigned char *p = buf;
|
|
|
|
while (slen > 1) {
|
|
if (!char_to_hex(&v1, str[0]) || !char_to_hex(&v2, str[1]))
|
|
return 0;
|
|
if (!bufsize)
|
|
return 0;
|
|
*(p++) = (v1 << 4) | v2;
|
|
str += 2;
|
|
slen -= 2;
|
|
bufsize--;
|
|
}
|
|
return slen == 0 && bufsize == 0;
|
|
}
|
|
|
|
|
|
int parse_node_id(const char *str, struct node_id *dest)
|
|
{
|
|
return hex_decode(str, strlen(str), dest->k, sizeof(*dest));
|
|
}
|
|
|
|
int pubkey_from_node_id(secp256k1_context *secp, struct pubkey *key,
|
|
const struct node_id *id)
|
|
{
|
|
return secp256k1_ec_pubkey_parse(secp, &key->pubkey,
|
|
memcheck(id->k, sizeof(id->k)),
|
|
sizeof(id->k));
|
|
}
|
|
|
|
|
|
static int read_all(int fd, void *data, size_t size)
|
|
{
|
|
while (size) {
|
|
ssize_t done;
|
|
|
|
done = read(fd, data, size);
|
|
if (done < 0 && errno == EINTR)
|
|
continue;
|
|
if (done <= 0)
|
|
return 0;
|
|
data = (char *)data + done;
|
|
size -= done;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int EXPORT lnsocket_make_default_initmsg(unsigned char *msgbuf, int buflen)
|
|
{
|
|
u8 global_features[2] = {0};
|
|
u8 features[5] = {0};
|
|
u16 len;
|
|
|
|
/*
|
|
struct tlv network_tlv;
|
|
u8 tlvbuf[1024];
|
|
const u8 genesis_block[] = {
|
|
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 0xc1, 0xa6, 0xa2, 0x46,
|
|
0xae, 0x63, 0xf7, 0x4f, 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
|
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
const u8 *blockids[] = { genesis_block };
|
|
|
|
if (!lnsocket_make_network_tlv(tlvbuf, sizeof(tlvbuf), blockids, 1,
|
|
&network_tlv))
|
|
return 0;
|
|
|
|
|
|
const struct tlv *init_tlvs[] = { &network_tlv } ;
|
|
*/
|
|
|
|
const struct tlv *init_tlvs[] = { } ;
|
|
|
|
if (!lnsocket_make_init_msg(msgbuf, buflen,
|
|
global_features, sizeof(global_features),
|
|
features, sizeof(features),
|
|
init_tlvs, 0,
|
|
&len))
|
|
return 0;
|
|
|
|
return (int)len;
|
|
}
|
|
|
|
/*
|
|
static void print_hex(u8 *bytes, int len) {
|
|
int i;
|
|
for (i = 0; i < len; ++i) {
|
|
printf("%02x", bytes[i]);
|
|
}
|
|
}
|
|
*/
|
|
|
|
int lnsocket_perform_init(struct lnsocket *ln)
|
|
{
|
|
u8 msgbuf[1024];
|
|
u16 len;
|
|
u8 *buf;
|
|
|
|
// read the init message from the other side and ignore it
|
|
if (!lnsocket_read(ln, &buf, &len))
|
|
return 0;
|
|
|
|
if (!(len = lnsocket_make_default_initmsg(msgbuf, sizeof(msgbuf))))
|
|
return 0;
|
|
|
|
if (!lnsocket_write(ln, msgbuf, len))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// simple helper that pushes a message type and payload
|
|
int lnsocket_send(struct lnsocket *ln, unsigned short msg_type, const unsigned char *payload, unsigned short payload_len)
|
|
{
|
|
reset_cursor(&ln->msgbuf);
|
|
|
|
if (!cursor_push_u16(&ln->msgbuf, msg_type))
|
|
return note_error(&ln->errs, "could not write type to msgbuf?");
|
|
|
|
if (!cursor_push(&ln->msgbuf, payload, payload_len))
|
|
return note_error(&ln->errs, "payload too big");
|
|
|
|
return lnsocket_write(ln, ln->msgbuf.start, ln->msgbuf.p - ln->msgbuf.start);
|
|
}
|
|
|
|
// simple helper that receives a message type and payload
|
|
int lnsocket_recv(struct lnsocket *ln, u16 *msg_type, unsigned char **payload, u16 *payload_len)
|
|
{
|
|
struct cursor cur;
|
|
u8 *msg;
|
|
u16 msglen;
|
|
|
|
if (!lnsocket_read(ln, &msg, &msglen))
|
|
return 0;
|
|
|
|
make_cursor(msg, msg + msglen, &cur);
|
|
|
|
if (!cursor_pull_u16(&cur, msg_type))
|
|
return note_error(&ln->errs, "could not read msgtype");
|
|
|
|
*payload_len = msglen - 2;
|
|
*payload = cur.p;
|
|
|
|
if (*payload + *payload_len > cur.end)
|
|
return note_error(&ln->errs, "recv buffer overflow?");
|
|
|
|
return 1;
|
|
}
|
|
|
|
int EXPORT lnsocket_decrypt(struct lnsocket *ln, unsigned char *packet, int size)
|
|
{
|
|
struct cursor enc, dec;
|
|
|
|
make_cursor(packet, packet + size, &enc);
|
|
reset_cursor(&ln->msgbuf);
|
|
if (!cursor_slice(&ln->msgbuf, &dec, size - 16))
|
|
return note_error(&ln->errs, "out of memory: %d + %d = %d > %d",
|
|
ln->msgbuf.end - ln->msgbuf.p, size,
|
|
ln->msgbuf.end - ln->msgbuf.p + size,
|
|
MSGBUF_MEM
|
|
);
|
|
|
|
if (!cryptomsg_decrypt_body(&ln->crypto_state,
|
|
enc.start, enc.end - enc.start,
|
|
dec.start, dec.end - dec.start))
|
|
return note_error(&ln->errs, "error decrypting body");
|
|
|
|
return dec.end - dec.start;
|
|
}
|
|
|
|
// this is used in js
|
|
int EXPORT lnsocket_decrypt_header(struct lnsocket *ln, unsigned char *hdr)
|
|
{
|
|
u16 size = 0;
|
|
if (!cryptomsg_decrypt_header(&ln->crypto_state, hdr, &size))
|
|
return note_error(&ln->errs,
|
|
"Failed hdr decrypt with rn=%"PRIu64,
|
|
ln->crypto_state.rn-1);
|
|
return size;
|
|
}
|
|
|
|
int lnsocket_read(struct lnsocket *ln, unsigned char **buf, unsigned short *len)
|
|
{
|
|
struct cursor enc, dec;
|
|
u8 hdr[18];
|
|
u16 size;
|
|
|
|
reset_cursor(&ln->errs.cur);
|
|
reset_cursor(&ln->msgbuf);
|
|
|
|
if (!read_all(ln->socket, hdr, sizeof(hdr)))
|
|
return note_error(&ln->errs,"Failed reading header: %s",
|
|
strerror(errno));
|
|
|
|
if (!cryptomsg_decrypt_header(&ln->crypto_state, hdr, &size))
|
|
return note_error(&ln->errs,
|
|
"Failed hdr decrypt with rn=%"PRIu64,
|
|
ln->crypto_state.rn-1);
|
|
|
|
if (!cursor_slice(&ln->msgbuf, &enc, size + 16))
|
|
return note_error(&ln->errs, "out of memory");
|
|
|
|
if (!cursor_slice(&ln->msgbuf, &dec, size))
|
|
return note_error(&ln->errs, "out of memory: %d + %d = %d > %d",
|
|
ln->msgbuf.end - ln->msgbuf.p, size,
|
|
ln->msgbuf.end - ln->msgbuf.p + size,
|
|
MSGBUF_MEM
|
|
);
|
|
|
|
if (!read_all(ln->socket, enc.p, enc.end - enc.start))
|
|
return note_error(&ln->errs, "Failed reading body: %s",
|
|
strerror(errno));
|
|
|
|
if (!cryptomsg_decrypt_body(&ln->crypto_state,
|
|
enc.start, enc.end - enc.start,
|
|
dec.start, dec.end - dec.start))
|
|
return note_error(&ln->errs, "error decrypting body");
|
|
|
|
*buf = dec.start;
|
|
*len = dec.end - dec.start;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int highest_byte(unsigned char *buf, int buflen)
|
|
{
|
|
int i, highest;
|
|
for (i = 0, highest = 0; i < buflen; i++) {
|
|
if (buf[i] != 0)
|
|
highest = i;
|
|
}
|
|
return highest;
|
|
}
|
|
|
|
#define max(a,b) ((a) > (b) ? (a) : (b))
|
|
int lnsocket_set_feature_bit(unsigned char *buf, int buflen, int *newlen, unsigned int bit)
|
|
{
|
|
if (newlen == NULL)
|
|
return 0;
|
|
|
|
if (bit / 8 >= buflen)
|
|
return 0;
|
|
|
|
*newlen = max(highest_byte(buf, buflen), (bit / 8) + 1);
|
|
buf[*newlen - 1 - bit / 8] |= (1 << (bit % 8));
|
|
|
|
return 1;
|
|
}
|
|
#undef max
|
|
|
|
int cursor_push_tlv(struct cursor *cur, const struct tlv *tlv)
|
|
{
|
|
/* BOLT #1:
|
|
*
|
|
* The sending node:
|
|
...
|
|
* - MUST minimally encode `type` and `length`.
|
|
*/
|
|
return cursor_push_bigsize(cur, tlv->type) &&
|
|
cursor_push_bigsize(cur, tlv->length) &&
|
|
cursor_push(cur, tlv->value, tlv->length);
|
|
}
|
|
|
|
int cursor_push_tlvs(struct cursor *cur, const struct tlv **tlvs, int n_tlvs)
|
|
{
|
|
int i;
|
|
for (i = 0; i < n_tlvs; i++) {
|
|
if (!cursor_push_tlv(cur, tlvs[i]))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int lnsocket_make_network_tlv(unsigned char *buf, int buflen,
|
|
const unsigned char **blockids, int num_blockids,
|
|
struct tlv *tlv_out)
|
|
{
|
|
struct cursor cur;
|
|
|
|
if (!tlv_out)
|
|
return 0;
|
|
|
|
tlv_out->type = 1;
|
|
tlv_out->value = buf;
|
|
|
|
make_cursor(buf, buf + buflen, &cur);
|
|
|
|
for (size_t i = 0; i < num_blockids; i++) {
|
|
if (!cursor_push(&cur, memcheck(blockids[i], 32), 32))
|
|
return 0;
|
|
}
|
|
|
|
tlv_out->length = cur.p - cur.start;
|
|
return 1;
|
|
}
|
|
|
|
int lnsocket_make_ping_msg(unsigned char *buf, int buflen, u16 num_pong_bytes, u16 ignored_bytes)
|
|
{
|
|
struct cursor msg;
|
|
int i;
|
|
|
|
make_cursor(buf, buf + buflen, &msg);
|
|
|
|
if (!cursor_push_u16(&msg, WIRE_PING))
|
|
return 0;
|
|
if (!cursor_push_u16(&msg, num_pong_bytes))
|
|
return 0;
|
|
if (!cursor_push_u16(&msg, ignored_bytes))
|
|
return 0;
|
|
for (i = 0; i < ignored_bytes; i++) {
|
|
if (!cursor_push_byte(&msg, 0))
|
|
return 0;
|
|
}
|
|
|
|
return msg.p - msg.start;
|
|
}
|
|
|
|
int lnsocket_make_init_msg(unsigned char *buf, int buflen,
|
|
const unsigned char *globalfeatures, u16 gflen,
|
|
const unsigned char *features, u16 flen,
|
|
const struct tlv **tlvs,
|
|
unsigned short num_tlvs,
|
|
unsigned short *outlen)
|
|
{
|
|
struct cursor msg;
|
|
|
|
make_cursor(buf, buf + buflen, &msg);
|
|
|
|
if (!cursor_push_u16(&msg, WIRE_INIT))
|
|
return 0;
|
|
|
|
if (!cursor_push_u16(&msg, gflen))
|
|
return 0;
|
|
|
|
if (!cursor_push(&msg, globalfeatures, gflen))
|
|
return 0;
|
|
|
|
if (!cursor_push_u16(&msg, flen))
|
|
return 0;
|
|
|
|
if (!cursor_push(&msg, features, flen))
|
|
return 0;
|
|
|
|
if (!cursor_push_tlvs(&msg, tlvs, num_tlvs))
|
|
return 0;
|
|
|
|
*outlen = msg.p - msg.start;
|
|
|
|
return 1;
|
|
}
|
|
|
|
unsigned char* EXPORT lnsocket_msgbuf(struct lnsocket *ln)
|
|
{
|
|
return ln->msgbuf.start;
|
|
}
|
|
|
|
int EXPORT lnsocket_encrypt(struct lnsocket *ln, const u8 *msg, unsigned short msglen)
|
|
{
|
|
ssize_t outcap;
|
|
size_t outlen;
|
|
|
|
// this is just temporary so we don't need to move the memory cursor
|
|
reset_cursor(&ln->msgbuf);
|
|
|
|
u8 *out = ln->msgbuf.start;
|
|
outcap = ln->msgbuf.end - ln->msgbuf.start;
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
if (!ln->socket)
|
|
return note_error(&ln->errs, "not connected");
|
|
#endif
|
|
|
|
if (outcap <= 0)
|
|
return note_error(&ln->errs, "out of memory");
|
|
|
|
if (!cryptomsg_encrypt_msg(&ln->crypto_state, msg, msglen, out, &outlen, (size_t)outcap))
|
|
return note_error(&ln->errs, "encrypt message failed, out of memory");
|
|
|
|
return outlen;
|
|
}
|
|
|
|
int lnsocket_write(struct lnsocket *ln, const u8 *msg, unsigned short msglen)
|
|
{
|
|
ssize_t writelen, outlen;
|
|
u8 *out = ln->msgbuf.start;
|
|
|
|
if (!(outlen = lnsocket_encrypt(ln, msg, msglen)))
|
|
return 0;
|
|
|
|
if ((writelen = write(ln->socket, out, outlen)) != outlen)
|
|
return note_error(&ln->errs,
|
|
"write failed. wrote %ld bytes, expected %ld %s",
|
|
writelen, outlen,
|
|
writelen < 0 ? strerror(errno) : "");
|
|
|
|
return 1;
|
|
}
|
|
|
|
struct lnsocket *lnsocket_create()
|
|
{
|
|
struct cursor mem;
|
|
int memory = MSGBUF_MEM + ERROR_MEM + sizeof(struct lnsocket);
|
|
|
|
void *arena = malloc(memory);
|
|
|
|
if (!arena)
|
|
return NULL;
|
|
|
|
make_cursor(arena, arena + memory, &mem);
|
|
struct lnsocket *lnsocket = cursor_alloc(&mem, sizeof(*lnsocket));
|
|
|
|
if (!lnsocket)
|
|
return NULL;
|
|
|
|
lnsocket->secp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY |
|
|
SECP256K1_CONTEXT_SIGN);
|
|
|
|
if (!cursor_slice(&mem, &lnsocket->msgbuf, MSGBUF_MEM))
|
|
return NULL;
|
|
|
|
if (!cursor_slice(&mem, &lnsocket->errs.cur, ERROR_MEM))
|
|
return NULL;
|
|
|
|
lnsocket->errs.enabled = 1;
|
|
|
|
lnsocket->mem = mem;
|
|
return lnsocket;
|
|
}
|
|
|
|
void lnsocket_destroy(struct lnsocket *lnsocket)
|
|
{
|
|
if (!lnsocket)
|
|
return;
|
|
|
|
secp256k1_context_destroy(lnsocket->secp);
|
|
free(lnsocket->mem.start);
|
|
}
|
|
|
|
int is_zero(void *vp, int size)
|
|
{
|
|
u8 *p = (u8*)vp;
|
|
const u8 *start = p;
|
|
|
|
for (; p < start+size; p++) {
|
|
if (*p != 0)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int io_fd_block(int fd, int block)
|
|
{
|
|
int flags = fcntl(fd, F_GETFL);
|
|
|
|
if (flags == -1)
|
|
return 0;
|
|
|
|
if (block)
|
|
flags &= ~O_NONBLOCK;
|
|
else
|
|
flags |= O_NONBLOCK;
|
|
|
|
return fcntl(fd, F_SETFL, flags) != -1;
|
|
}
|
|
|
|
const char *parse_port(char *host)
|
|
{
|
|
int i, len;
|
|
|
|
len = strlen(host);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (host[i] == ':') {
|
|
host[i] = 0;
|
|
return &host[i+1];
|
|
}
|
|
}
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int lnsocket_connect_with(struct lnsocket *ln, const char *node_id, const char *host, int timeout_ms)
|
|
{
|
|
int ret;
|
|
struct addrinfo *addrs = NULL;
|
|
struct pubkey their_id;
|
|
struct node_id their_node_id;
|
|
struct timeval timeout = {0};
|
|
char onlyhost[strlen(host)+1];
|
|
strncpy(onlyhost, host, sizeof(onlyhost));
|
|
fd_set set;
|
|
|
|
timeout.tv_sec = timeout_ms / 1000;
|
|
timeout.tv_usec = (timeout_ms % 1000) * 1000;
|
|
|
|
FD_ZERO(&set); /* clear the set */
|
|
|
|
if (is_zero(&ln->key, sizeof(ln->key)))
|
|
return note_error(&ln->errs, "key not initialized, use lnsocket_set_key() or lnsocket_genkey()");
|
|
|
|
// convert node_id string to bytes
|
|
if (!parse_node_id(node_id, &their_node_id))
|
|
return note_error(&ln->errs, "failed to parse node id");
|
|
|
|
// encode node_id bytes to secp pubkey
|
|
if (!pubkey_from_node_id(ln->secp, &their_id, &their_node_id))
|
|
return note_error(&ln->errs, "failed to convert node_id to pubkey");
|
|
|
|
// parse ip into addrinfo
|
|
const char *port = parse_port(onlyhost);
|
|
if ((ret = getaddrinfo(onlyhost, port ? port : "9735", NULL, &addrs)) || !addrs)
|
|
return note_error(&ln->errs, "%s", gai_strerror(ret));
|
|
|
|
// create our network socket for comms
|
|
if (!(ln->socket = socket(AF_INET, SOCK_STREAM, 0)))
|
|
return note_error(&ln->errs, "creating socket failed");
|
|
|
|
FD_SET(ln->socket, &set); /* add our file descriptor to the set */
|
|
|
|
if (!io_fd_block(ln->socket, 0))
|
|
return note_error(&ln->errs, "failed setting socket to non-blocking");
|
|
|
|
// connect to the node!
|
|
connect(ln->socket, addrs->ai_addr, addrs->ai_addrlen);
|
|
|
|
if (!io_fd_block(ln->socket, 1))
|
|
return note_error(&ln->errs, "failed setting socket to blocking");
|
|
|
|
ret = select(ln->socket + 1, NULL, &set, NULL, &timeout);
|
|
if (ret == -1) {
|
|
return note_error(&ln->errs, "select error");
|
|
} else if (ret == 0) {
|
|
return note_error(&ln->errs, "connection timeout");
|
|
}
|
|
|
|
// prepare some data for ACT1
|
|
new_handshake(ln->secp, &ln->handshake, &their_id);
|
|
|
|
ln->handshake.side = INITIATOR;
|
|
ln->handshake.their_id = their_id;
|
|
|
|
// let's do this!
|
|
return act_one_initiator(ln);
|
|
}
|
|
|
|
int lnsocket_connect(struct lnsocket *ln, const char *node_id, const char *host)
|
|
{
|
|
return lnsocket_connect_with(ln, node_id, host, DEFAULT_TIMEOUT);
|
|
}
|
|
|
|
int lnsocket_fd(struct lnsocket *ln, int *fd)
|
|
{
|
|
*fd = ln->socket;
|
|
return 1;
|
|
}
|
|
|
|
void * lnsocket_secp(struct lnsocket *ln)
|
|
{
|
|
return ln->secp;
|
|
}
|
|
|
|
void lnsocket_genkey(struct lnsocket *ln)
|
|
{
|
|
ln->key = generate_key(ln->secp);
|
|
}
|
|
|
|
void lnsocket_print_errors(struct lnsocket *ln)
|
|
{
|
|
print_error_backtrace(&ln->errs);
|
|
}
|
|
|
|
|