Пример #1
0
/* 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;
}
Пример #2
0
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;
}
Пример #3
0
/* 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;
}
Пример #4
0
/* 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;
}