diff --git a/wire/Makefile b/wire/Makefile index 493c8fa47..d72b4cba0 100644 --- a/wire/Makefile +++ b/wire/Makefile @@ -4,11 +4,14 @@ wire-wrongdir: $(MAKE) -C .. wire-all -WIRE_HEADERS := wire/wire.h wire/wire_sync.h +WIRE_HEADERS := wire/wire.h \ + wire/wire_sync.h \ + wire/wire_io.h WIRE_GEN_HEADERS := wire/gen_peer_wire.h wire/gen_onion_wire.h WIRE_GEN_SRC := wire/gen_peer_wire.c WIRE_GEN_ONION_SRC := wire/gen_onion_wire.c WIRE_SRC := wire/wire_sync.c \ + wire/wire_io.c \ wire/fromwire.c \ wire/towire.c diff --git a/wire/wire_io.c b/wire/wire_io.c new file mode 100644 index 000000000..bb02780cb --- /dev/null +++ b/wire/wire_io.c @@ -0,0 +1,129 @@ +#include +/* FIXME: io_plan needs size_t */ + #include +#include +#include +#include + +/* + * OK, this is a little tricky. ccan/io lets you create your own plans, + * beyond the standard io_read/io_write etc. It provides a union to place + * scratch data, and it's almost enough for our purposes. + */ + +/* 2 bytes for the length header. */ +#define HEADER_LEN (sizeof(le16)) + +/* Since length can only be 64k, this is an impossible value. */ +#define INSIDE_HEADER_BIT 0x80000000 + +/* arg->u2.s contains length we've read, arg->u1.vp contains u8 **data. */ +static int do_read_wire_header(int fd, struct io_plan_arg *arg) +{ + ssize_t ret; + size_t len = arg->u2.s & ~INSIDE_HEADER_BIT; + u8 *p = *(u8 **)arg->u1.vp; + + ret = read(fd, p + len, HEADER_LEN - len); + if (ret <= 0) + return -1; + arg->u2.s += ret; + + /* Both bytes read? Set up for normal read of data. */ + if (arg->u2.s == INSIDE_HEADER_BIT + HEADER_LEN) { + arg->u2.s = be16_to_cpu(*(be16 *)p); + /* A type-only message is not unheard of, so optimize a little */ + if (arg->u2.s != HEADER_LEN) + tal_resize((u8 **)arg->u1.vp, arg->u2.s); + arg->u1.vp = *(u8 **)arg->u1.vp; + } + + return arg->u2.s == 0; +} + +static int do_read_wire(int fd, struct io_plan_arg *arg) +{ + ssize_t ret; + + /* Still reading header? */ + if (arg->u2.s & INSIDE_HEADER_BIT) + return do_read_wire_header(fd, arg); + + /* Normal read */ + ret = read(fd, arg->u1.cp, arg->u2.s); + if (ret <= 0) + return -1; + + arg->u1.cp += ret; + arg->u2.s -= ret; + return arg->u2.s == 0; +} + +struct io_plan *io_read_wire_(struct io_conn *conn, + const tal_t *ctx, + u8 **data, + struct io_plan *(*next)(struct io_conn *, void *), + void *next_arg) +{ + struct io_plan_arg *arg = io_plan_arg(conn, IO_IN); + + /* We allocate data now; saves storing ctx, and lets us read in len. */ + arg->u1.vp = data; + *data = tal_arr(ctx, u8, HEADER_LEN); + + /* We use u2 to store the length we've read. */ + arg->u2.s = INSIDE_HEADER_BIT; + return io_set_plan(conn, IO_IN, do_read_wire, next, next_arg); +} + +/* arg->u2.s contains length we've written, arg->u1 contains u8 *data. */ +static int do_write_wire_header(int fd, struct io_plan_arg *arg) +{ + ssize_t ret; + size_t len = arg->u2.s & ~INSIDE_HEADER_BIT; + be16 hdr = cpu_to_be16(tal_count(arg->u1.const_vp)); + + ret = write(fd, (char *)&hdr + len, HEADER_LEN - len); + if (ret <= 0) + return -1; + arg->u2.s += ret; + + /* Both bytes written? Set up for normal write of data. */ + if (arg->u2.s == INSIDE_HEADER_BIT + HEADER_LEN) + arg->u2.s = be16_to_cpu(hdr); + + return arg->u2.s == 0; +} + +static int do_write_wire(int fd, struct io_plan_arg *arg) +{ + ssize_t ret; + + /* Still writing header? */ + if (arg->u2.s & INSIDE_HEADER_BIT) + return do_write_wire_header(fd, arg); + + /* Normal write */ + ret = write(fd, arg->u1.cp, arg->u2.s); + if (ret <= 0) + return -1; + + arg->u1.cp += ret; + arg->u2.s -= ret; + return arg->u2.s == 0; +} + +/* Write message from data (tal_count(data) gives length). */ +struct io_plan *io_write_wire_(struct io_conn *conn, + const u8 *data, + struct io_plan *(*next)(struct io_conn *, void *), + void *next_arg) +{ + struct io_plan_arg *arg = io_plan_arg(conn, IO_OUT); + + arg->u1.const_vp = data; + + /* We use u2 to store the length we've written. */ + arg->u2.s = INSIDE_HEADER_BIT; + return io_set_plan(conn, IO_OUT, do_write_wire, next, next_arg); +} diff --git a/wire/wire_io.h b/wire/wire_io.h new file mode 100644 index 000000000..e2b085b13 --- /dev/null +++ b/wire/wire_io.h @@ -0,0 +1,31 @@ +#ifndef LIGHTNING_WIRE_WIRE_IO_H +#define LIGHTNING_WIRE_WIRE_IO_H +#include "config.h" +#include +#include + +/* Read message into *data, allocating off ctx. */ +struct io_plan *io_read_wire_(struct io_conn *conn, + const tal_t *ctx, + u8 **data, + struct io_plan *(*next)(struct io_conn *, void *), + void *next_arg); + +#define io_read_wire(conn, ctx, data, next, arg) \ + io_read_wire_((conn), (ctx), (data), \ + typesafe_cb_preargs(struct io_plan *, void *, \ + (next), (arg), struct io_conn *), \ + (arg)) + +/* Write message from data (tal_count(data) gives length). */ +struct io_plan *io_write_wire_(struct io_conn *conn, + const u8 *data, + struct io_plan *(*next)(struct io_conn *, void *), + void *next_arg); + +#define io_write_wire(conn, data, next, arg) \ + io_write_wire_((conn), (data), \ + typesafe_cb_preargs(struct io_plan *, void *, \ + (next), (arg), struct io_conn *), \ + (arg)) +#endif /* LIGHTNING_WIRE_WIRE_IO_H */