1 /*======================================================================*
2 * Copyright (C) 2008 Light Weight Event System *
3 * All rights reserved. *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
18 * Boston, MA 02110-1301 USA. *
19 *======================================================================*/
24 #include "xport_udp.h"
29 #include <arpa/inet.h>
30 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #include <sys/types.h>
39 struct in_addr address
;
40 short port
; /* In network byte order. */
43 struct sockaddr_in ip_addr
;
49 int joined_multi_group
;
53 static void destructor (struct xport
* this_xport
)
55 struct priv
* ppriv
= (struct priv
*)this_xport
->priv
;
65 static int xopen (struct xport
* this_xport
, int flags
)
67 char optval
= arg_ttl
; // Configured TTL
68 struct priv
* ppriv
= (struct priv
*)this_xport
->priv
;
69 (void)flags
; /* appease -Wall -Werror */
71 memset(&ppriv
->ip_addr
, 0, sizeof(ppriv
->ip_addr
));
73 ppriv
->ip_addr
.sin_family
= AF_INET
;
74 ppriv
->ip_addr
.sin_addr
= ppriv
->address
;
75 ppriv
->ip_addr
.sin_port
= ppriv
->port
;
77 ppriv
->mreq
.imr_multiaddr
= ppriv
->ip_addr
.sin_addr
;
79 if ( ppriv
->iface
&& ppriv
->iface
[0] )
81 ppriv
->mreq
.imr_interface
.s_addr
= inet_addr(ppriv
->iface
);
85 ppriv
->mreq
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
88 if ( (ppriv
->fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0 )
93 setsockopt(ppriv
->fd
, SOL_IP
, IP_MULTICAST_TTL
, &optval
, 1) ;
95 /* If the interface is specified, then we set the interface. */
96 if ( ppriv
->iface
&& ppriv
->iface
[0] )
100 addr
.s_addr
= inet_addr(ppriv
->iface
);
101 if ( setsockopt(ppriv
->fd
, IPPROTO_IP
, IP_MULTICAST_IF
,
102 &addr
, sizeof(addr
)) < 0 )
104 PERROR("can't set interface");
112 static int xclose (struct xport
* this_xport
)
114 struct priv
* ppriv
= (struct priv
*)this_xport
->priv
;
116 if ( -1 == ppriv
->fd
)
118 LOG_ER("Close transport called with xport already closed.\n");
122 if ( ppriv
->joined_multi_group
)
124 /* remove ourselves from the multicast channel */
125 if ( setsockopt(ppriv
->fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
126 &ppriv
->mreq
, sizeof(ppriv
->mreq
)) < 0 )
128 PERROR("can't drop multicast group");
138 static int xread (struct xport
* this_xport
, void* buf
, size_t count
,
139 unsigned long* addr
, short* port
)
141 struct priv
* ppriv
= (struct priv
*)this_xport
->priv
;
144 struct sockaddr_in sender_ip_addr
;
145 socklen_t sender_ip_socket_size
;
146 struct sockaddr_in ip_addr
;
148 /* Set the size for use below: */
149 sender_ip_socket_size
= (socklen_t
)sizeof(sender_ip_addr
);
151 /* This should be done only with multi-cast addresses. */
156 LOG_PROG("About to join multi-cast group.\n");
158 /* Set address for reuse. */
159 if ( setsockopt(ppriv
->fd
, SOL_SOCKET
, SO_REUSEADDR
,
160 &on
, sizeof(on
)) < 0 )
162 PERROR("setsockopt -- can't reuse address\n");
166 /* Set the receive buffer size. */
167 if ( arg_sockbuffer
)
169 if ( setsockopt(ppriv
->fd
, SOL_SOCKET
, SO_RCVBUF
,
170 (char*)&arg_sockbuffer
, sizeof(arg_sockbuffer
)) < 0 )
172 PERROR("Setsockopt:SO_RCVBUF");
174 /* We'll try to continue with the default buffer size. */
177 /* Bind the socket to the port. */
178 memset(&ip_addr
, 0, sizeof(ip_addr
));
179 ip_addr
.sin_family
= AF_INET
;
180 ip_addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
181 ip_addr
.sin_port
= ppriv
->port
;
183 if ( bind(ppriv
->fd
, (struct sockaddr
*)&ip_addr
, sizeof(ip_addr
) ) < 0 )
185 PERROR("can't bind to local address\n");
190 if ( ppriv
->join_group
&& !ppriv
->joined_multi_group
)
192 /* add the multicast channel given */
193 if ( setsockopt(ppriv
->fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
194 &ppriv
->mreq
, sizeof(ppriv
->mreq
)) < 0 )
196 PERROR("setsockopt can't add to multicast group\n");
199 ppriv
->joined_multi_group
= 1;
203 LOG_PROG("about to call recvfrom().\n");
205 if ( (recvfrom_ret
= recvfrom(ppriv
->fd
, buf
, count
, 0,
206 (struct sockaddr
*)&sender_ip_addr
,
207 &sender_ip_socket_size
)) < 0 )
214 case EINTR
: /* Quiet return on interrupt. */
219 *addr
= sender_ip_addr
.sin_addr
.s_addr
;
220 *port
= ntohs(sender_ip_addr
.sin_port
);
226 static int xwrite (struct xport
* this_xport
, const void* buf
, size_t count
)
228 struct priv
* ppriv
= (struct priv
*)this_xport
->priv
;
230 LOG_PROG("about to call sendto().\n");
232 return sendto (ppriv
->fd
, buf
, count
, 0,
233 (struct sockaddr
*)&ppriv
->ip_addr
, sizeof(ppriv
->ip_addr
));
237 int xport_udp_ctor (struct xport
* this_xport
,
243 static struct xport_vtbl vtbl
= {
252 this_xport
->vtbl
= 0;
253 this_xport
->priv
= 0;
255 ppriv
= (struct priv
*)malloc(sizeof(*ppriv
));
258 LOG_ER("Failed to allocate %d bytes for xport data.\n", sizeof(*ppriv
));
261 memset(ppriv
, 0, sizeof(*ppriv
));
264 if ( 0 == inet_aton(address
, &ppriv
->address
) )
266 LOG_ER("invalid address \"%s\"\n", address
);
271 ppriv
->port
= htons(port
);
273 ppriv
->iface
= strdup(iface
);
274 if ( 0 == ppriv
->iface
)
276 LOG_ER("strdup failed attempting to allocate %d bytes\n", strlen(iface
));
281 ppriv
->join_group
= join
;
283 this_xport
->vtbl
= &vtbl
;
284 this_xport
->priv
= ppriv
;