Package: redir
Version: 2.2.1-8

I've patched redir to optionally output a libpcap-format dump file,
because I needed to capture and analyze some traffic on a box where I
don't have have root.

Filing this in debian BTS for tracking & discussion at the request of dkg.

I've attached patches against the original and debian-patched versions
of redir (2.2.1 and 2.2.1-8)

Cheers,
Jeff
diff -ur redir-2.2.1/redir.c redir-2.2.x/redir.c
--- redir-2.2.1/redir.c	1999-12-26 20:50:06.000000000 +0000
+++ redir-2.2.x/redir.c	2009-04-30 20:31:56.772613656 +0100
@@ -61,6 +61,12 @@
  *   - Emmanuel Chantréau <ech...@maretmanu.org>
  */
 
+/* xxxxxx Added libpcap-format capture file creation, with faked link-layer/ip/tcp
+ *        headers. Useful for snooping on traffic and/or analysis with wireshark.
+ *
+ *   - Jeff Snyder <jeff -at- caffeinated -dot- me -dot- uk>
+ */
+
 #define  VERSION "2.2.1"
 
 #include <stdio.h>
@@ -83,6 +89,17 @@
 #include <tcpd.h>
 #endif
 
+#ifndef NO_CAPTURE
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <time.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#endif
+
 #define debug(x)	if (dodebug) fprintf(stderr, x)
 #define debug1(x,y)	if (dodebug) fprintf(stderr, x, y)
 
@@ -125,6 +142,47 @@
 int     deny_severity = LOG_WARNING;
 #endif /* USE_TCP_WRAPPERS */
 
+#ifndef NO_CAPTURE
+int cap_fd = -1;
+struct sockaddr_in client_sockaddr;
+struct sockaddr_in target_sockaddr;
+u_short ip_id_in = 0;
+u_short ip_id_out = 0;
+u_int32_t tcp_seq_in;
+u_int32_t tcp_seq_out;
+u_int32_t tcp_lastack_in;
+u_int32_t tcp_lastack_out;
+#define NO_TCPFLAGS 0
+#define TCPFLAG_SYN 1
+#define TCPFLAG_FIN 2
+
+struct pcap_file_header {
+	u_int32_t magic;
+	u_short version_major;
+	u_short version_minor;
+	int32_t thiszone; /* gmt to local correction */
+	u_int32_t sigfigs;    /* accuracy of timestamps */
+	u_int32_t snaplen;    /* max length saved portion of each pkt */
+	u_int32_t linktype;   /* data link type (LINKTYPE_*) */
+};
+
+struct pcap_frame {
+	int32_t   tv_sec;
+	int32_t   tv_usec;
+	u_int32_t caplen;     /* length of portion present */
+	u_int32_t len;        /* length this packet (off wire) */
+};
+
+struct linux_cooked_capture {
+	u_short type;
+	u_short arphrd;
+	u_short ll_addr_size;
+	u_char  ll_hdr_data[8];
+	u_short proto_type;
+};
+
+#endif
+
 #ifdef NEED_STRRCHR
 #define strrchr rindex
 #endif /* NEED_STRRCHR */
@@ -135,6 +193,9 @@
 /* prototype anything needing it */
 void do_accept(int servsock, struct sockaddr_in *target);
 int bindsock(char *addr, int port, int fail);
+#ifndef NO_CAPTURE
+int write_pcap_frame(int fd, const char* data, size_t size, int in, unsigned char tcpflags);
+#endif
 
 #ifndef NO_SHAPER
 /* Used in this program to write something in a socket, it has the same
@@ -238,6 +299,9 @@
         fprintf(stderr, "\t\t--wait_in_out=<flag>\t1 wait for in, 2 out, 3 in&out\n");
         /* end options for bandwidth */
 #endif
+#ifndef NO_CAPTURE
+	fprintf(stderr, "\t\t--cap_file=<file>\tcreate a libpcap-format capture file\n");
+#endif
 	fprintf(stderr, "\n\tVersion %s.\n", VERSION);
 	exit(2);
 }
@@ -264,6 +328,9 @@
            int * random_wait,
            int * wait_in_out,
 #endif
+#ifndef NO_CAPTURE
+	   char **cap_file,
+#endif
 	   char **connect_str)
 {
 	static struct option long_options[] = {
@@ -285,6 +352,7 @@
                 {"max_bandwidth",  required_argument,       0, 'm'},
                 {"random_wait",  required_argument,       0, 'w'},
                 {"wait_in_out",  required_argument,       0, 'o'},
+		{"cap-file",  required_argument,       0, 'u'},
 		{0,0,0,0}		/* End marker */
 	};
 	
@@ -357,6 +425,13 @@
 				redir_usage(argv[0]);
 				exit(1);
 			}
+#	ifndef NO_CAPTURE
+			if(cap_file) {
+				fprintf(stderr, "Using --cap-file with --ftp is not supported\n\n");
+				redir_usage(argv[0]);
+				exit(1);
+			}
+#	endif
 			break;
 #endif	     
 
@@ -383,6 +458,20 @@
                   wait_out=*wait_in_out & 2;
                   break;
 #endif 
+
+#ifndef NO_CAPTURE
+		case 'u':
+#	ifndef NO_FTP
+			if(ftp_type) {
+				fprintf(stderr, "Using --cap-file with --ftp is not supported\n\n");
+				redir_usage(argv[0]);
+				exit(1);
+			}
+#	endif
+			*cap_file = optarg;
+			break;
+#endif
+
 		default:
 			redir_usage(argv[0]);
 			exit(1);
@@ -460,7 +549,7 @@
 	int lport, rport;
 	int remip[4];
 	int localsock;
-	int socksize = sizeof(struct sockaddr_in);
+	size_t socksize = sizeof(struct sockaddr_in);
 
 	struct sockaddr_in newsession;
 	struct sockaddr_in sockname;
@@ -614,6 +703,20 @@
 		max_fd = outsock;
 	}
 
+#ifndef NO_CAPTURE
+	{
+		struct timeval temp;
+		gettimeofday(&temp, 0);
+		tcp_seq_in += temp.tv_usec;
+		tcp_lastack_out = tcp_seq_in;
+		tcp_seq_out += ~(temp.tv_usec);
+		tcp_lastack_in = tcp_seq_out;
+	}
+	write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_SYN);
+	write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_SYN);
+	write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, NO_TCPFLAGS);
+#endif
+
 	debug1("Entering copyloop() - timeout is %d\n", timeout_secs);
 	while(1) {
 		(void) memcpy(&c_iofds, &iofds, sizeof(iofds));
@@ -629,8 +732,14 @@
 		}
 
 		if(FD_ISSET(insock, &c_iofds)) {
-			if((bytes = read(insock, buf, sizeof(buf))) <= 0)
+			if((bytes = read(insock, buf, sizeof(buf))) <= 0) {
+#ifndef NO_CAPTURE
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, NO_TCPFLAGS);
+#endif
 				break;
+			}
 #ifndef NO_FTP
 			if (ftp & FTP_PORT)
 				/* if we're correcting FTP, lookup for a PORT commando
@@ -641,11 +750,20 @@
 #endif
 				if(redir_write(outsock, buf, bytes, REDIR_OUT) != bytes)
 					break;
+#ifndef NO_CAPTURE
+			write_pcap_frame(cap_fd, buf, bytes, REDIR_OUT, NO_TCPFLAGS);
+#endif
 			bytes_out += bytes;
 		}
 		if(FD_ISSET(outsock, &c_iofds)) {
-			if((bytes = read(outsock, buf, sizeof(buf))) <= 0)
+			if((bytes = read(outsock, buf, sizeof(buf))) <= 0) {
+#ifndef NO_CAPTURE
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, NO_TCPFLAGS);
+#endif
 				break;
+			}
 			/* if we're correcting for PASV on ftp redirections, then
 			   fix buf and bytes to have the new address, among other
 			   things */
@@ -656,6 +774,9 @@
 #endif
 				if(redir_write(insock, buf, bytes, REDIR_IN) != bytes)
 					break;
