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