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>
20 #include "unaligned.h"
23 static beforepoll_fn udp_beforepoll
;
24 static afterpoll_fn udp_afterpoll
;
25 static comm_request_notify_fn request_notify
;
26 static comm_release_notify_fn release_notify
;
27 static comm_sendmsg_fn udp_sendmsg
;
32 struct notify_list
*next
;
43 struct buffer_if
*rbuf
;
44 struct notify_list
*notify
;
46 struct sockaddr_in proxy
;
49 static int udp_beforepoll(void *state
, struct pollfd
*fds
, int *nfds_io
,
50 int *timeout_io
, const struct timeval
*tv
,
64 static void udp_afterpoll(void *state
, struct pollfd
*fds
, int nfds
,
65 const struct timeval
*tv
, uint64_t *now
)
68 struct sockaddr_in from
;
70 struct notify_list
*n
;
74 if (nfds
&& (fds
->revents
& POLLIN
)) {
77 BUF_ASSERT_FREE(st
->rbuf
);
78 BUF_ALLOC(st
->rbuf
,"udp_afterpoll");
79 rv
=recvfrom(st
->fd
, st
->rbuf
->start
, st
->rbuf
->len
, 0,
80 (struct sockaddr
*)&from
, &fromlen
);
84 /* Check that the packet came from our poxy server;
85 we shouldn't be contacted directly by anybody else
86 (since they can trivially forge source addresses) */
87 if (memcmp(&from
.sin_addr
,&st
->proxy
.sin_addr
,4)!=0 ||
88 memcmp(&from
.sin_port
,&st
->proxy
.sin_port
,2)!=0) {
89 Message(M_INFO
,"udp: received packet that's not "
94 memcpy(&from
.sin_addr
,buf_unprepend(st
->rbuf
,4),4);
95 buf_unprepend(st
->rbuf
,2);
96 memcpy(&from
.sin_port
,buf_unprepend(st
->rbuf
,2),2);
99 for (n
=st
->notify
; n
; n
=n
->next
) {
100 if (n
->fn(n
->state
, st
->rbuf
, &from
)) {
106 uint32_t source
,dest
;
107 /* Manufacture and send NAK packet */
108 source
=get_uint32(st
->rbuf
->start
); /* Us */
109 dest
=get_uint32(st
->rbuf
->start
+4); /* Them */
110 Message(M_INFO
,"udp (port %d): sending NAK\n",st
->port
);
111 buffer_init(st
->rbuf
,0);
112 buf_append_uint32(st
->rbuf
,dest
);
113 buf_append_uint32(st
->rbuf
,source
);
114 buf_append_uint32(st
->rbuf
,0); /* NAK is msg type 0 */
115 sendto(st
->fd
, st
->rbuf
->start
, st
->rbuf
->size
, 0,
116 (struct sockaddr
*)&from
, sizeof(from
));
119 BUF_ASSERT_FREE(st
->rbuf
);
127 static void request_notify(void *commst
, void *nst
, comm_notify_fn
*fn
)
129 struct udp
*st
=commst
;
130 struct notify_list
*n
;
132 n
=safe_malloc(sizeof(*n
),"request_notify");
139 static void release_notify(void *commst
, void *nst
, comm_notify_fn
*fn
)
141 struct udp
*st
=commst
;
142 struct notify_list
*n
, **p
, *t
;
146 for (n
=st
->notify
; n
; )
148 if (n
->state
==nst
&& n
->fn
==fn
) {
160 static bool_t
udp_sendmsg(void *commst
, struct buffer_if
*buf
,
161 struct sockaddr_in
*dest
)
163 struct udp
*st
=commst
;
168 memcpy(sa
,&dest
->sin_addr
,4);
170 memcpy(sa
+6,&dest
->sin_port
,2);
171 sendto(st
->fd
,sa
,buf
->size
+8,0,(struct sockaddr
*)&st
->proxy
,
174 sendto(st
->fd
, buf
->start
, buf
->size
, 0,
175 (struct sockaddr
*)dest
, sizeof(*dest
));
181 static void udp_phase_hook(void *sst
, uint32_t new_phase
)
184 struct sockaddr_in addr
;
186 st
->fd
=socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
188 fatal_perror("udp (%s:%d): socket",st
->loc
.file
,st
->loc
.line
);
190 if (fcntl(st
->fd
, F_SETFL
, fcntl(st
->fd
, F_GETFL
)|O_NONBLOCK
)==-1) {
191 fatal_perror("udp (%s:%d): fcntl(set O_NONBLOCK)",
192 st
->loc
.file
,st
->loc
.line
);
194 if (fcntl(st
->fd
, F_SETFD
, FD_CLOEXEC
)==-1) {
195 fatal_perror("udp (%s:%d): fcntl(set FD_CLOEXEC)",
196 st
->loc
.file
,st
->loc
.line
);
199 memset(&addr
, 0, sizeof(addr
));
200 addr
.sin_family
=AF_INET
;
201 addr
.sin_addr
.s_addr
=htonl(st
->addr
);
202 addr
.sin_port
=htons(st
->port
);
207 /* XXX this fork() and waitpid() business needs to be hidden
208 in some system-specific library functions. */
211 fatal_perror("udp_phase_hook: fork() for authbind");
214 char *argv
[4], addrstr
[9], portstr
[5];
215 sprintf(addrstr
,"%08lX",(long)st
->addr
);
216 sprintf(portstr
,"%04X",st
->port
);
217 argv
[0]=st
->authbind
;
222 execvp(st
->authbind
,argv
);
225 while (waitpid(c
,&status
,0)==-1) {
226 if (errno
==EINTR
) continue;
227 fatal_perror("udp (%s:%d): authbind",st
->loc
.file
,st
->loc
.line
);
229 if (WIFSIGNALED(status
)) {
230 fatal("udp (%s:%d): authbind died on signal %d",st
->loc
.file
,
231 st
->loc
.line
, WTERMSIG(status
));
233 if (WIFEXITED(status
) && WEXITSTATUS(status
)!=0) {
234 fatal("udp (%s:%d): authbind died with status %d",st
->loc
.file
,
235 st
->loc
.line
, WEXITSTATUS(status
));
238 if (bind(st
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
))!=0) {
239 fatal_perror("udp (%s:%d): bind",st
->loc
.file
,st
->loc
.line
);
243 register_for_poll(st
,udp_beforepoll
,udp_afterpoll
,1,"udp");
246 static list_t
*udp_apply(closure_t
*self
, struct cloc loc
, dict_t
*context
,
255 st
=safe_malloc(sizeof(*st
),"udp_apply(st)");
257 st
->cl
.description
="udp";
260 st
->cl
.interface
=&st
->ops
;
262 st
->ops
.min_start_pad
=0;
263 st
->ops
.min_end_pad
=0;
264 st
->ops
.request_notify
=request_notify
;
265 st
->ops
.release_notify
=release_notify
;
266 st
->ops
.sendmsg
=udp_sendmsg
;
271 if (!i
|| i
->type
!=t_dict
) {
272 cfgfatal(st
->loc
,"udp","first argument must be a dictionary\n");
276 j
=dict_find_item(d
,"address",False
,"udp",st
->loc
);
277 st
->addr
=j?st
->addr
=string_item_to_ipaddr(j
, "udp"):INADDR_ANY
;
278 st
->port
=dict_read_number(d
,"port",True
,"udp",st
->loc
,0);
279 st
->rbuf
=find_cl_if(d
,"buffer",CL_BUFFER
,True
,"udp",st
->loc
);
280 st
->authbind
=dict_read_string(d
,"authbind",False
,"udp",st
->loc
);
281 l
=dict_lookup(d
,"proxy");
284 memset(&st
->proxy
,0,sizeof(st
->proxy
));
285 st
->proxy
.sin_family
=AF_INET
;
287 if (!i
|| i
->type
!=t_string
) {
288 cfgfatal(st
->loc
,"udp","proxy must supply ""addr"",port\n");
290 a
=string_item_to_ipaddr(i
,"proxy");
291 st
->proxy
.sin_addr
.s_addr
=htonl(a
);
293 if (!i
|| i
->type
!=t_number
) {
294 cfgfatal(st
->loc
,"udp","proxy must supply ""addr"",port\n");
296 st
->proxy
.sin_port
=htons(i
->data
.number
);
297 st
->ops
.min_start_pad
=8;
300 add_hook(PHASE_GETRESOURCES
,udp_phase_hook
,st
);
302 return new_closure(&st
->cl
);
305 void udp_module(dict_t
*dict
)
307 add_closure(dict
,"udp",udp_apply
);