94 #include <sys/types.h>
95 #include <sys/socket.h>
96 #include <sys/select.h>
98 #include <netinet/in.h>
99 #include <arpa/inet.h>
114 #define LINUX_NETFILTER
117 #ifdef LINUX_NETFILTER
119 #include <linux/netfilter_ipv4.h>
123 #define PARSE_LONG_OPT
124 #ifdef PARSE_LONG_OPT
129 #define VERSION "1.1"
131 #define MAX_BUF 100000
134 #define ERR(x...) fprintf(stderr,x)
140 #define DBG(x...) printf(x)
147 #define UDP_TIMEOUT 30
239 if (why)
ERR(
"Error: %s\n\n",why);
240 ERR(
"Usage: netsed [option] proto lport rhost rport rule1 [ rule2 ... ]\n\n");
241 ERR(
" use netsed -h for more information on usage.\n");
249 if (why)
ERR(
"Error: %s\n\n",why);
250 ERR(
"Usage: netsed [option] proto lport rhost rport rule1 [ rule2 ... ]\n\n");
251 #ifdef PARSE_LONG_OPT
252 ERR(
" options - can be --ipv4 or -4 to force address resolution in IPv4,\n");
253 ERR(
" --ipv6 or -6 to force address resolution in IPv6,\n");
254 ERR(
" --ipany to resolve the address in either IPv4 or IPv6.\n");
255 ERR(
" - --help or -h to display this usage informations.\n");
257 ERR(
" options - can be nothing, -4 to force address resolution in IPv4\n");
258 ERR(
" or -6 to force address resolution in IPv6.\n");
259 ERR(
" - -h to display this usage informations.\n");
261 ERR(
" proto - protocol specification (tcp or udp)\n");
262 ERR(
" lport - local port to listen on (see README for transparent\n");
263 ERR(
" traffic intercepting on some systems)\n");
264 ERR(
" rhost - where connection should be forwarded (0 = use destination\n");
265 ERR(
" address of incoming connection, see README)\n");
266 ERR(
" rport - destination port (0 = dst port of incoming connection)\n");
267 ERR(
" ruleN - replacement rules (see below)\n\n");
268 ERR(
"General syntax of replacement rules: s/pat1/pat2[/expire]\n\n");
269 ERR(
"This will replace all occurrences of pat1 with pat2 in any matching packet.\n");
270 ERR(
"An additional parameter (count) can be used to expire a rule after 'count'\n");
271 ERR(
"successful substitutions for a given connection. Eight-bit characters,\n");
272 ERR(
"including NULL and '/', can be passed using HTTP-like hex escape\n");
273 ERR(
"sequences (e.g. CRLF as %%0a%%0d).\n");
274 ERR(
"A match on '%%' can be achieved by specifying '%%%%'. Examples:\n\n");
275 ERR(
" 's/andrew/mike/1' - replace 'andrew' with 'mike' (only first time)\n");
276 ERR(
" 's/andrew/mike' - replace all occurrences of 'andrew' with 'mike'\n");
277 ERR(
" 's/andrew/mike%%00%%00' - replace 'andrew' with 'mike\\x00\\x00'\n");
278 ERR(
" (manually padding to keep original size)\n");
279 ERR(
" 's/%%%%/%%2f/20' - replace the 20 first occurrence of '%%' with '/'\n\n");
280 ERR(
"Rules are not active across packet boundaries, and they are evaluated\n");
281 ERR(
"from first to last, not yet expired rule, as stated on the command line.\n");
291 if(conn->
csa != NULL) {
306 while(connections != NULL) {
308 connections = conn->
n;
316 in_port_t
get_port(
struct sockaddr *sa) __attribute__ ((noinline));
317 void set_port(
struct sockaddr *sa, in_port_t port) __attribute__ ((noinline));
323 switch (sa->sa_family) {
325 return ntohs(((
struct sockaddr_in *) sa)->sin_port);
327 return ntohs(((
struct sockaddr_in6 *) sa)->sin6_port);
336 void set_port(
struct sockaddr *sa, in_port_t port) {
337 switch (sa->sa_family) {
339 ((
struct sockaddr_in *) sa)->sin_port = htons(port);
342 ((
struct sockaddr_in6 *) sa)->sin6_port = htons(port);
352 switch (sa->sa_family) {
354 return (((
struct sockaddr_in *) sa)->sin_addr.s_addr == htonl(INADDR_ANY));
356 return !memcmp(&((
struct sockaddr_in6 *) sa)->sin6_addr, &in6addr_any,
sizeof(in6addr_any));
365 ERR(
"[-] Error: %s\n",reason);
366 ERR(
"netsed: exiting.\n");
372 char hex[]=
"0123456789ABCDEF";
380 r->
to=malloc(strlen(r->
torig));
381 if ((!r->
from) || (!r->
to))
error(
"shrink_to_binary: unable to malloc() buffers");
383 for (i=0;i<strlen(r->
forig);i++) {
384 if (r->
forig[i]==
'%') {
387 if (r->
forig[i]==
'%') {
394 if (!r->
forig[i])
error(
"shrink_to_binary: src pattern: unexpected end.");
395 if (!r->
forig[i+1])
error(
"shrink_to_binary: src pattern: unexpected end.");
396 x=strchr(hex,toupper(r->
forig[i]));
397 if (!x)
error(
"shrink_to_binary: src pattern: non-hex sequence.");
399 x=strchr(hex,toupper(r->
forig[i+1]));
400 if (!x)
error(
"shrink_to_binary: src pattern: non-hex sequence.");
412 for (i=0;i<strlen(r->
torig);i++) {
413 if (r->
torig[i]==
'%') {
416 if (r->
torig[i]==
'%') {
423 if (!r->
torig[i])
error(
"shrink_to_binary: dst pattern: unexpected end.");
424 if (!r->
torig[i+1])
error(
"shrink_to_binary: dst pattern: unexpected end.");
425 x=strchr(hex,toupper(r->
torig[i]));
426 if (!x)
error(
"shrink_to_binary: dst pattern: non-hex sequence.");
428 x=strchr(hex,toupper(r->
torig[i+1]));
429 if (!x)
error(
"shrink_to_binary: dst pattern: non-hex sequence.");
449 #ifdef PARSE_LONG_OPT
450 static struct option long_options[] = {
454 {
"ipany", 0, &
family, AF_UNSPEC},
458 while ((i = getopt_long(argc, argv,
"46h", long_options, NULL)) != -1)
460 while ((i = getopt(argc, argv,
"46h")) != -1)
483 if (strcasecmp(argv[optind],
"tcp")*strcasecmp(argv[optind],
"udp"))
short_usage_hints(
"incorrect protocol");
484 tcp = strncasecmp(argv[optind++],
"udp", 3);
487 lport = argv[optind++];
490 rhost = argv[optind++];
491 rport = argv[optind++];
494 rule=malloc((argc-optind)*
sizeof(
struct rule_s));
495 rule_live=malloc((argc-optind)*
sizeof(
int));
497 for (i=optind;i<argc;i++) {
498 char *fs=0, *ts=0, *cs=0;
499 printf(
"[*] Parsing rule %s...\n",argv[i]);
500 fs=strchr(argv[i],
'/');
501 if (!fs)
error(
"missing first '/' in rule");
504 if (!ts)
error(
"missing second '/' in rule");
508 if (cs) { *cs=0; cs++; }
518 printf(
"[+] Loaded %d rule%s...\n",
rules, (
rules > 1) ?
"s" :
"");
529 struct addrinfo hints, *res, *reslist;
531 memset(&hints,
'\0',
sizeof(hints));
532 hints.ai_family = af;
533 hints.ai_flags = AI_PASSIVE;
534 hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM;
536 if ((ret = getaddrinfo(NULL, portstr, &hints, &reslist))) {
537 ERR(
"getaddrinfo(): %s\n", gai_strerror(ret));
538 error(
"Impossible to resolve listening port.");
541 for (res = reslist; res; res = res->ai_next) {
544 if ( (
lsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
546 setsockopt(
lsock, SOL_SOCKET, SO_REUSEADDR, &one,
sizeof(one));
549 one = (
family == AF_UNSPEC) ? 0 : 1;
550 if (res->ai_family == AF_INET6)
551 if (setsockopt(
lsock, IPPROTO_IPV6, IPV6_V6ONLY, &one,
sizeof(one)))
552 printf(
" Failed to unset IPV6_V6ONLY: %s.\n", strerror(errno));
553 if (bind(
lsock, res->ai_addr, res->ai_addrlen) < 0) {
554 ERR(
"bind(): %s", strerror(errno));
559 if (listen(
lsock, 16) < 0) {
565 setsockopt(
lsock,SOL_SOCKET,SO_OOBINLINE,&one,
sizeof(
int));
570 freeaddrinfo(reslist);
572 error(
"Listening socket failed.");
590 for (j=0;j<
rules;j++) {
591 if ((!memcmp(&buf[i],rule[j].from,rule[j].fs)) && (live[j]!=0)) {
594 printf(
" Applying rule s/%s/%s...\n",rule[j].forig,rule[j].torig);
596 if (live[j]==0) printf(
" (rule just expired)\n");
597 memcpy(&b2[newsize],rule[j].to,rule[j].ts);
609 if (!changes) printf(
"[*] Forwarding untouched packet of size %d.\n",siz);
610 else printf(
"[*] Done %d replacements, forwarding packet of size %d (orig %d).\n",
611 changes,newsize,siz);
625 rd=read(conn->
fsock,buf,
sizeof(buf));
626 if ((rd<0) && (errno!=EAGAIN))
628 DBG(
"[!] server disconnected. (rd err) %s\n",strerror(errno));
633 DBG(
"[!] server disconnected. (rd)\n");
637 printf(
"[+] Caught server -> client packet.\n");
641 if (sendto(conn->
csock,b2,rd,0,conn->
csa, conn->
csl)<=0) {
642 DBG(
"[!] client disconnected. (wr)\n");
652 rd=read(conn->
csock,buf,
sizeof(buf));
653 if ((rd<0) && (errno!=EAGAIN))
655 DBG(
"[!] client disconnected. (rd err)\n");
660 DBG(
"[!] client disconnected. (rd)\n");
671 printf(
"[+] Caught client -> server packet.\n");
674 if (write(conn->
fsock,b2,rd)<=0) {
675 DBG(
"[!] server disconnected. (wr)\n");
684 DBG(
"[!] user interrupt request (%d)\n",getpid());
689 int main(
int argc,
char* argv[]) {
691 in_port_t fixedport = 0;
692 struct sockaddr_storage fixedhost;
693 struct addrinfo hints, *res, *reslist;
696 memset(&fixedhost,
'\0',
sizeof(fixedhost));
697 printf(
"netsed " VERSION " by Julien VdG <julien@silicone.homelinux.org>\n"
698 " based on 0.01c from Michal Zalewski <lcamtuf@ids.pl>\n");
699 setbuffer(stdout,NULL,0);
703 memset(&hints,
'\0',
sizeof(hints));
705 hints.ai_flags = AI_CANONNAME;
706 hints.ai_socktype =
tcp ? SOCK_STREAM : SOCK_DGRAM;
708 if ((ret = getaddrinfo(
rhost,
rport, &hints, &reslist))) {
709 ERR(
"getaddrinfo(): %s\n", gai_strerror(ret));
710 error(
"Impossible to resolve remote address or port.");
713 for (res = reslist; res; res = res->ai_next) {
716 if ( (sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
722 memcpy(&fixedhost, res->ai_addr, res->ai_addrlen);
726 freeaddrinfo(reslist);
728 error(
"Failed in resolving remote host.");
730 if (fixedhost.ss_family && fixedport)
731 printf(
"[+] Using fixed forwarding to %s,%s.\n",
rhost,
rport);
733 printf(
"[+] Using dynamic (transparent proxy) forwarding with fixed port %s.\n",
rport);
734 else if (fixedhost.ss_family)
735 printf(
"[+] Using dynamic (transparent proxy) forwarding with fixed addr %s.\n",
rhost);
737 printf(
"[+] Using dynamic (transparent proxy) forwarding.\n");
741 printf(
"[+] Listening on port %s/%s.\n",
lport, (
tcp)?
"tcp":
"udp");
743 signal(SIGPIPE, SIG_IGN);
746 sigemptyset(&sa.sa_mask);
748 if (sigaction(SIGINT, &sa, NULL) == -1)
error(
"netsed: sigaction() failed");
751 struct sockaddr_storage s;
752 socklen_t l =
sizeof(s);
753 struct sockaddr_storage conho;
755 char ipstr[INET6_ADDRSTRLEN], portstr[12];
759 struct timeval timeout, *ptimeout;
762 FD_SET(
lsock,&rd_set);
769 while(conn != NULL) {
771 FD_SET(conn->
csock, &rd_set);
772 if (nfds < conn->csock) nfds = conn->
csock;
776 if (remain < 0) remain = 0;
777 if (timeout.tv_sec > remain) {
778 timeout.tv_sec = remain;
783 FD_SET(conn->
fsock, &rd_set);
784 if (nfds < conn->fsock) nfds = conn->
fsock;
790 sel=select(nfds+1, &rd_set, (fd_set*)0, (fd_set*)0, ptimeout);
797 DBG(
"[!] select fail! %s\n", strerror(errno));
801 DBG(
"[*] select timeout. now: %d\n",
now);
807 if (FD_ISSET(
lsock, &rd_set)) {
811 csock = accept(
lsock,(
struct sockaddr*)&s,&l);
816 rd = recvfrom(
lsock,buf,
sizeof(buf),0,(
struct sockaddr*)&s,&l);
819 while(conn != NULL) {
821 if ((conn->
csl == l) && (0 == memcmp(&s, conn->
csa, l))) {
833 DBG(
"[+] Got incoming datagram from existing connection.\n");
836 ERR(
"recvfrom(): %s", strerror(errno));
843 getnameinfo((
struct sockaddr *) &s, l, ipstr,
sizeof(ipstr),
844 portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
845 printf(
"[+] Got incoming connection from %s,%s", ipstr, portstr);
847 if(NULL == conn)
error(
"netsed: unable to malloc() connection tracker struct");
850 setsockopt(csock,SOL_SOCKET,SO_OOBINLINE,&one,
sizeof(
int));
855 conn->
csa = malloc(l);
856 if(NULL == conn->
csa)
error(
"netsed: unable to malloc() connection tracker sockaddr struct");
857 memcpy(conn->
csa, &s, l);
865 if(NULL == conn->
live)
error(
"netsed: unable to malloc() connection tracker sockaddr struct");
869 #ifndef LINUX_NETFILTER
871 getsockname(csock,(
struct sockaddr*)&s,&l);
874 getsockopt(csock, SOL_IP, SO_ORIGINAL_DST,(
struct sockaddr*)&s,&l);
876 getnameinfo((
struct sockaddr *) &s, l, ipstr,
sizeof(ipstr),
877 portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
878 printf(
" to %s,%s\n", ipstr, portstr);
879 conpo =
get_port((
struct sockaddr *) &s);
881 memcpy(&conho, &s,
sizeof(conho));
883 if (fixedport) conpo=fixedport;
884 if (fixedhost.ss_family)
885 memcpy(&conho, &fixedhost,
sizeof(conho));
888 memcpy(&s, &conho,
sizeof(s));
889 set_port((
struct sockaddr *) &s, conpo);
890 getnameinfo((
struct sockaddr *) &s, l, ipstr,
sizeof(ipstr),
891 portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
892 printf(
"[*] Forwarding connection to %s,%s\n", ipstr, portstr);
895 conn->
fsock = socket(s.ss_family,
tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
897 if (connect(conn->
fsock,(
struct sockaddr*)&s,l)) {
898 printf(
"[!] Cannot connect to remote server, dropping connection.\n");
902 setsockopt(conn->
fsock,SOL_SOCKET,SO_OOBINLINE,&one,
sizeof(
int));
908 if((rd >= 0) && (conn != NULL)) {
915 while(conn != NULL) {
917 if(
tcp && FD_ISSET(conn->
csock, &rd_set)) {
920 if(FD_ISSET(conn->
fsock, &rd_set)) {
924 DBG(
"[!] connection last time: %d, now: %d\n", conn->
time,
now);
926 DBG(
"[!] connection timeout.\n");