1 /* @(#) implementation of network operations for udpxy
3 * Copyright 2008-2011 Pavel V. Cherenkov (pcherenkov@gmail.com)
5 * This file is part of udpxy.
7 * udpxy is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * udpxy is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with udpxy. If not, see <http://www.gnu.org/licenses/>.
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <arpa/inet.h>
24 #include <netinet/in.h>
41 extern FILE* g_flog
; /* application log */
44 /* set up (server) listening sockfd
47 setup_listener( const char* ipaddr
, int port
, int* sockfd
, int bklog
)
49 #define LOWMARK 10 /* do not accept input of less than X octets */
50 int rc
, lsock
, wmark
= LOWMARK
;
51 struct sockaddr_in servaddr
;
54 extern const char IPv4_ALL
[];
56 assert( (port
> 0) && sockfd
&& ipaddr
);
58 TRACE( (void)tmfprintf( g_flog
, "Setting up listener for [%s:%d]\n",
59 ipaddr
[0] ? ipaddr
: IPv4_ALL
, port
) );
63 lsock
= socket( AF_INET
, SOCK_STREAM
, 0 );
64 if( -1 == lsock
) break;
66 (void) memset( &servaddr
, 0, sizeof(servaddr
) );
67 servaddr
.sin_family
= AF_INET
;
68 servaddr
.sin_port
= htons( (short)port
);
70 if( '\0' != ipaddr
[0] ) {
71 if( 1 != inet_aton(ipaddr
, &servaddr
.sin_addr
) ) {
72 TRACE( (void)tmfprintf( g_flog
, "Invalid server IP: [%s]\n",
80 servaddr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
83 rc
= setsockopt( lsock
, SOL_SOCKET
, SO_REUSEADDR
,
86 mperror(g_flog
, errno
, "%s: setsockopt SO_REUSEADDR",
92 rc
= set_nblock (lsock
, NONBLOCK
);
95 TRACE( (void)tmfprintf (g_flog
, "Setting low watermark for "
96 "server socket [%d] to [%d]\n", lsock
, wmark
) );
97 rc
= setsockopt (lsock
, SOL_SOCKET
, SO_RCVLOWAT
,
98 (char*)&wmark
, sizeof(wmark
));
100 mperror (g_flog
, errno
, "%s: setsockopt SO_RCVLOWAT",
105 rc
= bind( lsock
, (struct sockaddr
*)&servaddr
, sizeof(servaddr
) );
108 rc
= listen (lsock
, (bklog
> 0 ? bklog
: 1));
116 mperror(g_flog
, errno
, "%s: socket/bind/listen error",
119 (void) close( lsock
);
124 TRACE( (void)tmfprintf( g_flog
, "Created server socket=[%d], backlog=[%d]\n",
132 /* add or drop membership in a multicast group
135 set_multicast( int msockfd
, const struct in_addr
* mifaddr
,
138 struct sockaddr_in addr
;
139 a_socklen_t len
= sizeof(addr
);
144 ((IP_DROP_MEMBERSHIP
== opname
) ? "DROP" :
145 ((IP_ADD_MEMBERSHIP
== opname
) ? "ADD" : ""));
148 assert( (msockfd
> 0) && mifaddr
);
150 (void) memset( &mreq
, 0, sizeof(mreq
) );
151 (void) memcpy( &mreq
.imr_interface
, mifaddr
,
152 sizeof(struct in_addr
) );
154 (void) memset( &addr
, 0, sizeof(addr
) );
155 rc
= getsockname( msockfd
, (struct sockaddr
*)&addr
, &len
);
157 mperror( g_flog
, errno
, "%s: getsockname", __func__
);
161 (void) memcpy( &mreq
.imr_multiaddr
, &addr
.sin_addr
,
162 sizeof(struct in_addr
) );
164 rc
= setsockopt( msockfd
, IPPROTO_IP
, opname
,
165 &mreq
, sizeof(mreq
) );
167 mperror( g_flog
, errno
, "%s: setsockopt MCAST option: %s",
172 TRACE( (void)tmfprintf( g_flog
, "multicast-group [%s]\n",
178 /* set up the socket to receive multicast data
182 setup_mcast_listener( struct sockaddr_in
* sa
,
183 const struct in_addr
* mifaddr
,
189 int buflen
= sockbuflen
;
190 size_t rcvbuf_len
= 0;
192 assert( sa
&& mifaddr
&& mcastfd
&& (sockbuflen
>= 0) );
194 TRACE( (void)tmfprintf( g_flog
, "Setting up multicast listener\n") );
197 sockfd
= socket( AF_INET
, SOCK_DGRAM
, 0 );
199 mperror(g_flog
, errno
, "%s: socket", __func__
);
204 rc
= get_rcvbuf( sockfd
, &rcvbuf_len
);
207 if ((size_t)buflen
> rcvbuf_len
) {
208 rc
= set_rcvbuf( sockfd
, buflen
);
213 TRACE( (void)tmfprintf( g_flog
, "Must not adjust buffer size "
214 "for mcast socket [%d]\n", sockfd
) );
217 rc
= setsockopt( sockfd
, SOL_SOCKET
, SO_REUSEADDR
,
220 mperror(g_flog
, errno
, "%s: setsockopt SO_REUSEADDR",
226 /* On some systems (such as FreeBSD) SO_REUSEADDR
227 just isn't enough to subscribe to N same channels for different clients.
229 rc
= setsockopt( sockfd
, SOL_SOCKET
, SO_REUSEPORT
,
232 mperror(g_flog
, errno
, "%s: setsockopt SO_REUSEPORT",
236 #endif /* SO_REUSEPORT */
238 rc
= bind( sockfd
, (struct sockaddr
*)sa
, sizeof(*sa
) );
240 mperror(g_flog
, errno
, "%s: bind", __func__
);
244 rc
= set_multicast( sockfd
, mifaddr
, IP_ADD_MEMBERSHIP
);
251 TRACE( (void)tmfprintf( g_flog
, "Mcast listener socket=[%d] set up\n",
262 /* unsubscribe from multicast and close the reader socket
265 close_mcast_listener( int msockfd
, const struct in_addr
* mifaddr
)
269 if( msockfd
<= 0 ) return;
271 (void) set_multicast( msockfd
, mifaddr
, IP_DROP_MEMBERSHIP
);
272 (void) close( msockfd
);
274 TRACE( (void)tmfprintf( g_flog
, "Mcast listener socket=[%d] closed\n",
280 /* drop from and add into a multicast group
283 renew_multicast( int msockfd
, const struct in_addr
* mifaddr
)
287 rc
= set_multicast( msockfd
, mifaddr
, IP_DROP_MEMBERSHIP
);
288 if( 0 != rc
) return rc
;
290 rc
= set_multicast( msockfd
, mifaddr
, IP_ADD_MEMBERSHIP
);
295 /* set send/receive timeouts on socket(s)
298 set_timeouts( int rsock
, int ssock
,
299 u_short rcv_tmout_sec
, u_short rcv_tmout_usec
,
300 u_short snd_tmout_sec
, u_short snd_tmout_usec
)
302 struct timeval rtv
, stv
;
306 rtv
.tv_sec
= rcv_tmout_sec
;
307 rtv
.tv_usec
= rcv_tmout_usec
;
308 rc
= setsockopt( rsock
, SOL_SOCKET
, SO_RCVTIMEO
, &rtv
, sizeof(rtv
) );
310 mperror(g_flog
, errno
, "%s: setsockopt - SO_RCVTIMEO",
314 TRACE( (void)tmfprintf (g_flog
, "socket %d: RCV timeout set to %ld sec, %ld usec\n",
315 rsock
, rtv
.tv_sec
, rtv
.tv_usec
) );
319 stv
.tv_sec
= snd_tmout_sec
;
320 stv
.tv_usec
= snd_tmout_usec
;
321 rc
= setsockopt( ssock
, SOL_SOCKET
, SO_SNDTIMEO
, &stv
, sizeof(stv
) );
323 mperror(g_flog
, errno
, "%s: setsockopt - SO_SNDTIMEO", __func__
);
326 TRACE( (void)tmfprintf (g_flog
, "socket %d: SEND timeout set to %ld sec, %ld usec\n",
327 rsock
, rtv
.tv_sec
, rtv
.tv_usec
) );
335 /* set socket's send/receive buffer size
338 set_sockbuf_size( int sockfd
, int option
, const size_t len
,
339 const char* bufname
)
341 size_t data_len
= len
;
346 rc
= setsockopt( sockfd
, SOL_SOCKET
, option
,
347 &data_len
, sizeof(data_len
) );
349 mperror(g_flog
, errno
, "%s: setsockopt %s [%d]",
350 __func__
, bufname
, option
);
354 TRACE( (void)tmfprintf( g_flog
,
355 "%s buffer size set to [%u] bytes for socket [%d]\n",
356 bufname
, data_len
, sockfd
) );
363 /* set socket's send buffer value
366 set_sendbuf( int sockfd
, const size_t len
)
368 return set_sockbuf_size( sockfd
, SO_SNDBUF
, len
, "send" );
372 /* set socket's send buffer value
375 set_rcvbuf( int sockfd
, const size_t len
)
377 return set_sockbuf_size( sockfd
, SO_RCVBUF
, len
, "receive" );
382 get_sockbuf_size( int sockfd
, int option
, size_t* const len
,
383 const char* bufname
)
387 socklen_t varsz
= sizeof(buflen
);
389 assert( sockfd
&& len
&& bufname
);
391 rc
= getsockopt( sockfd
, SOL_SOCKET
, option
, &buflen
, &varsz
);
393 mperror( g_flog
, errno
, "%s: getsockopt (%s) [%d]", __func__
,
398 TRACE( (void)tmfprintf( g_flog
,
399 "current %s buffer size is [%u] bytes for socket [%d]\n",
400 bufname
, buflen
, sockfd
) );
408 /* get socket's send buffer size
411 get_sendbuf( int sockfd
, size_t* const len
)
413 return get_sockbuf_size( sockfd
, SO_SNDBUF
, len
, "send" );
417 /* get socket's receive buffer size
420 get_rcvbuf( int sockfd
, size_t* const len
)
422 return get_sockbuf_size( sockfd
, SO_RCVBUF
, len
, "receive" );
427 /* set/clear file/socket's mode as non-blocking
430 set_nblock( int fd
, int set
)
434 flags
= fcntl( fd
, F_GETFL
, 0 );
436 mperror( g_flog
, errno
, "%s: fcntl() getting flags on fd=[%d]",
444 flags
&= ~O_NONBLOCK
;
446 if( fcntl( fd
, F_SETFL
, flags
) < 0 ) {
447 mperror( g_flog
, errno
, "%s: fcntl() %s non-blocking mode "
448 "on fd=[%d]", __func__
, (set
? "setting" : "clearing"),
458 sock_info (int peer
, int sockfd
, char* addr
, size_t alen
, int* port
)
463 char data
[sizeof(struct sockaddr_in6
)];
467 const void* dst
= NULL
;
468 struct sockaddr_in6
*sa6
= NULL
;
469 struct sockaddr_in
*sa4
= NULL
;
471 len
= sizeof (gsa
.data
);
472 rc
= peer
? getpeername (sockfd
, (struct sockaddr
*)gsa
.data
, &len
):
473 getsockname (sockfd
, (struct sockaddr
*)gsa
.data
, &len
);
476 (void) fprintf (g_flog
, "getsockname error [%d]: %s\n",
481 switch (gsa
.sa
.sa_family
) {
483 sa4
= (struct sockaddr_in
*)&gsa
.sa
;
485 dst
= inet_ntop (gsa
.sa
.sa_family
, &(sa4
->sin_addr
),
486 addr
, (socklen_t
)alen
);
488 if (port
) *port
= (int) ntohs (sa4
->sin_port
);
491 sa6
= (struct sockaddr_in6
*)&gsa
.sa
;
493 dst
= inet_ntop (gsa
.sa
.sa_family
, &(sa6
->sin6_addr
),
494 addr
, (socklen_t
)alen
);
496 if (port
) *port
= (int) ntohs (sa6
->sin6_port
);
499 (void) tmfprintf (g_flog
, "%s: unsupported socket family: %d\n",
500 __func__
, (int)gsa
.sa
.sa_family
);
505 (void) tmfprintf (g_flog
, "%s: inet_pton error [%d]: %s\n",
506 __func__
, rc
, strerror (rc
));
513 enum {e_host
= 0, e_peer
= 1};
515 get_sockinfo (int sockfd
, char* addr
, size_t alen
, int* port
)
517 return sock_info (e_host
, sockfd
, addr
, alen
, port
);
521 get_peerinfo (int sockfd
, char* addr
, size_t alen
, int* port
)
523 return sock_info (e_peer
, sockfd
, addr
, alen
, port
);