/* 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; }
/* send statistics update to server (if it's time) */ void tpstat_update( struct server_ctx* ctx, struct tps_data* d, ssize_t nbytes ) { static const double MAX_NOUPDATE_ITER = 1000.0; static const double MAX_SEC = 10.0; struct tput_stat ts; int writefd = -1, rc = 0; const int DO_NOT_SET_PID = 0; ssize_t nwr = -1; double nsec; assert( ctx && d ); d->nbytes += nbytes; nsec = difftime( time(NULL), d->tm_from ); d->niter++; if( !((d->niter >= MAX_NOUPDATE_ITER) || (nsec >= MAX_SEC)) ) return; /* attempt to send update to server */ d->niter = 0; ts.sender_id = d->pid; ts.nbytes = d->nbytes; ts.nsec = nsec; writefd = ctx->cpipe[1]; do { nwr = write( writefd, &ts, sizeof(ts) ); if( nwr <= 0 ) { if( (EINTR != errno) && (EAGAIN != errno) ) { mperror( g_flog, errno, "%s: write", __func__ ); rc = -1; break; } /* if it's an interrupt or pipe full - ignore */ TRACE( (void)tmfprintf( g_flog, "%s - write error [%s] - ignored\n", __func__, strerror(errno)) ); break; } if( sizeof(ts) != (size_t)nwr ) { (void)tmfprintf( g_flog, "%s - wrote [%d] bytes to pipe, " "expected [%u]\n", __func__, nwr, (int)sizeof(ts) ); rc = -1; break; } /* TRACE( (void)tmfprintf( g_flog, "Sent TSTAT={ sender=[%ld], bytes=[%f], seconds=[%f] }\n", (long)ts.sender_id, ts.nbytes, ts.nsec) ); */ } while(0); if( 0 == rc ) { tpstat_init( d, DO_NOT_SET_PID ); } return; }