1 /* UDP send/receive module for secnet */
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
8 * Packets are offered to registered receivers in turn. Once one
9 * accepts it, it isn't offered to any more. */
17 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
23 #include "unaligned.h"
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
;
36 struct notify_list
*next
;
46 struct buffer_if
*rbuf
;
47 struct notify_list
*notify
;
52 static const char *addr_to_string(void *commst
, const struct comm_addr
*ca
) {
53 struct udp
*st
=commst
;
54 static char sbuf
[100];
56 snprintf(sbuf
, sizeof(sbuf
), "udp:%s-%s",
57 iaddr_to_string(&st
->addr
), iaddr_to_string(&ca
->ia
));
61 static int udp_beforepoll(void *state
, struct pollfd
*fds
, int *nfds_io
,
75 static void udp_afterpoll(void *state
, struct pollfd
*fds
, int nfds
)
80 struct notify_list
*n
;
84 if (nfds
&& (fds
->revents
& POLLIN
)) {
87 BUF_ASSERT_FREE(st
->rbuf
);
88 BUF_ALLOC(st
->rbuf
,"udp_afterpoll");
89 buffer_init(st
->rbuf
,calculate_max_start_pad());
90 rv
=recvfrom(st
->fd
, st
->rbuf
->start
,
91 buf_remaining_space(st
->rbuf
),
92 0, &from
.sa
, &fromlen
);
96 /* Check that the packet came from our poxy server;
97 we shouldn't be contacted directly by anybody else
98 (since they can trivially forge source addresses) */
99 if (!iaddr_equal(&from
,&st
->proxy
)) {
100 Message(M_INFO
,"udp: received packet that's not "
105 /* proxy protocol supports ipv4 transport only */
106 from
.sa
.sa_family
=AF_INET
;
107 memcpy(&from
.sin
.sin_addr
,buf_unprepend(st
->rbuf
,4),4);
108 buf_unprepend(st
->rbuf
,2);
109 memcpy(&from
.sin
.sin_port
,buf_unprepend(st
->rbuf
,2),2);
115 for (n
=st
->notify
; n
; n
=n
->next
) {
116 if (n
->fn(n
->state
, st
->rbuf
, &ca
)) {
123 if (st
->rbuf
->size
>12 /* prevents traffic amplification */
124 && ((msgtype
=get_uint32(st
->rbuf
->start
+8))
126 uint32_t source
,dest
;
127 /* Manufacture and send NAK packet */
128 source
=get_uint32(st
->rbuf
->start
); /* Us */
129 dest
=get_uint32(st
->rbuf
->start
+4); /* Them */
130 send_nak(&ca
,source
,dest
,msgtype
,st
->rbuf
,"unwanted");
134 BUF_ASSERT_FREE(st
->rbuf
);
142 static void request_notify(void *commst
, void *nst
, comm_notify_fn
*fn
)
144 struct udp
*st
=commst
;
145 struct notify_list
*n
;
147 n
=safe_malloc(sizeof(*n
),"request_notify");
154 static void release_notify(void *commst
, void *nst
, comm_notify_fn
*fn
)
156 struct udp
*st
=commst
;
157 struct notify_list
*n
, **p
, *t
;
161 for (n
=st
->notify
; n
; )
163 if (n
->state
==nst
&& n
->fn
==fn
) {
175 static bool_t
udp_sendmsg(void *commst
, struct buffer_if
*buf
,
176 const struct comm_addr
*dest
)
178 struct udp
*st
=commst
;
182 sa
=buf_prepend(buf
,8);
183 if (dest
->ia
.sa
.sa_family
!= AF_INET
) {
185 "udp: proxy means dropping outgoing non-IPv4 packet to %s\n",
186 iaddr_to_string(&dest
->ia
));
189 memcpy(sa
,&dest
->ia
.sin
.sin_addr
,4);
191 memcpy(sa
+6,&dest
->ia
.sin
.sin_port
,2);
192 sendto(st
->fd
,sa
,buf
->size
+8,0,&st
->proxy
.sa
,
193 iaddr_socklen(&st
->proxy
));
194 buf_unprepend(buf
,8);
196 sendto(st
->fd
, buf
->start
, buf
->size
, 0,
197 &dest
->ia
.sa
, iaddr_socklen(&dest
->ia
));
203 static void udp_make_socket(struct udp
*st
, struct udp
*us
)
205 const union iaddr
*addr
=&us
->addr
;
206 us
->fd
=socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
208 fatal_perror("udp (%s:%d): socket",st
->loc
.file
,st
->loc
.line
);
210 if (fcntl(us
->fd
, F_SETFL
, fcntl(us
->fd
, F_GETFL
)|O_NONBLOCK
)==-1) {
211 fatal_perror("udp (%s:%d): fcntl(set O_NONBLOCK)",
212 st
->loc
.file
,st
->loc
.line
);
220 /* XXX this fork() and waitpid() business needs to be hidden
221 in some system-specific library functions. */
224 fatal_perror("udp_phase_hook: fork() for authbind");
227 char *argv
[4], addrstr
[9], portstr
[5];
228 switch (addr
->sa
.sa_family
) {
230 sprintf(addrstr
,"%08lX",(long)addr
->sin
.sin_addr
.s_addr
);
231 sprintf(portstr
,"%04X",addr
->sin
.sin_port
);
234 fatal("udp (%s:%d): unsupported address family for authbind",
235 st
->loc
.file
,st
->loc
.line
);
237 argv
[0]=st
->authbind
;
242 execvp(st
->authbind
,argv
);
245 while (waitpid(c
,&status
,0)==-1) {
246 if (errno
==EINTR
) continue;
247 fatal_perror("udp (%s:%d): authbind",st
->loc
.file
,st
->loc
.line
);
249 if (WIFSIGNALED(status
)) {
250 fatal("udp (%s:%d): authbind died on signal %d",st
->loc
.file
,
251 st
->loc
.line
, WTERMSIG(status
));
253 if (WIFEXITED(status
) && WEXITSTATUS(status
)!=0) {
254 fatal("udp (%s:%d): authbind died with status %d",st
->loc
.file
,
255 st
->loc
.line
, WEXITSTATUS(status
));
258 if (bind(st
->fd
, &addr
->sa
, iaddr_socklen(addr
))!=0) {
259 fatal_perror("udp (%s:%d): bind",st
->loc
.file
,st
->loc
.line
);
264 static void udp_phase_hook(void *sst
, uint32_t new_phase
)
267 udp_make_socket(st
,st
);
268 register_for_poll(st
,udp_beforepoll
,udp_afterpoll
,1,"udp");
271 static list_t
*udp_apply(closure_t
*self
, struct cloc loc
, dict_t
*context
,
280 st
=safe_malloc(sizeof(*st
),"udp_apply(st)");
282 st
->cl
.description
="udp";
285 st
->cl
.interface
=&st
->ops
;
287 st
->ops
.request_notify
=request_notify
;
288 st
->ops
.release_notify
=release_notify
;
289 st
->ops
.sendmsg
=udp_sendmsg
;
290 st
->ops
.addr_to_string
=addr_to_string
;
294 if (!i
|| i
->type
!=t_dict
) {
295 cfgfatal(st
->loc
,"udp","first argument must be a dictionary\n");
299 st
->addr
.sa
.sa_family
=AF_INET
;
300 j
=dict_find_item(d
,"address",False
,"udp",st
->loc
);
301 st
->addr
.sin
.sin_addr
.s_addr
=j?
string_item_to_ipaddr(j
, "udp"):INADDR_ANY
;
302 st
->addr
.sin
.sin_port
=dict_read_number(d
,"port",True
,"udp",st
->loc
,0);
303 st
->rbuf
=find_cl_if(d
,"buffer",CL_BUFFER
,True
,"udp",st
->loc
);
304 st
->authbind
=dict_read_string(d
,"authbind",False
,"udp",st
->loc
);
305 l
=dict_lookup(d
,"proxy");
308 st
->proxy
.sa
.sa_family
=AF_INET
;
310 if (!i
|| i
->type
!=t_string
) {
311 cfgfatal(st
->loc
,"udp","proxy must supply ""addr"",port\n");
313 a
=string_item_to_ipaddr(i
,"proxy");
314 st
->proxy
.sin
.sin_addr
.s_addr
=htonl(a
);
316 if (!i
|| i
->type
!=t_number
) {
317 cfgfatal(st
->loc
,"udp","proxy must supply ""addr"",port\n");
319 st
->proxy
.sin
.sin_port
=htons(i
->data
.number
);
322 update_max_start_pad(&comm_max_start_pad
, st
->use_proxy ?
8 : 0);
324 add_hook(PHASE_GETRESOURCES
,udp_phase_hook
,st
);
326 return new_closure(&st
->cl
);
329 void udp_module(dict_t
*dict
)
331 add_closure(dict
,"udp",udp_apply
);