Skip to content →

Category: Projects & Toys

Here are the projects I’ve participated in, and some toys I wrote.

SNS Privacy Control

I wrote a paper on privacy control in social networking sites. You can find the full paper at http://www.cse.hut.fi/en/publications/B/11/papers/li.pdf. Here’s the abstract:

As social networking services become increasingly popular, more and more attacks against users’ private information are reported. As a result, privacy protection becomes an important concern among users. Previous research has produced many different approaches to deal with privacy control in different social networking sites. In this paper, we make a survey on different approaches proposed to tackle the privacy issue in social networking sites. In particular, we put current approaches into three general categories, i.e. approaches addressing end users’ active participation, security automation based on machine learning algorithms, and privacy preserving by using a decentralized architecture for social networkingservices. Then we introduce and analyze some of the approaches in each category. Finally, we give some suggestions that may help privacy control in online social networks.

Leave a Comment

Testing Bouncer

Static testing is used to test the bouncer, that is, a separate set of test cases and sample outputs are provided. And the bouncer is given the test cases and its outputs are compared with sample outputs.

A verifier is used to accomplish the above function, and another bash script is written to automate the process.

The bash script is as follows:

#!/bin/sh

USAGE="Usage: $0 [-v] bouncer" 
CMD="./verifier "
TESTDIR="cases"

while getopts "v" optname
do
    case "$optname" in
        "v")
        CMD="./verifier -v "
        ;;
        *)
        echo $USAGE

exit 1
        ;;
    esac
done
shift $(($OPTIND-1))
BCER=$*

if [ -f "$BCER" ]
then
    find "$TESTDIR" -iname "*pcap" | while read i
do
    if [ -f "$i" ] && [ -f "$i"-ref ]
    then
        echo "======================================================================"
        echo "Verifying file: $i"
        $BCER -t -l 1.1.1.1 -s 3.3.3.3 <"$i" >"$i"-out
	BCRT=$?
	echo
"----------------------------------------------------------------------"
        if [ $BCRT != 0 ]
        then
            echo "$BCER returned with $?"
            echo "Exiting $0"
            exit 1
        fi
        $CMD -i "$i"-out -r "$i"-ref
	CMDRT=$?
	echo "----------------------------------------------------------------------"
        echo "Result for $i:"
	echo "$CMDRT packets has passed validation"
        echo
"======================================================================"
	echo "n"
    fi
done
else
    echo $USAGE
fi

exit 0

The verifier is as follows:

#include "verifier.h"
typedef unsigned short u16;
typedef unsigned long u32;

/* CRC
 * Adopted from http://www.netfor2.com/ipsum.htm

*/
u16 ip_sum_calc(u16 len_ip_header, u16 buff[]) {
    u16 word16;
    u32 sum = 0;
    u16 i;

    // make 16 bit words out of every two adjacent 8 bit words in the packet
    // and add them up
    for (i = 0; i < len_ip_header; i = i + 2) {
        word16 = ((buff[i] << 8)&0xFF00)+(buff[i + 1]&0xFF);
        sum = sum + (u32) word16;
    }

    // take only 16 bits out of the 32 bit sum and add up the carries
    while
(sum >> 16)
        sum = (sum & 0xFFFF)+(sum >> 16);

    // one's complement the result
    sum = ~sum;

    return ((u16) sum);
}

int usage() {
    fprintf(stderr, "Usage: verifier [-v] -i input_file -r reference_filen");
    exit(1);
}

