Skip to content

Instantly share code, notes, and snippets.

@rickyzhang-cn
Created June 2, 2015 08:36
Show Gist options
  • Select an option

  • Save rickyzhang-cn/cfcba6e1ad042c0e4b48 to your computer and use it in GitHub Desktop.

Select an option

Save rickyzhang-cn/cfcba6e1ad042c0e4b48 to your computer and use it in GitHub Desktop.

Revisions

  1. rickyzhang-cn created this gist Jun 2, 2015.
    1,170 changes: 1,170 additions & 0 deletions minivpn.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1170 @@
    /*
    * tunproxy.c --- small demo program for tunneling over UDP with tun/tap
    *
    * Copyright (C) 2003 Philippe Biondi <[email protected]>
    *
    * This program is free software; you can redistribute it and/or modify it
    * under the terms of the GNU Lesser General Public License as published by
    * the Free Software Foundation.
    *
    * This program is distributed in the hope that it will be useful, but
    * WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    * Lesser General Public License for more details.
    */

    #include <openssl/evp.h>
    #include <openssl/hmac.h>

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/timerfd.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <net/if.h>
    #include <linux/if_tun.h>
    #include <getopt.h>
    #include <sys/ioctl.h>

    #include <memory.h>
    #include <errno.h>
    #include <arpa/inet.h>
    #include <netdb.h>

    #include <openssl/rsa.h> /* SSLeay stuff */
    #include <openssl/crypto.h>
    #include <openssl/x509.h>
    #include <openssl/pem.h>
    #include <openssl/ssl.h>
    #include <openssl/err.h>

    //#define exit(a)

    #define PERROR(x) do { perror(x); exit(1); } while (0)
    #define ERROR(x, args ...) do { fprintf(stderr,"ERROR:" x, ## args); exit(1); } while (0)
    #define CHK_NULL(x) if ((x)==NULL) exit (1)
    #define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
    #define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }

    //#define SLEEP(a) if (DEBUG) {sleep(a);}
    #define SLEEP(a) {sleep(a);}

    #define ABORT() {killpg(getpgid(getpid()),SIGTERM);}

    #define CLIENT 0
    #define SERVER 1

    #define MAX_KEY_LENGTH (32)
    #define IV_LENGTH (16)
    #define BUFFER_LENGTH (4096)
    #define HASH_LENGTH (32)
    #define MAX_BLOCK_LENGTH (BUFFER_LENGTH + EVP_MAX_BLOCK_LENGTH + HASH_LENGTH)

    #define POKE_INTERVAL_IN_SECS 3

    /* from cli.cpp */
    #define CCERTF "client.crt"
    #define CKEYF "client.key"
    #define CCACERT "ca.crt"

    /* from serv.cpp */
    /* define HOME to be dir for key and cert files... */
    #define HOME "./"
    /* Make these what you want for cert & key files */
    #define SCERTF HOME "server.crt"
    #define SKEYF HOME "server.key"
    #define SCACERT HOME "ca.crt"


    char KEY[MAX_KEY_LENGTH+1] = "Wazaaaaaaaaaaahhhh !";
    unsigned char IV[IV_LENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    char pokemsg[BUFFER_LENGTH]; /* Used for client/server "poke" communication */
    char msg[BUFFER_LENGTH]; /* Used for client/server SSL comm while tunnels are off and messages from parent/child */
    char buf[MAX_BLOCK_LENGTH]; /* Used exclusively within child processes for tunnel/UDP transfers */
    unsigned char out[MAX_BLOCK_LENGTH];
    unsigned char md_value[HASH_LENGTH];
    char szCommonName[512];
    int md_len = 0;
    int DEBUG = 0;
    char *progname;

    void do_encrypt(char *in, int inl, char *out, int *outl)
    {
    EVP_CIPHER_CTX ctx;
    int tmpl = 0;

    if (DEBUG) write(1,"0", 1);
    EVP_CIPHER_CTX_init(&ctx);
    if (DEBUG) write(1,"1", 1);
    if(0 == EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, KEY, IV)) PERROR("EVP_EncryptInit_ex");
    if (DEBUG) write(1,"2", 1);
    if(0 == EVP_EncryptUpdate(&ctx, out, outl, in, inl)) PERROR("EVP_EncryptUpdate");
    if (DEBUG) write(1,"3", 1);
    if(0 == EVP_EncryptFinal_ex(&ctx, out+*outl, &tmpl)) PERROR("EVP_EncryptFinal_ex");
    *outl += tmpl;
    if (DEBUG) write(1,"4", 1);
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (DEBUG) write(1,"5", 1);
    }

    void do_decrypt(char *in, int inl, char *out, int *outl)
    {
    EVP_CIPHER_CTX ctx;
    int tmpl = 0;

    if (DEBUG) write(1,"6", 1);
    EVP_CIPHER_CTX_init(&ctx);
    if (DEBUG) write(1,"7", 1);
    if(0 == EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, KEY, IV)) PERROR("EVP_DecryptInit_ex");
    if (DEBUG) write(1,"8", 1);
    if(0 == EVP_DecryptUpdate(&ctx, out, outl, in, inl)) PERROR("EVP_DecryptUpdate");
    if (DEBUG) write(1,"9", 1);
    if(0 == EVP_DecryptFinal_ex(&ctx, out+*outl, &tmpl)) PERROR("EVP_DecryptFinal_ex");
    *outl += tmpl;
    if (DEBUG) write(1,"a", 1);
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (DEBUG) write(1,"b", 1);
    }

    int isEqual(char *h1, char *h2, int len){
    int i = 0, j = 0;
    unsigned char a, b;

    for(i = 0; i < len; i++){
    a = (unsigned char)h1[i];
    b = (unsigned char)h2[i];
    #if 1
    for(j = 0; j < 8; j++){
    if((a & 0x01) != (b & 0x01)){
    return 0;
    }

    a = a >> 1;
    b = b >> 1;
    }// for
    #else
    if (a != b) {
    return 0;
    }// if
    #endif
    }// for

    return 1;
    }

    void printHash(unsigned char *hash,int len)
    {
    int i=0;

    while ( i<len) {
    printf("%02x",(unsigned int)hash[i]);
    i++;
    }
    }

    void dumpBuf(unsigned char *buf, int len)
    {
    int i = 0;

    for (i=0; i<len; i++) {
    printf("%02X",buf[i]);
    }
    }

    void createHash(unsigned char *in, int inl, unsigned char *md_value, int *md_len)
    {
    HMAC_CTX ctx;
    const EVP_MD *md;

    // Create new digest
    OpenSSL_add_all_digests();
    md = EVP_get_digestbyname("sha256");
    HMAC_CTX_init(&ctx);
    HMAC_Init_ex(&ctx, KEY, sizeof(KEY), md, NULL);
    HMAC_Update(&ctx, in, inl);
    HMAC_Final(&ctx, md_value, md_len);
    if (DEBUG) {printf("in: "); dumpBuf(in,inl); printf(" inl: %d md_value: ",inl);}
    if (DEBUG) printHash(md_value,*md_len);
    if (DEBUG) printf(" md_len: %d\n",*md_len);
    }

    void usage()
    {
    fprintf(stderr, "Usage: %s [-s port|-c targetip:port] [-e]\n", progname);
    exit(0);
    }

    int getch()
    {
    int r;
    unsigned char c;
    if ((r = read(0, &c, sizeof(c))) < 0) {
    return r;
    } else {
    return c;
    }
    }

    void randomizeString(char *pszStr,int len)
    {
    int i;
    for (i=0; i<len; i++) {
    pszStr[i] = rand() % 93 + 33;
    }//for
    }

    void randomizeArray(unsigned char *pArray,int len)
    {
    int i;
    for (i=0; i<len; i++) {
    pArray[i] = rand() % 255;
    }//for
    }

    void displayClientMenu(void)
    {
    /* Display menu of choices */
    printf("\n");
    printf("k - Enter KEY\n");
    printf("i - Enter IV\n");
    printf("r - Randomize KEY/IV pair\n");
    printf("c - Clear Session\n");
    printf("s - STOP\n");
    printf("> - ");
    fflush(stdout);
    }

    int main(int argc, char *argv[])
    {
    struct sockaddr_in sin, from, sout;
    struct ifreq ifr;
    int fd_tunnel, fd_udp, fd_tcp, fromlen, soutlen, port, PORT, l;
    char c, *p, *ip;
    fd_set fdset;
    int outl = sizeof(out);
    int cliserv = -1; /* must be specified on cmd line */
    int flags = IFF_TUN; /* default */
    char if_name[IFNAMSIZ] = "";
    int i;
    int fds[2];
    pid_t pid;

    /* initialize random seed */
    srand(time(NULL));

    progname = argv[0];

    while ((c = getopt(argc, argv, "s:c:ehd")) != -1) {
    switch (c) {
    case 'h':
    usage();
    case 'd':
    DEBUG++;
    break;
    case 's':
    cliserv = SERVER;
    PORT = atoi(optarg);
    break;
    case 'c':
    cliserv = CLIENT;
    p = memchr(optarg,':',16);
    if (!p) ERROR("invalid argument : [%s]\n",optarg);
    *p = 0;
    ip = optarg;
    port = atoi(p+1);
    PORT = 0;
    break;
    case 'e':
    flags = IFF_TAP;
    break;
    default:
    usage();
    }//switch
    }//while
    if (cliserv < 0) usage();

    /* Establish the Tunnel connection */
    if ((fd_tunnel = open("/dev/net/tun",O_RDWR)) < 0) PERROR("open");

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = flags;
    strncpy(ifr.ifr_name, "toto%d", IFNAMSIZ);
    if (ioctl(fd_tunnel, TUNSETIFF, (void *)&ifr) < 0) PERROR("ioctl");

    printf("Allocated interface %s. Configure and use it\n", ifr.ifr_name);

    /* Establish the UDP Network connection */
    fd_udp = socket(PF_INET, SOCK_DGRAM, 0);
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(PORT);
    if (bind(fd_udp,(struct sockaddr *)&sin, sizeof(sin)) < 0) PERROR("UDP-bind");

    /* Establish the TCP Network connection */
    fd_tcp = socket(PF_INET, SOCK_STREAM, 0);
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(PORT);
    if (bind(fd_tcp,(struct sockaddr *)&sin, sizeof(sin)) < 0) PERROR("TCP-bind");

    if (cliserv == CLIENT) {
    /* CLIENT */
    int err = -1;
    int sd = -1;
    struct sockaddr_in sa;
    int salen = sizeof(sa);
    SSL_CTX* ctx = NULL;
    SSL* ssl = NULL;
    X509* server_cert;
    char* str = NULL;
    SSL_METHOD *meth = SSLv23_client_method(); //SSLv2_client_method();
    char input;
    int cnValid = 0;
    int keyValid = 0;
    int ivValid = 0;
    int poke_count = 0;

    /* Create a pipe with two ends of pipe placed in fds[2], read_end=0, write_end=1 */
    pipe(fds);
    /* Fork a child process */
    pid = fork();
    if (pid > (pid_t) 0) { /* TCP/SSL get Keys */
    /* Client-Parent process */

    /* close our copy of the read end of pipe */
    close(fds[0]);

    /* Display startup message */
    printf("MiniVPN Client...\n");

    /* Output menu of choices */
    displayClientMenu();
    fflush(stdout);

    while(1) {
    /* Wait for TCP or user to make choice */
    FD_ZERO(&fdset);
    FD_SET(STDIN_FILENO,&fdset);
    FD_SET(fd_tcp,&fdset);
    if (select(STDIN_FILENO+fd_tcp+1,&fdset,NULL,NULL,NULL) < 0) PERROR("Client-Parent select");
    if (FD_ISSET(fd_tcp, &fdset)) {
    if (ssl) {
    /* Process POKE from Server */
    if (DEBUG) printf("Client Waiting on Server... ");
    err = SSL_read(ssl,pokemsg,sizeof(pokemsg)-1);
    if (!strncmp(pokemsg,"POKE",4)) {
    if (DEBUG) printf("Received '%s', ",pokemsg);
    if (DEBUG) printf("Sending POKE_ACK, #pokes=%d\n",poke_count++);
    err = SSL_write(ssl,"POKE_ACK",strlen("POKE_ACK"));
    if (-1 == err) {ERR_print_errors_fp(stderr); continue;}
    }//if
    else if (-1 == err) {
    PERROR("Client-Parent select");
    }//else if
    }//if ssl
    }//if fd_tcp
    if (FD_ISSET(STDIN_FILENO, &fdset)) {
    /* Notify the child process to STOP send/receive packets */
    strcpy(msg,"STOP");
    write(fds[1],msg,strlen(msg)+1);
    SLEEP(1);

    /* Close down current SSL session */
    printf("Clearing SSL session... ");
    /* send SSL/TLS close_notify */
    if (NULL != ssl) SSL_shutdown(ssl);
    /* Clean up. */
    if (sd >= 0) {close (sd); sd = -1;}
    if (NULL != ssl) {SSL_free(ssl); ssl = NULL;}
    if (NULL != ctx) {SSL_CTX_free(ctx); ctx = NULL;}
    poke_count = 0;
    printf("Done\n");
    SLEEP(1);

    /* Get User Choice */
    input = getch();

    /* Process User Choice */
    if ('k' == tolower(input)) {
    printf("\nKEY option...");
    printf("\nEnter CommonName(CN)\n");
    printf(">");
    fflush(stdout);
    memset(szCommonName,0,sizeof(szCommonName));
    scanf("%s",szCommonName);
    printf("\nEnter KEY\n");
    printf("[--------------------------------]\n");
    printf(">");
    fflush(stdout);
    memset(msg,0,sizeof(msg));
    scanf("%s",msg);
    memset(KEY,0,sizeof(KEY));
    strncpy(KEY,msg,MAX_KEY_LENGTH);
    }//if
    else if ('i' == tolower(input)) {
    printf("\nIV option...");
    printf("\nEnter CommonName(CN)\n");
    printf(">");
    fflush(stdout);
    memset(szCommonName,0,sizeof(szCommonName));
    scanf("%s",szCommonName);
    printf("\nEnter IV (%d numbers)\n",IV_LENGTH);
    printf("[----------------]\n");
    printf(">");
    fflush(stdout);
    for (i=0; i<IV_LENGTH; i++) IV[i]=getch();
    getch();
    }//else if
    else if ('r' == tolower(input)) {
    printf("\nRandomize KEY/IV option...");
    printf("\nEnter CommonName(CN)\n");
    printf(">");
    fflush(stdout);
    memset(szCommonName,0,sizeof(szCommonName));
    scanf("%s",szCommonName);
    printf("Random KEY and IV are being generated... ");
    /* Randomly generate ... KEY, IV */
    memset(KEY,0,sizeof(KEY));
    randomizeString(KEY,sizeof(KEY)-1);
    randomizeArray(IV,sizeof(IV));
    printf("Done\n");
    }//else if
    else if ('c' == tolower(input)) {
    /* Close down current SSL session */
    printf("Clearing SSL session... ");
    /* send SSL/TLS close_notify */
    if (NULL != ssl) SSL_shutdown(ssl);
    /* Clean up. */
    if (sd >= 0) {close (sd); sd = -1;}
    if (NULL != ssl) {SSL_free(ssl); ssl = NULL;}
    if (NULL != ctx) {SSL_CTX_free(ctx); ctx = NULL;}
    poke_count = 0;
    printf("Done\n");
    SLEEP(1);
    continue;
    }//else if
    else if ('s' == tolower(input)) {
    printf("\nClosing Tunnel... ");
    close(fd_tunnel);
    printf("Cleanup Done\n");
    printf("GoodBye\n");
    printf("Killing process tree\n");
    SLEEP(1);
    killpg(getpgid(getpid()),SIGTERM); /* Kill the complete process tree including child. */
    SLEEP(2);
    printf("Press Control-C to terminate\n");
    while(1);
    }//else if
    else {
    /* Output menu of choices */
    displayClientMenu();

    continue;
    }//else

    /* Echo back to the user the CN + KEY + IV being used */
    if (DEBUG) printf("CN is: [%s]\n",szCommonName);
    if (DEBUG) printf("KEY is: [%s]\n",KEY);
    if (DEBUG) {printf("IV is: ["); dumpBuf(IV,IV_LENGTH); printf("]\n\n");}

    if (DEBUG) printf("Session: ctx=%p, ssl=%p, sd=%d\n",ctx,ssl,sd);

    /* PKI: Authenticate the Server */
    if (!ctx || !ssl || (sd < 0)) {
    if (DEBUG) printf("PKI: Authenticating the Server...\n");
    SSLeay_add_ssl_algorithms();
    SSL_load_error_strings();
    ctx = SSL_CTX_new(meth);
    if (NULL == ctx) {if (DEBUG) printf("ctx error\n");err=-1;continue;};

    SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
    SSL_CTX_load_verify_locations(ctx,CCACERT,NULL);

    if (SSL_CTX_use_certificate_file(ctx,CCERTF,SSL_FILETYPE_PEM)<=0){
    ERR_print_errors_fp(stderr);err=-2;continue;}
    if (SSL_CTX_use_PrivateKey_file(ctx,CKEYF,SSL_FILETYPE_PEM) <=0){
    ERR_print_errors_fp(stderr);err=-3;continue;}
    if (!SSL_CTX_check_private_key(ctx)){
    printf("Private key doesn't match cert. public key\n");err=-4;continue;}

    /* Create a socket and connect to server using normal socket calls. */
    sd = socket (AF_INET, SOCK_STREAM, 0);
    if (-1 == sd) {perror("socket");err=-5;continue;}

    memset(&sa,0,sizeof(sa));
    sa.sin_family = AF_INET;
    inet_aton(ip, &sa.sin_addr); /* Server IP */
    sa.sin_port = htons(port); /* Server Port */

    err = connect(sd,(struct sockaddr*)&sa,sizeof(sa));
    if (-1 == err) {perror("connect");err=-6;continue;}

    /* Now we have TCP connection. Start SSL negotiation. */
    ssl = SSL_new(ctx); if (NULL == ssl) {printf("TCP connect failure\n");err=-7;continue;}
    SSL_set_fd(ssl,sd);
    err = SSL_connect(ssl); if (-1 == err) {ERR_print_errors_fp(stderr);err=-8;continue;};

    /* Following two steps are optional and not required for data exchange to be successful. */

    /* Get the cipher - opt */
    if (DEBUG) printf("SSL connection using %s\n",SSL_get_cipher(ssl));

    /* Get server's certificate (note: beware of dynamic allocation) - opt */
    server_cert = SSL_get_peer_certificate(ssl);
    if (NULL == server_cert){err=-9;continue;}
    if (DEBUG) printf("Server certificate:\n");

    str = X509_NAME_oneline(X509_get_subject_name(server_cert),0,0);
    if (NULL == str){err=-10;continue;}
    //if (subjectstr) strcpy(subjectstr,str);
    if (DEBUG) printf("\t subject: %s\n",str);
    OPENSSL_free(str);

    str = X509_NAME_oneline(X509_get_issuer_name(server_cert),0,0);
    if (NULL == str){err=-11;continue;}
    //if (issuerstr) strcpy(issuerstr,str);
    if (DEBUG) printf("\t issuer: %s\n",str);
    OPENSSL_free(str);

    /* We can do all sorts of certificate verification stuff here before
    deallocating the certificate. */
    X509_free(server_cert);
    }//if

    /* DATA EXCHANGE AREA */
    if (ssl) {
    /* Write CommonName (CN) to Server */
    if (DEBUG) printf("Writing CN [%s] to Server\n",szCommonName);
    strcpy(msg,"CN:");
    strcat(msg,szCommonName);
    err = SSL_write(ssl,msg,strlen(msg)); if (-1 == err) {ERR_print_errors_fp(stderr); continue;}
    /* Read CN reply from Server */
    err = SSL_read(ssl,msg,sizeof(msg)-1); if (-1 == err) {ERR_print_errors_fp(stderr); continue;}
    msg[err] = '\0';
    if (DEBUG) printf("Server replied with %d chars:'%s'\n",err,msg);
    if (strncmp(msg,"CN_ACK",6)) {cnValid=0;printf("Client_CN_ACK_Error\n");}
    else cnValid=1;

    /* Write KEY to Server */
    if (DEBUG) printf("Writing KEY [%s] to Server\n",KEY);
    strcpy(msg,"KEY:");
    strcat(msg,KEY);
    err = SSL_write(ssl,msg,strlen(msg)); if (-1 == err) {ERR_print_errors_fp(stderr); continue;}
    /* Read KEY reply from Server */
    err = SSL_read(ssl,msg,sizeof(msg)-1); if (-1 == err) {ERR_print_errors_fp(stderr); continue;}
    msg[err] = '\0';
    if (DEBUG) printf("Server replied with %d chars:'%s'\n",err,msg);
    if (strncmp(msg,"KEY_ACK",7)) {keyValid=0;printf("Client_KEY_ACK_Error\n");}
    else keyValid=1;

    /* Write IV to Server */
    if (DEBUG) {printf("Writing IV ["); dumpBuf(IV,IV_LENGTH); printf("] to Server\n");}
    strcpy(msg,"IV:");
    memcpy(&msg[3],&IV,sizeof(IV));
    err = SSL_write(ssl,msg,3+sizeof(IV)); if (-1 == err) {ERR_print_errors_fp(stderr); continue;}
    /* Read IV reply from Server */
    err = SSL_read(ssl,msg,sizeof(msg)-1); if (-1 == err) {ERR_print_errors_fp(stderr); continue;}
    msg[err] = '\0';
    if (DEBUG) printf("Server replied with %d chars:'%s'\n",err,msg);
    if (strncmp(msg,"IV_ACK",6)) {ivValid=0;printf("Client_IV_ACK_Error\n");}
    else ivValid=1;

    /* Configure Client-Child to enable tunnel */
    if (cnValid && keyValid && ivValid) {
    /* Write KEY to the child process */
    if (DEBUG) printf("Issuing KEY to Client-Child\n");
    strcpy(msg,"KEY:");
    strcat(msg,KEY);
    write(fds[1],msg,strlen(msg)+1);
    SLEEP(1);

    /* Write IV to the child process */
    if (DEBUG) printf("Issuing IV to Client-Child\n");
    strcpy(msg,"IV:");
    memcpy(&msg[3],&IV,sizeof(IV));
    write(fds[1],msg,3+sizeof(IV));
    SLEEP(1);

    /* Initialize FROM to point to Server's ip:port */
    memset(&from,0,sizeof(from));
    fromlen = sizeof(from);
    from.sin_family = AF_INET;
    from.sin_port = htons(port);
    inet_aton(ip,&from.sin_addr);

    /* Write FROM to the child process */
    if (DEBUG) printf("Informing Client-Child the server's UDP addr+port\n");
    strcpy(msg,"FROM:");
    memcpy(&msg[5],&from,sizeof(from));
    write(fds[1],msg,5+sizeof(from));
    SLEEP(1);

    /* Notify the child process to be ready to send/receive packets */
    if (DEBUG) printf("Issuing START to Client-Child\n");
    strcpy(msg,"START");
    write(fds[1],msg,strlen(msg)+1);
    SLEEP(1);
    }//if
    }//if ssl
    }//else if STDIN_FILENO
    }//while
    }//if Client-PARENT
    else { /* UDP use keys */
    /* Client-Child process */
    int okToStart = 0;
    struct sockaddr_in from;

    /* close our copy of the write end of pipe */
    close(fds[1]);

    /* Set unblock pipe read */

    /* Tunnel connection */

    /* Keep reading from fds[0], fd_udp, fd_tunnel */

    /* If notification is not received, do not send/receive */

    /* while(true){if (read(fds[0]) == "start") break;} */

    /* while(true){select from fd_udp and fd_tunnel, encryption/decryption, hash, sending/receiving packets} */

    while (1) {
    FD_ZERO(&fdset);
    FD_SET(fds[0], &fdset); /* Pipe from Parent */
    FD_SET(fd_tunnel,&fdset); /* Tunnel */
    FD_SET(fd_udp, &fdset); /* Net-UDP */
    if (select(fds[0]+fd_tunnel+fd_udp+1,&fdset,NULL,NULL,NULL) < 0) PERROR("Client-Child select");
    if (FD_ISSET(fds[0], &fdset)) {
    memset(buf,0,sizeof(buf));
    l = read(fds[0], buf, sizeof(buf));
    if (l < 0) PERROR("read");
    if (!strncmp(buf,"KEY:",4)) {
    if (DEBUG) printf("KEY: [%s] received from Client-Parent\n",&buf[4]);
    strncpy(KEY,&buf[4],sizeof(KEY));
    }//if
    else if (!strncmp(buf,"IV:",3)) {
    if (DEBUG) {printf("IV: [");dumpBuf(&buf[3],sizeof(IV));printf("] received from Client-Parent\n",&buf[3]);}
    memcpy(IV,&buf[3],sizeof(IV));
    }//else if
    else if (!strncmp(buf,"FROM:",5)) {
    if (DEBUG) printf("FROM: received from Client-Parent\n");
    memcpy(&from,&buf[5],sizeof(from));
    }//else if
    else if (!strncmp(buf,"START",5)) {
    if (DEBUG) printf("START received from Client-Parent\n");
    okToStart = 1;
    /*if (DEBUG)*/ printf("Client Tunnel running...\n");
    }//else if
    else if (!strncmp(buf,"STOP",4)) {
    if (DEBUG) printf("STOP received from Client-Parent\n");
    okToStart = 0;
    /*if (DEBUG)*/ printf("Client Tunnel stopped\n");
    }//else if
    else okToStart = 0;
    }//if
    else if (FD_ISSET(fd_tunnel, &fdset)) {
    if (okToStart) {
    /* wait for data from tunnel, then encrypt+hash, then send to "remote/from" network socket */
    if (DEBUG) write(1,">", 1);
    l = read(fd_tunnel, buf, sizeof(buf));
    if (l < 0) PERROR("read");
    else {
    if (DEBUG) {printf("\nTx=%d[",l); dumpBuf(buf,l); printf("]\n");}
    outl = sizeof(out);
    do_encrypt(buf,l,out,&outl);
    if (outl > MAX_BLOCK_LENGTH) PERROR("Encrypt Too Big");
    if (DEBUG) {printf("\nEncrypt=%d[",outl); dumpBuf(out,outl); printf("]\n");}
    createHash(buf, l, md_value, &md_len);
    if ((outl + md_len) > MAX_BLOCK_LENGTH) PERROR("Encrypt+Hash Too Big");
    if (DEBUG) {printf("Hash=%d[",md_len); dumpBuf(md_value,md_len); printf("]\n");}
    for (i=0; i<md_len; i++) {out[outl+i] = md_value[i];}
    outl += md_len;
    if (sendto(fd_udp, out, outl, 0, (struct sockaddr *)&from, sizeof(from)) < 0)PERROR("sendto");
    }//else
    }//if okToStart
    else if (!okToStart) { /* read from Tunnel and DISCARD */
    l = read(fd_tunnel, buf, sizeof(buf));
    if (l < 0) PERROR("read");
    }//else if !okToStart
    }//else if
    else if (FD_ISSET(fd_udp, &fdset)) {
    if (okToStart) {
    /* wait for data from "remote/from" node, audit connection, decrypt+rehash+audit,
    then forward to tunnel */
    if (DEBUG) write(1,"<", 1);
    /* do *not* use "from" so that we can audit which node the revieved packet comes from */
    soutlen = sizeof(sout);
    l = recvfrom(fd_udp, buf, sizeof(buf), 0, (struct sockaddr *)&sout, &soutlen);
    if (l < 0) {
    printf("fd_udp=%d,buf=%p,bufsize=%d,flags=%d,sockaddr=%p,soutlen=%p\n",
    fd_udp,buf,sizeof(buf),0,&sout,&soutlen);
    PERROR("recvfrom");
    }//if
    else {
    /* audit to make sure data came from node "established" at connection time */
    if ((sout.sin_addr.s_addr != from.sin_addr.s_addr) || (sout.sin_port != from.sin_port)) {
    #if 0
    printf("Got packet from %s:%i instead of %s:%i\n",
    (char *)inet_ntoa(sout.sin_addr/*.s_addr*/), ntohs(sout.sin_port),
    (char *)inet_ntoa(from.sin_addr/*.s_addr*/), ntohs(from.sin_port));
    #endif
    }//if
    else {
    /* data from correct node, subtract off HASH, decrypt+rehash, audit hash,
    then forward to tunnel */
    l -= HASH_LENGTH;
    outl = sizeof(out);
    if (DEBUG) {printf("\nRx=%d[",l); dumpBuf(buf,l); printf("]\n");}
    if (DEBUG) {printf("Hash=%d[",HASH_LENGTH); dumpBuf(&buf[l],HASH_LENGTH); printf("]\n");}
    do_decrypt(buf,l,out,&outl);
    if (DEBUG) {printf("\nDecrypt=%d[",outl); dumpBuf(out,outl); printf("]\n");}
    createHash(out, outl, md_value, &md_len);
    if (!isEqual(&buf[l], md_value, md_len)) PERROR("HASH");
    if (write(fd_tunnel, out, outl) < 0) PERROR("write");
    }//else
    }//else
    }//if okToStart
    else if (!okToStart) { /* receive from node and DISCARD */
    soutlen = sizeof(sout);
    l = recvfrom(fd_udp, buf, sizeof(buf), 0, (struct sockaddr *)&sout, &soutlen);
    if (l < 0) {
    printf("fd_udp=%d,buf=%p,bufsize=%d,flags=%d,sockaddr=%p,soutlen=%p\n",
    fd_udp,buf,sizeof(buf),0,&sout,&soutlen);
    PERROR("recvfrom");
    }//if
    }//else if !okToStart
    }//else if
    }//while
    }// else Client-CHILD
    }// if CLIENT
    else {
    /* SERVER */
    int err = -1;
    int listen_sd = fd_tcp;
    int sd = -1;
    //struct sockaddr_in sa_serv;
    struct sockaddr_in sa_cli;
    size_t client_len;
    SSL_CTX* ctx = NULL;
    SSL* ssl = NULL;
    X509* client_cert = NULL;
    char* str = NULL;
    char subjectstr[512];
    char issuerstr[512];
    SSL_METHOD *meth = SSLv23_server_method();
    int cnValid = 0;
    int keyValid = 0;
    int ivValid = 0;
    struct itimerspec time_period = {{POKE_INTERVAL_IN_SECS,0},{POKE_INTERVAL_IN_SECS,0}}; /* periodic timer */
    struct timeval sel_timeout;
    int timer_fd;
    int result;
    int poke_count = 0;

    /* create time to fire every 1 second */
    timer_fd = timerfd_create(CLOCK_MONOTONIC,0);
    if (timer_fd < 0) {printf("Timer fd failed\n");killpg(getpgid(getpid()),SIGTERM);}

    result = timerfd_settime(timer_fd,0,&time_period,NULL);
    if (result < 0) {printf("timer fd set time failed\n");killpg(getpgid(getpid()),SIGTERM);}

    /* Create a pipe with two ends of pipe placed in fds[2], read_end=0, write_end=1 */
    pipe(fds);
    /* Fork a child process */
    pid = fork();
    if (pid > (pid_t) 0) { /* TCP get Keys */
    /* Server-Parent process */

    /* close our copy of the read end of pipe */
    close(fds[0]);

    /* Display startup message */
    printf("MiniVPN Server...\n");
    fflush(stdout);

    while(1) {
    FD_ZERO(&fdset);
    FD_SET(fd_tcp,&fdset);
    FD_SET(timer_fd,&fdset);
    if (select(fd_tcp+timer_fd+1,&fdset,NULL,NULL,NULL) < 0) PERROR("Server-Parent select");
    if (FD_ISSET(timer_fd, &fdset)) {
    int s;
    uint64_t exp;
    s = read(timer_fd, &exp, sizeof(uint64_t));

    /* Reset time-interval in case altered */
    time_period.it_interval.tv_sec = POKE_INTERVAL_IN_SECS;
    time_period.it_interval.tv_nsec = 0;
    time_period.it_value.tv_sec = POKE_INTERVAL_IN_SECS;
    time_period.it_value.tv_nsec = 0;

    /* Is SSL connection currently active with Client? */
    if (ssl) {
    /* POKE_Client via SSL*/
    if (DEBUG) printf("Server POKE Client, Waiting... ");
    err = SSL_write(ssl,"POKE",strlen("POKE"));
    memset(pokemsg,0,sizeof(pokemsg));
    err = SSL_read(ssl,pokemsg,sizeof(msg)-1);
    if ((-1 == err) || (strncmp(pokemsg,"POKE_ACK",8)))
    {
    /* Client did not respond -OR- invalid reponse */
    if (err >= 0) {
    pokemsg[err] = '\0';
    if (DEBUG) printf("Received '%s'\n",pokemsg);
    }//if
    else {
    if (DEBUG) printf("Timeout\n");
    }//else

    if (DEBUG) printf("Closing SSL connection with Client\n");

    /* Close SSL connection with Client. */
    if (sd >= 0) {close(sd); sd = -1;}
    if (NULL != ssl) {SSL_free(ssl); ssl = NULL;}
    if (NULL != ctx) {SSL_CTX_free(ctx); ctx = NULL;}

    poke_count = 0;

    /* Notify the child process to STOP send/receive packets */
    strcpy(pokemsg,"STOP");
    write(fds[1],pokemsg,strlen(pokemsg)+1);
    }//if disconnect Client
    else {
    pokemsg[err] = '\0';
    if (DEBUG) printf("Received '%s', #pokes=%d\n",pokemsg,poke_count++);
    }//else
    }//if ssl
    }//if timer_fd
    if (FD_ISSET(fd_tcp, &fdset)) {
    /* Notify the child process to STOP send/receive packets */
    strcpy(msg,"STOP");
    write(fds[1],msg,strlen(msg)+1);
    SLEEP(1);

    if (DEBUG) printf("Session: ctx=%p, ssl=%p, sd=%d\n",ctx,ssl,sd);

    /* PKI: Authenticate the Client */
    if (!ctx || !ssl || (sd < 0)) {
    if (DEBUG) printf("PKI: Authenticating the Client...\n");

    /* SSL preliminaries. We keep the certificate and key with the context. */
    SSLeay_add_ssl_algorithms();
    SSL_load_error_strings();
    ctx = SSL_CTX_new (meth);
    if (!ctx) {ERR_print_errors_fp(stderr);err=-1;continue;}

    SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /* whether verify the certificate */
    SSL_CTX_load_verify_locations(ctx,SCACERT,NULL);

    if (SSL_CTX_use_certificate_file(ctx,SCERTF,SSL_FILETYPE_PEM)<=0){
    ERR_print_errors_fp(stderr);err=-2;continue;}
    if (SSL_CTX_use_PrivateKey_file(ctx,SKEYF,SSL_FILETYPE_PEM) <=0){
    ERR_print_errors_fp(stderr);err=-3;continue;}
    if (!SSL_CTX_check_private_key(ctx)){
    fprintf(stderr,"Private key does not match the certificate public key\n");err=-4;continue;}

    /* Prepare TCP socket for receiving connections */
    //listen_sd = socket(AF_INET,SOCK_STREAM,0);
    if (-1 == listen_sd) {perror("socket");err=-5;continue;}

    //memset(&sa_serv,0,sizeof(sa_serv));
    //sa_serv.sin_family = AF_INET;
    //sa_serv.sin_addr.s_addr = INADDR_ANY;
    //sa_serv.sin_port = htons(PORT); /* Server Port number */

    //err = bind(listen_sd,(struct sockaddr*)&sa_serv,sizeof(sa_serv));
    //if (-1 == err) {perror("bind");err=-6;continue;}

    /* Receive a TCP connection. */
    err = listen(listen_sd,5);
    if (-1 == err) {perror("listen");err=-7;continue;}

    client_len = sizeof(sa_cli);
    sd = accept(listen_sd,(struct sockaddr*)&sa_cli,&client_len);
    if (-1 == sd) {perror("accept");err=-8;continue;}
    //close(listen_sd);

    //if (DEBUG) printf("Connection from %lx, port %d\n",sa_cli.sin_addr.s_addr,sa_cli.sin_port);
    if (DEBUG) printf("Connection from ip:port %s:%i established\n",
    inet_ntoa(sa_cli.sin_addr/*.s_addr*/),ntohs(sa_cli.sin_port));

    /* TCP connection is ready. Do server side SSL. */
    ssl = SSL_new(ctx); if (NULL == ssl) {printf("TCP connect failure\n");err=-9;continue;}
    SSL_set_fd(ssl,sd);
    err = SSL_accept(ssl); if (-1 == err) {ERR_print_errors_fp(stderr);err=-10;continue;}

    /* Get the cipher - opt */
    if (DEBUG) printf("SSL connection using %s\n",SSL_get_cipher(ssl));

    /* Get client's certificate (note: beware of dynamic allocation) - opt */
    client_cert = SSL_get_peer_certificate(ssl);
    if (NULL == client_cert) {printf("Client does not have certificate.\n");err=-11;continue;}
    if (DEBUG) printf("Client certificate:\n");

    str = X509_NAME_oneline(X509_get_subject_name(client_cert),0,0);
    if (NULL == str) {err=-12;continue;}
    strncpy(subjectstr,str,sizeof(subjectstr)-1);
    if (DEBUG) printf("\t subject: %s\n",str);
    OPENSSL_free(str);

    str = X509_NAME_oneline(X509_get_issuer_name(client_cert),0,0);
    if (NULL == str) {err=-13;continue;}
    strncpy(issuerstr,str,sizeof(issuerstr)-1);
    if (DEBUG) printf("\t issuer: %s\n",str);
    OPENSSL_free(str);

    /* We can do all sorts of certificate verification stuff here before
    deallocating the certificate. */
    X509_free (client_cert);
    }//if

    /* DATA EXCHANGE - Receive messages and send replies */
    if (ssl) {
    /* Read CN from Client */
    err = SSL_read(ssl,msg,sizeof(msg)-1);
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-14;continue;}
    msg[err] = '\0';
    if (DEBUG) printf("Received from Client %d chars:'%s'\n",err,msg);
    /* Audit CN from Client */
    cnValid = 0;
    if (!strncmp(msg,"CN:",3)) {
    char *pch_start = strstr(subjectstr,"/CN=");
    if (NULL != pch_start) {
    char *pch_end = strstr(&pch_start[1],"/");
    if (NULL != pch_end) {
    int cn_len = (int)pch_end - (int)&pch_start[4];
    if (!strncmp(&pch_start[4],&msg[3],cn_len)) {
    cnValid=1;
    }//if
    }//if
    }//if
    }//if
    /* Write CN reply to Client */
    if (1 == cnValid) {
    err = SSL_write(ssl,"CN_ACK",strlen("CN_ACK"));
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-15;continue;}
    }//if
    else {
    err = SSL_write(ssl,"CN_NACK",strlen("CN_NACK"));
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-16;continue;}
    }//else

    /* Read KEY from Client */
    err = SSL_read(ssl,msg,sizeof(msg)-1);
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-17;continue;}
    msg[err] = '\0';
    if (DEBUG) printf("Received from Client %d chars:'%s'\n",err,msg);
    /* Write KEY reply to Client */
    if (!strncmp(msg,"KEY:",4)) {
    strncpy(KEY,&msg[4],MAX_KEY_LENGTH);
    keyValid=1;
    err = SSL_write(ssl,"KEY_ACK",strlen("KEY_ACK"));
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-18;continue;}
    }//if
    else {
    keyValid=0;
    err = SSL_write(ssl,"KEY_NACK",strlen("KEY_NACK"));
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-19;continue;}
    }//else

    /* Read IV from Client */
    err = SSL_read(ssl,msg,sizeof(msg)-1);
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-20;continue;}
    msg[err] = '\0';
    if (DEBUG) {printf("Received from Client %d chars:'%c%c%c",err,msg[0],msg[1],msg[2]);
    dumpBuf(&msg[3],err-3);printf("'\n");}
    /* Write IV reply to Client */
    if (!strncmp(msg,"IV:",3)) {
    memcpy(IV,&msg[3],IV_LENGTH);
    ivValid=1;
    err = SSL_write(ssl,"IV_ACK",strlen("IV_ACK"));
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-21;continue;}
    }//if
    else {
    ivValid=0;
    err = SSL_write(ssl,"IV_NACK",strlen("IV_NACK"));
    if (-1 == err) {ERR_print_errors_fp(stderr);err=-22;continue;}
    }//else

    #if 0
    /* Clean up. */
    if (sd >= 0) {close(sd); sd = -1;}
    if (NULL != ssl) {SSL_free(ssl); ssl = NULL;}
    if (NULL != ctx) {SSL_CTX_free(ctx); ctx = NULL;}
    #endif

    if (cnValid && keyValid && ivValid) {
    /* Write KEY to the child process */
    strcpy(msg,"KEY:");
    strcat(msg,KEY);
    write(fds[1],msg,strlen(msg)+1);
    SLEEP(1);

    /* Write IV to the child process */
    strcpy(msg,"IV:");
    memcpy(&msg[3],&IV,sizeof(IV));
    write(fds[1],msg,3+sizeof(IV));
    SLEEP(1);

    /* Initialize FROM to point to Client's ip:port */
    memset(&from,0,sizeof(from));
    fromlen = sizeof(from);
    from = sa_cli;

    /* Write FROM to the child process */
    strcpy(msg,"FROM:");
    memcpy(&msg[5],&from,sizeof(from));
    write(fds[1],msg,5+sizeof(from));
    SLEEP(1);

    /* Notify the child process to be ready to send/receive packets */
    strcpy(msg,"START");
    write(fds[1],msg,strlen(msg)+1);
    SLEEP(1);
    }//if VALID
    }// ssl
    }//else if TCP
    }// while
    }// if Server-PARENT
    else { /* UDP use keys */
    /* Server-Child process */
    int okToStart = 0;
    struct sockaddr_in from;

    /* close our copy of the write end of pipe */
    close(fds[1]);

    /* Set unblock pipe read */

    /* Tunnel connection */

    /* Keep reading from fds[0], fd_udp, fd_tunnel */

    /* If notification is not received, do not send/receive */

    while (1) {
    FD_ZERO(&fdset);
    FD_SET(fds[0], &fdset); /* Pipe from Parent */
    FD_SET(fd_tunnel,&fdset); /* Tunnel */
    FD_SET(fd_udp, &fdset); /* Net-UDP */
    if (select(fds[0]+fd_tunnel+fd_udp+1,&fdset,NULL,NULL,NULL) < 0) PERROR("Server-Child select");
    if (FD_ISSET(fds[0], &fdset)) {
    memset(buf,0,sizeof(buf));
    l = read(fds[0], buf, sizeof(buf));
    if (l < 0) PERROR("read");
    if (!strncmp(buf,"KEY:",4)) {
    if (DEBUG) printf("KEY: [%s] received from Server-Parent\n",&buf[4]);
    strncpy(KEY,&buf[4],sizeof(KEY));
    }//if
    else if (!strncmp(buf,"IV:",3)) {
    if (DEBUG) {
    printf("IV: [");
    dumpBuf(&buf[3],sizeof(IV));
    printf("] received from Server-Parent\n",&buf[3]);
    }//if
    memcpy(IV,&buf[3],sizeof(IV));
    }//else if
    else if (!strncmp(buf,"FROM:",5)) {
    if (DEBUG) printf("FROM: received from Server-Parent\n");
    memcpy(&from,&buf[5],sizeof(from));
    }//else if
    else if (!strncmp(buf,"START",5)) {
    if (DEBUG) printf("START received from Server-Parent\n");
    okToStart = 1;
    /*if (DEBUG)*/ printf("Server Tunnel running...\n");
    }//else if
    else if (!strncmp(buf,"STOP",4)) {
    if (DEBUG) printf("STOP received from Server-Parent\n");
    okToStart = 0;
    /*if (DEBUG)*/ printf("Server Tunnel stopped\n");
    }//else if
    else okToStart = 0;
    }//if
    else if (FD_ISSET(fd_tunnel, &fdset)) {
    if (okToStart) {
    /* wait for data from tunnel, then encrypt+hash, then send to "remote/from" network socket */
    if (DEBUG) write(1,">", 1);
    l = read(fd_tunnel, buf, sizeof(buf));
    if (l < 0) PERROR("read");
    else {
    if (DEBUG) {printf("\nTx=%d[",l); dumpBuf(buf,l); printf("]\n");}
    outl = sizeof(out);
    do_encrypt(buf,l,out,&outl);
    if (outl > MAX_BLOCK_LENGTH) PERROR("Encrypt Too Big");
    if (DEBUG) {printf("\nEncrypt=%d[",outl); dumpBuf(out,outl); printf("]\n");}
    createHash(buf, l, md_value, &md_len);
    if ((outl + md_len) > MAX_BLOCK_LENGTH) PERROR("Encrypt+Hash Too Big");
    if (DEBUG) {printf("Hash=%d[",md_len); dumpBuf(md_value,md_len); printf("]\n");}
    for (i=0; i<md_len; i++) {out[outl+i] = md_value[i];}
    outl += md_len;
    if (sendto(fd_udp, out, outl, 0, (struct sockaddr *)&from, sizeof(from)) < 0)PERROR("sendto");
    }//else
    }//if okToStart
    else if (!okToStart) { /* "read" from Tunnel and DISCARD */
    l = read(fd_tunnel, buf, sizeof(buf));
    if (l < 0) PERROR("read");
    }//else if !okToStart
    }//else if
    else if (FD_ISSET(fd_udp, &fdset)) {
    if (okToStart) {
    /* wait for data from "remote/from" node, audit connection, decrypt+rehash+audit,
    then forward to tunnel */
    if (DEBUG) write(1,"<", 1);
    /* do *not* use "from" so that we can audit which node the recieved packet comes from */
    soutlen = sizeof(sout);
    l = recvfrom(fd_udp, buf, sizeof(buf), 0, (struct sockaddr *)&sout, &soutlen);
    if (l < 0) {
    printf("fd_udp=%d,buf=%p,bufsize=%d,flags=%d,sockaddr=%p,soutlen=%p\n",
    fd_udp,buf,sizeof(buf),0,&sout,&soutlen);
    PERROR("recvfrom");
    }//if
    else {
    /* audit to make sure data came from node "established" at connection time */
    if ((sout.sin_addr.s_addr != from.sin_addr.s_addr) || (sout.sin_port != from.sin_port)) {
    #if 0
    printf("Got packet from %s:%i instead of %s:%i\n",
    (char *)inet_ntoa(sout.sin_addr/*.s_addr*/), ntohs(sout.sin_port),
    (char *)inet_ntoa(from.sin_addr/*.s_addr*/), ntohs(from.sin_port));
    printf("Updating FROM with this new ip:port\n");
    #endif
    from = sout;
    }//if
    else
    {
    /* data from correct node, subtract off HASH, decrypt+rehash, audit hash,
    then forward to tunnel */
    l -= HASH_LENGTH;
    outl = sizeof(out);
    if (DEBUG) {printf("\nRx=%d[",l); dumpBuf(buf,l); printf("]\n");}
    if (DEBUG) {printf("Hash=%d[",HASH_LENGTH); dumpBuf(&buf[l],HASH_LENGTH); printf("]\n");}
    do_decrypt(buf,l,out,&outl);
    if (DEBUG) {printf("\nDecrypt=%d[",outl); dumpBuf(out,outl); printf("]\n");}
    createHash(out, outl, md_value, &md_len);
    if (!isEqual(&buf[l], md_value, md_len)) PERROR("HASH");
    if (write(fd_tunnel, out, outl) < 0) PERROR("write");
    }//else
    }//else
    }//if okToStart
    else if (!okToStart) { /* "receive" from node and DISCARD */
    soutlen = sizeof(sout);
    l = recvfrom(fd_udp, buf, sizeof(buf), 0, (struct sockaddr *)&sout, &soutlen);
    if (l < 0) {
    printf("fd_udp=%d,buf=%p,bufsize=%d,flags=%d,sockaddr=%p,soutlen=%p\n",
    fd_udp,buf,sizeof(buf),0,&sout,&soutlen);
    PERROR("recvfrom");
    }//if
    }//else if !okToStart
    }//else if
    }//while
    }// else Server-CHILD
    }// else SERVER

    }//main