forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdns.c
222 lines (187 loc) · 5.23 KB
/
dns.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* Async dns helper. */
#include "dns.h"
#include "lightningd.h"
#include "log.h"
#include "peer.h"
#include <assert.h>
#include <ccan/err/err.h>
#include <ccan/read_write_all/read_write_all.h>
#include <ccan/tal/str/str.h>
#include <ccan/tal/tal.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
struct dns_async {
struct lightningd_state *dstate;
struct io_plan *(*init)(struct io_conn *, struct lightningd_state *,
void *);
void (*fail)(struct lightningd_state *, void *arg);
const char *name;
void *arg;
int pid;
size_t num_addresses;
struct netaddr *addresses;
};
/* This runs in the child */
static void lookup_and_write(int fd, const char *name, const char *port)
{
struct addrinfo *addr, *i;
struct netaddr *addresses;
size_t num;
struct addrinfo hints;
/* We don't want UDP sockets (yet?) */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(name, port, &hints, &addr) != 0)
return;
num = 0;
for (i = addr; i; i = i->ai_next)
num++;
addresses = tal_arr(NULL, struct netaddr, num);
num = 0;
for (i = addr; i; i = i->ai_next) {
addresses[num].type = i->ai_socktype;
addresses[num].protocol = i->ai_protocol;
addresses[num].addrlen = i->ai_addrlen;
memset(&addresses[num].saddr, 0, sizeof(addresses[num].saddr));
/* Let parent report this error. */
if (i->ai_addrlen <= sizeof(addresses[num].saddr))
memcpy(&addresses[num].saddr, i->ai_addr, i->ai_addrlen);
num++;
}
if (!num) {
tal_free(addresses);
return;
}
if (write_all(fd, &num, sizeof(num)))
write_all(fd, addresses, num * sizeof(addresses[0]));
tal_free(addresses);
}
static struct io_plan *connected(struct io_conn *conn, struct dns_async *d)
{
struct io_plan *plan;
/* No longer need to try more connections via connect_failed. */
io_set_finish(conn, NULL, NULL);
plan = d->init(conn, d->dstate, d->arg);
tal_free(d);
return plan;
}
static void try_connect_one(struct dns_async *d);
/* If this connection failed, try connecting to another address. */
static void connect_failed(struct io_conn *conn, struct dns_async *d)
{
try_connect_one(d);
}
static struct io_plan *init_conn(struct io_conn *conn, struct dns_async *d)
{
struct addrinfo a;
netaddr_to_addrinfo(&a, &d->addresses[0]);
/* Consume that address. */
d->addresses++;
d->num_addresses--;
io_set_finish(conn, connect_failed, d);
/* That new connection owns d */
return io_connect(conn, &a, connected, d);
}
static void try_connect_one(struct dns_async *d)
{
int fd;
while (d->num_addresses) {
const struct netaddr *a = &d->addresses[0];
/* Now we can warn if it's overlength */
if (a->addrlen > sizeof(a->saddr)) {
log_broken(d->dstate->base_log,
"DNS lookup gave overlength address for %s"
" for family %u, len=%u",
d->name, a->saddr.s.sa_family, a->addrlen);
} else {
/* Might not even be able to create eg. IPv6 sockets */
fd = socket(a->saddr.s.sa_family, a->type, a->protocol);
if (fd >= 0) {
io_new_conn(d->dstate, fd, init_conn, d);
return;
}
}
/* Consume that address. */
d->addresses++;
d->num_addresses--;
}
/* We're out of things to try. Fail. */
d->fail(d->dstate, d->arg);
tal_free(d);
}
static struct io_plan *start_connecting(struct io_conn *conn,
struct dns_async *d)
{
assert(d->num_addresses);
/* OK, we've read all we want, child should exit. */
waitpid(d->pid, NULL, 0);
/* No need to call dns_lookup_failed now. */
io_set_finish(conn, NULL, NULL);
try_connect_one(d);
return io_close(conn);
}
static struct io_plan *read_addresses(struct io_conn *conn, struct dns_async *d)
{
d->addresses = tal_arr(d, struct netaddr, d->num_addresses);
return io_read(conn, d->addresses,
d->num_addresses * sizeof(d->addresses[0]),
start_connecting, d);
}
static struct io_plan *init_dns_conn(struct io_conn *conn, struct dns_async *d)
{
return io_read(conn, &d->num_addresses, sizeof(d->num_addresses),
read_addresses, d);
}
static void dns_lookup_failed(struct io_conn *conn, struct dns_async *d)
{
waitpid(d->pid, NULL, 0);
d->fail(d->dstate, d->arg);
tal_free(d);
}
struct dns_async *dns_resolve_and_connect_(struct lightningd_state *dstate,
const char *name, const char *port,
struct io_plan *(*init)(struct io_conn *,
struct lightningd_state *,
void *arg),
void (*fail)(struct lightningd_state *, void *arg),
void *arg)
{
int pfds[2];
struct dns_async *d = tal(dstate, struct dns_async);
struct io_conn *conn;
d->dstate = dstate;
d->init = init;
d->fail = fail;
d->arg = arg;
d->name = tal_fmt(d, "%s:%s", name, port);
/* First fork child to get addresses. */
if (pipe(pfds) != 0) {
log_unusual(dstate->base_log,
"Creating pipes for dns lookup: %s",
strerror(errno));
return NULL;
}
fflush(stdout);
d->pid = fork();
switch (d->pid) {
case -1:
log_unusual(dstate->base_log, "forking for dns lookup: %s",
strerror(errno));
close(pfds[0]);
close(pfds[1]);
return NULL;
case 0:
close(pfds[0]);
lookup_and_write(pfds[1], name, port);
exit(0);
}
close(pfds[1]);
conn = io_new_conn(dstate, pfds[0], init_dns_conn, d);
io_set_finish(conn, dns_lookup_failed, d);
return d;
}