int main(int argc, char **argv) {
    int verbose = 0;
    opterr = 0;
    char c;
    char *ifilename, *rfilename;
    struct stat st;
    while ((c = getopt(argc, argv,
"vi:r:")) != -1) {
        if (c == 'i') {
            ifilename = optarg;
            if (stat(ifilename, &st) != 0) {
                fprintf(stderr, "File does not exist!n");
                exit(1);
            }
        } else if (c == 'r') {
            rfilename = optarg;
            if (stat(rfilename, &st) != 0) {
                fprintf(stderr, "File does not exist!n");
                exit(1);
            }

} else if (c == 'v') {
            verbose = 1;
        } else {
            usage();
        }
    }

    pcap_t *ihandle = NULL, *rhandle = NULL;
    char errbufi[PCAP_ERRBUF_SIZE], errbufr[PCAP_ERRBUF_SIZE];
    ihandle = pcap_open_offline(ifilename, errbufi);
    rhandle = pcap_open_offline(rfilename, errbufr);
    struct pcap_pkthdr ihdr, rhdr;
    const u_char *ipkt, *rpkt;
    ipkt = malloc(MAX_PACKET_SIZE);
    rpkt =
malloc(MAX_PACKET_SIZE);
    if (ipkt == NULL || rpkt == NULL) {
        perror("malloc");
        exit(1);
    }
    int pc = -1; /* Packet counter */
    char flags[MAX_PACKETS_NO]; /* Broken or Not */
    memset(flags, PKT_OK, MAX_PACKETS_NO);
    while ((ipkt = pcap_next(ihandle, &ihdr)) && (rpkt = pcap_next(rhandle, &rhdr))) {
        pc++;
        /* Typecasting input packet*/
        const struct
sniff_ethernet *iethernet; /* The ethernet header */
        const struct sniff_ip *iip; /* The IP header */
        const struct sniff_icmp *iicmp; /* The ICMP header */
        const char *ipayload; /* Packet payload */
        u_int isize_ip;
        iethernet = (struct sniff_ethernet*) (ipkt);
        iip = (struct sniff_ip*) (ipkt + SIZE_ETHERNET);
        isize_ip = IP_HL(iip)*4;
        if (isize_ip < 20) {
            fprintf(stderr, "   * Invalid
IP header length: %u bytesn", isize_ip);
            continue;
        }
        iicmp = (struct sniff_icmp*) (ipkt + SIZE_ETHERNET + isize_ip);
        ipayload = (char *) (ipkt + SIZE_ETHERNET + isize_ip + SIZE_ICMP);
        /* Typecasting reference packet*/
        const struct sniff_ethernet *rethernet; /* The ethernet header */
        const struct sniff_ip *rip; /* The IP header */
        const struct sniff_icmp *ricmp; /* The ICMP header */

const char *rpayload; /* Packet payload */
        u_int rsize_ip;
        rethernet = (struct sniff_ethernet*) (rpkt);
        rip = (struct sniff_ip*) (rpkt + SIZE_ETHERNET);
        rsize_ip = IP_HL(rip)*4;
        if (rsize_ip < 20) {
            fprintf(stderr, "   * Invalid IP header length: %u bytesn", rsize_ip);
            continue;
        }
        ricmp = (struct sniff_icmp*) (rpkt + SIZE_ETHERNET + rsize_ip);
        rpayload = (char *) (rpkt
+ SIZE_ETHERNET + rsize_ip + SIZE_ICMP);

        /* Here comes the validation process */
        /* Check IP checksum */
        u16 ipbuf[isize_ip];
        u16 ipsum = ntohs(iip->ip_sum);
        int i;
        for (i = 0; i < isize_ip; i++) {
            if (i == 10 || i == 11)
                ipbuf[i] = 0x00;
            else
                ipbuf[i] = *((u_char *) (ipkt + SIZE_ETHERNET + i));
        }
        if (ipsum !=
ip_sum_calc(isize_ip, ipbuf)) {
            flags[pc] = ERR_IP_SUM;
            continue;
        }
        /* Check ICMP checksum */
        int plen = ihdr.len - SIZE_ETHERNET - isize_ip - 8;
        u16 icmpbuf[8 + plen];
        u16 icmpsum = ntohs(iicmp->icmp_sum);
        for (i = 0; i < 8; i++) {
            if (i == 2 || i == 3)
                icmpbuf[i] = 0x00;
            else
                icmpbuf[i] = *((u_char *) (ipkt +
SIZE_ETHERNET + isize_ip + i));
        }
        for (i = 0; i < plen; i++) {
            icmpbuf[i + 8] = *((u_char *) (ipayload + i));
        }
        if (icmpsum != ip_sum_calc(8 + plen, icmpbuf)) {
            flags[pc] = ERR_ICMP_SUM;
            continue;
        }
        /* Check IP TTL */
        if (iip->ip_ttl <= 0) {
            flags[pc] = ERR_IP_TTL;
            continue;
        }
        /* Check IP source
address */
        if (iip->ip_src.s_addr != rip->ip_src.s_addr) {
            flags[pc] = ERR_IP_SADDR;
            continue;
        }
        /* Check IP destinataion address */
        if (iip->ip_dst.s_addr != rip->ip_dst.s_addr) {
            flags[pc] = ERR_IP_DADDR;
            continue;
        }

        /* Check ICMP type */
        if (iicmp->icmp_type != ricmp->icmp_type) {
            flags[pc] = ERR_ICMP_TYPE;

continue;
        }
        /* Check ICMP ID */
        if (iicmp->icmp_id != ricmp->icmp_id) {
            flags[pc] = ERR_ICMP_ID;
            continue;
        }
        /* Check ICMP sequence number */
        if (iicmp->icmp_sequence != ricmp->icmp_sequence) {
            flags[pc] = ERR_ICMP_SEQ;
            continue;
        }
    }
    pc++;
    int bc = 0; /* Broken packet counter */
    int i;
    for (i = 1; i <= pc;
i++) {
        if (flags[i-1])
            bc++;
        if (verbose) {
            switch (flags[i-1]) {
                case PKT_OK:
                    fprintf(stdout, "Validation passed: %d/%dn", i, pc);
                    break;
                case ERR_IP_SUM:
                    fprintf(stdout, "IP checksum failed: %d/%dn", i, pc);
                    break;
                case ERR_IP_TTL:
                    fprintf(stdout, "IP TTL check
failed: %d/%dn", i, pc);
                    break;
                case ERR_IP_SADDR:
                    fprintf(stdout, "IP source address check failed: %d/%dn", i, pc);
                    break;
                case ERR_IP_DADDR:
                    fprintf(stdout, "IP destination address check failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_SUM:
                    fprintf(stdout, "ICMP checksum
failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_TYPE:
                    fprintf(stdout, "ICMP type check failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_ID:
                    fprintf(stdout, "IP ID check failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_SEQ:
                    fprintf(stdout, "IP sequence check failed: %d/%dn", i, pc);

break;
                default:
                    fprintf(stderr, "ERROR: Unknwon error type.n");
                    break;
            }
        }
    }
    if (bc == 0)
        fprintf(stdout, "%d packets passed validationn", pc);
    else
        fprintf(stdout, "%d out of %d packets failed validationn", bc, pc);
    /* Return number of packets passed validation */
    return (pc - bc);
}
Leave a Comment

Technical Details

We define a BPF filter as “icmp and dst host <bouncer_ip>”, which filters out all none-ICMP packets and packets not destined to the bouncer. The we compile this filter and set the filter to the capture device.

When the capture device captures a packet, a process_pkt function is called to process the packet. And that is where we validate the packets and then, if the packet is valid, update
the packet and then send it out, or write the packet to a dump file.

The process of validating the packets are as follows:

  1. Validate checksum of IP header.
  2. Validate TTL of IP.
  3. Validate IP source address
  4. Validate ICMP header checksum. 
  5. Validate ICMP type and code. 

Since we write the packet to a dump file in a separate function, so a pointer to the dump file handler is passed to the processing function when the process_pkt function is called. The same method is used to pass server IP address and test mode flag.

We keep a linked list of all the ICMP echo requests. When a ICMP echo reply is received, we go through the linked list to find out where
the original echo request comes from.

The code is as follows.

bouncer.h:

/* Global definitions for the port bouncer
 * Packet headers and so on
 */

#define _BSD_SOURCE 1

#include 
#include 
#include 
#include 
#include 

/* PCAP declarations*/
#include 

/* Standard networking declaration */
#include 
#include 
#include 

/*
 * The following system include files should provide you with the 
 * necessary declarations for Ethernet, IP, and TCP headers
 */

#include 
#include 
#include 
#include 

/* Add any otherdeclarations you may need here... */

#define MAX_PACKET_SIZE 65535

/* Ethernet addresses are 6 bytes */
//#define ETHER_ADDR_LEN	6

/* Ethernet header */
struct sniff_ethernet {
    u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
    u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
    u_short ether_type; /* IP? ARP? RARP? etc */
};

/* IP header */
struct sniff_ip {
    u_char ip_vhl; /*version << 4 | header length >> 2 */
    u_char ip_tos; /* type of service */
    u_short ip_len; /* total length */
    u_short ip_id; /* identification */
    u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000		/* reserved fragment flag */
#define IP_DF 0x4000		/* dont fragment flag */
#define IP_MF 0x2000		/* more fragments flag */
#define IP_OFFMASK 0x1fff	/* mask for fragmenting bits */
    u_char ip_ttl; /*time to live */
    u_char ip_p; /* protocol */
    u_short ip_sum; /* checksum */
    struct in_addr ip_src, ip_dst; /* source and dest address */
};
#define IP_HL(ip)		(((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)		(((ip)->ip_vhl) >> 4)

/* ICMP header */
struct sniff_icmp {
    u_char icmp_type;
#define ICMP_ECHO 0x8
#define ICMP_REPLY 0x0
    u_char icmp_code;
    u_int16_t icmp_sum;
    u_int16_t icmp_id;
u_int16_t icmp_sequence;
};

#define SIZE_ETHERNET 14
#define SIZE_ICMP 8

#define IP_QUAD(ip)  (ip)>>24,((ip)&0x00ff0000)>>16,((ip)&0x0000ff00)>>8,((ip)&0x000000ff)

struct request{
    struct request* next;
    struct sniff_ip* ip;
    struct sniff_icmp* icmp;
};

process_pkt.c:

#include "bouncer.h"

/* CRC
 * Adopted from http://www.netfor2.com/ipsum.htm
 */
typedef unsigned short u16;
typedef unsigned long u32;

u16 ip_sum_calc(u16 len_ip_header, u16 buff[]) {
    u16 word16;
    u32 sum =0;
    u16 i;

    // make 16 bit words out of every two adjacent 8 bit words in the packet
    // and add them up
    for (i = 0; i < len_ip_header; i = i + 2) {
        word16 = ((buff[i] << 8)&0xFF00)+(buff[i + 1]&0xFF);
        sum = sum + (u32) word16;
    }

    // take only 16 bits out of the 32 bit sum and add up the carries
    while (sum >> 16)
        sum = (sum & 0xFFFF)+(sum >> 16);

    // one'scomplement the result
    sum = ~sum;

    return ((u16) sum);
}

void process_pkt(u_char *args, struct pcap_pkthdr *header,
        u_char *packet) {
    if(header->caplen < header->len){
        fprintf(stderr, "ERROR: Packet data not captured completely\n");
        return;
    }
    /* Main function to perform the bouncing */
    u_char testmode = *args;
    char *serv_addr = (char *) (args + 1);
    u_int32_t dst_addr =inet_addr(serv_addr);
    //pcap_dumper_t *dumper = NULL;
    u_char *dumper = NULL;
    memcpy(&dumper, args + 2 + strlen(serv_addr), sizeof (pcap_dumper_t *));
    struct request ** prequests = NULL;
    memcpy(&prequests, args + 2 + strlen(serv_addr) + sizeof (pcap_dumper_t *), sizeof (struct request**));
    //struct request *requests = *prequests;

    /* Typecasting packet*/
    struct sniff_ethernet *ethernet; /* The ethernet header */
struct sniff_ip *ip; /* The IP header */
    struct sniff_icmp *icmp; /* The ICMP header */
    char *padding; /* Packet padding */
    u_int size_ip;
    ethernet = (struct sniff_ethernet*) (packet);
    ip = (struct sniff_ip*) (packet + SIZE_ETHERNET);
    size_ip = IP_HL(ip)*4;
    u_int32_t srcip = ntohl(ip->ip_src.s_addr);
    if (size_ip < 20 || size_ip > header->len - SIZE_ETHERNET) {
        fprintf(stderr, "ERROR: Invalid IP header length: %ubytes\n", size_ip);
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    icmp = (struct sniff_icmp*) (packet + SIZE_ETHERNET + size_ip);
    padding = (u_char *) (packet + SIZE_ETHERNET + size_ip + SIZE_ICMP);

    /* Validate the packet */
    /* Validate IP header */
    /* Check IP version */
    u_char v = ip->ip_vhl>> 4;
    if(v != 4){
        fprintf(stderr, "ERROR: Wrong IP verison %d\n", v);
        return;
    }
    /* Validate CRC */
    u16 ipbuf[size_ip];
    u16 ipsum = ntohs(ip->ip_sum);
    int i;
    for (i = 0; i < size_ip; i++) {
        if (i == 10 || i == 11)
            ipbuf[i] = 0x00;
        else
            ipbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + i));
    }
    if (ipsum != ip_sum_calc(size_ip, ipbuf)) {
fprintf(stderr, "ERROR: ip checksum mismatch. Dropping packet from %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    /* Validate TTL */
    if (ip->ip_ttl <= 0) {
        fprintf(stderr, "ERROR: ip TTL expired. Dropping packet from %u.%u.%u.%u\n",
IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    /* Validate IP source address */
    if (srcip == 0x00000000 || srcip >= 0xE0000000
            || (srcip & 0x000000FF) == 0x000000FF || (srcip & 0x000000FF) == 0x00000000) {
        fprintf(stderr, "ERROR: ip source address invalid. Dropping packetfrom %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }

    /* Validate ICMP header */
    /* Validate ICMP type and code */
    if (!(icmp->icmp_code == 0 && (icmp->icmp_type == 0 || icmp->icmp_type == 8))) {
        fprintf(stderr, "ERROR: icmp type orcode unsupported. Dropping packet from %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    /* Validate CRC */
    int plen = header->len - SIZE_ETHERNET - size_ip - 8;
    u16 icmpbuf[8 + plen];
    u16 icmpsum = ntohs(icmp->icmp_sum);
    for (i = 0; i < 8;i++) {
        if (i == 2 || i == 3)
            icmpbuf[i] = 0x00;
        else
            icmpbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + size_ip + i));
    }
    for (i = 0; i < plen; i++) {
        icmpbuf[i + 8] = *((u_char *) (padding + i));
    }
    if (icmpsum != ip_sum_calc(8 + plen, icmpbuf)) {
        fprintf(stderr, "ERROR: icmp checksum mismatch. Dropping packet from %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if(testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }

    /* Update the packet */
    if (icmp->icmp_type == 0) {
        /* ICMP echo reply */
        ip->ip_src.s_addr = ip->ip_dst.s_addr;
        /* Search the linked list for client address */
        if (*prequests == NULL) {
            fprintf(stderr, "ERROR: process_pkt: nullrequest linked list\n");
            return;
        } else {
            /* Find where the request comes from */
            struct request *r;
            for (r = *prequests; r != NULL; r = r->next) {
                if (r->icmp->icmp_id == icmp->icmp_id && r->icmp->icmp_sequence == icmp->icmp_sequence) {
                    ip->ip_dst.s_addr = r->ip->ip_src.s_addr;
                    break;
                }
}
            if (r == NULL) {
                fprintf(stderr, "ERROR: no match echo requests in stack\n");
                return;
            }
            /* Remove the request from the linked list */
            struct request *rr = *prequests;
            if (rr == r) {
                free(rr->icmp);
                free(rr->ip);
                *prequests = r->next;
                free(r);
            } else {
                while (rr!= r)
                    rr = rr->next;
                free(rr->icmp);
                free(rr->ip);
                rr->next = r->next;
                free(r);
            }
        }
    } else {
        /* ICMP echo request */
        /* Add new request to linked list */
        struct request *r;
        r = malloc(sizeof (struct request));
        if (r == NULL) {
            perror("ERROR: process_pkt:malloc");
            return;
        }
        struct sniff_ip *ipt = malloc(sizeof (struct sniff_ip));
        if (ipt == NULL) {
            perror("ERROR: process_pkt: malloc");
            return;
        }
        memcpy(ipt, ip, sizeof (struct sniff_ip));
        r->ip = ipt;
        struct sniff_icmp* icmpt = malloc(sizeof (struct sniff_icmp));
        if (icmpt == NULL) {
            perror("ERROR: process_pkt: malloc");
return;
        }
        memcpy(icmpt, icmp, sizeof (struct sniff_icmp));
        r->icmp = icmpt;
        r->next = *prequests;
        *prequests = r;
        /* Update destination address */
        ip->ip_src.s_addr = ip->ip_dst.s_addr;
        ip->ip_dst.s_addr = dst_addr;
    }
    /* Recaculate CRC */
    for (i = 0; i < size_ip; i++) {
        if (i == 10 || i == 11)
            ipbuf[i] = 0x00;
        else
ipbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + i));
    }
    ip->ip_sum = htons(ip_sum_calc(size_ip, ipbuf));
    if (testmode == 0) {
        pcap_t *handle = NULL;
        char errbuf[PCAP_ERRBUF_SIZE], *device = "tap0";
        memset(errbuf, 0, PCAP_ERRBUF_SIZE);
        if ((handle = pcap_open_live(device, MAX_PACKET_SIZE, 1, 512, errbuf)) == NULL) {
            fprintf(stderr, "ERROR: %s\n", errbuf);
            exit(1);
        }
        /*Send the packet to network */
        if (pcap_sendpacket(handle, packet, header->len) != 0) {
            perror("ERROR: process_pkt: pcap_sendpacket");
            exit(1);
        }
        pcap_close(handle);
        fprintf(stderr, "Bouncer: packet sent\n");
    } else {
        /* Or put it back on stdout */
        int i = 0;
        for (i = 0; i < ETHER_ADDR_LEN; i++)
            ethernet->ether_shost[i] = 1;
        if(icmp->icmp_type == 0) {
            for (i = 0; i < ETHER_ADDR_LEN; i++)
                ethernet->ether_dhost[i] = 2;
        } else if (icmp->icmp_type == 8) {
            for (i = 0; i < ETHER_ADDR_LEN; i++)
                ethernet->ether_dhost[i] = 3;
        }
        pcap_dump(dumper, header, packet);

////////////////////////////////////////////////////////////////////////
/*
        pcap_t *handle = NULL;
        char errbuf[PCAP_ERRBUF_SIZE], *device = "tap0";
        memset(errbuf, 0, PCAP_ERRBUF_SIZE);
        if ((handle = pcap_open_live(device, MAX_PACKETS_NO, 1, 512, errbuf)) == NULL) {
            fprintf(stderr, "ERROR: %s\n", errbuf);
            exit(1);
        }
        if (pcap_sendpacket(handle, packet, header->len) != 0) {
perror("ERROR: process_pkt: pcap_sendpacket");
            exit(1);
        }
        pcap_close(handle);
        fprintf(stderr, "Packet sent\n");
*/
        ////////////////////////////////////////////////////////////////////////

    }
    return;
}
Leave a Comment

Packet Bouncer Overview

A bouncer bounces packets it receives from clients to servers. This is useful when people want to hide their internal network topologies. In this report, we examine a very simple case of bouncer — a bouncer that only bounces ICMP requests. When it receives an ICMP echo request, it modify the packet and forward it to the server specified by the user. After receiving the echo reply packet, it
again modifies the packet and finds out where to send the packet and then sends out the reply packet.

Leave a Comment

Code

The code looks as follows.


#include 
#include 
#include 
#include 
#include
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "event.h"
#include "rudp.h"
#include "rudp_api.h"

//#define DROP 3

rudp_socket_t rs_head = NULL;

peer_t find_peer(peer_t *firstp, structsockaddr_in* sa) {
    peer_t p;
    //int pc = 0;
    for (p = *firstp; p != NULL; p = p->next) {
        //printf("peer count: %d\n", ++pc);
        if (memcmp(p->sa, sa, sizeof (struct sockaddr_in)) == 0)
            return p;
    }
    //printf("peer not found, creating new peer\n");
    p = malloc(sizeof (struct peer));
    if (p == NULL) {
        perror("find_peer: malloc");
        return NULL;
    }
    struct sockaddr_in*sin;
    sin = malloc(sizeof (struct sockaddr_in));
    if (sin == NULL) {
        perror("peerfind: malloc");
        return NULL;
    }
    memcpy(sin, sa, sizeof (struct sockaddr_in));
    p->sa = sin;
    p->packets = NULL;
    p->status = PEER_STATUS_INIT;
    p->sb = 0;
    p->sm = RUDP_WINDOW - 1;
    p->sn = 0;
    p->next = (*firstp);
    *firstp = p;
    return p;
}

int remove_peer(peer_t *firstp,peer_t rp) {
    peer_t p, *pp;
    pp = firstp;
    for (p = *firstp; p != NULL; p = p->next) {
        if (p == rp) {
            *pp = rp->next;
            free(rp->sa);
            free(rp);
            return 0;
        }
        pp = &p->next;
    }
    return -1;
}

/*
 * Add packet into a linked list.
 * Inserted in a way that packets with smaller seqno are always placed in front
 */
int add_packet(packet_t*packet_head, packet_t *pkt) {
    if (*packet_head == NULL) {
        (*pkt)->next = *packet_head;
        *packet_head = *pkt;
    } else {
        packet_t *p = malloc(sizeof (packet_t));
        for ((*p) = *packet_head; (*p) != NULL; (*p) = (*p)->next) {
            if ((*pkt)->header->seqno == (*p)->header->seqno + 1) {
                (*pkt)->next = (*p)->next;
                (*p)->next = *pkt;
                break;
}
        }
        //return -1;
    }
    //printf("packet added: %d type %d\n", (*pkt)->header->seqno, (*pkt)->header->type);
    return 0;
}

/*
 * Remove a packet from a linked list
 */
int remove_packet(packet_t *packet_head, packet_t pkt) {
    packet_t p, *pp;
    pp = packet_head;
    for (p = *packet_head; p != NULL; p = p->next) {
        if (pkt == p) {
            //printf("packet seq no %d removed\n",pkt->header->seqno);
            *pp = p->next;
            free(p);
            return 0;
        }
        pp = &p->next;
    }
    return -1;
}

/*
 * rudp_socket: Create a RUDP socket.
 * May use a random port by setting port to zero.
 */

rudp_socket_t rudp_socket(int port32) {
    int sockfd;
    sockfd = socket(PF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("rudp_socket: socket");
return NULL;
    }
    struct sockaddr_in *sin;
    sin = malloc(sizeof (struct sockaddr_in));
    if (sin == NULL) {
        perror("rudp_socket: malloc");
        return NULL;
    }
    sin->sin_addr.s_addr = htonl(INADDR_ANY);
    //sin->sin_addr.s_addr = inet_addr("127.0.0.1");
    sin->sin_family = AF_INET;
    uint16_t port = port32;
    if (port == 0) {
        srand(time(0));
        port = (rand() % (65535 - 1024)) + 1024;
sin->sin_port = htons(port);
        while (bind(sockfd, (struct sockaddr*) sin, sizeof (struct sockaddr_in)) < 0) {
            port = (rand() % (65535 - 1024)) + 1024;
            sin->sin_port = htons(port);
        }
    } else {
        sin->sin_port = htons(port);
        if (bind(sockfd, (struct sockaddr*) sin, sizeof (struct sockaddr_in)) < 0) {
            perror("rudp_socket: bind");
            return NULL;
        }
}
    //printf("rudp_socket: bind successfully on port: %d\n", port);
    rudp_socket_t rs;
    rs = malloc(sizeof (struct rudp_socket));
    if (rs == NULL) {
        perror("rudp_socket: malloc");
        return NULL;
    }
    rs->sockfd = sockfd;
    rs->peers = NULL;
    rs->next = rs_head;
    rs_head = rs;
    /*
        int rc = 0;
        rudp_socket_t prs;
        for(prs = rs_head; prs != NULL; prs =prs->next)
            printf("rudp socket count: %d\n", ++rc);
     */
    event_fd(rs->sockfd, rudp_listen, rs, "rudp_listen");
    return rs;
}

/*
 * Listens on the socket
 */
int rudp_listen(int fd, void *arg) {
    rudp_socket_t rs;
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        if (rs->sockfd == fd)
            break;
    }
    if (rs == NULL) {
        return -1;
    }
    int (*handler)(rudp_socket_t,struct sockaddr_in *, char *, int);

    /*
        client_addr.sin_family = AF_INET;
        client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        client_addr.sin_port = htons(0);
     */
    unsigned int clientlen = sizeof(struct sockaddr);
    struct sockaddr client_sockaddr;
    int bufsize = RUDP_MAXPKTSIZE + sizeof (struct rudp_hdr);
    int msglen = 0;
    char message[bufsize];
    memset(message, 0, bufsize);
    msglen =recvfrom(rs->sockfd, message, bufsize, 0, & client_sockaddr, &clientlen);
    if (msglen < 0) {
        perror("rudp_listen: recvfrom");
        return -1;
    }
    struct sockaddr_in client_addr;
    memcpy(&client_addr, &client_sockaddr, clientlen);
    if (ntohs(client_addr.sin_port) == 0) {
        perror("rudp_listen: recvfrom port 0");
        return -1;
    }
    //printf("received from port: %d\n",ntohs(client_addr.sin_port));
    peer_t p = find_peer(&(rs->peers), &client_addr);
    if (p == NULL) {
        return -1;
    }
    //printf("peer: %d\n", ntohs(p->sa->sin_port));
    if (msglen < 0) {
        perror("rudp_recvfrom: recvfrom");
        return -1;
    } else if (msglen < (sizeof (struct rudp_hdr))) {
        fprintf(stderr, "rudp_recvfrom: error receiving packet");
        return -1;
    }
    structrudp_hdr *msgheader = malloc(sizeof (struct rudp_hdr));
    if (msgheader == NULL) {
        perror("rudp_recvfrom: malloc");
        return -1;
    }
    memcpy(msgheader, message, sizeof (struct rudp_hdr));

    //printf("status=%d sn=%d\n", p->status, p->sn);
    //printf("message from port %d, type=%d seqno=%d\n", ntohs(p->sa->sin_port), msgheader->type, msgheader->seqno);
    struct rudp_hdr *replyheader = malloc(sizeof (structrudp_hdr));
    if (replyheader == NULL) {
        perror("rudp_recvfrom: malloc");
        return -1;
    }
    replyheader->version = RUDP_VERSION;
    switch (p->status) {
        case PEER_STATUS_INIT:
            if (msgheader->type == RUDP_SYN) {
                p->status = PEER_STATUS_ACTIVE;
                p->sn = msgheader->seqno + 1;
                replyheader->seqno = msgheader->seqno + 1;
replyheader->type = RUDP_ACK;
                sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                        (struct sockaddr*) & client_addr, clientlen);
                //printf("ACK sent. Turns active\n");
            }
            break;
        case PEER_STATUS_PASSIVE:
            if (msgheader->type == RUDP_ACK && msgheader->seqno == 1) {
                p->status = PEER_STATUS_ACTIVE;
                packet_t pkt;
for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                    if (pkt->header->seqno == msgheader->seqno - 1) {
                        pkt->status = ACKED;
                        //printf("syn packet no. %d acked\n", pkt->header->seqno);
                        break;
                    }
                }
                //p->sn = 1;
                p->sb = 1;
                p->sm = RUDP_WINDOW;
    }
            break;
        case PEER_STATUS_ACTIVE:
            switch (msgheader->type) {
                case RUDP_ACK:
                    //printf("sb: %d sm: %d\n", p->sb, p->sm);
                    if (msgheader->seqno >= p->sb) {
                        packet_t pkt;
                        for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                            if (pkt->header->seqno == msgheader->seqno -    1) {
                                pkt->status = ACKED;
                                //printf("data packet no. %d acked\n", pkt->header->seqno);
                                break;
                            }
                        }
                        p->sm += msgheader->seqno - p->sb;
                        p->sb = msgheader->seqno;
                    }
                    break;
    case RUDP_DATA:
#ifdef DROP
                    srand(time(0));
                    if (rand() % DROP == 0) {
                        printf("packet dropped\n");
                        break;
                    }
#endif
                    if (msgheader->seqno == p->sn) {
                        p->sn = msgheader->seqno + 1;
                        replyheader->seqno = msgheader->seqno + 1;
    replyheader->type = RUDP_ACK;
                        handler = malloc(sizeof (int (*)()));
                        if (handler == NULL) {
                            perror("rudp_listen: malloc");
                            return -1;
                        }
                        handler = (int (*)(rudp_socket_t, struct sockaddr_in*, char *, int))arg;
                        void *data;
                        data = malloc(msglen -    sizeof (struct rudp_hdr));
                        if (data == NULL) {
                            perror("rudp_listen: malloc");
                            return -1;
                        }
                        memcpy(data, message + sizeof (struct rudp_hdr),
                                msglen - sizeof (struct rudp_hdr));
                        handler(rs, &client_addr, data, msglen - sizeof (struct    rudp_hdr));
                        sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                                (struct sockaddr*) & client_addr, clientlen);
                    }
                    break;
                case RUDP_SYN:
                    p->status = PEER_STATUS_ACTIVE;
                    p->sn = msgheader->seqno + 1;
                    replyheader->seqno = msgheader->seqno + 1;
    replyheader->type = RUDP_ACK;
                    sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                            (struct sockaddr*) & client_addr, clientlen);
                    break;
                case RUDP_FIN:
                    p->status = PEER_STATUS_CLOSED;
                    p->sn = msgheader->seqno + 1;
                    replyheader->seqno = msgheader->seqno + 1;
    replyheader->type = RUDP_ACK;
                    sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                            (struct sockaddr*) & client_addr, clientlen);
                    break;
                default:
                    fprintf(stderr, "rudp_listen: invalid RUDP type %d\n",
                            msgheader->type);
                    break;
            }
            break;
        case    PEER_STATUS_CLOSING:
            if (msgheader->type == RUDP_ACK && msgheader->seqno == p->sn + 1) {
                p->status = PEER_STATUS_CLOSED;
                //printf("peer closed\n");
            }
            break;
        case PEER_STATUS_CLOSED:
            if (msgheader->type == RUDP_FIN) {
                p->status = PEER_STATUS_CLOSED;
                p->sn = msgheader->seqno + 1;
    replyheader->seqno = msgheader->seqno + 1;
                replyheader->type = RUDP_ACK;
                sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                        (struct sockaddr*) & client_addr, clientlen);
            }
            /*
                        if (msgheader->type == RUDP_SYN) {
                            p->status = PEER_STATUS_ACTIVE;
    p->sn = msgheader->seqno + 1;
                            replyheader->seqno = msgheader->seqno + 1;
                            replyheader->type = RUDP_ACK;
                            sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                                    (struct sockaddr*) & client_addr, clientlen);
                        } else if (msgheader->type == RUDP_FIN) {
                            p->status =    PEER_STATUS_CLOSED;
                            p->sn = msgheader->seqno + 1;
                            replyheader->seqno = msgheader->seqno + 1;
                            replyheader->type = RUDP_ACK;
                            sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                                    (struct sockaddr*) & client_addr, clientlen);
                        }
             */
            break;
    default:
            fprintf(stderr, "rudp_listen: invalid peer status %d\n",
                    p->status);
            break;
    }
    return 0;
}

/*
 * Keep checking if there are packets to be sent out
 */
int rudp_send(int fd, void *arg) {
    event_timeout_delete(rudp_send, arg);
    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    rudp_socket_t rs;
    peer_t p;
    packet_t pkt;
    void *msg;
    int len = sizeof (struct rudp_hdr);
    //int rsc = 0;
    if (rs_head == NULL) {
        return 0;
    }
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        //printf("rudp socket count: %d\n", ++rsc);
        for (p = rs->peers; p != NULL; p = p->next) {
            if (p->status == PEER_STATUS_CLOSED) {
    if (remove_peer(&(rs->peers), p) < 0) {
                    perror("rudp_send: remove_peer");
                    return -1;
                } else {
                    event_timeout(t, rudp_send, NULL, "rudp_send");
                }
                return 0;
            } else if (p->status != PEER_STATUS_ACTIVE) {
                event_timeout(t, rudp_send, NULL, "rudp_send");
                return 0;
            }
        //int pc = 0;
            for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                //printf("pakcet %d: seq number %d status %d\n", ++pc, pkt->header->seqno, pkt->status);
                if (pkt->status == ACKED) {
                    if (remove_packet(&(p->packets), pkt) == -1) {
                        return -1;
                    }
                    event_timeout(t, rudp_send, NULL, "rudp_send");
        return 0;
                }
                if ((pkt->status == IN_QUEUE || pkt->status == TIMEOUT) &&
                        pkt->header->seqno >= p->sb &&
                        pkt->header->seqno <= p->sm) {
                    if (pkt->header->type == RUDP_FIN) {
                        p->status = PEER_STATUS_CLOSING;
                    }
                    len = sizeof (struct        rudp_hdr) + pkt->datalen;
                    msg = malloc(len);
                    if (msg == NULL) {
                        perror("rudp_send: malloc");
                        return -1;
                    }
                    memcpy(msg, pkt->header, sizeof (struct rudp_hdr));
                    memcpy(msg + sizeof (struct rudp_hdr), pkt->data, pkt->datalen);
                    sendto(rs->sockfd, msg,        len, 0,
                            (struct sockaddr*) p->sa, sizeof (struct sockaddr));
                    pkt->status = SENT;
                    pkt->trans_count++;
                    struct timeval now, interval, t;
                    gettimeofday(&now, NULL);
                    if (RUDP_TIMEOUT >= 1000) {
                        interval.tv_sec = RUDP_TIMEOUT / 1000;
                        interval.tv_usec = 0;
        } else {
                        interval.tv_sec = 0;
                        interval.tv_usec = RUDP_TIMEOUT * 1000;
                    }
                    timeradd(&now, &interval, &t);
                    event_timeout(t, rudp_timeout, pkt, "rudp_timeout");
                }

            }
        }
    }
    event_timeout(t, rudp_send, NULL, "rudp_send");
    return 0;
}

int rudp_timeout(int a, void *arg) {
        packet_t pkt = (packet_t) arg;
    event_timeout_delete(rudp_timeout, pkt);
    if (pkt->status != ACKED) {
        //printf("Timeout: %d\n", pkt->header->seqno);
        pkt->status = TIMEOUT;
        if (pkt->trans_count >= RUDP_MAXRETRANS)
            pkt->status = FAILED;
    }
    return 0;
}

/*
 *rudp_close: Close socket
 */

int rudp_close(rudp_socket_t rsocket) {

    struct timeval now, interval,        t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_close_session, rsocket, "rudp_close_socket");
    return 0;
    /*
        peer_t p;
        packet_t pkt;
        struct rudp_hdr *hdr;
        hdr = malloc(sizeof (struct rudp_hdr));
        if (hdr == NULL) {
            perror("rudp_sendto: malloc");
        return -1;
        }
        pkt = malloc(sizeof (struct packet));
        if (pkt == NULL) {
            perror("rudp_sendto: malloc");
            return -1;
        }
        for (p = rsocket->peers; p != NULL; p = p->next) {
            hdr->version = RUDP_VERSION;
            hdr->type = RUDP_FIN;
            hdr->seqno = p->sn++;
            pkt->data = NULL;
            pkt->datalen = 0;
        pkt->header = hdr;
            pkt->next = NULL;
            pkt->trans_count = 0;
            pkt->status = IN_QUEUE;
            if (add_packet(&p->packets, &pkt) < 0) {
                perror("rudp_sendto: add_packet");
                return -1;
            }
        }
        return 0;
     */
}