+#ifndef NO_CAPTURE
+			write_pcap_frame(cap_fd, buf, bytes, REDIR_IN, NO_TCPFLAGS);
+#endif
 			bytes_in += bytes;
 		}
 	}
@@ -716,7 +837,7 @@
 	int clisock;
 	int targetsock;
 	struct sockaddr_in client;
-	int clientlen = sizeof(client);
+	size_t clientlen = sizeof(client);
 	int accept_errno;
      
 	debug("top of accept loop\n");
@@ -886,6 +1007,11 @@
         }
 #endif
 
+#ifndef NO_CAPTURE
+	if(cap_fd != -1)
+		memcpy(&client_sockaddr, &client, sizeof(struct sockaddr_in));
+#endif
+
 	copyloop(clisock, targetsock, timeout);
 	exit(0);	/* Exit after copy */
 }
@@ -982,6 +1108,176 @@
 	return servsock;
 }
 
+#ifndef NO_CAPTURE
+int write_pcap_header(int fd)
+{
+	struct pcap_file_header header;
+	header.magic = 0xa1b2c3d4;
+	header.version_major = 2;
+	header.version_minor = 4;
+	header.thiszone =0;
+	header.sigfigs = 0;
+	header.snaplen = 65535;
+	header.linktype = 113;
+	if(write(fd, &header, sizeof(header)) != sizeof(header))
+		return -1;
+	return 0;
+}
+
+u_short iphdr_cksum(void *data, int len)
+{
+	u_int32_t sum=0;
+	size_t i;
+	for (i=0; i < len/2; ++i)
+		sum += ntohs(((u_short*)data)[i]);
+	while (sum & 0xFFFF0000)
+		sum = (sum & 0xFFFF)+(sum >> 16);
+	return ((u_short) ~sum);
+}
+
+u_short tcphdr_cksum(struct iphdr *ip, struct tcphdr *tcp, const char *payload, size_t payload_len)
+{
+	int padd;
+	u_short tcp_len;
+	size_t buff_len;
+	char* buff;
+	u_short result;
+	struct {
+		u_int32_t saddr;
+		u_int32_t daddr;
+		unsigned char reserved;
+		unsigned char proto;
+		u_short tcp_len;
+	} pseudoheader;
+	
+	padd = payload_len&1;
+	tcp_len = payload_len + sizeof(struct tcphdr);
+	buff_len = sizeof(pseudoheader) + tcp_len + padd;
+	buff = malloc(buff_len);
+
+	pseudoheader.saddr = ip->saddr;
+	pseudoheader.daddr = ip->daddr;
+	pseudoheader.reserved = 0;
+	pseudoheader.proto = ip->protocol;
+	pseudoheader.tcp_len = htons(tcp_len);
+
+
+	memcpy(buff,                                           &pseudoheader, sizeof(pseudoheader));
+	memcpy(buff+sizeof(pseudoheader),                       tcp,          sizeof(struct tcphdr));
+	memcpy(buff+sizeof(pseudoheader)+sizeof(struct tcphdr), payload,      payload_len);
+	if(padd)
+		buff[buff_len-1] = 0;
+
+	result = iphdr_cksum(buff, buff_len);
+	free(buff);
+	return result;
+}
+
+int write_pcap_frame(int fd, const char* data, size_t size, int in, unsigned char tcpflags)
+{
+	struct timeval time;
+	struct pcap_frame pcap_header;
+	struct linux_cooked_capture lcc_header;
+	struct iphdr ip_header;
+	struct tcphdr tcp_header;
+
+	/* in=1 means REDIR_IN means from target to client */
+	if(flock(fd, LOCK_EX) < 0) {
+		if(close(fd < 0))
+			exit(1);
+		cap_fd = -1;
+		return -1;
+	}
+
+	/* we have to rather tediously fake a pcap header, linux cooked capture haeder, ip header and tcp header */
+	/* pcap header */
+	gettimeofday(&time,0);
+	pcap_header.tv_sec = time.tv_sec;
+	pcap_header.tv_usec = time.tv_usec;
+	pcap_header.caplen = sizeof(struct linux_cooked_capture) + sizeof(struct iphdr) + sizeof(struct tcphdr) + size;
+	pcap_header.len = sizeof(struct linux_cooked_capture) + sizeof(struct iphdr) + sizeof(struct tcphdr) + size;
+	if(write(fd, &pcap_header, sizeof(pcap_header)) != sizeof(pcap_header))
+		if(close(fd < 0))
+			exit(1);
+
+	/* linux cooked capture header */
+	lcc_header.type = htons(in?0:4); /* 0 sent is to us, 4 is sent by us */
+	lcc_header.arphrd = htons(ARPHRD_ETHER);
+	lcc_header.ll_addr_size = htons(6);
+	memset(lcc_header.ll_hdr_data, 0, sizeof(lcc_header.ll_hdr_data));
+	memset(lcc_header.ll_hdr_data, in?2:1, 6);
+	lcc_header.proto_type = htons(ETHERTYPE_IP);
+	if(write(fd, &lcc_header, sizeof(lcc_header)) != sizeof(lcc_header))
+		if(close(fd < 0))
+			exit(1);
+
+	/* ip header */
+	ip_header.version = IPVERSION;
+	ip_header.ihl = sizeof(struct iphdr)/sizeof(u_int32_t);
+	ip_header.tos = IPTOS_TOS(0);
+	ip_header.tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + size);
+	ip_header.id = in?ip_id_in++:ip_id_out++;
+	ip_header.frag_off = htons(0x4000); /* don't fragment */
+	ip_header.ttl = 64; /* arbitrary */
+	ip_header.protocol = IPPROTO_TCP;
+	if(in) {
+		ip_header.saddr = target_sockaddr.sin_addr.s_addr;
+		ip_header.daddr = client_sockaddr.sin_addr.s_addr;
+	} else {
+		ip_header.saddr = client_sockaddr.sin_addr.s_addr;
+		ip_header.daddr = target_sockaddr.sin_addr.s_addr;
+	}
+	ip_header.check = 0;
+	ip_header.check = htons(iphdr_cksum(&ip_header, sizeof(struct iphdr)));
+	if(write(fd, &ip_header, sizeof(ip_header)) != sizeof(ip_header))
+		if(close(fd < 0))
+			exit(1);
+
+	memset(&tcp_header, 0, sizeof(struct tcphdr));
+	if(tcpflags & TCPFLAG_SYN)
+		tcp_header.syn=1;
+	if(tcpflags & TCPFLAG_FIN)
+		tcp_header.fin=1;
+	if(in) {
+		tcp_header.source = target_sockaddr.sin_port;
+		tcp_header.dest = client_sockaddr.sin_port;
+		/* seq counts data in the direction we're sending */
+		tcp_header.seq = htonl(tcp_seq_in);
+		tcp_seq_in += (tcp_header.syn||tcp_header.fin) ? 1 : size;
+		/* ack-seq is the seq+size of the last packet we saw going the other way */
+		if(tcp_seq_out != tcp_lastack_in) {
+			tcp_header.ack_seq = htonl(tcp_seq_out);
+			tcp_lastack_in = tcp_seq_out;
+			tcp_header.ack=1;
+		}
+	} else {
+		tcp_header.source = client_sockaddr.sin_port;
+		tcp_header.dest = target_sockaddr.sin_port;
+		tcp_header.seq = htonl(tcp_seq_out);
+		tcp_seq_out += (tcp_header.syn||tcp_header.fin) ? 1 : size;
+		if(tcp_seq_in != tcp_lastack_out) {
+			tcp_header.ack_seq = htonl(tcp_seq_in);
+			tcp_lastack_out = tcp_seq_in;
+			tcp_header.ack=1;
+		}
+	}
+	tcp_header.doff = 5; /* no options */
+	tcp_header.window = htons(32768);
+	tcp_header.check = htons(tcphdr_cksum(&ip_header, &tcp_header, data, size));
+	if(write(fd, &tcp_header, sizeof(tcp_header)) != sizeof(tcp_header))
+		if(close(fd < 0))
+			exit(1);
+
+	if(size && write(fd, data, size) != size)
+		if(close(fd < 0))
+			exit(1);
+
+	if(flock(fd, LOCK_UN) < 0)
+		exit(1);
+	return 0;
+}
+#endif
+
 int
 main(int argc, char *argv[])
 {
@@ -994,6 +1290,9 @@
 	int inetd = 0;
 	char * target_ip;
 	char * ip_to_target;
+#ifndef NO_CAPTURE
+	char * cap_file = 0;
+#endif
 
 	debug("parse args\n");
 	parse_args(argc, argv, &target_addr, &target_port, &local_addr, 
@@ -1006,6 +1305,9 @@
 		   &bufsize, &max_bandwidth, &random_wait,
 		   &wait_in_out,
 #endif
+#ifndef NO_CAPTURE
+		   &cap_file,
+#endif
                    &connect_str);
 
 	/* Set up target */
@@ -1047,12 +1349,33 @@
 		ip_to_target = strdup(inet_ntoa(addr_out.sin_addr));
 		debug1("IP address for target is %s\n", ip_to_target);
 	}
