1/* @(#) implementation of network operations for udpxy 2 * 3 * Copyright 2008-2011 Pavel V. Cherenkov (pcherenkov@gmail.com) 4 * 5 * This file is part of udpxy. 6 * 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. 11 * 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. 16 * 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/>. 19 */ 20 21#include <sys/types.h> 22#include <sys/socket.h> 23#include <arpa/inet.h> 24#include <netinet/in.h> 25 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> 33 34#include "udpxy.h" 35#include "netop.h" 36#include "util.h" 37#include "mtrace.h" 38#include "osdef.h" 39 40 41extern FILE* g_flog; /* application log */ 42 43 44/* set up (server) listening sockfd 45 */ 46int 47setup_listener( const char* ipaddr, int port, int* sockfd, int bklog ) 48{ 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; 53 54 extern const char IPv4_ALL[]; 55 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) ); 60 61 rc = ERR_INTERNAL; 62 do { 63 lsock = socket( AF_INET, SOCK_STREAM, 0 ); 64 if( -1 == lsock ) break; 65 66 (void) memset( &servaddr, 0, sizeof(servaddr) ); 67 servaddr.sin_family = AF_INET; 68 servaddr.sin_port = htons( (short)port ); 69 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) ); 74 75 rc = ERR_PARAM; 76 break; 77 } 78 } 79 else { 80 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 81 } 82 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; 89 } 90 91 #define NONBLOCK 1 92 rc = set_nblock (lsock, NONBLOCK); 93 if (0 != rc) break; 94 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; 103 } 104 105 rc = bind( lsock, (struct sockaddr*)&servaddr, sizeof(servaddr) ); 106 if( 0 != rc ) break; 107 108 rc = listen (lsock, (bklog > 0 ? bklog : 1)); 109 if( 0 != rc ) break; 110 111 rc = 0; 112 } while(0); 113 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 ); 120 } 121 } 122 else { 123 *sockfd = lsock; 124 TRACE( (void)tmfprintf( g_flog, "Created server socket=[%d], backlog=[%d]\n", 125 lsock, bklog) ); 126 } 127 128 return rc; 129} 130 131 132/* add or drop membership in a multicast group 133 */ 134int 135set_multicast( int msockfd, const struct in_addr* mifaddr, 136 int opname ) 137{ 138 struct sockaddr_in addr; 139 a_socklen_t len = sizeof(addr); 140 struct ip_mreq mreq; 141 142 int rc = 0; 143 const char *opstr = 144 ((IP_DROP_MEMBERSHIP == opname) ? "DROP" : 145 ((IP_ADD_MEMBERSHIP == opname) ? "ADD" : "")); 146 assert( opstr[0] ); 147 148 assert( (msockfd > 0) && mifaddr ); 149 150 (void) memset( &mreq, 0, sizeof(mreq) ); 151 (void) memcpy( &mreq.imr_interface, mifaddr, 152 sizeof(struct in_addr) ); 153 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; 159 } 160 161 (void) memcpy( &mreq.imr_multiaddr, &addr.sin_addr, 162 sizeof(struct in_addr) ); 163 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; 170 } 171 172 TRACE( (void)tmfprintf( g_flog, "multicast-group [%s]\n", 173 opstr ) ); 174 return rc; 175} 176 177 178/* set up the socket to receive multicast data 179 * 180 */ 181int 182setup_mcast_listener( struct sockaddr_in* sa, 183 const struct in_addr* mifaddr, 184 int* mcastfd, 185 int sockbuflen ) 186{ 187 int sockfd, rc; 188 int ON = 1; 189 int buflen = sockbuflen; 190 size_t rcvbuf_len = 0; 191 192 assert( sa && mifaddr && mcastfd && (sockbuflen >= 0) ); 193 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; 201 } 202 203 if (buflen != 0) { 204 rc = get_rcvbuf( sockfd, &rcvbuf_len ); 205 if (0 != rc) break; 206 207 if ((size_t)buflen > rcvbuf_len) { 208 rc = set_rcvbuf( sockfd, buflen ); 209 if (0 != rc) break; 210 } 211 } 212 else { 213 TRACE( (void)tmfprintf( g_flog, "Must not adjust buffer size " 214 "for mcast socket [%d]\n", sockfd ) ); 215 } 216 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; 223 } 224 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. 228 */ 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; 235 } 236#endif /* SO_REUSEPORT */ 237 238 rc = bind( sockfd, (struct sockaddr*)sa, sizeof(*sa) ); 239 if( 0 != rc ) { 240 mperror(g_flog, errno, "%s: bind", __func__); 241 break; 242 } 243 244 rc = set_multicast( sockfd, mifaddr, IP_ADD_MEMBERSHIP ); 245 if( 0 != rc ) 246 break; 247 } while(0); 248 249 if( 0 == rc ) { 250 *mcastfd = sockfd; 251 TRACE( (void)tmfprintf( g_flog, "Mcast listener socket=[%d] set up\n", 252 sockfd) ); 253 } 254 else { 255 (void)close(sockfd); 256 } 257 258 return rc; 259} 260 261 262/* unsubscribe from multicast and close the reader socket 263 */ 264void 265close_mcast_listener( int msockfd, const struct in_addr* mifaddr ) 266{ 267 assert( mifaddr ); 268 269 if( msockfd <= 0 ) return; 270 271 (void) set_multicast( msockfd, mifaddr, IP_DROP_MEMBERSHIP ); 272 (void) close( msockfd ); 273 274 TRACE( (void)tmfprintf( g_flog, "Mcast listener socket=[%d] closed\n", 275 msockfd) ); 276 return; 277} 278 279 280/* drop from and add into a multicast group 281 */ 282int 283renew_multicast( int msockfd, const struct in_addr* mifaddr ) 284{ 285 int rc = 0; 286 287 rc = set_multicast( msockfd, mifaddr, IP_DROP_MEMBERSHIP ); 288 if( 0 != rc ) return rc; 289 290 rc = set_multicast( msockfd, mifaddr, IP_ADD_MEMBERSHIP ); 291 return rc; 292} 293 294 295/* set send/receive timeouts on socket(s) 296 */ 297int 298set_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 ) 301{ 302 struct timeval rtv, stv; 303 int rc = 0; 304 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; 313 } 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) ); 316 } 317 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; 325 } 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) ); 328 } 329 330 return rc; 331} 332 333 334 335/* set socket's send/receive buffer size 336 */ 337static int 338set_sockbuf_size( int sockfd, int option, const size_t len, 339 const char* bufname ) 340{ 341 size_t data_len = len; 342 int rc = 0; 343 344 assert( bufname ); 345 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; 352 } 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 ) ); 357 } 358 359 return rc; 360} 361 362 363/* set socket's send buffer value 364 */ 365int 366set_sendbuf( int sockfd, const size_t len ) 367{ 368 return set_sockbuf_size( sockfd, SO_SNDBUF, len, "send" ); 369} 370 371 372/* set socket's send buffer value 373 */ 374int 375set_rcvbuf( int sockfd, const size_t len ) 376{ 377 return set_sockbuf_size( sockfd, SO_RCVBUF, len, "receive" ); 378} 379 380 381static int 382get_sockbuf_size( int sockfd, int option, size_t* const len, 383 const char* bufname ) 384{ 385 int rc = 0; 386 size_t buflen = 0; 387 socklen_t varsz = sizeof(buflen); 388 389 assert( sockfd && len && bufname ); 390 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; 396 } 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; 402 } 403 404 return rc; 405 406} 407 408/* get socket's send buffer size 409 */ 410int 411get_sendbuf( int sockfd, size_t* const len ) 412{ 413 return get_sockbuf_size( sockfd, SO_SNDBUF, len, "send" ); 414} 415 416 417/* get socket's receive buffer size 418 */ 419int 420get_rcvbuf( int sockfd, size_t* const len ) 421{ 422 return get_sockbuf_size( sockfd, SO_RCVBUF, len, "receive" ); 423} 424 425 426 427/* set/clear file/socket's mode as non-blocking 428 */ 429int 430set_nblock( int fd, int set ) 431{ 432 int flags = 0; 433 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; 439 } 440 441 if( set ) 442 flags |= O_NONBLOCK; 443 else 444 flags &= ~O_NONBLOCK; 445 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; 451 } 452 453 return 0; 454} 455 456 457static int 458sock_info (int peer, int sockfd, char* addr, size_t alen, int* port) 459{ 460 int rc = 0; 461 union { 462 struct sockaddr sa; 463 char data [sizeof(struct sockaddr_in6)]; 464 } gsa; 465 socklen_t len; 466 467 const void* dst = NULL; 468 struct sockaddr_in6 *sa6 = NULL; 469 struct sockaddr_in *sa4 = NULL; 470 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; 479 } 480 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); 487 } 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); 495 } 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; 502 } 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; 508 } 509 510 return rc; 511} 512 513enum {e_host = 0, e_peer = 1}; 514int 515get_sockinfo (int sockfd, char* addr, size_t alen, int* port) 516{ 517 return sock_info (e_host, sockfd, addr, alen, port); 518} 519 520int 521get_peerinfo (int sockfd, char* addr, size_t alen, int* port) 522{ 523 return sock_info (e_peer, sockfd, addr, alen, port); 524} 525 526 527/* __EOF__ */ 528 529