int rudp_close_session(int fd, void *arg) {
    event_timeout_delete(rudp_close_session, arg);
    rudp_socket_t rs        = (rudp_socket_t) arg;
    peer_t p;
    //printf("closing rudp socket %d\n", rs->sockfd);
    //printf("peer: %p\n", rs->peers);
    if (rs->peers == NULL) {
        //printf("peer null\n");
        event_fd_delete(rudp_listen, rs);
        close(rs->sockfd);
        rudp_socket_t r, *rr;
        rr = &rs_head;
        for (r = rs_head; r != NULL; r = r->next) {
            if (r == rs) {
                *rr = r->next;
        free(r);
                return 0;
            }
            rr = &r->next;
        }
        return -1;
    }
    //printf("peers not null\n");
    for (p = rs->peers; p != NULL; p = p->next) {
        //check if there are still packets to be sent
        if (p->packets != NULL) {
            struct timeval now, interval, t;
            gettimeofday(&now, NULL);
            interval.tv_sec = 0;
        interval.tv_usec = RUDP_CHECK_INTERVAL;
            timeradd(&now, &interval, &t);
            event_timeout(t, rudp_close_session, arg, "rudp_close_socket");
            return 0;
        }
        //printf("no packets in this peer, status %d\n", p->status);
        if (p->status != PEER_STATUS_CLOSED) {
            p->status = PEER_STATUS_CLOSING;
            struct rudp_hdr header;
            header.seqno = p->sn;
        header.type = RUDP_FIN;
            header.version = RUDP_VERSION;
            sendto(rs->sockfd, &header, sizeof (struct rudp_hdr), 0,
                    (struct sockaddr*) p->sa, sizeof (struct sockaddr_in));
            struct timeval now, interval, t;
            gettimeofday(&now, NULL);
            if (RUDP_TIMEOUT >= 1000) {
                interval.tv_sec = RUDP_TIMEOUT / 1000;
                interval.tv_usec = 0;
        } else {
                interval.tv_sec = 0;
                interval.tv_usec = RUDP_TIMEOUT * 1000;
            }
            timeradd(&now, &interval, &t);
            event_timeout(t, rudp_close_session, arg, "rudp_close_socket");
            //printf("FIN sent, check again 2s later\n");
            return 0;
        }
        //printf("peer status closed\n");
        if (remove_peer(&(rs->peers), p) < 0)        {
            perror("rudp_close_session: remove_peer");
            return -1;
        } else {
            //printf("peer successfully removed\n");
            struct timeval now, interval, t;
            gettimeofday(&now, NULL);
            interval.tv_sec = 0;
            interval.tv_usec = RUDP_CHECK_INTERVAL;
            timeradd(&now, &interval, &t);
            event_timeout(t, rudp_close_session, arg, "rudp_close_socket");
        return 0;
        }
    }
    return 0;
}

