usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / udpxy / netop.c
blob68c880424e7f63096cfe3065470207be327d3ba6
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 ) );
218 rc = setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR,
219 &ON, sizeof(ON) );
220 if( 0 != rc ) {
221 mperror(g_flog, errno, "%s: setsockopt SO_REUSEADDR",
222 __func__);
223 break;
226 rc = bind( sockfd, (struct sockaddr*)sa, sizeof(*sa) );
227 if( 0 != rc ) {
228 mperror(g_flog, errno, "%s: bind", __func__);
229 break;
232 rc = set_multicast( sockfd, mifaddr, IP_ADD_MEMBERSHIP );
233 if( 0 != rc )
234 break;
235 } while(0);
237 if( 0 == rc ) {
238 *mcastfd = sockfd;
239 TRACE( (void)tmfprintf( g_flog, "Mcast listener socket=[%d] set up\n",
240 sockfd) );
242 else {
243 (void)close(sockfd);
246 return rc;
250 /* unsubscribe from multicast and close the reader socket
252 void
253 close_mcast_listener( int msockfd, const struct in_addr* mifaddr )
255 assert( mifaddr );
257 if( msockfd <= 0 ) return;
259 (void) set_multicast( msockfd, mifaddr, IP_DROP_MEMBERSHIP );
260 (void) close( msockfd );
262 TRACE( (void)tmfprintf( g_flog, "Mcast listener socket=[%d] closed\n",
263 msockfd) );
264 return;
268 /* drop from and add into a multicast group
271 renew_multicast( int msockfd, const struct in_addr* mifaddr )
273 int rc = 0;
275 rc = set_multicast( msockfd, mifaddr, IP_DROP_MEMBERSHIP );
276 if( 0 != rc ) return rc;
278 rc = set_multicast( msockfd, mifaddr, IP_ADD_MEMBERSHIP );
279 return rc;
283 /* set send/receive timeouts on socket(s)
286 set_timeouts( int rsock, int ssock,
287 u_short rcv_tmout_sec, u_short rcv_tmout_usec,
288 u_short snd_tmout_sec, u_short snd_tmout_usec )
290 struct timeval rtv, stv;
291 int rc = 0;
293 if( rsock ) {
294 rtv.tv_sec = rcv_tmout_sec;
295 rtv.tv_usec = rcv_tmout_usec;
296 rc = setsockopt( rsock, SOL_SOCKET, SO_RCVTIMEO, &rtv, sizeof(rtv) );
297 if( -1 == rc ) {
298 mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO",
299 __func__);
300 return ERR_INTERNAL;
302 TRACE( (void)tmfprintf (g_flog, "socket %d: RCV timeout set to %ld sec, %ld usec\n",
303 rsock, rtv.tv_sec, rtv.tv_usec) );
306 if( ssock ) {
307 stv.tv_sec = snd_tmout_sec;
308 stv.tv_usec = snd_tmout_usec;
309 rc = setsockopt( ssock, SOL_SOCKET, SO_SNDTIMEO, &stv, sizeof(stv) );
310 if( -1 == rc ) {
311 mperror(g_flog, errno, "%s: setsockopt - SO_SNDTIMEO", __func__);
312 return ERR_INTERNAL;
314 TRACE( (void)tmfprintf (g_flog, "socket %d: SEND timeout set to %ld sec, %ld usec\n",
315 rsock, rtv.tv_sec, rtv.tv_usec) );
318 return rc;
323 /* set socket's send/receive buffer size
325 static int
326 set_sockbuf_size( int sockfd, int option, const size_t len,
327 const char* bufname )
329 size_t data_len = len;
330 int rc = 0;
332 assert( bufname );
334 rc = setsockopt( sockfd, SOL_SOCKET, option,
335 &data_len, sizeof(data_len) );
336 if( 0 != rc ) {
337 mperror(g_flog, errno, "%s: setsockopt %s [%d]",
338 __func__, bufname, option);
339 return -1;
341 else {
342 TRACE( (void)tmfprintf( g_flog,
343 "%s buffer size set to [%u] bytes for socket [%d]\n",
344 bufname, data_len, sockfd ) );
347 return rc;
351 /* set socket's send buffer value
354 set_sendbuf( int sockfd, const size_t len )
356 return set_sockbuf_size( sockfd, SO_SNDBUF, len, "send" );
360 /* set socket's send buffer value
363 set_rcvbuf( int sockfd, const size_t len )
365 return set_sockbuf_size( sockfd, SO_RCVBUF, len, "receive" );
369 static int
370 get_sockbuf_size( int sockfd, int option, size_t* const len,
371 const char* bufname )
373 int rc = 0;
374 size_t buflen = 0;
375 socklen_t varsz = sizeof(buflen);
377 assert( sockfd && len && bufname );
379 rc = getsockopt( sockfd, SOL_SOCKET, option, &buflen, &varsz );
380 if (0 != rc) {
381 mperror( g_flog, errno, "%s: getsockopt (%s) [%d]", __func__,
382 bufname, option);
383 return -1;
385 else {
386 TRACE( (void)tmfprintf( g_flog,
387 "current %s buffer size is [%u] bytes for socket [%d]\n",
388 bufname, buflen, sockfd ) );
389 *len = buflen;
392 return rc;
396 /* get socket's send buffer size
399 get_sendbuf( int sockfd, size_t* const len )
401 return get_sockbuf_size( sockfd, SO_SNDBUF, len, "send" );
405 /* get socket's receive buffer size
408 get_rcvbuf( int sockfd, size_t* const len )
410 return get_sockbuf_size( sockfd, SO_RCVBUF, len, "receive" );
415 /* set/clear file/socket's mode as non-blocking
418 set_nblock( int fd, int set )
420 int flags = 0;
422 flags = fcntl( fd, F_GETFL, 0 );
423 if( flags < 0 ) {
424 mperror( g_flog, errno, "%s: fcntl() getting flags on fd=[%d]",
425 __func__, fd );
426 return -1;
429 if( set )
430 flags |= O_NONBLOCK;
431 else
432 flags &= ~O_NONBLOCK;
434 if( fcntl( fd, F_SETFL, flags ) < 0 ) {
435 mperror( g_flog, errno, "%s: fcntl() %s non-blocking mode "
436 "on fd=[%d]", __func__, (set ? "setting" : "clearing"),
437 fd );
438 return -1;
441 return 0;
445 static int
446 sock_info (int peer, int sockfd, char* addr, size_t alen, int* port)
448 int rc = 0;
449 union {
450 struct sockaddr sa;
451 char data [sizeof(struct sockaddr_in6)];
452 } gsa;
453 socklen_t len;
455 const void* dst = NULL;
456 struct sockaddr_in6 *sa6 = NULL;
457 struct sockaddr_in *sa4 = NULL;
459 len = sizeof (gsa.data);
460 rc = peer ? getpeername (sockfd, (struct sockaddr*)gsa.data, &len):
461 getsockname (sockfd, (struct sockaddr*)gsa.data, &len);
462 if (0 != rc) {
463 rc = errno;
464 (void) fprintf (g_flog, "getsockname error [%d]: %s\n",
465 rc, strerror (rc));
466 return rc;
469 switch (gsa.sa.sa_family) {
470 case AF_INET:
471 sa4 = (struct sockaddr_in*)gsa.data;
472 if (addr) {
473 dst = inet_ntop (gsa.sa.sa_family, &(sa4->sin_addr),
474 addr, (socklen_t)alen);
476 if (port) *port = (int) ntohs (sa4->sin_port);
477 break;
478 case AF_INET6:
479 sa6 = (struct sockaddr_in6*)gsa.data;
480 if (addr) {
481 dst = inet_ntop (gsa.sa.sa_family, &(sa6->sin6_addr),
482 addr, (socklen_t)alen);
484 if (port) *port = (int) ntohs (sa6->sin6_port);
485 break;
486 default:
487 (void) tmfprintf (g_flog, "%s: unsupported socket family: %d\n",
488 __func__, (int)gsa.sa.sa_family);
489 return EAFNOSUPPORT;
491 if (addr && !dst) {
492 rc = errno;
493 (void) tmfprintf (g_flog, "%s: inet_pton error [%d]: %s\n",
494 __func__, rc, strerror (rc));
495 return rc;
498 return rc;
501 enum {e_host = 0, e_peer = 1};
503 get_sockinfo (int sockfd, char* addr, size_t alen, int* port)
505 return sock_info (e_host, sockfd, addr, alen, port);
509 get_peerinfo (int sockfd, char* addr, size_t alen, int* port)
511 return sock_info (e_peer, sockfd, addr, alen, port);
515 /* __EOF__ */