forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
wire_io.c
146 lines (119 loc) · 3.75 KB
/
wire_io.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* FIXME: io_plan needs size_t */
#include <unistd.h>
#include <ccan/io/io_plan.h>
#include <ccan/mem/mem.h>
#include <ccan/take/take.h>
#include <ccan/tal/tal.h>
#include <errno.h>
#include <wire/wire_io.h>
/*
* 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.
*/
/* 4 bytes for the length header. */
#define HEADER_LEN (sizeof(wire_len_t))
/* We carefully never allow sizes > 64M, so this is an impossible value. */
#define INSIDE_HEADER_BIT WIRE_LEN_LIMIT
/* 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;
/* Length bytes read? Set up for normal read of data. */
if (arg->u2.s == INSIDE_HEADER_BIT + HEADER_LEN) {
arg->u2.s = wirelen_to_cpu(*(wire_len_t *)p);
if (arg->u2.s >= INSIDE_HEADER_BIT) {
errno = E2BIG;
return -1;
}
/* 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;
wire_len_t hdr = cpu_to_wirelen(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 = 0;
return 0;
}
static int do_write_wire(int fd, struct io_plan_arg *arg)
{
ssize_t ret;
size_t totlen = tal_bytelen(arg->u1.cp);
/* 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, totlen - arg->u2.s);
if (ret < 0)
return -1;
arg->u2.s += ret;
if (arg->u2.s != totlen)
return 0;
tal_free(arg->u1.cp);
return 1;
}
/* 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);
if (tal_bytelen(data) >= INSIDE_HEADER_BIT) {
errno = E2BIG;
return io_close(conn);
}
arg->u1.const_vp = tal_dup_arr(conn, u8,
memcheck(data, tal_bytelen(data)),
tal_bytelen(data), 0);
/* 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);
}