/*
 *rudp_recvfrom_handler: Register receive callback function
 */

int rudp_recvfrom_handler(rudp_socket_t rsocket,
        int (*handler)(rudp_socket_t, struct sockaddr_in *,
        char *, int)) {
    event_fd(rsocket->sockfd, rudp_listen, handler, "rudp_listen");
    event_fd_delete(rudp_listen, rsocket);
    return 0;
}

/*
 *rudp_event_handler: Register event handler        callback function
 */
int rudp_event_handler(rudp_socket_t rsocket,
        int (*handler)(rudp_socket_t, rudp_event_t,
        struct sockaddr_in *)) {
    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_check_status, handler, "rudp_check_status");
    return 0;
}

int        rudp_check_status(int fd, void * arg) {
    event_timeout_delete(rudp_check_status, arg);
    int (*handler)(rudp_socket_t, rudp_event_t, struct sockaddr_in *);
    handler = (int (*)(rudp_socket_t, rudp_event_t, struct sockaddr_in *))arg;
    rudp_socket_t rs;
    peer_t p;
    packet_t pkt;
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        for (p = rs->peers; p != NULL; p = p->next) {
            if (p->status == PEER_STATUS_CLOSED)        {
                handler(rs, RUDP_EVENT_CLOSED, p->sa);
                if (remove_peer(&(rs->peers), p) < 0)
                    return -1;
                return 0;
            }
            for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                if (pkt->status == FAILED) {
                    handler(rs, RUDP_EVENT_TIMEOUT, p->sa);
                    if (remove_peer(&(rs->peers), p) < 0)
        return -1;
                    return 0;
                }
            }
        }
    }
    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_check_status, handler, "rudp_check_status");
    return 0;
}

