poll: Abolish max_nfds
[secnet] / udp.c
CommitLineData
2fe58dfd
SE
1/* UDP send/receive module for secnet */
2
3/* This module enables sites to communicate by sending UDP
4 * packets. When an instance of the module is created we can
5 * optionally bind to a particular local IP address (not implemented
6 * yet).
7 *
2fe58dfd
SE
8 * Packets are offered to registered receivers in turn. Once one
9 * accepts it, it isn't offered to any more. */
10
8689b3a9 11#include "secnet.h"
2fe58dfd
SE
12#include <stdio.h>
13#include <unistd.h>
14#include <fcntl.h>
15#include <string.h>
16#include <errno.h>
2fe58dfd 17#include <sys/socket.h>
b2a56f7c 18#include <sys/wait.h>
5edf478f
IJ
19#include <netinet/in.h>
20#include <arpa/inet.h>
2fe58dfd 21#include "util.h"
dd9209d1 22#include "magic.h"
794f2398 23#include "unaligned.h"
ff05a229 24#include "ipaddr.h"
136740e6 25#include "magic.h"
2fe58dfd
SE
26
27static beforepoll_fn udp_beforepoll;
28static afterpoll_fn udp_afterpoll;
29static comm_request_notify_fn request_notify;
30static comm_release_notify_fn release_notify;
31static comm_sendmsg_fn udp_sendmsg;
32
6bad2cd5 33struct comm_notify_entry {
2fe58dfd
SE
34 comm_notify_fn *fn;
35 void *state;
6bad2cd5 36 LIST_ENTRY(comm_notify_entry) entry;
2fe58dfd 37};
6bad2cd5 38LIST_HEAD(comm_notify_list, comm_notify_entry) notify;
2fe58dfd 39
08b62a6c
IJ
40#define MAX_SOCKETS 3 /* 2 ought to do really */
41
42struct udpsock {
43 union iaddr addr;
44 int fd;
45};
46
2fe58dfd
SE
47struct udp {
48 closure_t cl;
49 struct comm_if ops;
50 struct cloc loc;
08b62a6c
IJ
51 int n_socks;
52 struct udpsock socks[MAX_SOCKETS];
b2a56f7c 53 string_t authbind;
2fe58dfd 54 struct buffer_if *rbuf;
6bad2cd5 55 struct comm_notify_list notify;
ff05a229 56 bool_t use_proxy;
a32d56fb 57 union iaddr proxy;
2fe58dfd
SE
58};
59
08b62a6c
IJ
60/*
61 * Re comm_addr.ix: This field allows us to note in the comm_addr
62 * which socket an incoming packet was received on. This is required
63 * for conveniently logging the actual source of a packet. But the ix
64 * does not formally form part of the address: it is not used when
65 * sending, nor when comparing two comm_addrs.
66 *
67 * The special value -1 means that the comm_addr was constructed by
68 * another module in secnet (eg the resolver), rather than being a
69 * description of the source of an incoming packet.
70 */
71
5edf478f
IJ
72static const char *addr_to_string(void *commst, const struct comm_addr *ca) {
73 struct udp *st=commst;
74 static char sbuf[100];
08b62a6c 75 int ix=ca->ix>=0 ? ca->ix : 0;
5edf478f 76
08b62a6c
IJ
77 assert(ix>=0 && ix<st->n_socks);
78 snprintf(sbuf, sizeof(sbuf), "udp:%s%s-%s",
79 iaddr_to_string(&st->socks[ix].addr),
80 ca->ix<0 ? "&" : "",
81 iaddr_to_string(&ca->ia));
5edf478f
IJ
82 return sbuf;
83}
84
2fe58dfd 85static int udp_beforepoll(void *state, struct pollfd *fds, int *nfds_io,
90a39563 86 int *timeout_io)
2fe58dfd 87{
08b62a6c 88 int i;
2fe58dfd 89 struct udp *st=state;
ee697dd9 90 BEFOREPOLL_WANT_FDS(st->n_socks);
08b62a6c
IJ
91 for (i=0; i<st->n_socks; i++) {
92 fds[i].fd=st->socks[i].fd;
93 fds[i].events=POLLIN;
94 }
2fe58dfd
SE
95 return 0;
96}
97
90a39563 98static void udp_afterpoll(void *state, struct pollfd *fds, int nfds)
2fe58dfd
SE
99{
100 struct udp *st=state;
a32d56fb 101 union iaddr from;
b1a0f651 102 socklen_t fromlen;
6bad2cd5 103 struct comm_notify_entry *n;
2fe58dfd
SE
104 bool_t done;
105 int rv;
08b62a6c 106 int i;
2fe58dfd 107
08b62a6c
IJ
108 for (i=0; i<st->n_socks; i++) {
109 if (i>=nfds) continue;
110 if (!(fds[i].revents & POLLIN)) continue;
111 assert(fds[i].fd == st->socks[i].fd);
112 int fd=st->socks[i].fd;
2fe58dfd
SE
113 do {
114 fromlen=sizeof(from);
115 BUF_ASSERT_FREE(st->rbuf);
116 BUF_ALLOC(st->rbuf,"udp_afterpoll");
6af9a984 117 buffer_init(st->rbuf,calculate_max_start_pad());
08b62a6c 118 rv=recvfrom(fd, st->rbuf->start,
92795040 119 buf_remaining_space(st->rbuf),
a32d56fb 120 0, &from.sa, &fromlen);
2fe58dfd
SE
121 if (rv>0) {
122 st->rbuf->size=rv;
ff05a229
SE
123 if (st->use_proxy) {
124 /* Check that the packet came from our poxy server;
125 we shouldn't be contacted directly by anybody else
126 (since they can trivially forge source addresses) */
a32d56fb 127 if (!iaddr_equal(&from,&st->proxy)) {
ff05a229
SE
128 Message(M_INFO,"udp: received packet that's not "
129 "from the proxy\n");
130 BUF_FREE(st->rbuf);
131 continue;
132 }
a32d56fb
IJ
133 /* proxy protocol supports ipv4 transport only */
134 from.sa.sa_family=AF_INET;
135 memcpy(&from.sin.sin_addr,buf_unprepend(st->rbuf,4),4);
ff05a229 136 buf_unprepend(st->rbuf,2);
a32d56fb 137 memcpy(&from.sin.sin_port,buf_unprepend(st->rbuf,2),2);
ff05a229 138 }
8534d602 139 struct comm_addr ca;
8534d602 140 ca.comm=&st->ops;
a32d56fb 141 ca.ia=from;
08b62a6c 142 ca.ix=i;
2fe58dfd 143 done=False;
6bad2cd5 144 LIST_FOREACH(n, &st->notify, entry) {
a15faeb2 145 if (n->fn(n->state, st->rbuf, &ca)) {
2fe58dfd
SE
146 done=True;
147 break;
148 }
149 }
150 if (!done) {
bf28fc73
IJ
151 uint32_t msgtype;
152 if (st->rbuf->size>12 /* prevents traffic amplification */
153 && ((msgtype=get_uint32(st->rbuf->start+8))
154 != LABEL_NAK)) {
155 uint32_t source,dest;
156 /* Manufacture and send NAK packet */
157 source=get_uint32(st->rbuf->start); /* Us */
158 dest=get_uint32(st->rbuf->start+4); /* Them */
8534d602 159 send_nak(&ca,source,dest,msgtype,st->rbuf,"unwanted");
bf28fc73 160 }
2fe58dfd
SE
161 BUF_FREE(st->rbuf);
162 }
163 BUF_ASSERT_FREE(st->rbuf);
164 } else {
165 BUF_FREE(st->rbuf);
166 }
167 } while (rv>=0);
168 }
169}
170
171static void request_notify(void *commst, void *nst, comm_notify_fn *fn)
172{
173 struct udp *st=commst;
6bad2cd5 174 struct comm_notify_entry *n;
2fe58dfd
SE
175
176 n=safe_malloc(sizeof(*n),"request_notify");
177 n->fn=fn;
178 n->state=nst;
6bad2cd5 179 LIST_INSERT_HEAD(&st->notify, n, entry);
2fe58dfd
SE
180}
181
182static void release_notify(void *commst, void *nst, comm_notify_fn *fn)
183{
184 struct udp *st=commst;
6bad2cd5 185 struct comm_notify_entry *n, *t;
2fe58dfd
SE
186
187 /* XXX untested */
6bad2cd5 188 LIST_FOREACH_SAFE(n, &st->notify, entry, t) {
2fe58dfd 189 if (n->state==nst && n->fn==fn) {
6bad2cd5
IJ
190 LIST_REMOVE(n, entry);
191 free(n);
2fe58dfd
SE
192 }
193 }
194}
195
196static bool_t udp_sendmsg(void *commst, struct buffer_if *buf,
a15faeb2 197 const struct comm_addr *dest)
2fe58dfd
SE
198{
199 struct udp *st=commst;
ff05a229 200 uint8_t *sa;
2fe58dfd 201
ff05a229 202 if (st->use_proxy) {
3abd18e8 203 sa=buf_prepend(buf,8);
a32d56fb
IJ
204 if (dest->ia.sa.sa_family != AF_INET) {
205 Message(M_INFO,
206 "udp: proxy means dropping outgoing non-IPv4 packet to %s\n",
207 iaddr_to_string(&dest->ia));
208 return False;
209 }
210 memcpy(sa,&dest->ia.sin.sin_addr,4);
ff05a229 211 memset(sa+4,0,4);
a32d56fb 212 memcpy(sa+6,&dest->ia.sin.sin_port,2);
08b62a6c 213 sendto(st->socks[0].fd,sa,buf->size+8,0,&st->proxy.sa,
a32d56fb 214 iaddr_socklen(&st->proxy));
3abd18e8 215 buf_unprepend(buf,8);
ff05a229 216 } else {
08b62a6c
IJ
217 int i,r;
218 bool_t allunsupported=True;
219 for (i=0; i<st->n_socks; i++) {
220 if (dest->ia.sa.sa_family != st->socks[i].addr.sa.sa_family)
221 /* no point even trying */
222 continue;
223 r=sendto(st->socks[i].fd, buf->start, buf->size, 0,
224 &dest->ia.sa, iaddr_socklen(&dest->ia));
225 if (r>=0) return True;
226 if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH))
227 /* who knows what that error means? */
228 allunsupported=False;
229 }
230 return !allunsupported; /* see doc for comm_sendmsg_fn in secnet.h */
ff05a229 231 }
2fe58dfd
SE
232
233 return True;
234}
235
08b62a6c 236static void udp_make_socket(struct udp *st, struct udpsock *us)
baa06aeb 237{
f164f167 238 const union iaddr *addr=&us->addr;
08b62a6c 239 us->fd=socket(addr->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
f164f167 240 if (us->fd<0) {
baa06aeb
SE
241 fatal_perror("udp (%s:%d): socket",st->loc.file,st->loc.line);
242 }
f164f167 243 if (fcntl(us->fd, F_SETFL, fcntl(us->fd, F_GETFL)|O_NONBLOCK)==-1) {
baa06aeb
SE
244 fatal_perror("udp (%s:%d): fcntl(set O_NONBLOCK)",
245 st->loc.file,st->loc.line);
246 }
f164f167 247 setcloexec(us->fd);
08b62a6c
IJ
248#ifdef CONFIG_IPV6
249 if (addr->sa.sa_family==AF_INET6) {
250 int r;
251 int optval=1;
252 socklen_t optlen=sizeof(optval);
253 r=setsockopt(us->fd,IPPROTO_IPV6,IPV6_V6ONLY,&optval,optlen);
254 if (r) fatal_perror("udp (%s:%d): setsockopt(,IPV6_V6ONLY,&1,)",
255 st->loc.file,st->loc.line);
256 }
257#endif
baa06aeb 258
b2a56f7c
SE
259 if (st->authbind) {
260 pid_t c;
261 int status;
262
263 /* XXX this fork() and waitpid() business needs to be hidden
264 in some system-specific library functions. */
265 c=fork();
266 if (c==-1) {
267 fatal_perror("udp_phase_hook: fork() for authbind");
268 }
269 if (c==0) {
3ff31eda
IJ
270 char *argv[5], addrstr[33], portstr[5];
271 const char *addrfam;
272 int port;
f164f167 273 switch (addr->sa.sa_family) {
a32d56fb 274 case AF_INET:
f164f167 275 sprintf(addrstr,"%08lX",(long)addr->sin.sin_addr.s_addr);
3ff31eda
IJ
276 port=addr->sin.sin_port;
277 addrfam=NULL;
a32d56fb 278 break;
3ff31eda
IJ
279#ifdef CONFIG_IPV6
280 case AF_INET6: {
281 int i;
282 for (i=0; i<16; i++)
283 sprintf(addrstr+i*2,"%02X",addr->sin6.sin6_addr.s6_addr[i]);
284 port=addr->sin6.sin6_port;
285 addrfam="6";
286 break;
287 }
288#endif /*CONFIG_IPV6*/
a32d56fb
IJ
289 default:
290 fatal("udp (%s:%d): unsupported address family for authbind",
291 st->loc.file,st->loc.line);
292 }
3ff31eda 293 sprintf(portstr,"%04X",port);
b2a56f7c 294 argv[0]=st->authbind;
3b83c932
SE
295 argv[1]=addrstr;
296 argv[2]=portstr;
3ff31eda
IJ
297 argv[3]=(char*)addrfam;
298 argv[4]=NULL;
f164f167 299 dup2(us->fd,0);
b2a56f7c 300 execvp(st->authbind,argv);
3b83c932 301 _exit(255);
b2a56f7c 302 }
3b83c932
SE
303 while (waitpid(c,&status,0)==-1) {
304 if (errno==EINTR) continue;
b2a56f7c
SE
305 fatal_perror("udp (%s:%d): authbind",st->loc.file,st->loc.line);
306 }
3b83c932
SE
307 if (WIFSIGNALED(status)) {
308 fatal("udp (%s:%d): authbind died on signal %d",st->loc.file,
309 st->loc.line, WTERMSIG(status));
310 }
311 if (WIFEXITED(status) && WEXITSTATUS(status)!=0) {
312 fatal("udp (%s:%d): authbind died with status %d",st->loc.file,
313 st->loc.line, WEXITSTATUS(status));
314 }
b2a56f7c 315 } else {
08b62a6c 316 if (bind(us->fd, &addr->sa, iaddr_socklen(addr))!=0) {
b2a56f7c
SE
317 fatal_perror("udp (%s:%d): bind",st->loc.file,st->loc.line);
318 }
baa06aeb 319 }
f164f167 320}
baa06aeb 321
f164f167
IJ
322static void udp_phase_hook(void *sst, uint32_t new_phase)
323{
324 struct udp *st=sst;
08b62a6c
IJ
325 int i;
326 for (i=0; i<st->n_socks; i++)
327 udp_make_socket(st,&st->socks[i]);
328
32fc582f 329 register_for_poll(st,udp_beforepoll,udp_afterpoll,"udp");
baa06aeb
SE
330}
331
2fe58dfd
SE
332static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context,
333 list_t *args)
334{
335 struct udp *st;
c649eafe 336 item_t *item;
08b62a6c 337 list_t *caddrl;
2fe58dfd 338 dict_t *d;
ff05a229
SE
339 list_t *l;
340 uint32_t a;
08b62a6c 341 int i;
2fe58dfd
SE
342
343 st=safe_malloc(sizeof(*st),"udp_apply(st)");
344 st->loc=loc;
345 st->cl.description="udp";
346 st->cl.type=CL_COMM;
347 st->cl.apply=NULL;
348 st->cl.interface=&st->ops;
349 st->ops.st=st;
350 st->ops.request_notify=request_notify;
351 st->ops.release_notify=release_notify;
352 st->ops.sendmsg=udp_sendmsg;
5edf478f 353 st->ops.addr_to_string=addr_to_string;
ff05a229 354 st->use_proxy=False;
6bad2cd5 355 LIST_INIT(&st->notify);
2fe58dfd 356
c649eafe
IJ
357 item=list_elem(args,0);
358 if (!item || item->type!=t_dict) {
2fe58dfd
SE
359 cfgfatal(st->loc,"udp","first argument must be a dictionary\n");
360 }
c649eafe 361 d=item->data.dict;
2fe58dfd 362
08b62a6c
IJ
363 int port=dict_read_number(d,"port",True,"udp",st->loc,0);
364
365 union iaddr defaultaddrs[] = {
366#ifdef CONFIG_IPV6
367 { .sin6 = { .sin6_family=AF_INET6,
368 .sin6_port=htons(port),
369 .sin6_addr=IN6ADDR_ANY_INIT } },
370#endif
371 { .sin = { .sin_family=AF_INET,
372 .sin_port=htons(port),
373 .sin_addr= { .s_addr=INADDR_ANY } } }
374 };
375
376 caddrl=dict_lookup(d,"address");
377 st->n_socks=caddrl ? list_length(caddrl) : (int)ARRAY_SIZE(defaultaddrs);
378 if (st->n_socks<=0 || st->n_socks>MAX_SOCKETS)
379 cfgfatal(st->loc,"udp","`address' must be 1..%d addresses",
380 MAX_SOCKETS);
381
382 for (i=0; i<st->n_socks; i++) {
383 struct udpsock *us=&st->socks[i];
384 if (!list_length(caddrl)) {
385 us->addr=defaultaddrs[i];
386 } else {
387 string_item_to_iaddr(list_elem(caddrl,i),port,&us->addr,"udp");
388 }
389 us->fd=-1;
390 }
391
2fe58dfd 392 st->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,"udp",st->loc);
b2a56f7c 393 st->authbind=dict_read_string(d,"authbind",False,"udp",st->loc);
ff05a229
SE
394 l=dict_lookup(d,"proxy");
395 if (l) {
396 st->use_proxy=True;
a32d56fb 397 st->proxy.sa.sa_family=AF_INET;
c649eafe
IJ
398 item=list_elem(l,0);
399 if (!item || item->type!=t_string) {
ff05a229
SE
400 cfgfatal(st->loc,"udp","proxy must supply ""addr"",port\n");
401 }
c649eafe 402 a=string_item_to_ipaddr(item,"proxy");
a32d56fb 403 st->proxy.sin.sin_addr.s_addr=htonl(a);
c649eafe
IJ
404 item=list_elem(l,1);
405 if (!item || item->type!=t_number) {
ff05a229
SE
406 cfgfatal(st->loc,"udp","proxy must supply ""addr"",port\n");
407 }
c649eafe 408 st->proxy.sin.sin_port=htons(item->data.number);
ff05a229 409 }
2fe58dfd 410
3abd18e8
IJ
411 update_max_start_pad(&comm_max_start_pad, st->use_proxy ? 8 : 0);
412
baa06aeb 413 add_hook(PHASE_GETRESOURCES,udp_phase_hook,st);
2fe58dfd
SE
414
415 return new_closure(&st->cl);
416}
417
2fe58dfd
SE
418void udp_module(dict_t *dict)
419{
420 add_closure(dict,"udp",udp_apply);
421}