-           
+
+#ifndef NO_CAPTURE
+	if(cap_file) {
+		int flags = O_WRONLY|O_CREAT;
+		flags |= inetd?O_APPEND:O_TRUNC;
+		cap_fd = open(cap_file, flags, 0666);
+		if(cap_fd != -1) {
+			if(flock(cap_fd, LOCK_EX) < 0) {
+				if(close(cap_fd < 0))
+					exit(1);
+				cap_fd = -1;
+			} else {
+				if(lseek(cap_fd, 0, SEEK_CUR) == 0) {
+					write_pcap_header(cap_fd);
+				}
+				if(flock(cap_fd, LOCK_UN) < 0)
+					exit(1);
+			}
+			memcpy(&target_sockaddr, &target, sizeof(struct sockaddr_in));
+		}
+	}
+#endif
 
 	if (inetd) {
 		int targetsock;
 		struct sockaddr_in client;
-		int client_size = sizeof(client);
+		size_t client_size = sizeof(client);
 
 #ifdef USE_TCP_WRAPPERS
 		request_init(&request, RQ_DAEMON, ident, RQ_FILE, 0, 0);
@@ -1113,6 +1436,11 @@
 			       target_ip, target.sin_port);
 		}
 
+#ifndef NO_CAPTURE
+		if(cap_fd != -1)
+			memcpy(&client_sockaddr, &client, sizeof(struct sockaddr_in));
+#endif
+
 		/* Just start copying - one side of the loop is stdin - 0 */
 		copyloop(0, targetsock, timeout);
 	} else {
diff -ur redir-2.2.1/redir.man redir-2.2.x/redir.man
--- redir-2.2.1/redir.man	1999-12-26 20:52:24.000000000 +0000
+++ redir-2.2.x/redir.man	2009-04-30 20:31:56.772613656 +0100
@@ -21,6 +21,7 @@
 .RB [ \--maxbandwidth=n ]
 .RB [ \--random_wait=n ]
 .RB [ \--wait_in_out=n ]
+.RB [ \--cap_file=file ]
 .ll -8
 .br
 .B redir
@@ -38,6 +39,7 @@
 .RB [ \--maxbandwidth=n ]
 .RB [ \--random_wait=n ]
 .RB [ \--wait_in_out=n ]
+.RB [ \--cap_file=file ]
 .ll -8
 .br
 .SH DESCRIPTION
@@ -117,6 +119,10 @@
 .B \--wait_in_out n
 Apply --maxbandwidth and --random_wait for input if n=1, output if n=2 and
 both if n=3.
+.TP
+.B \--cap_file file
+Create a libpcap-format capture file and record data passing through redir
+in it. The link-layer, ip and tcp headers in the file will be synthesized.
 .SH "SEE ALSO"
 inetd(1)
 
diff -ur redir-2.2.1-deb/redir.c redir-2.2.x-deb/redir.c
--- redir-2.2.1-deb/redir.c	2009-04-30 20:19:40.737611229 +0100
+++ redir-2.2.x-deb/redir.c	2009-04-30 20:43:49.932583488 +0100
@@ -61,6 +61,12 @@
  *   - Emmanuel Chantréau <ech...@maretmanu.org>
  */
 
+/* xxxxxx Added libpcap-format capture file creation, with faked link-layer/ip/tcp
+ *        headers. Useful for snooping on traffic and/or analysis with wireshark.
+ *
+ *   - Jeff Snyder <jeff -at- caffeinated -dot- me -dot- uk>
+ */
+
 #define  VERSION "2.2.1"
 
 #include <stdio.h>
@@ -84,6 +90,17 @@
 #include <tcpd.h>
 #endif
 
+#ifndef NO_CAPTURE
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <time.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#endif
+
 #define debug(x)	if (dodebug) fprintf(stderr, x)
 #define debug1(x,y)	if (dodebug) fprintf(stderr, x, y)
 
@@ -126,6 +143,47 @@
 int     deny_severity = LOG_WARNING;
 #endif /* USE_TCP_WRAPPERS */
 
+#ifndef NO_CAPTURE
+int cap_fd = -1;
+struct sockaddr_in client_sockaddr;
+struct sockaddr_in target_sockaddr;
+u_short ip_id_in = 0;
+u_short ip_id_out = 0;
+u_int32_t tcp_seq_in;
+u_int32_t tcp_seq_out;
+u_int32_t tcp_lastack_in;
+u_int32_t tcp_lastack_out;
+#define NO_TCPFLAGS 0
+#define TCPFLAG_SYN 1
+#define TCPFLAG_FIN 2
+
+struct pcap_file_header {
+	u_int32_t magic;
+	u_short version_major;
+	u_short version_minor;
+	int32_t thiszone; /* gmt to local correction */
+	u_int32_t sigfigs;    /* accuracy of timestamps */
+	u_int32_t snaplen;    /* max length saved portion of each pkt */
+	u_int32_t linktype;   /* data link type (LINKTYPE_*) */
+};
+
+struct pcap_frame {
+	int32_t   tv_sec;
+	int32_t   tv_usec;
+	u_int32_t caplen;     /* length of portion present */
+	u_int32_t len;        /* length this packet (off wire) */
+};
+
+struct linux_cooked_capture {
+	u_short type;
+	u_short arphrd;
+	u_short ll_addr_size;
+	u_char  ll_hdr_data[8];
+	u_short proto_type;
+};
+
+#endif
+
 #ifdef NEED_STRRCHR
 #define strrchr rindex
 #endif /* NEED_STRRCHR */
@@ -136,6 +194,9 @@
 /* prototype anything needing it */
 void do_accept(int servsock, struct sockaddr_in *target);
 int bindsock(char *addr, int port, int fail);
+#ifndef NO_CAPTURE
+int write_pcap_frame(int fd, const char* data, size_t size, int in, unsigned char tcpflags);
+#endif
 
 #ifndef NO_SHAPER
 /* Used in this program to write something in a socket, it has the same
@@ -239,6 +300,9 @@
         fprintf(stderr, "\t\t--wait_in_out=<flag>\t1 wait for in, 2 out, 3 in&out\n");
         /* end options for bandwidth */
 #endif
+#ifndef NO_CAPTURE
+	fprintf(stderr, "\t\t--cap_file=<file>\tcreate a libpcap-format capture file\n");
+#endif
 	fprintf(stderr, "\n\tVersion %s.\n", VERSION);
 	exit(2);
 }
@@ -265,6 +329,9 @@
            int * random_wait,
            int * wait_in_out,
 #endif
+#ifndef NO_CAPTURE
+	   char **cap_file,
+#endif
 	   char **connect_str)
 {
 	static struct option long_options[] = {
@@ -286,6 +353,7 @@
                 {"max_bandwidth",  required_argument,       0, 'm'},
                 {"random_wait",  required_argument,       0, 'w'},
                 {"wait_in_out",  required_argument,       0, 'o'},
+		{"cap-file",  required_argument,       0, 'u'},
 		{0,0,0,0}		/* End marker */
 	};
 	
@@ -358,6 +426,13 @@
 				redir_usage(argv[0]);
 				exit(1);
 			}
