Miniupnpd: update from 1.8 (20140422) to 1.9 (20141209)
[tomato.git] / release / src / router / udpxy / netop.c
blob0d26a64a2513e22accdb453f61e7a27935a4e5ce
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>
26 #include <assert.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <fcntl.h>
34 #include "udpxy.h"
35 #include "netop.h"
36 #include "util.h"
37 #include "mtrace.h"
38 #include "osdef.h"
41 extern FILE* g_flog; /* application log */
44 /* set up (server) listening sockfd
46 int
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;
52 const int ON = 1;
54 extern const char IPv4_ALL[];
56 assert( (port > 0) && sockfd && ipaddr );
57 (void)IPv4_ALL;
58 TRACE( (void)tmfprintf( g_flog, "Setting up listener for [%s:%d]\n",
59 ipaddr[0] ? ipaddr : IPv4_ALL, port) );
61 rc = ERR_INTERNAL;
62 do {
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",
73 ipaddr) );
75 rc = ERR_PARAM;
76 break;
79 else {
80 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
83 rc = setsockopt( lsock, SOL_SOCKET, SO_REUSEADDR,
84 &ON, sizeof(ON) );
85 if( 0 != rc ) {
86 mperror(g_flog, errno, "%s: setsockopt SO_REUSEADDR",
87 __func__);
88 break;
91 #define NONBLOCK 1
92 rc = set_nblock (lsock, NONBLOCK);
93 if (0 != rc) break;
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));
99 if (rc) {
100 mperror (g_flog, errno, "%s: setsockopt SO_RCVLOWAT",
101 __func__);
102 break;
105 rc = bind( lsock, (struct sockaddr*)&servaddr, sizeof(servaddr) );
106 if( 0 != rc ) break;
108 rc = listen (lsock, (bklog > 0 ? bklog : 1));
109 if( 0 != rc ) break;
111 rc = 0;
112 } while(0);
114 if( 0 != rc ) {
115 if(errno)
116 mperror(g_flog, errno, "%s: socket/bind/listen error",
117 __func__);
118 if( lsock ) {
119 (void) close( lsock );
122 else {
123 *sockfd = lsock;
124 TRACE( (void)tmfprintf( g_flog, "Created server socket=[%d], backlog=[%d]\n",
125 lsock, bklog) );
128 return rc;
132 /* add or drop membership in a multicast group
135 set_multicast( int msockfd, const struct in_addr* mifaddr,
136 int opname )
138 struct sockaddr_in addr;
139 a_socklen_t len = sizeof(addr);
140 struct ip_mreq mreq;
142 int rc = 0;
143 const char *opstr =
144 ((IP_DROP_MEMBERSHIP == opname) ? "DROP" :
145 ((IP_ADD_MEMBERSHIP == opname) ? "ADD" : ""));
146 assert( opstr[0] );
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 );
156 if( 0 != rc ) {
157 mperror( g_flog, errno, "%s: getsockname", __func__ );
158 return -1;
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) );
166 if( 0 != rc ) {
167 mperror( g_flog, errno, "%s: setsockopt MCAST option: %s",
168 __func__, opstr );
169 return rc;
172 TRACE( (void)tmfprintf( g_flog, "multicast-group [%s]\n",
173 opstr ) );
174 return rc;
178 /* set up the socket to receive multicast data
182 setup_mcast_listener( struct sockaddr_in* sa,
183 const struct in_addr* mifaddr,
184 int* mcastfd,
185 int sockbuflen )
187 int sockfd, rc;
188 int ON = 1;
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") );
195 rc = ERR_INTERNAL;
196 do {
197 sockfd = socket( AF_INET, SOCK_DGRAM, 0 );
198 if( -1 == sockfd ) {
199 mperror(g_flog, errno, "%s: socket", __func__);
200 break;
203 if (buflen != 0) {
204 rc = get_rcvbuf( sockfd, &rcvbuf_len );
205 if (0 != rc) break;
207 if ((size_t)buflen > rcvbuf_len) {
208 rc = set_rcvbuf( sockfd, buflen );
209 if (0 != rc) break;
212 else {
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,
218 &ON, sizeof(ON) );
219 if( 0 != rc ) {
220 mperror(g_flog, errno, "%s: setsockopt SO_REUSEADDR",
221 __func__);
222 break;
225 #ifdef SO_REUSEPORT
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,
230 &ON, sizeof(ON) );
231 if( 0 != rc ) {
232 mperror(g_flog, errno, "%s: setsockopt SO_REUSEPORT",
233 __func__);
234 break;
236 #endif /* SO_REUSEPORT */
238 rc = bind( sockfd, (struct sockaddr*)sa, sizeof(*sa) );
239 if( 0 != rc ) {
240 mperror(g_flog, errno, "%s: bind", __func__);
241 break;
244 rc = set_multicast( sockfd, mifaddr, IP_ADD_MEMBERSHIP );
245 if( 0 != rc )
246 break;
247 } while(0);
249 if( 0 == rc ) {
250 *mcastfd = sockfd;
251 TRACE( (void)tmfprintf( g_flog, "Mcast listener socket=[%d] set up\n",
252 sockfd) );
254 else {
255 (void)close(sockfd);
258 return rc;
262 /* unsubscribe from multicast and close the reader socket
264 void
265 close_mcast_listener( int msockfd, const struct in_addr* mifaddr )
267 assert( 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",
275 msockfd) );
276 return;
280 /* drop from and add into a multicast group
283 renew_multicast( int msockfd, const struct in_addr* mifaddr )
285 int rc = 0;
287 rc = set_multicast( msockfd, mifaddr, IP_DROP_MEMBERSHIP );
288 if( 0 != rc ) return rc;
290 rc = set_multicast( msockfd, mifaddr, IP_ADD_MEMBERSHIP );
291 return rc;
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;
303 int rc = 0;
305 if( rsock ) {
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) );
309 if( -1 == rc ) {
310 mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO",
311 __func__);
312 return ERR_INTERNAL;
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) );
318 if( ssock ) {
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) );
322 if( -1 == rc ) {
323 mperror(g_flog, errno, "%s: setsockopt - SO_SNDTIMEO", __func__);
324 return ERR_INTERNAL;
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) );
330 return rc;
335 /* set socket's send/receive buffer size
337 static int
338 set_sockbuf_size( int sockfd, int option, const size_t len,
339 const char* bufname )
341 size_t data_len = len;
342 int rc = 0;
344 assert( bufname );
346 rc = setsockopt( sockfd, SOL_SOCKET, option,
347 &data_len, sizeof(data_len) );
348 if( 0 != rc ) {
349 mperror(g_flog, errno, "%s: setsockopt %s [%d]",
350 __func__, bufname, option);
351 return -1;
353 else {
354 TRACE( (void)tmfprintf( g_flog,
355 "%s buffer size set to [%u] bytes for socket [%d]\n",
356 bufname, data_len, sockfd ) );
359 return rc;
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" );
381 static int
382 get_sockbuf_size( int sockfd, int option, size_t* const len,
383 const char* bufname )
385 int rc = 0;
386 size_t buflen = 0;
387 socklen_t varsz = sizeof(buflen);
389 assert( sockfd && len && bufname );
391 rc = getsockopt( sockfd, SOL_SOCKET, option, &buflen, &varsz );
392 if (0 != rc) {
393 mperror( g_flog, errno, "%s: getsockopt (%s) [%d]", __func__,
394 bufname, option);
395 return -1;
397 else {
398 TRACE( (void)tmfprintf( g_flog,
399 "current %s buffer size is [%u] bytes for socket [%d]\n",
400 bufname, buflen, sockfd ) );
401 *len = buflen;
404 return rc;
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 )
432 int flags = 0;
434 flags = fcntl( fd, F_GETFL, 0 );
435 if( flags < 0 ) {
436 mperror( g_flog, errno, "%s: fcntl() getting flags on fd=[%d]",
437 __func__, fd );
438 return -1;
441 if( set )
442 flags |= O_NONBLOCK;
443 else
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"),
449 fd );
450 return -1;
453 return 0;
457 static int
458 sock_info (int peer, int sockfd, char* addr, size_t alen, int* port)
460 int rc = 0;
461 union {
462 struct sockaddr sa;
463 char data [sizeof(struct sockaddr_in6)];
464 } gsa;
465 socklen_t len;
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);
474 if (0 != rc) {
475 rc = errno;
476 (void) fprintf (g_flog, "getsockname error [%d]: %s\n",
477 rc, strerror (rc));
478 return rc;
481 switch (gsa.sa.sa_family) {
482 case AF_INET:
483 sa4 = (struct sockaddr_in*)&gsa.sa;
484 if (addr) {
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);
489 break;
490 case AF_INET6:
491 sa6 = (struct sockaddr_in6*)&gsa.sa;
492 if (addr) {
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);
497 break;
498 default:
499 (void) tmfprintf (g_flog, "%s: unsupported socket family: %d\n",
500 __func__, (int)gsa.sa.sa_family);
501 return EAFNOSUPPORT;
503 if (addr && !dst) {
504 rc = errno;
505 (void) tmfprintf (g_flog, "%s: inet_pton error [%d]: %s\n",
506 __func__, rc, strerror (rc));
507 return rc;
510 return 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);
527 /* __EOF__ */