#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#define LOCAL_SOCKET	1
#ifndef FD_ALLOC
#define FD_ALLOC(nfds) ((fd_set*) malloc((nfds+7) / 8 ) )
#endif

extern void debug(const char *fmt, ...);
extern void error(const char *fmt, ...);


//Return values:
// 1: Error (in select)
// 2: Server has disconnected
// 3: Viewer has disconnected
// 4: Error when reading from viewer 
// 5: Error when reading from server
int doRepeater(int server, int viewer)
{

        /** vars for viewer input data **/
    char viewerBuf[1024];       /* viewer input buffer */
    int viewerBufLen;          /* available data in viewerBuf */

        /** vars for server input data **/
    char serverBuf[1024];       /* server input buffer */
    int serverBufLen;          /* available data in serverBuf */

        /** other variables **/
    int nfds, len;
    fd_set *ifds, *ofds;
    
    /*
     * repeater between server and viewer 
     */
    nfds = ((viewer < server) ? server : viewer) + 1;
    ifds = FD_ALLOC(nfds);
    ofds = FD_ALLOC(nfds);
    viewerBufLen = 0;
    serverBufLen = 0;

    while (true) {
        FD_ZERO(ifds);
        FD_ZERO(ofds);

                /** prepare for reading viewer input **/
        if ((viewerBufLen < (int) sizeof(viewerBuf))) {
            FD_SET(viewer, ifds);
        }

                /** prepare for reading server input **/
        if ((serverBufLen < (int) sizeof(serverBuf))) {
            FD_SET(server, ifds);
        }

        if (select(nfds, ifds, ofds, NULL, NULL) == -1) {

            /*EINTR is normal if user presses ctrl+c */
            if (errno != EINTR) {
                /*
                * error in select, parent process should close both connections  
                */
                error("doRepeater(): select() failed, errno=%d (%s)\n", errno, strerror(errno));
            }
            return 1;
        }

        /*
         * server => viewer 
         */
        if (FD_ISSET(server, ifds)
            && (serverBufLen < (int) sizeof(serverBuf))) {
            len = recv(server, serverBuf + serverBufLen, sizeof(serverBuf) - serverBufLen, 0);

            if (len == 0) {
                debug("doRepeater(): connection closed by server\n");
                
                //When server closes connection, parent process should also disconnect viewer
                return 2;
            }
            else if (len == -1) {
                if (errno != ECONNRESET) {

                    /*
                     * error 
                     */
                    debug("doRepeater(): recv() from server failed, errno=%d (%s)\n", errno, strerror(errno));
                    return 5;
                }
                else {
                    debug("doRepeater(): ECONNRESET detected\n");
                }
            }
            else {
                serverBufLen += len;
            }
        }

        /*
         * viewer => server 
         */
        if (FD_ISSET(viewer, ifds)
            && (viewerBufLen < (int) sizeof(viewerBuf))) {
            len = recv(viewer, viewerBuf + viewerBufLen, sizeof(viewerBuf) - viewerBufLen, 0);

            if (len == 0) {
                debug("doRepeater(): connection closed by viewer\n");

                //When viewer closes connection,parent process should also disconnect server 
                return 3;
            }
            else if (len == -1) {

                /*
                 * error on reading from viewer 
                 */
                debug("doRepeater(): recv() from viewer failed, errno = %d (%s)\n", errno, strerror(errno));
                return 4;
            }
            else {

                /*
                 * repeat 
                 */
                viewerBufLen += len;
            }
        }

        /*
         * flush data in viewerbuffer to server 
         */
        if (0 < viewerBufLen) {
            len = send(server, viewerBuf, viewerBufLen, 0);

            if (len == -1) {
                debug("doRepeater(): send() (to server) failed, errno=%d (%s)\n", errno, strerror(errno));
            }
            else if (0 < len) {

                /*
                 * move data on to top of buffer 
                 */
                viewerBufLen -= len;

                if (0 < viewerBufLen)
                    memcpy(viewerBuf, viewerBuf + len, viewerBufLen);

                assert(0 <= viewerBufLen);
            }
        }

        /*
         * flush data in serverbuffer to viewer 
         */
        if (0 < serverBufLen) {
            len = send(viewer, serverBuf, serverBufLen, 0);

            if (len == -1) {
                debug("doRepeater(): send() (to viewer) failed, errno=%d (%s)\n", errno, strerror(errno));
            }
            else {
                serverBufLen -= len;

                if (len < serverBufLen)
                    memcpy(serverBuf, serverBuf + len, serverBufLen);

                assert(0 <= serverBufLen);
            }
        }
    }
}


syntax highlighted by Code2HTML, v. 0.9.1