+#	ifndef NO_CAPTURE
+			if(cap_file) {
+				fprintf(stderr, "Using --cap-file with --ftp is not supported\n\n");
+				redir_usage(argv[0]);
+				exit(1);
+			}
+#	endif
 			break;
 #endif	     
 
@@ -384,6 +459,20 @@
                   wait_out=*wait_in_out & 2;
                   break;
 #endif 
+
+#ifndef NO_CAPTURE
+		case 'u':
+#	ifndef NO_FTP
+			if(ftp_type) {
+				fprintf(stderr, "Using --cap-file with --ftp is not supported\n\n");
+				redir_usage(argv[0]);
+				exit(1);
+			}
+#	endif
+			*cap_file = optarg;
+			break;
+#endif
+
 		default:
 			redir_usage(argv[0]);
 			exit(1);
@@ -611,6 +700,20 @@
 		max_fd = outsock;
 	}
 
+#ifndef NO_CAPTURE
+	{
+		struct timeval temp;
+		gettimeofday(&temp, 0);
+		tcp_seq_in += temp.tv_usec;
+		tcp_lastack_out = tcp_seq_in;
+		tcp_seq_out += ~(temp.tv_usec);
+		tcp_lastack_in = tcp_seq_out;
+	}
+	write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_SYN);
+	write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_SYN);
+	write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, NO_TCPFLAGS);
+#endif
+
 	debug1("Entering copyloop() - timeout is %d\n", timeout_secs);
 	while(1) {
 		(void) memcpy(&c_iofds, &iofds, sizeof(iofds));
@@ -633,8 +736,14 @@
 		}
 
 		if(FD_ISSET(insock, &c_iofds)) {
-			if((bytes = read(insock, buf, bufsize)) <= 0)
+			if((bytes = read(insock, buf, bufsize)) <= 0) {
+#ifndef NO_CAPTURE
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, NO_TCPFLAGS);
+#endif
 				break;
+			}
 #ifndef NO_FTP
 			if (ftp & FTP_PORT)
 				/* if we're correcting FTP, lookup for a PORT commando
@@ -645,11 +754,20 @@
 #endif
 				if(redir_write(outsock, buf, bytes, REDIR_OUT) != bytes)
 					break;
+#ifndef NO_CAPTURE
+			write_pcap_frame(cap_fd, buf, bytes, REDIR_OUT, NO_TCPFLAGS);
+#endif
 			bytes_out += bytes;
 		}
 		if(FD_ISSET(outsock, &c_iofds)) {
-			if((bytes = read(outsock, buf, bufsize)) <= 0)
+			if((bytes = read(outsock, buf, bufsize)) <= 0) {
+#ifndef NO_CAPTURE
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, NO_TCPFLAGS);
+#endif
 				break;
+			}
 			/* if we're correcting for PASV on ftp redirections, then
 			   fix buf and bytes to have the new address, among other
 			   things */
@@ -660,6 +778,9 @@
 #endif
 				if(redir_write(insock, buf, bytes, REDIR_IN) != bytes)
 					break;
+#ifndef NO_CAPTURE
+			write_pcap_frame(cap_fd, buf, bytes, REDIR_IN, NO_TCPFLAGS);
+#endif
 			bytes_in += bytes;
 		}
 	}
@@ -891,6 +1012,11 @@
         }
 #endif
 
+#ifndef NO_CAPTURE
+	if(cap_fd != -1)
+		memcpy(&client_sockaddr, &client, sizeof(struct sockaddr_in));
+#endif
+
 	copyloop(clisock, targetsock, timeout);
 	exit(0);	/* Exit after copy */
 }
@@ -987,6 +1113,176 @@
 	return servsock;
 }
 
+#ifndef NO_CAPTURE
+int write_pcap_header(int fd)
+{
+	struct pcap_file_header header;
+	header.magic = 0xa1b2c3d4;
+	header.version_major = 2;
+	header.version_minor = 4;
+	header.thiszone =0;
+	header.sigfigs = 0;
+	header.snaplen = 65535;
+	header.linktype = 113;
+	if(write(fd, &header, sizeof(header)) != sizeof(header))
+		return -1;
+	return 0;
+}
+
+u_short iphdr_cksum(void *data, int len)
+{
+	u_int32_t sum=0;
+	size_t i;
+	for (i=0; i < len/2; ++i)
+		sum += ntohs(((u_short*)data)[i]);
+	while (sum & 0xFFFF0000)
+		sum = (sum & 0xFFFF)+(sum >> 16);
+	return ((u_short) ~sum);
+}
+
+u_short tcphdr_cksum(struct iphdr *ip, struct tcphdr *tcp, const char *payload, size_t payload_len)
+{
+	int padd;
+	u_short tcp_len;
+	size_t buff_len;
+	char* buff;
+	u_short result;
+	struct {
+		u_int32_t saddr;
+		u_int32_t daddr;
+		unsigned char reserved;
+		unsigned char proto;
+		u_short tcp_len;
+	} pseudoheader;
+	
+	padd = payload_len&1;
+	tcp_len = payload_len + sizeof(struct tcphdr);
+	buff_len = sizeof(pseudoheader) + tcp_len + padd;
+	buff = malloc(buff_len);
+
+	pseudoheader.saddr = ip->saddr;
+	pseudoheader.daddr = ip->daddr;
+	pseudoheader.reserved = 0;
+	pseudoheader.proto = ip->protocol;
+	pseudoheader.tcp_len = htons(tcp_len);
+
+
+	memcpy(buff,                                           &pseudoheader, sizeof(pseudoheader));
+	memcpy(buff+sizeof(pseudoheader),                       tcp,          sizeof(struct tcphdr));
+	memcpy(buff+sizeof(pseudoheader)+sizeof(struct tcphdr), payload,      payload_len);
+	if(padd)
+		buff[buff_len-1] = 0;
+
+	result = iphdr_cksum(buff, buff_len);
+	free(buff);
+	return result;
+}
+
+int write_pcap_frame(int fd, const char* data, size_t size, int in, unsigned char tcpflags)
+{
+	struct timeval time;
+	struct pcap_frame pcap_header;
+	struct linux_cooked_capture lcc_header;
+	struct iphdr ip_header;
+	struct tcphdr tcp_header;
+
+	/* in=1 means REDIR_IN means from target to client */
+	if(flock(fd, LOCK_EX) < 0) {
+		if(close(fd < 0))
+			exit(1);
+		cap_fd = -1;
+		return -1;
+	}
+
+	/* we have to rather tediously fake a pcap header, linux cooked capture haeder, ip header and tcp header */
+	/* pcap header */
+	gettimeofday(&time,0);
+	pcap_header.tv_sec = time.tv_sec;
+	pcap_header.tv_usec = time.tv_usec;
+	pcap_header.caplen = sizeof(struct linux_cooked_capture) + sizeof(struct iphdr) + sizeof(struct tcphdr) + size;
+	pcap_header.len = sizeof(struct linux_cooked_capture) + sizeof(struct iphdr) + sizeof(struct tcphdr) + size;
+	if(write(fd, &pcap_header, sizeof(pcap_header)) != sizeof(pcap_header))
+		if(close(fd < 0))
+			exit(1);
+
+	/* linux cooked capture header */
+	lcc_header.type = htons(in?0:4); /* 0 sent is to us, 4 is sent by us */
+	lcc_header.arphrd = htons(ARPHRD_ETHER);
+	lcc_header.ll_addr_size = htons(6);
+	memset(lcc_header.ll_hdr_data, 0, sizeof(lcc_header.ll_hdr_data));
+	memset(lcc_header.ll_hdr_data, in?2:1, 6);
+	lcc_header.proto_type = htons(ETHERTYPE_IP);
+	if(write(fd, &lcc_header, sizeof(lcc_header)) != sizeof(lcc_header))
+		if(close(fd < 0))
+			exit(1);
+
+	/* ip header */
+	ip_header.version = IPVERSION;
+	ip_header.ihl = sizeof(struct iphdr)/sizeof(u_int32_t);
+	ip_header.tos = IPTOS_TOS(0);
+	ip_header.tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + size);
+	ip_header.id = in?ip_id_in++:ip_id_out++;
+	ip_header.frag_off = htons(0x4000); /* don't fragment */
+	ip_header.ttl = 64; /* arbitrary */
+	ip_header.protocol = IPPROTO_TCP;
+	if(in) {
+		ip_header.saddr = target_sockaddr.sin_addr.s_addr;
+		ip_header.daddr = client_sockaddr.sin_addr.s_addr;
+	} else {
+		ip_header.saddr = client_sockaddr.sin_addr.s_addr;
+		ip_header.daddr = target_sockaddr.sin_addr.s_addr;
+	}
+	ip_header.check = 0;
+	ip_header.check = htons(iphdr_cksum(&ip_header, sizeof(struct iphdr)));
+	if(write(fd, &ip_header, sizeof(ip_header)) != sizeof(ip_header))
+		if(close(fd < 0))
+			exit(1);
+
+	memset(&tcp_header, 0, sizeof(struct tcphdr));
+	if(tcpflags & TCPFLAG_SYN)
+		tcp_header.syn=1;
+	if(tcpflags & TCPFLAG_FIN)
+		tcp_header.fin=1;
+	if(in) {
+		tcp_header.source = target_sockaddr.sin_port;
+		tcp_header.dest = client_sockaddr.sin_port;
+		/* seq counts data in the direction we're sending */
+		tcp_header.seq = htonl(tcp_seq_in);
+		tcp_seq_in += (tcp_header.syn||tcp_header.fin) ? 1 : size;
+		/* ack-seq is the seq+size of the last packet we saw going the other way */
+		if(tcp_seq_out != tcp_lastack_in) {
+			tcp_header.ack_seq = htonl(tcp_seq_out);
+			tcp_lastack_in = tcp_seq_out;
+			tcp_header.ack=1;
+		}
+	} else {
+		tcp_header.source = client_sockaddr.sin_port;
+		tcp_header.dest = target_sockaddr.sin_port;
+		tcp_header.seq = htonl(tcp_seq_out);
+		tcp_seq_out += (tcp_header.syn||tcp_header.fin) ? 1 : size;
+		if(tcp_seq_in != tcp_lastack_out) {
+			tcp_header.ack_seq = htonl(tcp_seq_in);
+			tcp_lastack_out = tcp_seq_in;
+			tcp_header.ack=1;
+		}
+	}
+	tcp_header.doff = 5; /* no options */
+	tcp_header.window = htons(32768);
+	tcp_header.check = htons(tcphdr_cksum(&ip_header, &tcp_header, data, size));
+	if(write(fd, &tcp_header, sizeof(tcp_header)) != sizeof(tcp_header))
+		if(close(fd < 0))
+			exit(1);
+
+	if(size && write(fd, data, size) != size)
+		if(close(fd < 0))
+			exit(1);
+
+	if(flock(fd, LOCK_UN) < 0)
+		exit(1);
+	return 0;
+}
+#endif
+
 int
 main(int argc, char *argv[])
 {
@@ -999,6 +1295,9 @@
 	int inetd = 0;
 	char * target_ip;
 	char * ip_to_target;
+#ifndef NO_CAPTURE
+	char * cap_file = 0;
+#endif
 
 	debug("parse args\n");
 	parse_args(argc, argv, &target_addr, &target_port, &local_addr, 
@@ -1011,6 +1310,9 @@
 		   &bufsize, &max_bandwidth, &random_wait,
 		   &wait_in_out,
 #endif
+#ifndef NO_CAPTURE
+		   &cap_file,
+#endif
                    &connect_str);
 
 	/* Set up target */
@@ -1052,7 +1354,28 @@
 		ip_to_target = strdup(inet_ntoa(addr_out.sin_addr));
 		debug1("IP address for target is %s\n", ip_to_target);
 	}
