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)