/*
 * rudp_sendto: Send a block of data to the receiver.
        */

int rudp_sendto(rudp_socket_t rsocket, void* data, int len, struct sockaddr_in* to) {
    peer_t p;
    packet_t pkt;
    struct rudp_hdr *hdr;
    hdr = malloc(sizeof (struct rudp_hdr));
    if (hdr == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    pkt = malloc(sizeof (struct packet));
    if (pkt == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    p =        find_peer(&(rsocket->peers), to);
    if (p == NULL) {
        perror("rudp_sendto: find_peer");
        return -1;
    }
    if (p->status == PEER_STATUS_INIT) {
        rudp_setup_session(0, p);
    }
    hdr = malloc(sizeof (struct rudp_hdr));
    if (hdr == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    pkt = malloc(sizeof (struct packet));
    if (pkt == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    hdr->version = RUDP_VERSION;
    hdr->type = RUDP_DATA;
    hdr->seqno = p->sn++;
    pkt->data = malloc(len);
    if (pkt->data == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    memcpy(pkt->data, data, len);
    pkt->datalen = len;
    pkt->header = hdr;
    pkt->next = NULL;
    pkt->trans_count = 0;
        pkt->status = IN_QUEUE;
    //printf("adding packet %d to %d\n", pkt->header->seqno, ntohs(p->sa->sin_port));
    if (add_packet(&p->packets, &pkt) < 0) {
        perror("rudp_sendto: add_packet");
        return -1;
    }
    return 0;
}

int rudp_setup_session(int fd, void *arg) {
    event_timeout_delete(rudp_setup_session, arg);
    peer_t p = (peer_t) arg;
    if (p == NULL) {
        perror("rudp_setup_session: error finding peer");
        return -1;
    }
    int sockfd;
    rudp_socket_t rs;
    peer_t pp;
    int flag = 0;
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        for (pp = rs->peers; pp != NULL; pp = pp->next) {
            if (pp == p) {
                sockfd = rs->sockfd;
                flag = 1;
                break;
            }
        }
        if (flag == 1)
        break;
    }
    struct rudp_hdr header;
    header.seqno = p->sn++;
    header.type = RUDP_SYN;
    header.version = RUDP_VERSION;
    sendto(sockfd, &header, sizeof (struct rudp_hdr), 0,
            (struct sockaddr*) p->sa, sizeof (struct sockaddr_in));
    p->status = PEER_STATUS_PASSIVE;

    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    if (RUDP_TIMEOUT >= 1000) {
        interval.tv_sec        = RUDP_TIMEOUT / 1000;
        interval.tv_usec = 0;
    } else {
        interval.tv_sec = 0;
        interval.tv_usec = RUDP_TIMEOUT * 1000;
    }
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_session_check, arg, "rudp_timeout");


    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t,        rudp_send, NULL, "rudp_send");
    return 0;
}

int rudp_session_check(int fd, void *arg) {
    event_timeout_delete(rudp_session_check, arg);
    peer_t p = (peer_t) arg;
    if (p == NULL) {
        perror("rudp_setup_session: error finding peer");
        return -1;
    }
    if (p->status == PEER_STATUS_PASSIVE) {
        struct timeval now, interval, t;
        gettimeofday(&now, NULL);
        interval.tv_usec = 0;
        interval.tv_sec = 0;
        timeradd(&now, &interval, &t);
        event_timeout(t, rudp_setup_session, arg, "rudp_setup_session");
    }
    return 0;
}
Leave a Comment

Multiple Sessions

A linked list of all RUDP sockets is maintained. When rudp_socket() is called, an RUDP socket is created and added to the linked list. An RUDP socket keeps a record of the pees/sessions it talks with. When RUDP receives a packet from an unknown socket address, or when RUDP receives a send packet request to an unknown socket address, a new session is created. And for each session, a linked list of all buffered packets is kept.

Leave a Comment

Session Establishment and Tearing Down

When rudp_sendto() is called, the protocol first check if there exists a session between the sender and receiver. If not, the protocol will try to setup a session by sending RUDP_SYN messages. And the packet the application wants to send will be buffered in the created session. After an RUDP_ACK message is received, the server side socket start sending out packets. Go back N protocol is used to control the sending process. After the protocol receives a rudp_close() signal, it will first check whether there are still active sessions and packets in the sending buffer. If not, the protocol will send out RUDP_FIN messages and after receiving RUDP_ACKs, the session is torn down.

Leave a Comment

RUDP Overview

RUDP is a protocol that ensures transfer reliability with UDP. A sliding window protocol (Go back N) is used to realize reliability. Using RUDP, applications can send and receive data packets without worrying about lost packets.

The lines in red signifies state change for RUDP clients (receiver side); while the black lines signifies state change for RUDP servers (sender side).

Leave a Comment

Transferring Voice Data

This is done using JMF (Java Media Framework). Voice data is read from a file using `MediaLocator’. It is used as the data source for a `Processor’. The `Processor’ specifies and converts the audio data to a certain format and then output the converted data into a `DataSink’. The `DataSink’ then transfers the stream to its destination address and port.

Leave a Comment

Multiple Sessions

A SessionManager' is used to manage all the sessions. The SessionManager’ keeps a list of sessions. When it receives data, it will first check which session the data belongs to. Then the data is given to the corresponding session. If it does not belong to any session in the list, a new session will be created and added to the session list.

The basic code looks like this:


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Daoyuan
 */
public class SessionManager extends Thread {

String user;
    String host;
    int port;
    String voiceFile;
    String defaultVoice;
    boolean listening = true;
    public static ArrayList sessionList;
    DatagramSocket serverSocket = null;

    public SessionManager(String user, String host, int port, String voiceFile,
            String defaultVoice) {
        this.user = user;
        this.host = host;
        this.port = port;
        this.voiceFile = voiceFile;
this.defaultVoice = defaultVoice;
        sessionList = new ArrayList();
        try {
            serverSocket = new DatagramSocket(port);
        } catch (SocketException ex) {
            Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Unable to listen on port " + port);
        }
    }

    @Override
    public void run() {

        byte[] receiveData = newbyte[2048];

        new Thread() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    if (sessionList.size() != 0) {
                        for (int i= 0; i < sessionList.size(); i++) {
                            if (sessionList.get(i).status.equals("destroyed")) {
                                sessionList.remove(i);
                            }
                        }
                    }
                }
            }
        }.start();

        while (listening) {
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
                serverSocket.receive(receivePacket);
            } catch (IOException ex) {
                Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
            }

            if (isNewSession(receivePacket)) {
                System.out.println("New session created!");
            }
        }

        serverSocket.close();
    }

    public void stopListening() {
        listening = false;
}

    private boolean isNewSession(DatagramPacket receivePacket) {
        InetAddress IPAddress = receivePacket.getAddress();
        int remotePort = receivePacket.getPort();
        String sessionName = IPAddress + ":" + remotePort;
        if (sessionList.size() == 0) {
            Session s = new Session(this.serverSocket, IPAddress, remotePort, voiceFile, defaultVoice, user);
            s.setName(sessionName);
s.requests.add(receivePacket);
            sessionList.add(s);
            s.start();
            return true;
        } else {
            for (int i = 0; i < sessionList.size(); i++) {
                if (sessionList.get(i).getName().equals(sessionName)) {
                    sessionList.get(i).requests.add(receivePacket);
                    return false;
                }
            }

            Session s = newSession(this.serverSocket, IPAddress, remotePort, voiceFile, defaultVoice, user);
            s.setName(sessionName);
            s.requests.add(receivePacket);
            sessionList.add(s);
            s.start();
            return true;
        }
    }
}
Leave a Comment

Session Establishment and Tearing Down

Each session has a ‘status’, it can be ‘new’, ‘establishing’, ‘cancelling’, ‘established’, ‘tearingdown’ and ‘destroyed’.

When a new session is created, its status is ‘new’.

When an ‘INVITE’ is received, it sends out an ‘OK’ message and change its status to ‘establishing’.

After receiving an ‘ACK’ message the status will be changed to ‘established’.

Then begins the transferring of voice data using RTP.

When the sending finishes the status will become ‘tearingdown’.

A ‘BYE’ message is also sent to the client.

The status becomes ‘destroyed’ after getting ‘OK’ from the client.

When a ‘CANCEL’ message is received, the status becomes ‘cancelling’.

Then it sends back ‘OK’ and ‘Request Terminated’ messages.

After received an ‘ACK’, the status becomes ‘destroyed’.

The thread is as follows:


@Override
public void run() {

    while (true) {
        if (requests.size() == 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            final DatagramPacket packet = requests.get(0);
            final String request = new String(packet.getData());
            System.err.println(request);
            sipRequest = new SIPRequest(request);
            if (request.toUpperCase().startsWith("INVITE")) {
                System.out.println("Got INVITE");
                if (!status.equals("new")) {
                    requests.remove(0);
                    continue;
                }

                String requestLine = request.substring(6).trim();
                String uri = requestLine.substring(0, requestLine.indexOf(' '));
                //System.out.println(uri);
                String usr = "";
                if (uri.toUpperCase().startsWith("SIP:")) {
                    usr = uri.substring(4, uri.indexOf('@'));
                } else {
                    usr = uri.substring(0, uri.indexOf('@'));
                }
                if (!usr.equalsIgnoreCase(user)) {
                    sendUserNotFound(sipRequest);
                    status = "tearingdown";
                    System.out.println("Status: " + status);
                    continue;
                }

                sendTrying(sipRequest);
                sendRinging(sipRequest);
                final String rtpport = request.substring(request.indexOf("m=audio") + 8, request.indexOf("m=audio") + 13);
                final String sdp = getSDP(request);
                new Thread() {

                    @Override
                    public void run() {
                        File file = new File("wav/" + voiceFile);
                        if (file.exists()) {
                            locator = new MediaLocator("file:/" + file.getAbsolutePath());
                        } else {
                            System.out.println("Using default voice file!");
                            locator = new MediaLocator("file:/" + file.getAbsolutePath());
                        }
                        transmitter = new Transmitter(locator, packet.getAddress().toString().substring(1), rtpport);
                        transmitter.prepare();
                    }
                }.start();

                new Thread() {

                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        if (status.equals("establishing")) {
                            sendOK(sipRequest, sdp, rtpport);
                        } else {
                            try {
                                transmitter.stop();
                            } catch (NullPointerException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                                System.out.println("Transmitter already stopped!");
                            }
                        }
                    }
                }.start();
                requests.remove(0);
                status = "establishing";
                System.out.println("Status: " + status);
            } else if (request.toUpperCase().startsWith("ACK")) {
                System.out.println("Got ACK");
                //sendOK(sipRequest);
                requests.remove(0);
                if (status.equals("establishing")) {
                    status = "established";
                    System.out.println("Status: " + status);
                    new Thread() {

                        @Override
                        public void run() {
                            transmitter.start();

                            long duration = new VoiceInfo(locator).getDuration();
                            try {
                                Thread.sleep(duration);
                            } catch (InterruptedException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                            }

                            try {
                                transmitter.stop();
                            } catch (NullPointerException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                                System.out.println("Transmitter already stopped!");
                            }
                            if (!status.equals("tearingdown") && !status.equals("destroyed")) {
                                sendBye(sipRequest);
                                status = "tearingdown";
                                System.out.println("Status: " + status);
                            }
                        }
                    }.start();
                } else if (status.equals("tearingdown")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                } else if (status.equals("cancelling")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                }
            } else if (request.toUpperCase().startsWith("BYE")) {
                System.out.println("Got BYE");
                sendOKWithoutSDP(sipRequest);
                requests.remove(0);
                try {
                    transmitter.stop();
                } catch (NullPointerException ex) {
                    Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                    System.out.println("Transmitter already stopped!");
                }
                status = "destroyed";
                System.out.println("Status: " + status);
                break;
            } else if (request.toUpperCase().startsWith("SIP/2.0 200")) {
                requests.remove(0);
                System.out.println("Got OK");
                if (status.equals("tearingdown")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                }
            } else if (request.toUpperCase().startsWith("CANCEL")) {
                requests.remove(0);
                System.out.println("Got CANCEL");
                status = "cancelling";
                System.out.println("Status: " + status);
                sendOKWithoutSDP(sipRequest);
                sendTerminated(sipRequest);
            } else {
                requests.remove(0);
            }
        }
    }
}
Leave a Comment

Generating Voice File

When the web server receives a POST' message from the web page, it will first check whether the message is valid. If so, freeTTS‘ is used to generate a voice file and the file is saved in the `wav’ directory with pre-configured file name.

The voice generator looks as follows:

import com.sun.speech.freetts.Voice;
import com.sun.speech.freetts.VoiceManager;
import com.sun.speech.freetts.audio.SingleFileAudioPlayer;

/**
 *
 * @author Daoyuan
 */
public class VoiceGenerator {

    String message;

    public VoiceGenerator(String message){
        this.message = message;
    }

    public void generateVoiceFile(String filename){

Voice voice;
        VoiceManager vm = VoiceManager.getInstance();
        voice = vm.getVoice("kevin16");
        voice.allocate();

        String baseName = filename.substring(0, filename.toLowerCase().indexOf(".wav"));
        SingleFileAudioPlayer sfap = new SingleFileAudioPlayer("wav/" + baseName, javax.sound.sampled.AudioFileFormat.Type.WAVE);
        voice.setAudioPlayer(sfap);
        voice.speak(message);

        sfap.close();

voice.deallocate();
    }
}
Leave a Comment

SIP Speaker Overview

SIPSpeaker is a system that provides answering machine service. When the system receives a SIP call, it will establish a new session with user client and send voice data to the user. The user can configure a customized message using a web interface.

The administrator of the system can configure the interface and port number for the web interface; he/she can also configure SIP service interface, port number as well as user name, either using a configuration file or command line arguments.

The system supports multiple session to be communicated at the same time. It uses SIP to manage sessions, and SDP as the handshake protocol to reach an agreement on media format transfered on wire.

JMF is used to support audio playback. And FreeTTS is used to convert message from text to audio format.

Leave a Comment

Non-ASCII characters in Email subject

In this system, Email subjects are encoded in Q' scheme, a scheme similar to quoted printable’. The format is “=?charset?Q?encoded subject?=”, in our case, the charset is `ISO-8859-15′.

For example, the Email object in this system is as follows:


importjava.text.SimpleDateFormat;
import java.util.Calendar;

/**
 * Email object.
 *
 * @author Daoyuan Li
 */
public class Mail {

    /**
     * Sender's Email address
     */
    private String from;

    /**
     * Recipient's Email address
     */
    private String to;

    /**
     * Email subject
     */
    private String subject;

    /**
     * Email message body
     */
    private Stringmessage;

    /**
     * Constructor of Mail
     * @param from Sender's Email address.
     * @param to Recipient's Email address.
     * @param subject Email subject.
     * @param message Email message.
     */
    public Mail(String from, String to, String subject, String message) {
        this.from = from;
        this.to = to;
        this.subject = subject;
        this.message = message;
    }

    /**
     * Get sender'sEmail address.
     * @return Sender's Email address.
     */
    public String getFrom() {
        return this.from;
    }

    /**
     * Get Recipient's Email address.
     * @return Recipient's Email address.
     */
    public String getTo() {
        return this.to;
    }

    /**
     * Get Email subject.
     * @return Email subject.
     */
    public String getSubject() {
        return this.subject;
}

    /**
     * Get Email message.
     * @return Email message.
     */
    public String getMessage() {
        return this.message;
    }

    /**
     * Set sender's Email address.
     * @param from Sender's Email address.
     */
    public void setFrom(String from) {
        this.from = from;
    }

    /**
     * Set Recipient's Email address.
     * @param to Recipient's Email address.
     */
public void setTo(String to) {
        this.to = to;
    }

    /**
     * Set Email subject.
     * @param subject Email subject.
     */
    public void setSubject(String subject) {
        this.subject = subject;
    }

    /**
     * Set Email message.
     * @param message Email message.
     */
    public void setMessage(String message) {
        this.message = message;
    }

    /**
     * Encode Email into MIMEformat.
     * @return The MIME encoded Email.
     */
    public String encode() {
        String FORMAT = "EEE, dd MMM yyyy HH:mm:ss Z";
        //String FORMAT = "yyyy-MM-dd HH:mm:ss Z";
        Calendar cal = null;
        cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat(FORMAT);
        //System.out.println(sdf.format(cal.getTime()));
        String s = "From: <" + this.getFrom() + ">\n"
                + "To:<" + this.getTo() + ">\n"
                //+ "Subject: " + this.getSubject() + "\n"
                + "Subject: =?ISO-8859-15?Q?"
                + new QuotedPrintableEncoder().encodeWithoutLineBreak(this.subject) + "?=\n"
                + "Date: " + sdf.format(cal.getTime()) + "\n"
                + "Message-ID: " + cal.getTimeInMillis() + "@ik2213.lab\n"
                + "MIME-Version: 1.0\n"
                + "Content-Type:text/plain; charset=ISO-8859-15\n"
                + "Content-Transfer-Encoding: quoted-printable\n";
        return s.concat(new QuotedPrintableEncoder().encode(this.message));
    }
}
Leave a Comment

MIME Encoding

Quoted printable characters are encoded in the format =XX', where XX’ stands for the hexadecimal value of the character.

The encoder looks as follows:

import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * It encodes data into quoted printable format.
 *
* @author Daoyuan Li
 */
public class QuotedPrintableEncoder {
    /**
     * Encodes data into quoted printable format.
     * @param s Data to be encoded.
     * @return The encoded data.
     */
    public String encode(String s) {
        byte[] b = null;
        try {
            b = s.getBytes("ISO-8859-1");
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(Mail.class.getName()).log(Level.SEVERE, null, ex);
return "";
        }
        String code = "";
        int wc = 0;
        for (int i = 0; i < b.length; i++) {
            byte c = b[i];
            if(c == 13){
                code = code.concat("\n");
                wc = 0;
                continue;
            } else if(c == 10) {
                //do nothing
                continue;
            }
            code = code.concat("=" + Integer.toHexString(c &255).toUpperCase());
            wc += 3;
            if (wc >= 75) {
                code = code.concat("=\n");
                wc = 0;
            }
        }
        return code;
    }

    /**
     * Encodes data into quoted printable format, without soft line breaks.
     * @param s Data to be encoded.
     * @return The encoded data.
     */
    public String encodeWithoutLineBreak(String s) {
        byte[] b = null;
        try{
            b = s.getBytes("ISO-8859-1");
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(Mail.class.getName()).log(Level.SEVERE, null, ex);
            return "";
        }
        String code = "";
        for (int i = 0; i < b.length; i++) {
            byte c = b[i];
            if(c == 13){
                code = code.concat("\n");
                continue;
            } else if (c == 10){
//do nothing
                continue;
            }
            code = code.concat("=" + Integer.toHexString(c & 255).toUpperCase());
        }
        return code;
    }
}
Leave a Comment