-           
+
+#ifndef NO_CAPTURE
+	if(cap_file) {
+		int flags = O_WRONLY|O_CREAT;
+		flags |= inetd?O_APPEND:O_TRUNC;
+		cap_fd = open(cap_file, flags, 0666);
+		if(cap_fd != -1) {
+			if(flock(cap_fd, LOCK_EX) < 0) {
+				if(close(cap_fd < 0))
+					exit(1);
+				cap_fd = -1;
+			} else {
+				if(lseek(cap_fd, 0, SEEK_CUR) == 0) {
+					write_pcap_header(cap_fd);
+				}
+				if(flock(cap_fd, LOCK_UN) < 0)
+					exit(1);
+			}
+			memcpy(&target_sockaddr, &target, sizeof(struct sockaddr_in));
+		}
+	}
+#endif
 
 	if (inetd) {
 		int targetsock;
@@ -1118,6 +1441,11 @@
 			       target_ip, ntohs(target.sin_port));
 		}
 
+#ifndef NO_CAPTURE
+		if(cap_fd != -1)
+			memcpy(&client_sockaddr, &client, sizeof(struct sockaddr_in));
+#endif
+
 		/* Just start copying - one side of the loop is stdin - 0 */
 		copyloop(0, targetsock, timeout);
 	} else {
diff -ur redir-2.2.1-deb/redir.c.orig redir-2.2.x-deb/redir.c.orig
--- redir-2.2.1-deb/redir.c.orig	2009-04-30 20:19:40.733562390 +0100
+++ redir-2.2.x-deb/redir.c.orig	2009-04-30 20:43:59.137608159 +0100
@@ -61,6 +61,12 @@
  *   - Emmanuel Chantréau <ech...@maretmanu.org>
  */
 
+/* xxxxxx Added libpcap-format capture file creation, with faked link-layer/ip/tcp
+ *        headers. Useful for snooping on traffic and/or analysis with wireshark.
+ *
+ *   - Jeff Snyder <jeff -at- caffeinated -dot- me -dot- uk>
+ */
+
 #define  VERSION "2.2.1"
 
 #include <stdio.h>
@@ -73,7 +79,6 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
-#include <time.h>
 #include <sys/wait.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -84,6 +89,17 @@
 #include <tcpd.h>
 #endif
 
+#ifndef NO_CAPTURE
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <time.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#endif
+
 #define debug(x)	if (dodebug) fprintf(stderr, x)
 #define debug1(x,y)	if (dodebug) fprintf(stderr, x, y)
 
@@ -126,6 +142,47 @@
 int     deny_severity = LOG_WARNING;
 #endif /* USE_TCP_WRAPPERS */
 
+#ifndef NO_CAPTURE
+int cap_fd = -1;
+struct sockaddr_in client_sockaddr;
+struct sockaddr_in target_sockaddr;
+u_short ip_id_in = 0;
+u_short ip_id_out = 0;
+u_int32_t tcp_seq_in;
+u_int32_t tcp_seq_out;
+u_int32_t tcp_lastack_in;
+u_int32_t tcp_lastack_out;
+#define NO_TCPFLAGS 0
+#define TCPFLAG_SYN 1
+#define TCPFLAG_FIN 2
+
+struct pcap_file_header {
+	u_int32_t magic;
+	u_short version_major;
+	u_short version_minor;
+	int32_t thiszone; /* gmt to local correction */
+	u_int32_t sigfigs;    /* accuracy of timestamps */
+	u_int32_t snaplen;    /* max length saved portion of each pkt */
+	u_int32_t linktype;   /* data link type (LINKTYPE_*) */
+};
+
+struct pcap_frame {
+	int32_t   tv_sec;
+	int32_t   tv_usec;
+	u_int32_t caplen;     /* length of portion present */
+	u_int32_t len;        /* length this packet (off wire) */
+};
+
+struct linux_cooked_capture {
+	u_short type;
+	u_short arphrd;
+	u_short ll_addr_size;
+	u_char  ll_hdr_data[8];
+	u_short proto_type;
+};
+
+#endif
+
 #ifdef NEED_STRRCHR
 #define strrchr rindex
 #endif /* NEED_STRRCHR */
@@ -136,6 +193,9 @@
 /* prototype anything needing it */
 void do_accept(int servsock, struct sockaddr_in *target);
 int bindsock(char *addr, int port, int fail);
+#ifndef NO_CAPTURE
+int write_pcap_frame(int fd, const char* data, size_t size, int in, unsigned char tcpflags);
+#endif
 
 #ifndef NO_SHAPER
 /* Used in this program to write something in a socket, it has the same
@@ -234,11 +294,14 @@
 #ifndef NO_SHAPER
         /* options for bandwidth */
         fprintf(stderr, "\t\t--bufsize=<octets>\tsize of the buffer\n");
-        fprintf(stderr, "\t\t--max_bandwidth=<bit-per-sec>\tlimit the bandwidth\n");
+        fprintf(stderr, "\t\t--maxbandwidth=<bit-per-sec>\tlimit the bandwidth\n");
         fprintf(stderr, "\t\t--random_wait=<millisec>\twait before each packet\n");
         fprintf(stderr, "\t\t--wait_in_out=<flag>\t1 wait for in, 2 out, 3 in&out\n");
         /* end options for bandwidth */
 #endif
+#ifndef NO_CAPTURE
+	fprintf(stderr, "\t\t--cap_file=<file>\tcreate a libpcap-format capture file\n");
+#endif
 	fprintf(stderr, "\n\tVersion %s.\n", VERSION);
 	exit(2);
 }
