/* send server status as HTTP response to the given socket */ static int report_status( int sockfd, const struct server_ctx* ctx, int options ) { char *buf = NULL; int rc = 0; ssize_t n = -1; size_t nlen = 0, bufsz, i; struct client_ctx *clc = NULL; enum {BLOCKING = 0, NON_BLOCKING = 1}; enum {BYTES_HDR = 4096, BYTES_PER_CLI = 512}; assert( (sockfd > 0) && ctx ); bufsz = BYTES_HDR; for (i = 0, clc=ctx->cl; i < ctx->clmax; ++i, ++clc) { if( ctx->cl[i].pid > 0 ) bufsz += BYTES_PER_CLI + strlen(clc->tail); } buf = malloc(bufsz); if( !buf ) { mperror(g_flog, ENOMEM, "malloc for %ld bytes for HTTP buffer " "failed in %s", (long)bufsz, __func__ ); return ERR_INTERNAL; } (void) memset( buf, 0, sizeof(bufsz) ); nlen = bufsz; rc = mk_status_page( ctx, buf, &nlen, options | MSO_HTTP_HEADER ); (void) set_nblock(sockfd, BLOCKING); n = send( sockfd, buf, (int)nlen, 0 ); if( (-1 == n) && (EINTR != errno) ) { mperror(g_flog, errno, "%s: send", __func__); rc = ERR_INTERNAL; } (void) set_nblock(sockfd, NON_BLOCKING); if( 0 != rc ) { TRACE( (void)tmfprintf( g_flog, "Error generating status report\n" ) ); } else { /* DEBUG only TRACE( (void)tmfprintf( g_flog, "Saved status buffer to file\n" ) ); TRACE( (void)save_buffer(buf, nlen, "/tmp/status-udpxy.html") ); */ } free(buf); return rc; }
void accept_requests (int sockfd, tmfd_t* asock, size_t* alen) { int new_sockfd = -1, err = 0, peer_port = -1, wmark = g_uopt.rcv_lwmark; size_t nmax = *alen, naccepted = 0; struct sockaddr_in cliaddr; a_socklen_t addrlen = sizeof (cliaddr); char peer_addr [128] = "#undef#"; static const int YES = 1; while (naccepted < nmax) { TRACE( (void)tmfputs ("Accepting new connection\n", g_flog) ); new_sockfd = accept (sockfd, (struct sockaddr*)&cliaddr, &addrlen ); if (-1 == new_sockfd) { err = errno; if ((EWOULDBLOCK == err) || (EAGAIN == err)) { TRACE((void)tmfputs ("Nothing more to accept\n", g_flog)); break; } if ((ECONNABORTED == err) || (ECONNRESET == err) || (EPROTO == err)) { TRACE( (void)tmfprintf (g_flog, "Connection aborted/reset " "at accept point, errno=%d\n", err) ); continue; } mperror(g_flog, err, "%s: accept", __func__); break; } if (0 != set_nblock (new_sockfd, 1)) { (void) close (new_sockfd); /* TODO: error-aware close */ continue; } /* if (0 != set_timeouts(new_sockfd, new_sockfd, g_uopt.sr_tmout, 0, g_uopt.sw_tmout, 0)) { (void) close (new_sockfd); continue; } */ if (wmark > 0) { if (0 != setsockopt (new_sockfd, SOL_SOCKET, SO_RCVLOWAT, (char*)&wmark, sizeof(wmark))) { mperror (g_flog, errno, "%s: setsockopt SO_RCVLOWAT [%d]", __func__, wmark); (void) close (new_sockfd); /* TODO: error-aware close */ continue; } else { TRACE( (void)tmfprintf (g_flog, "Receive LOW WATERMARK [%d] applied " "to newly-accepted socket [%d]\n", wmark, new_sockfd) ); } } if (g_uopt.tcp_nodelay) { if (0 != setsockopt(new_sockfd, IPPROTO_TCP, TCP_NODELAY, &YES, sizeof(YES))) { mperror(g_flog, errno, "%s setsockopt TCP_NODELAY", __func__); } } asock [naccepted].fd = new_sockfd; asock [naccepted].atime = time (NULL); ++naccepted; (void) get_peerinfo (new_sockfd, peer_addr, sizeof(peer_addr)-1, &peer_port); TRACE( (void)tmfprintf( g_flog, "Accepted socket=[%d] from %s:%d " "n=%ld/nmax=%ld\n", new_sockfd, peer_addr, peer_port, (long)naccepted, (long)nmax) ); } /* while */ if (naccepted >= nmax) { (void)tmfprintf (g_flog, "Accept limit max=[%d] reached, " "%ld already accepted", (long)nmax, (long)naccepted); } *alen = naccepted; TRACE( (void)tmfprintf (g_flog, "%s: Sockets accepted: [%ld]\n", __func__, (long)naccepted)); return; }
/* relay traffic from source to destination socket * */ static int relay_traffic( int ssockfd, int dsockfd, struct server_ctx* ctx, int dfilefd, const struct in_addr* mifaddr ) { volatile sig_atomic_t quit = 0; int rc = 0; ssize_t nmsgs = -1; ssize_t nrcv = 0, nsent = 0, nwr = 0, lrcv = 0, lsent = 0; char* data = NULL; size_t data_len = g_uopt.rbuf_len; struct rdata_opt ropt; time_t pause_time = 0, rfr_tm = time(NULL); sigset_t ubset; const int ALLOW_PAUSES = get_flagval( "UDPXY_ALLOW_PAUSES", 0 ); const ssize_t MAX_PAUSE_MSEC = get_sizeval( "UDPXY_PAUSE_MSEC", 1000); /* permissible variation in data-packet size */ static const ssize_t t_delta = 0x20; struct dstream_ctx ds; static const int SET_PID = 1; struct tps_data tps; assert( ctx && mifaddr && MAX_PAUSE_MSEC > 0 ); (void) sigemptyset (&ubset); sigaddset (&ubset, SIGINT); sigaddset (&ubset, SIGQUIT); sigaddset (&ubset, SIGTERM); /* restore the ability to receive *quit* signals */ rc = sigprocmask (SIG_UNBLOCK, &ubset, NULL); if (0 != rc) { mperror (g_flog, errno, "%s: sigprocmask", __func__); return -1; } /* NOPs to eliminate warnings in lean version */ (void)&lrcv; (void)&lsent; (void)&t_delta; check_fragments( NULL, 0, 0, 0, 0, g_flog ); /* INIT */ rc = calc_buf_settings( &nmsgs, NULL ); if (0 != rc) return -1; TRACE( (void)tmfprintf( g_flog, "Data buffer will hold up to " "[%d] messages\n", nmsgs ) ); rc = init_dstream_ctx( &ds, ctx->cmd, g_uopt.srcfile, nmsgs ); if( 0 != rc ) return -1; (void) set_nice( g_uopt.nice_incr, g_flog ); do { if( NULL == g_uopt.srcfile ) { rc = set_timeouts( ssockfd, dsockfd, ctx->rcv_tmout, 0, ctx->snd_tmout, 0 ); if( 0 != rc ) break; } if( dsockfd > 0 ) { rc = sync_dsockbuf_len( ssockfd, dsockfd ); if( 0 != rc ) break; rc = send_http_response( dsockfd, 200, "OK" ); if( 0 != rc ) break; /* timeshift: to detect PAUSE make destination * socket non-blocking, otherwise make it blocking * (since it might have been set unblocking earlier) */ rc = set_nblock( dsockfd, (ALLOW_PAUSES ? 1 : 0) ); if( 0 != rc ) break; } data = malloc(data_len); if( NULL == data ) { mperror( g_flog, errno, "%s: malloc", __func__ ); break; } if( g_uopt.cl_tpstat ) tpstat_init( &tps, SET_PID ); } while(0); TRACE( (void)tmfprintf( g_flog, "Relaying traffic from socket[%d] " "to socket[%d], buffer size=[%d], Rmsgs=[%d], pauses=[%d]\n", ssockfd, dsockfd, data_len, g_uopt.rbuf_msgs, ALLOW_PAUSES) ); /* RELAY LOOP */ ropt.max_frgs = g_uopt.rbuf_msgs; ropt.buf_tmout = g_uopt.dhold_tmout; pause_time = 0; while( (0 == rc) && !(quit = must_quit()) ) { if( g_uopt.mcast_refresh > 0 ) { check_mcast_refresh( ssockfd, &rfr_tm, mifaddr ); } nrcv = read_data( &ds, ssockfd, data, data_len, &ropt ); if( -1 == nrcv ) break; TRACE( check_fragments( "received new", data_len, lrcv, nrcv, t_delta, g_flog ) ); lrcv = nrcv; if( dsockfd && (nrcv > 0) ) { nsent = write_data( &ds, data, nrcv, dsockfd ); if( -1 == nsent ) break; if ( nsent < 0 ) { if ( !ALLOW_PAUSES ) break; if ( 0 != pause_detect( nsent, MAX_PAUSE_MSEC, &pause_time ) ) break; } TRACE( check_fragments("sent", nrcv, lsent, nsent, t_delta, g_flog) ); lsent = nsent; } if( (dfilefd > 0) && (nrcv > 0) ) { nwr = write_data( &ds, data, nrcv, dfilefd ); if( -1 == nwr ) break; TRACE( check_fragments( "wrote to file", nrcv, lsent, nwr, t_delta, g_flog ) ); lsent = nwr; } if( ds.flags & F_SCATTERED ) reset_pkt_registry( &ds ); if( uf_TRUE == g_uopt.cl_tpstat ) tpstat_update( ctx, &tps, nsent ); } /* end of RELAY LOOP */ /* CLEANUP */ TRACE( (void)tmfprintf( g_flog, "Exited relay loop: received=[%ld], " "sent=[%ld], quit=[%ld]\n", (long)nrcv, (long)nsent, (long)quit ) ); free_dstream_ctx( &ds ); if( NULL != data ) free( data ); if( 0 != (quit = must_quit()) ) { TRACE( (void)tmfprintf( g_flog, "Child process=[%d] must quit\n", getpid()) ); } return rc; }
/* set up (server) listening sockfd */ int setup_listener( const char* ipaddr, int port, int* sockfd, int bklog ) { #define LOWMARK 10 /* do not accept input of less than X octets */ int rc, lsock, wmark = LOWMARK; struct sockaddr_in servaddr; const int ON = 1; extern const char IPv4_ALL[]; assert( (port > 0) && sockfd && ipaddr ); (void)IPv4_ALL; TRACE( (void)tmfprintf( g_flog, "Setting up listener for [%s:%d]\n", ipaddr[0] ? ipaddr : IPv4_ALL, port) ); rc = ERR_INTERNAL; do { lsock = socket( AF_INET, SOCK_STREAM, 0 ); if( -1 == lsock ) break; (void) memset( &servaddr, 0, sizeof(servaddr) ); servaddr.sin_family = AF_INET; servaddr.sin_port = htons( (short)port ); if( '\0' != ipaddr[0] ) { if( 1 != inet_aton(ipaddr, &servaddr.sin_addr) ) { TRACE( (void)tmfprintf( g_flog, "Invalid server IP: [%s]\n", ipaddr) ); rc = ERR_PARAM; break; } } else { servaddr.sin_addr.s_addr = htonl(INADDR_ANY); } rc = setsockopt( lsock, SOL_SOCKET, SO_REUSEADDR, &ON, sizeof(ON) ); if( 0 != rc ) { mperror(g_flog, errno, "%s: setsockopt SO_REUSEADDR", __func__); break; } #define NONBLOCK 1 rc = set_nblock (lsock, NONBLOCK); if (0 != rc) break; TRACE( (void)tmfprintf (g_flog, "Setting low watermark for " "server socket [%d] to [%d]\n", lsock, wmark) ); rc = setsockopt (lsock, SOL_SOCKET, SO_RCVLOWAT, (char*)&wmark, sizeof(wmark)); if (rc) { mperror (g_flog, errno, "%s: setsockopt SO_RCVLOWAT", __func__); break; } rc = bind( lsock, (struct sockaddr*)&servaddr, sizeof(servaddr) ); if( 0 != rc ) break; rc = listen (lsock, (bklog > 0 ? bklog : 1)); if( 0 != rc ) break; rc = 0; } while(0); if( 0 != rc ) { if(errno) mperror(g_flog, errno, "%s: socket/bind/listen error", __func__); if( lsock ) { (void) close( lsock ); } } else { *sockfd = lsock; TRACE( (void)tmfprintf( g_flog, "Created server socket=[%d], backlog=[%d]\n", lsock, bklog) ); } return rc; }