Listing 3. p2p Client

/* Copyright Girish Venkatachalam 2006
 * girish1729@gmail.com
 *
 * You are free to modify it to suit your requirements.
 * The licensing terms of this program is along the lines
 * of the BSD license. You are free to use this for commercial
 * purposes provide you retain this notice.
 *
 *
 *
 */
#include "p2pcommon.h"

int registermediator(struct context *ctx) {
        int n;
        struct msg m;
        struct sockaddr_in mypriv;

        m.op = REGISTER;
        m_st.rpl.id = htonl(getmyid());
        mypriv.sin_port = htons(17000);
        mypriv.sin_addr = getprivip();
        syslog(LOG_INFO,"My private address is
%s:%d",inet_ntoa(mypriv.sin_addr),ntohs(mypriv.sin_port));
        m_st.rpl.priv = mypriv;
        m_st.rpl.pub  = ctx->mypub;
        ctx->mypriv  = mypriv;

        do {
                n = send(ctx->tcpsock,(char *)&m,sizeof(m),0);
        }while(eblock());
        if(n == 0 || n == -1) {
                syslog(LOG_ERR,"Couldn't send registration message..
Exiting");
                exit(128);
        }
        n = recv(ctx->tcpsock,(char *)&m,sizeof(m),0);
        if(n == 0 || n == -1) {
                syslog(LOG_ERR,"Couldn't recv registration result..
Exiting");
                exit(128);
        }
        if(m.op == REGISTRATION_COMPLETED) {
                syslog(LOG_INFO,"Successfully registered with
mediator,id=%d",getmyid());
        }
        else {
                syslog(LOG_ERR,"Duplicate ID, can't register.. exiting");
                exit(128);
        }
        return 0;
}


int hdl_clreqib(struct context *ctx,struct req_payload *st) {
        int ret;
        struct connection *conn = (struct connection *)malloc(sizeof(struct
connection));
        conn->ctx = ctx;
        st->my.priv.sin_family = AF_INET;
        st->my.pub.sin_family = AF_INET;
        ret = holepunch_algo(conn,&st->my.priv,&st->my.pub,INBOUND);

        if(conn) {
                free(conn);
        }
        return 0;

}

int hdl_punchhole(int serversock,struct peercoord_payload *st) {
        struct sockaddr_in p2p;
        socklen_t l;
        p2p.sin_family = PF_INET;
        p2p.sin_port   = htons(15001);
        inet_aton("127.0.0.1",&p2p.sin_addr);
        l = sizeof(p2p);
        st->pub.sin_family = AF_INET;
        st->priv.sin_family = AF_INET;
        sendto(serversock,st,sizeof(*st),0,(struct sockaddr *)&p2p,l);

        return 0;
}

int peer_holepunch(struct connection *conn) {
        struct msg m;
        int ret;
        m.op = NEW_CLIENT_REQ_OUTBOUND;
        m_st.reqpl.my.pub = conn->ctx->mypub;
        m_st.reqpl.my.priv = conn->ctx->mypriv;
        m_st.reqpl.peer.id = conn->peerid;
        do {
                ret = send(conn->ctx->tcpsock,(char *)&m,sizeof(m),0);
        }while(eblock());
        if(ret == 0 || ret == -1) {
                syslog(LOG_ERR,"Send() problem with mediator.. Exiting");
                exit(128);
        }
        return 0;

}

int hdl_wrongid(int serversock) {
        struct sockaddr_in p2p;
        struct peercoord_payload pl;
        socklen_t l;

        p2p.sin_family = PF_INET;
        p2p.sin_port   = htons(15001);
        inet_aton("127.0.0.1",&p2p.sin_addr);
        l = sizeof(p2p);
        pl.id = 0;
        sendto(serversock,&pl,sizeof(pl),0,(struct sockaddr *)&p2p,l);

        return 0;
}

int hdl_hpend(int serversock) {
        struct msg m;
        struct sockaddr_in p2p;
        socklen_t l;

        p2p.sin_family = PF_INET;
        p2p.sin_port   = htons(15001);
        inet_aton("127.0.0.1",&p2p.sin_addr);
        l = sizeof(p2p);
        m.op = HOLEPUNCH_COMPLETED;
        sendto(serversock,&m,sizeof(m),0,(struct sockaddr *)&p2p,l);

        return 0;

}

/* This is the body of the client loop.
 *
 */
int servemediator(int sock,int serversock,struct context *ctx) {
        int b;
        struct msg m;

        b = recv(sock,(void *)&m,sizeof(m),0);

        if( b == -1 || b == 0) {
                perror("Recv() error from mediator.. Exiting");
                close(sock);
                exit(128);
        }

        switch(m.op) {
                case  NEW_CLIENT_REQ_INBOUND:
                        syslog(LOG_INFO,"New client inbound holepunch req
payload");
                        hdl_clreqib(ctx,&m_st.reqpl);
                        break;
                case PEER_COORD:
                        syslog(LOG_INFO,"Peer coord payload to initiate
hole punching");

                        hdl_punchhole(serversock,&m_st.peerpl);
                        break;
                case PEERID_WRONG:
                        syslog(LOG_INFO,"Wrong peer ID holepunch request");
                        hdl_wrongid(serversock);
                        break;
                case HOLEPUNCH_COMPLETED:
                        syslog(LOG_INFO,"*********** Holepunching complete
*********");
                        break;

                default:
                        syslog(LOG_ERR,"Garbled message from mediator ,
m.op = %d",m.op);

        }
        return 0;
}

int doecho(int udpsock,char *buf,int len,struct sockaddr_in *tsin) {
        /* Just bounce it back baby :-) */
        /* But send the ack first */
        struct request rq;
        char mybuf[4096];
        char c;
        if(!dataintegrity(buf,len-20,CHECK_INTEGRITY)) {
                c = 1;
        }
        else
                c = 2;
        sendto(udpsock,&c,1,0,(struct sockaddr *)tsin,sizeof(*tsin));

        memcpy(mybuf,buf+sizeof(rq),len-20-sizeof(rq));
        /* Now whatever is in mybuf is the payload sent by peer */

        sendreply(udpsock,buf+sizeof(rq),len-20-sizeof(rq),tsin);
        return 0;
}

void serveudp(int udpsock,int serversock)
{
        struct sockaddr_in tsin;
        socklen_t sinlen;
        unsigned char buf[4096];
        struct request rq;
        struct reply rp;
        int rc;

        sinlen = sizeof(tsin);
        rc = recvfrom(udpsock, buf, sizeof(buf), 0,
                        (struct sockaddr*)&tsin, &sinlen);

        rq = *(struct request *)buf;
        if((rc == sizeof(rq)) && ( rq.magic == htonl(REQMAGIC) ) ) {
                /* This is definitely a UDP bounce request */
                /* Build the reply and send it to the client */
                rp.magic = htonl(REPLMAGIC);
                rp.pubaddr = tsin.sin_addr;
                rp.pubport = tsin.sin_port;
                if (sendto(udpsock, &rp, sizeof(rp), 0,
                                        (struct sockaddr*)&tsin,
sizeof(tsin)) < 0)
                {
                        syslog(LOG_ERR,"Problem sending on udpsock to
%s:%d",inet_ntoa(tsin.sin_addr),ntohs(tsin.sin_port));
                        perror("sendto");
                }

                syslog(LOG_INFO, "UDP bounce request from %s:%d\n",
                                inet_ntoa(tsin.sin_addr),
                                ntohs(tsin.sin_port));


        }
        else {
                struct sockaddr_in p2p;
                socklen_t l;
                struct p2p_payload payload;

                p2p.sin_family = PF_INET;
                p2p.sin_port   = htons(15001);
                inet_aton("127.0.0.1",&p2p.sin_addr);
                l = sizeof(p2p);

                if(rq.magic  == htonl(SERVMAGIC)) {
                        /* XXX act as p2p server */
                        /* This is a dummy function just for testing
                         * purposes, to be replaced with actual p2p
                         *server code */
                        doecho(udpsock,buf,rc,&tsin);

                        syslog(LOG_INFO, "p2p Server UDP request from
%s:%d\n",
                                        inet_ntoa(tsin.sin_addr),
                                        ntohs(tsin.sin_port));


                }
                else {

                        /* If code reaches here , we are damn sure it is a
                         * response to a client request packet */

                        if( (rq.magic  == htonl(REPLMAGIC))
                                        || rc == 1) {
                                /* We have got a UDP bounce reply,
                                 * as part of hole punching, what
                                 * we have to do is bounce it to
                                 * our local socket
                                 * or else it is an ack packet */

                                sendto(serversock,buf,rc,0,(struct sockaddr
*)&p2p,l);
                        }
                        else {
                                /* Fill the payload to send for p2p
 * protocol backend */
                                payload.peer = tsin;
                                memcpy(payload.buf,buf,rc);
                                payload.len = rc;

                                sendto(serversock,&payload,sizeof(payload),0,(struct
sockaddr *)&p2p,l);

                                syslog(LOG_INFO, "Response to p2p client
request from %s:%d\n",
                                                inet_ntoa(tsin.sin_addr),
                                                ntohs(tsin.sin_port));
                        }

                }

        }

}

struct context *initializep2p()
{
        struct sockaddr_in sintcp,sinudp,udplocal;
        socklen_t sinlen;
        struct context *ctx;
        struct connection *conn;
        fd_set rfds,wfds;
        int udpsock,tcpsock,serversock;
        int  ret,on = 1,fds;
        uint16_t servport1 = 1234,servport2 = 80;
#ifdef WIN32
        WSADATA wsaData;

        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
                perrdie("Windows sockets 2.2 startup");
        {
                syslog(LOG_INFO, "Using %s (Status: %s)\n",
                                wsaData.szDescription,
wsaData.szSystemStatus);
                syslog(LOG_INFO, "with API versions %d.%d to %d.%d\n\n",
                                LOBYTE(wsaData.wVersion),
HIBYTE(wsaData.wVersion),
                                LOBYTE(wsaData.wHighVersion),
HIBYTE(wsaData.wHighVersion));
        }
#endif



        /* XXX Make this work on Windoze */
        openlog("vembu-p2p",LOG_PERROR | LOG_CONS | LOG_PID,LOG_LOCAL2);
        syslog(LOG_INFO,"Starting p2p ....");


        ctx = (struct context *)malloc(sizeof(struct context));

        lookup(MEDIATOR, &sintcp,servport1);
        tcpsock = mksock(SOCK_STREAM);
        sinlen = sizeof(sintcp);

        setsockopt(tcpsock,SOL_SOCKET,SO_KEEPALIVE,&on,sizeof(on));

        serversock = socket(PF_INET,SOCK_DGRAM,0);
        sinudp.sin_family = PF_INET;
        sinudp.sin_port   = htons(15000);
        sinudp.sin_addr.s_addr = INADDR_ANY;

        if(bind(serversock,(struct sockaddr *)&sinudp,sizeof(sinudp)) ==
-1) {
                syslog(LOG_ERR,"Coulnd't bind UDP loopback socket ..
dying");
                exit(128);
        }
        ctx->clientsock = socket(AF_INET,SOCK_DGRAM,0);
        udplocal.sin_family = PF_INET;
        udplocal.sin_port   = htons(15001);
        udplocal.sin_addr.s_addr = INADDR_ANY;

        if(bind(ctx->clientsock,(struct sockaddr
*)&udplocal,sizeof(udplocal)) != 0)
        {
                syslog(LOG_ERR,"Loopback UDP bind problem");
        }


        udpsock = socket(AF_INET,SOCK_DGRAM,0);
        sinudp.sin_family = PF_INET;
        sinudp.sin_port   = htons(17000);
        sinudp.sin_addr.s_addr = INADDR_ANY;

        if(bind(udpsock,(struct sockaddr *)&sinudp,sizeof(sinudp)) == -1) {
                syslog(LOG_ERR,"Coulnd't bind p2p UDP socket .. dying");
                exit(128);
        }

        ret = connect(tcpsock,(struct sockaddr *)&sintcp,sinlen);


        if(ret != 0) {
                perrdie("Mediator port 1234 connect");
                syslog(LOG_INFO,"Trying port 80");
                sintcp.sin_port = htons(servport2);
                ret = connect(tcpsock,(struct sockaddr *)&sintcp,sinlen);


                if(ret != 0) {
                        syslog(LOG_ERR,"Too bad, we coulnd't connect to
mediator,dying...");
                        exit(128);
                }

        }

        syslog(LOG_INFO,"Successfully connected to mediator");
        /* Good, now that we are connected let us first register */
        /* But hey before we register we have to punch a hole :-) */
        conn = (struct connection *)malloc (sizeof(struct connection));
        conn->peerid = 0;
        conn->ctx = ctx;
        conn->ctx->tcpsock = tcpsock;
        conn->ctx->udpsock = udpsock;
        ret = holepunch(conn);
        if(ret == LINK_DOWN) {
                syslog(LOG_ERR,"My probes are not going through, kindly
restart the daemon after you fix your network problem.. Exiting");
                exit(128);
        }


        registermediator(ctx);

        /* Now we have to fork into a separate process */
        /* XXX do something like fork() for Windoze ... */
        if ( fork() != 0) {
                return ctx;
        }



        fds = udpsock > tcpsock ? udpsock + 1 : tcpsock + 1;


        for(;;) {

                FD_ZERO(&rfds);
                FD_ZERO(&wfds);
                FD_SET(udpsock,&rfds);
                FD_SET(udpsock,&wfds);
                FD_SET(tcpsock,&rfds);
                FD_SET(tcpsock,&wfds);

                select(fds,&rfds,NULL,NULL,NULL);

                if(FD_ISSET(udpsock,&rfds)) {
                        serveudp(udpsock,serversock);
                }
                if(FD_ISSET(tcpsock,&rfds)) {
                        servemediator(tcpsock,serversock,ctx);
                }

        }


        return 0;

}

/* Now our user API functions */

struct connection *connectpeer(struct context *ctx,uint32_t peerid) {
        struct sockaddr_in peer;
        struct connection *conn;
        struct peercoord_payload st;

        socklen_t l = sizeof(peer);

        conn = (struct connection *)malloc(sizeof(struct connection));
        conn->ctx = ctx;
        conn->peerid = htonl(peerid);
        holepunch(conn);
        recvfrom(conn->ctx->clientsock,&st,sizeof(st),0,(struct sockaddr
*)&peer,&l);

        if(st.id == 0) /* Wrong peer id passed */ {
                return NULL;
        }
        holepunch_algo(conn,&st.priv,&st.pub,OUTBOUND);
        return conn;
}

int sendrequest(struct connection *conn,char *buf,int len) {
        int ret ;
        char c;
        struct sockaddr_in peer;
        struct request rq;
        unsigned char mybuf[4096];
        socklen_t l = sizeof(conn->peer);

        rq.magic = htonl(SERVMAGIC);
        memcpy(mybuf,&rq,sizeof(rq));
        memcpy(mybuf+sizeof(rq),buf,len);

        dataintegrity(mybuf,len+sizeof(rq),ADD_INTEGRITY);
        ret = sendto(conn->ctx->udpsock,mybuf,len+sizeof(rq)+20,0,(struct
sockaddr *)&conn->peer,l);
        if(ret == 0 || ret == -1) {

                perror("p2p Send");
                syslog(LOG_ERR,"Problem sending p2p protocol data to
peer");
        }
        /* Check whether the data reached safely */
        ret =  recvfrom(conn->ctx->clientsock,&c,1,0,(struct sockaddr
*)&peer,&l);

        if(c == 1)
                return 0;
        else
                return -1; /* Data integrity failed */

}

int sendreply(int udpsock,char *buf,int len,struct sockaddr_in *tsin) {
        int ret ;
        char c;
        struct sockaddr_in peer;
        struct request rq;
        unsigned char mybuf[4096];
        socklen_t l = sizeof(peer);


        rq.magic = htonl(CLIMAGIC);
        memcpy(mybuf,&rq,sizeof(rq));
        memcpy(mybuf+sizeof(rq),buf,len);

        dataintegrity(mybuf,len+sizeof(rq),ADD_INTEGRITY);
        ret = sendto(udpsock,mybuf,len+sizeof(rq)+20,0,(struct sockaddr
*)tsin,sizeof(*tsin));
        if(ret == 0 || ret == -1) {

                perror("p2p Send");
                syslog(LOG_ERR,"Problem sending p2p protocol data to
peer");
        }

        /* Check whether the data reached safely */
        ret =  recvfrom(udpsock,&c,1,0,(struct sockaddr *)&peer,&l);
        if(c == 1)
                return 0;
        else
                return -1; /* Data integrity failed */

}


int receivepeer(struct connection *conn,char *buf,int *len) {
        struct p2p_payload payload;
        struct sockaddr_in local;
        struct request rq;
        char c;
        socklen_t l = sizeof(local);

        recvfrom(conn->ctx->clientsock,&payload,sizeof(payload),0,(struct
sockaddr *)&local,&l);
        *len = payload.len-20-sizeof(rq);
        if(conn->peer.sin_addr.s_addr == payload.peer.sin_addr.s_addr) {
                if(!dataintegrity(payload.buf,payload.len-20,CHECK_INTEGRITY))
{
                        memcpy(buf,payload.buf+sizeof(rq),*len);
                        c = 1;
                        /* Sending ACK packet */
                        sendto(conn->ctx->udpsock,&c,1,0,(struct sockaddr
*)&conn->peer,l);

                        return 0;
                }
                else {
                        c = 2;
                        /* Sending NACK packet */
                        sendto(conn->ctx->udpsock,&c,1,0,(struct sockaddr
*)&conn->peer,l);
                        return -1; /* Our integrity check failed */
                }

        }
        return -1;
}

int closepeer(struct connection *conn) {
        if(conn) {
                free(conn);
        }
        conn = NULL;
        return 0;
}

Now an example application using this library.

#include "p2pcommon.h"

int main () {
        struct context *ctx = NULL;
        struct connection *conn = NULL;
        char buf[1024],output[1024];
        int len,ret = -1;

        ctx = initializep2p();

        conn = connectpeer(ctx,12345678);

        if(conn == NULL) {
                printf("Wrong ID passed or peer offline.. exiting\n");
                close(ctx->tcpsock);
                exit(128);
        }

        strcpy(buf,"God is great!");
        len = strlen(buf);

        ret = sendrequest(conn,buf,len);

        if(ret != 0) {
                printf("Reliable UDP sending failed, have to try again\n");
                exit(128);
        }


        ret = receivepeer(conn,output,&len);

        if(ret != 0) {
                printf("Reliable UDP reception failed\n");
                exit(128);
        }
        printf("I received %d bytes from peer\n",len);
        printf("Data received is [%s]\n",output);

        closepeer(conn);

        /* Good, if the program did not crash,then memory has
         * been freed successfully and there has not been any
         * memory corruption */
        return 0;
}