@@ -265,6 +328,9 @@
            int * random_wait,
            int * wait_in_out,
 #endif
+#ifndef NO_CAPTURE
+	   char **cap_file,
+#endif
 	   char **connect_str)
 {
 	static struct option long_options[] = {
@@ -286,6 +352,7 @@
                 {"max_bandwidth",  required_argument,       0, 'm'},
                 {"random_wait",  required_argument,       0, 'w'},
                 {"wait_in_out",  required_argument,       0, 'o'},
+		{"cap-file",  required_argument,       0, 'u'},
 		{0,0,0,0}		/* End marker */
 	};
 	
@@ -358,6 +425,13 @@
 				redir_usage(argv[0]);
 				exit(1);
 			}
+#	ifndef NO_CAPTURE
+			if(cap_file) {
+				fprintf(stderr, "Using --cap-file with --ftp is not supported\n\n");
+				redir_usage(argv[0]);
+				exit(1);
+			}
+#	endif
 			break;
 #endif	     
 
@@ -384,6 +458,20 @@
                   wait_out=*wait_in_out & 2;
                   break;
 #endif 
+
+#ifndef NO_CAPTURE
+		case 'u':
+#	ifndef NO_FTP
+			if(ftp_type) {
+				fprintf(stderr, "Using --cap-file with --ftp is not supported\n\n");
+				redir_usage(argv[0]);
+				exit(1);
+			}
+#	endif
+			*cap_file = optarg;
+			break;
+#endif
+
 		default:
 			redir_usage(argv[0]);
 			exit(1);
@@ -510,7 +598,7 @@
 	if(getsockname(localsock, (struct sockaddr *)&sockname, &socksize) < 0) {
 		perror("getsockname");
 		if (dosyslog)
-			syslog(LOG_ERR, "getsockname failed: %s",strerror(errno));
+			syslog(LOG_ERR, "getsockname failed: %m");
 		exit(1);
 	}
 
@@ -563,7 +651,7 @@
 	switch(fork())
 	{
      	case -1: /* Error */
-     		syslog(LOG_ERR, "Couldn't fork: %s",strerror(errno));
+     		syslog(LOG_ERR, "Couldn't fork: %m");
      		_exit(1);
      	case 0:  /* Child */
 	{
@@ -599,6 +687,10 @@
 	/* Record start time */
 	start_time = (unsigned int) time(NULL);
 
+	/* Set up timeout */
+	timeout.tv_sec = timeout_secs;
+	timeout.tv_usec = 0;
+
 	/* file descriptor bits */
 	FD_ZERO(&iofds);
 	FD_SET(insock, &iofds);
@@ -611,30 +703,43 @@
 		max_fd = outsock;
 	}
 
+#ifndef NO_CAPTURE
+	{
+		struct timeval temp;
+		gettimeofday(&temp, 0);
+		tcp_seq_in += temp.tv_usec;
+		tcp_lastack_out = tcp_seq_in;
+		tcp_seq_out += ~(temp.tv_usec);
+		tcp_lastack_in = tcp_seq_out;
+	}
+	write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_SYN);
+	write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_SYN);
+	write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, NO_TCPFLAGS);
+#endif
+
 	debug1("Entering copyloop() - timeout is %d\n", timeout_secs);
 	while(1) {
 		(void) memcpy(&c_iofds, &iofds, sizeof(iofds));
 
-		/* Set up timeout, Linux returns seconds left in this structure
-		 * so we have to reset it before each select(). */
-		timeout.tv_sec = timeout_secs;
-		timeout.tv_usec = 0;
-
 
 		if (select(max_fd + 1,
 			   &c_iofds,
 			   (fd_set *)0,
 			   (fd_set *)0,
 			   (timeout_secs ? &timeout : NULL)) <= 0) {
-		  if (dosyslog) {
-		    syslog(LOG_NOTICE,"connection timeout: %d sec",timeout_secs);
-		  }
-		  break;
+			/*	    syslog(LLEV,"connection timeout: %d sec",timeout.tv_sec);*/
+			break;
 		}
 
 		if(FD_ISSET(insock, &c_iofds)) {
-			if((bytes = read(insock, buf, sizeof(buf))) <= 0)
+			if((bytes = read(insock, buf, sizeof(buf))) <= 0) {
+#ifndef NO_CAPTURE
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, NO_TCPFLAGS);
+#endif
 				break;
+			}
 #ifndef NO_FTP
 			if (ftp & FTP_PORT)
 				/* if we're correcting FTP, lookup for a PORT commando
@@ -645,11 +750,20 @@
 #endif
 				if(redir_write(outsock, buf, bytes, REDIR_OUT) != bytes)
 					break;
+#ifndef NO_CAPTURE
+			write_pcap_frame(cap_fd, buf, bytes, REDIR_OUT, NO_TCPFLAGS);
+#endif
 			bytes_out += bytes;
 		}
 		if(FD_ISSET(outsock, &c_iofds)) {
-			if((bytes = read(outsock, buf, sizeof(buf))) <= 0)
+			if((bytes = read(outsock, buf, sizeof(buf))) <= 0) {
+#ifndef NO_CAPTURE
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_IN, TCPFLAG_FIN);
+				write_pcap_frame(cap_fd, 0, 0, REDIR_OUT, NO_TCPFLAGS);
+#endif
 				break;
+			}
 			/* if we're correcting for PASV on ftp redirections, then
 			   fix buf and bytes to have the new address, among other
 			   things */
@@ -660,6 +774,9 @@
 #endif
 				if(redir_write(insock, buf, bytes, REDIR_IN) != bytes)
 					break;
+#ifndef NO_CAPTURE
+			write_pcap_frame(cap_fd, buf, bytes, REDIR_IN, NO_TCPFLAGS);
+#endif
 			bytes_in += bytes;
 		}
 	}
@@ -731,7 +848,7 @@
 		perror("server: accept");
 
 		if (dosyslog)
-			syslog(LOG_ERR, "accept failed: %s",strerror(errno));
+			syslog(LOG_ERR, "accept failed: %m");
 
 		/* determine if this error is fatal */
 		switch(accept_errno) {
@@ -749,7 +866,7 @@
 	}
      
 	debug1("peer IP is %s\n", inet_ntoa(client.sin_addr));
-	debug1("peer socket is %d\n", ntohs(client.sin_port));
+	debug1("peer socket is %d\n", client.sin_port);
 
 	/*
 	 * Double fork here so we don't have to wait later
@@ -765,7 +882,7 @@
      		perror("(server) fork");
 
      		if (dosyslog)
-     			syslog(LOG_ERR, "(server) fork failed: %s",strerror(errno));
+     			syslog(LOG_ERR, "(server) fork failed: %m");
 
      		_exit(1);
      	case 0:  /* Child */
