/*
 * Example receiver to interface with the Freeze-TCP extension for Linux 2.6.29
 * Copyright (C) 2009 Nicta, Olivier Mehani <olivier.mehani@nicta.com.au>
 * 
 * $Id: freezetcp_recv.c 1022 2009-05-20 11:08:38Z omehani $
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h>
#include <signal.h>
#include <errno.h>

#define TCP_FREEZE	15 /* setsockopt option, not in standard headers */

#define LISTEN_PORT	4242
#define MAX_PENDING	1
#define BUF_SIZE	1024

/* DIRTY! */
int sockfd, clientfd;

void sig_handler(int sig) {
	int val = 0;
	switch(sig) {
	case SIGUSR1:
		fprintf(stderr, "\nsignal: freezing socket (SIGUSR1)\n");
		val = 1;
		break;
	case SIGUSR2:
		fprintf(stderr, "\nsignal: unfreezing socket (SIGUSR2)\n");
		break;
	}

	if(setsockopt(clientfd, IPPROTO_TCP, TCP_FREEZE,
				&val, sizeof(val)) < 0 ) {
		fprintf(stderr, "error: setsockopt() failed (%s)\n", strerror(errno));
	}
}

int main(void)
{
	struct sockaddr_in sin;
	unsigned int sin_size = sizeof (struct sockaddr_in);

	sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sockfd < 0) {
		fprintf(stderr, "error: creating listener socket\n");
		exit(1);
	}

	memset(&sin, 0, sin_size);
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
	sin.sin_port = htons(LISTEN_PORT);

	if (bind(sockfd, (struct sockaddr *) &sin, sin_size) < 0) {
		fprintf(stderr, "error: binding listener socket\n");
		exit(2);
	}

	if (listen(sockfd, MAX_PENDING) < 0) {
		fprintf(stderr, "error: listening on socket\n");
		exit(3);
	}
	fprintf(stderr, "info: listening on port %d\n", LISTEN_PORT);

	while (1) {
		char buf[BUF_SIZE];
		int recv_size, tot_size = 0;

		sin_size = sizeof (sin);

		clientfd = accept(sockfd, (struct sockaddr *) &sin, &sin_size);
		if (clientfd < 0) {
			fprintf(stderr, "error: accepting client\n");
			close(sockfd);
			exit(4);
		}
		
		fprintf(stderr, "info: client connected from %s\n",
				inet_ntoa(sin.sin_addr));
		strncpy(buf, "Hi, there!\n", BUF_SIZE);
		if (send(clientfd, buf, strlen(buf), 0)) {
			if (clientfd < 0) {
				fprintf(stderr, "error: sending welcome\n");
				close(sockfd);
				close(clientfd);
				exit(5);
			}
		}

		signal(SIGUSR1, sig_handler);
		signal(SIGUSR2, sig_handler);
		fprintf(stderr,"info: use SIGUSR1 to freeze and "\
				"SIGUSR2 unfreeze\n");

		while((recv_size = recv(clientfd, buf, BUF_SIZE, 0)) > 0) {
			int val, valsize = sizeof(int);
			if(getsockopt(clientfd, IPPROTO_TCP, TCP_FREEZE, &val,
						(socklen_t *) &valsize) < 0)
				val = -1;
			tot_size += recv_size;
			fprintf(stderr, "\rinfo: %d bytes received " \
					"(sending ZWA: %s)",
					tot_size, val?"yes":"no");
		}

		signal(SIGUSR1, SIG_DFL);
		signal(SIGUSR2, SIG_DFL);

		fprintf(stderr, "\ninfo: client has disconnected\n");

		close(clientfd);
		clientfd = 0;
	}

	return 0;	
}