@@ -792,7 +909,7 @@
      		perror("(child) fork");
 
      		if (dosyslog)
-     			syslog(LOG_ERR, "(child) fork failed: %s",strerror(errno));
+     			syslog(LOG_ERR, "(child) fork failed: %m");
 
      		_exit(1);
      	case 0:  /* Child */
@@ -806,8 +923,8 @@
 #ifdef USE_TCP_WRAPPERS
 	request_init(&request, RQ_DAEMON, ident, RQ_FILE, clisock, 0);
 	sock_host(&request);
-	sock_hostname(request.client);
-	sock_hostaddr(request.client);
+	sock_hostname(&request);
+	sock_hostaddr(&request);
 
 	if (!hosts_access(&request)) {
 		refuse(&request);
@@ -823,7 +940,7 @@
 		perror("target: socket");
 	  
 		if (dosyslog)
-			syslog(LOG_ERR, "socket failed: %s",strerror(errno));
+			syslog(LOG_ERR, "socket failed: %m");
 		
 		_exit(1);
 	}
@@ -847,7 +964,7 @@
    only be different if the input value is 0 (let the system pick a 
    port) */
 			if (dosyslog)
-				syslog(LOG_ERR, "bind failed: %s",strerror(errno));
+				syslog(LOG_ERR, "bind failed: %m");
 
 			_exit(1);
 		}
@@ -859,7 +976,7 @@
 		perror("target: connect");
 
 		if (dosyslog)
-			syslog(LOG_ERR, "bind failed: %s",strerror(errno));
+			syslog(LOG_ERR, "bind failed: %m");
 
 		_exit(1);
 	}
@@ -875,8 +992,8 @@
 		strcpy(tmp2, inet_ntoa(target->sin_addr));
 	  
 		syslog(LOG_NOTICE, "connecting %s/%d to %s/%d",
-		       tmp1, ntohs(client.sin_port),
-		       tmp2, ntohs(target->sin_port));
+		       tmp1, client.sin_port,
+		       tmp2, target->sin_port);
 	}
 
 	/* do proxy stuff */
@@ -890,6 +1007,11 @@
         }
 #endif
 
+#ifndef NO_CAPTURE
+	if(cap_fd != -1)
+		memcpy(&client_sockaddr, &client, sizeof(struct sockaddr_in));
+#endif
+
 	copyloop(clisock, targetsock, timeout);
 	exit(0);	/* Exit after copy */
 }
@@ -920,7 +1042,7 @@
 			perror("server: socket");
 
 			if (dosyslog)
-				syslog(LOG_ERR, "socket failed: %s",strerror(errno));
+				syslog(LOG_ERR, "socket failed: %m");
 
 			exit(1);
 		}
@@ -959,7 +1081,7 @@
 			perror("server: bind");
 
 			if (dosyslog)
-				syslog(LOG_ERR, "bind failed: %s",strerror(errno));
+				syslog(LOG_ERR, "bind failed: %m");
 
 			exit(1);
 		}
@@ -977,7 +1099,7 @@
 			perror("server: listen");
 
 			if (dosyslog)
-				syslog(LOG_ERR, "listen failed: %s",strerror(errno));
+				syslog(LOG_ERR, "listen failed: %m");
 
 			exit(1);
 		}
@@ -986,6 +1108,176 @@
 	return servsock;
 }
 
+#ifndef NO_CAPTURE
+int write_pcap_header(int fd)
+{
+	struct pcap_file_header header;
+	header.magic = 0xa1b2c3d4;
+	header.version_major = 2;
+	header.version_minor = 4;
+	header.thiszone =0;
+	header.sigfigs = 0;
+	header.snaplen = 65535;
+	header.linktype = 113;
+	if(write(fd, &header, sizeof(header)) != sizeof(header))
+		return -1;
+	return 0;
+}
+
+u_short iphdr_cksum(void *data, int len)
+{
+	u_int32_t sum=0;
+	size_t i;
+	for (i=0; i < len/2; ++i)
+		sum += ntohs(((u_short*)data)[i]);
+	while (sum & 0xFFFF0000)
+		sum = (sum & 0xFFFF)+(sum >> 16);
+	return ((u_short) ~sum);
+}
+
+u_short tcphdr_cksum(struct iphdr *ip, struct tcphdr *tcp, const char *payload, size_t payload_len)
+{
+	int padd;
+	u_short tcp_len;
+	size_t buff_len;
+	char* buff;
+	u_short result;
+	struct {
+		u_int32_t saddr;
+		u_int32_t daddr;
+		unsigned char reserved;
+		unsigned char proto;
+		u_short tcp_len;
+	} pseudoheader;
+	
+	padd = payload_len&1;
+	tcp_len = payload_len + sizeof(struct tcphdr);
+	buff_len = sizeof(pseudoheader) + tcp_len + padd;
+	buff = malloc(buff_len);
+
+	pseudoheader.saddr = ip->saddr;
+	pseudoheader.daddr = ip->daddr;
+	pseudoheader.reserved = 0;
+	pseudoheader.proto = ip->protocol;
+	pseudoheader.tcp_len = htons(tcp_len);
+
+
+	memcpy(buff,                                           &pseudoheader, sizeof(pseudoheader));
+	memcpy(buff+sizeof(pseudoheader),                       tcp,          sizeof(struct tcphdr));
+	memcpy(buff+sizeof(pseudoheader)+sizeof(struct tcphdr), payload,      payload_len);
+	if(padd)
+		buff[buff_len-1] = 0;
+
+	result = iphdr_cksum(buff, buff_len);
+	free(buff);
+	return result;
+}
+
+int write_pcap_frame(int fd, const char* data, size_t size, int in, unsigned char tcpflags)
+{
+	struct timeval time;
+	struct pcap_frame pcap_header;
+	struct linux_cooked_capture lcc_header;
+	struct iphdr ip_header;
+	struct tcphdr tcp_header;
+
+	/* in=1 means REDIR_IN means from target to client */
+	if(flock(fd, LOCK_EX) < 0) {
+		if(close(fd < 0))
+			exit(1);
+		cap_fd = -1;
+		return -1;
+	}
+
+	/* we have to rather tediously fake a pcap header, linux cooked capture haeder, ip header and tcp header */
+	/* pcap header */
+	gettimeofday(&time,0);
+	pcap_header.tv_sec = time.tv_sec;
+	pcap_header.tv_usec = time.tv_usec;
+	pcap_header.caplen = sizeof(struct linux_cooked_capture) + sizeof(struct iphdr) + sizeof(struct tcphdr) + size;
+	pcap_header.len = sizeof(struct linux_cooked_capture) + sizeof(struct iphdr) + sizeof(struct tcphdr) + size;
+	if(write(fd, &pcap_header, sizeof(pcap_header)) != sizeof(pcap_header))
+		if(close(fd < 0))
+			exit(1);
+
+	/* linux cooked capture header */
+	lcc_header.type = htons(in?0:4); /* 0 sent is to us, 4 is sent by us */
+	lcc_header.arphrd = htons(ARPHRD_ETHER);
+	lcc_header.ll_addr_size = htons(6);
+	memset(lcc_header.ll_hdr_data, 0, sizeof(lcc_header.ll_hdr_data));
+	memset(lcc_header.ll_hdr_data, in?2:1, 6);
+	lcc_header.proto_type = htons(ETHERTYPE_IP);
+	if(write(fd, &lcc_header, sizeof(lcc_header)) != sizeof(lcc_header))
+		if(close(fd < 0))
+			exit(1);
+
+	/* ip header */
+	ip_header.version = IPVERSION;
+	ip_header.ihl = sizeof(struct iphdr)/sizeof(u_int32_t);
+	ip_header.tos = IPTOS_TOS(0);
+	ip_header.tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + size);
+	ip_header.id = in?ip_id_in++:ip_id_out++;
+	ip_header.frag_off = htons(0x4000); /* don't fragment */
+	ip_header.ttl = 64; /* arbitrary */
+	ip_header.protocol = IPPROTO_TCP;
+	if(in) {
+		ip_header.saddr = target_sockaddr.sin_addr.s_addr;
+		ip_header.daddr = client_sockaddr.sin_addr.s_addr;
+	} else {
+		ip_header.saddr = client_sockaddr.sin_addr.s_addr;
+		ip_header.daddr = target_sockaddr.sin_addr.s_addr;
+	}
+	ip_header.check = 0;
+	ip_header.check = htons(iphdr_cksum(&ip_header, sizeof(struct iphdr)));
+	if(write(fd, &ip_header, sizeof(ip_header)) != sizeof(ip_header))
+		if(close(fd < 0))
+			exit(1);
+
+	memset(&tcp_header, 0, sizeof(struct tcphdr));
+	if(tcpflags & TCPFLAG_SYN)
+		tcp_header.syn=1;
+	if(tcpflags & TCPFLAG_FIN)
+		tcp_header.fin=1;
+	if(in) {
+		tcp_header.source = target_sockaddr.sin_port;
+		tcp_header.dest = client_sockaddr.sin_port;
+		/* seq counts data in the direction we're sending */
+		tcp_header.seq = htonl(tcp_seq_in);
+		tcp_seq_in += (tcp_header.syn||tcp_header.fin) ? 1 : size;
+		/* ack-seq is the seq+size of the last packet we saw going the other way */
+		if(tcp_seq_out != tcp_lastack_in) {
+			tcp_header.ack_seq = htonl(tcp_seq_out);
+			tcp_lastack_in = tcp_seq_out;
+			tcp_header.ack=1;
+		}
+	} else {
+		tcp_header.source = client_sockaddr.sin_port;
+		tcp_header.dest = target_sockaddr.sin_port;
+		tcp_header.seq = htonl(tcp_seq_out);
+		tcp_seq_out += (tcp_header.syn||tcp_header.fin) ? 1 : size;
+		if(tcp_seq_in != tcp_lastack_out) {
+			tcp_header.ack_seq = htonl(tcp_seq_in);
+			tcp_lastack_out = tcp_seq_in;
+			tcp_header.ack=1;
+		}
+	}
+	tcp_header.doff = 5; /* no options */
+	tcp_header.window = htons(32768);
+	tcp_header.check = htons(tcphdr_cksum(&ip_header, &tcp_header, data, size));
+	if(write(fd, &tcp_header, sizeof(tcp_header)) != sizeof(tcp_header))
+		if(close(fd < 0))
+			exit(1);
+
+	if(size && write(fd, data, size) != size)
+		if(close(fd < 0))
+			exit(1);
+
+	if(flock(fd, LOCK_UN) < 0)
+		exit(1);
+	return 0;
+}
+#endif
+
 int
 main(int argc, char *argv[])
 {
@@ -998,6 +1290,9 @@
 	int inetd = 0;
 	char * target_ip;
 	char * ip_to_target;
+#ifndef NO_CAPTURE
+	char * cap_file = 0;
+#endif
 
 	debug("parse args\n");
 	parse_args(argc, argv, &target_addr, &target_port, &local_addr, 
@@ -1010,6 +1305,9 @@
 		   &bufsize, &max_bandwidth, &random_wait,
 		   &wait_in_out,
 #endif
+#ifndef NO_CAPTURE
+		   &cap_file,
+#endif
                    &connect_str);
 
 	/* Set up target */
@@ -1051,7 +1349,28 @@
 		ip_to_target = strdup(inet_ntoa(addr_out.sin_addr));
 		debug1("IP address for target is %s\n", ip_to_target);
 	}
-           
+
+#ifndef NO_CAPTURE
+	if(cap_file) {
+		int flags = O_WRONLY|O_CREAT;
+		flags |= inetd?O_APPEND:O_TRUNC;
+		cap_fd = open(cap_file, flags, 0666);
+		if(cap_fd != -1) {
+			if(flock(cap_fd, LOCK_EX) < 0) {
+				if(close(cap_fd < 0))
+					exit(1);
+				cap_fd = -1;
+			} else {
+				if(lseek(cap_fd, 0, SEEK_CUR) == 0) {
+					write_pcap_header(cap_fd);
+				}
+				if(flock(cap_fd, LOCK_UN) < 0)
+					exit(1);
+			}
+			memcpy(&target_sockaddr, &target, sizeof(struct sockaddr_in));
+		}
+	}
+#endif
 
 	if (inetd) {
 		int targetsock;
@@ -1061,8 +1380,8 @@
 #ifdef USE_TCP_WRAPPERS
 		request_init(&request, RQ_DAEMON, ident, RQ_FILE, 0, 0);
 		sock_host(&request);
-		sock_hostname(request.client);
-		sock_hostaddr(request.client);
+		sock_hostname(&request);
+		sock_hostaddr(&request);
 	
 		if (!hosts_access(&request))
 			refuse(&request);
@@ -1070,13 +1389,13 @@
 
 		if (!getpeername(0, (struct sockaddr *) &client, &client_size)) {
 			debug1("peer IP is %s\n", inet_ntoa(client.sin_addr));
-			debug1("peer socket is %d\n", ntohs(client.sin_port));
+			debug1("peer socket is %d\n", client.sin_port);
 		}
 		if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 			perror("target: socket");
 
 			if (dosyslog)
-				syslog(LOG_ERR, "targetsock failed: %s",strerror(errno));
+				syslog(LOG_ERR, "targetsock failed: %m");
 
 			exit(1);
 		}
@@ -1094,7 +1413,7 @@
 				perror("bind_addr: cannot bind to forcerd outgoing addr");
 				 
 				if (dosyslog)
-					syslog(LOG_ERR, "bind failed: %s",strerror(errno));
+					syslog(LOG_ERR, "bind failed: %m");
 				 
 				exit(1);
 			}
@@ -1106,17 +1425,22 @@
 			perror("target: connect");
 
 			if (dosyslog)
-				syslog(LOG_ERR, "connect failed: %s",strerror(errno));
+				syslog(LOG_ERR, "connect failed: %m");
 
 			exit(1);
 		}
 
 		if (dosyslog) {
 			syslog(LOG_NOTICE, "connecting %s/%d to %s/%d",
-			       inet_ntoa(client.sin_addr), ntohs(client.sin_port),
-			       target_ip, ntohs(target.sin_port));
+			       inet_ntoa(client.sin_addr), client.sin_port,
+			       target_ip, target.sin_port);
 		}
 
+#ifndef NO_CAPTURE
+		if(cap_fd != -1)
+			memcpy(&client_sockaddr, &client, sizeof(struct sockaddr_in));
+#endif
+
 		/* Just start copying - one side of the loop is stdin - 0 */
 		copyloop(0, targetsock, timeout);
 	} else {
diff -ur redir-2.2.1-deb/redir.man redir-2.2.x-deb/redir.man
--- redir-2.2.1-deb/redir.man	2009-04-30 20:19:40.753562630 +0100
+++ redir-2.2.x-deb/redir.man	2009-04-30 20:43:49.936583928 +0100
@@ -21,6 +21,7 @@
 .RB [ \--max_bandwidth=n ]
 .RB [ \--random_wait=n ]
 .RB [ \--wait_in_out=n ]
+.RB [ \--cap_file=file ]
 .ll -8
 .br
 .B redir
@@ -38,6 +39,7 @@
 .RB [ \--max_bandwidth=n ]
 .RB [ \--random_wait=n ]
 .RB [ \--wait_in_out=n ]
+.RB [ \--cap_file=file ]
 .ll -8
 .br
 .SH DESCRIPTION
@@ -117,6 +119,10 @@
 .B \--wait_in_out n
 Apply --max_bandwidth and --random_wait for input if n=1, output if n=2 and
 both if n=3.
+.TP
+.B \--cap_file file
+Create a libpcap-format capture file and record data passing through redir
+in it. The link-layer, ip and tcp headers in the file will be synthesized.
 .SH "SEE ALSO"
 inetd(1)
 

Reply via email to