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>
111 #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.2"
130 #define MAX_BUF 100000
134 #define ERR(x...) fprintf(stderr,x)
139 #define DBG(x...) printf(x)
147 #define UDP_TIMEOUT 30
248 if (why)
ERR(
"Error: %s\n\n",why);
249 ERR(
"Usage: netsed [option] proto lport rhost rport rule1 [ rule2 ... ]\n\n");
250 ERR(
" use netsed -h for more information on usage.\n");
258 if (why)
ERR(
"Error: %s\n\n",why);
259 ERR(
"Usage: netsed [option] proto lport rhost rport rule1 [ rule2 ... ]\n\n");
260 #ifdef PARSE_LONG_OPT
261 ERR(
" options - can be --ipv4 or -4 to force address resolution in IPv4,\n");
262 ERR(
" --ipv6 or -6 to force address resolution in IPv6,\n");
263 ERR(
" --ipany to resolve the address in either IPv4 or IPv6.\n");
264 ERR(
" - --help or -h to display this usage information.\n");
266 ERR(
" options - can be nothing, -4 to force address resolution in IPv4\n");
267 ERR(
" or -6 to force address resolution in IPv6.\n");
268 ERR(
" - -h to display this usage information.\n");
270 ERR(
" proto - protocol specification (tcp or udp)\n");
271 ERR(
" lport - local port to listen on (see README for transparent\n");
272 ERR(
" traffic intercepting on some systems)\n");
273 ERR(
" rhost - where connection should be forwarded (0 = use destination\n");
274 ERR(
" address of incoming connection, see README)\n");
275 ERR(
" rport - destination port (0 = dst port of incoming connection)\n");
276 ERR(
" ruleN - replacement rules (see below)\n\n");
277 ERR(
"General syntax of replacement rules: s/pat1/pat2[/expire]\n\n");
278 ERR(
"This will replace all occurrences of pat1 with pat2 in any matching packet.\n");
279 ERR(
"An additional parameter, 'expire' of the form [CHAR][NUM], can be used to\n");
280 ERR(
"expire a rule after NUM successful substitutions during a given connection.\n");
281 ERR(
"The character CHAR is one of \"iIoO\", with the effect of restricting the rule\n");
282 ERR(
"to apply to incoming (\"iI\") or to outgoing (\"oO\") packets only, as seen from\n");
283 ERR(
"the client's perspective. Both of CHAR and NUM are optional.\n\n");
284 ERR(
"Eight-bit characters, including NULL and '/', can be applied using HTTP-like\n");
285 ERR(
"hex escape sequences (e.g. CRLF as %%0a%%0d).\n");
286 ERR(
"A match on '%%' can be achieved by specifying '%%%%'.\n\nExamples:\n");
287 ERR(
" 's/andrew/mike/1' - replace 'andrew' with 'mike' (only first time)\n");
288 ERR(
" 's/andrew/mike' - replace all occurrences of 'andrew' with 'mike'\n");
289 ERR(
" 's/andrew/mike%%00%%00' - replace 'andrew' with 'mike\\x00\\x00'\n");
290 ERR(
" (manually padding to keep original size)\n");
291 ERR(
" 's/%%%%/%%2f/20' - replace the 20 first occurrence of '%%' with '/'\n");
292 ERR(
" 's/andrew/mike/o' - the server will always see 'mike', never 'andrew'\n\n");
293 ERR(
" 's/Rilke/Proust/o s/Proust/Rilke/i'\n");
294 ERR(
" - let Rilke travel incognito as Proust\n\n");
295 ERR(
"Rules are not active across packet boundaries, and they are evaluated\n");
296 ERR(
"from first to last, not yet expired rule, as stated on the command line.\n");
306 if(conn->
csa != NULL) {
331 in_port_t
get_port(
struct sockaddr *sa) __attribute__ ((noinline));
332 void set_port(
struct sockaddr *sa, in_port_t port) __attribute__ ((noinline));
338 switch (sa->sa_family) {
340 return ntohs(((
struct sockaddr_in *) sa)->sin_port);
342 return ntohs(((
struct sockaddr_in6 *) sa)->sin6_port);
351 void set_port(
struct sockaddr *sa, in_port_t port) {
352 switch (sa->sa_family) {
354 ((
struct sockaddr_in *) sa)->sin_port = htons(port);
357 ((
struct sockaddr_in6 *) sa)->sin6_port = htons(port);
367 switch (sa->sa_family) {
369 return (((
struct sockaddr_in *) sa)->sin_addr.s_addr == htonl(INADDR_ANY));
371 return !memcmp(&((
struct sockaddr_in6 *) sa)->sin6_addr, &in6addr_any,
sizeof(in6addr_any));
380 ERR(
"[-] Error: %s\n",reason);
381 ERR(
"netsed: exiting.\n");
387 char hex[]=
"0123456789ABCDEF";
395 r->
to=malloc(strlen(r->
torig));
396 if ((!r->
from) || (!r->
to))
error(
"shrink_to_binary: unable to malloc() buffers");
398 for (i=0;i<strlen(r->
forig);i++) {
399 if (r->
forig[i]==
'%') {
402 if (r->
forig[i]==
'%') {
409 if (!r->
forig[i])
error(
"shrink_to_binary: src pattern: unexpected end.");
410 if (!r->
forig[i+1])
error(
"shrink_to_binary: src pattern: unexpected end.");
412 if (!x)
error(
"shrink_to_binary: src pattern: non-hex sequence.");
414 x=strchr(
hex,toupper(r->
forig[i+1]));
415 if (!x)
error(
"shrink_to_binary: src pattern: non-hex sequence.");
427 for (i=0;i<strlen(r->
torig);i++) {
428 if (r->
torig[i]==
'%') {
431 if (r->
torig[i]==
'%') {
438 if (!r->
torig[i])
error(
"shrink_to_binary: dst pattern: unexpected end.");
439 if (!r->
torig[i+1])
error(
"shrink_to_binary: dst pattern: unexpected end.");
441 if (!x)
error(
"shrink_to_binary: dst pattern: non-hex sequence.");
443 x=strchr(
hex,toupper(r->
torig[i+1]));
444 if (!x)
error(
"shrink_to_binary: dst pattern: non-hex sequence.");
464 #ifdef PARSE_LONG_OPT
465 static struct option long_options[] = {
469 {
"ipany", 0, &
family, AF_UNSPEC},
473 while ((i = getopt_long(argc, argv,
"46h", long_options, NULL)) != -1)
475 while ((i = getopt(argc, argv,
"46h")) != -1)
498 if (strcasecmp(argv[optind],
"tcp")*strcasecmp(argv[optind],
"udp"))
short_usage_hints(
"incorrect protocol");
499 tcp = strncasecmp(argv[optind++],
"udp", 3);
502 lport = argv[optind++];
505 rhost = argv[optind++];
506 rport = argv[optind++];
509 rule=malloc((argc-optind)*
sizeof(
struct rule_s));
510 rule_live=malloc((argc-optind)*
sizeof(
int));
512 for (i=optind;i<argc;i++) {
513 char *fs=0, *ts=0, *cs=0;
514 printf(
"[*] Parsing rule %s...\n",argv[i]);
515 fs=strchr(argv[i],
'/');
516 if (!fs)
error(
"missing first '/' in rule");
519 if (!ts)
error(
"missing second '/' in rule");
523 if (cs) { *cs=0; cs++; }
528 if (cs && *cs && strchr(
"iIoO", *cs)) {
539 printf(
"[+] Loaded %d rule%s...\n",
rules, (
rules > 1) ?
"s" :
"");
550 struct addrinfo hints, *res, *reslist;
552 memset(&hints,
'\0',
sizeof(hints));
553 hints.ai_family = af;
554 hints.ai_flags = AI_PASSIVE;
555 hints.ai_socktype =
tcp ? SOCK_STREAM : SOCK_DGRAM;
557 if ((ret = getaddrinfo(NULL, portstr, &hints, &reslist))) {
558 ERR(
"getaddrinfo(): %s\n", gai_strerror(ret));
559 error(
"Impossible to resolve listening port.");
562 for (res = reslist; res; res = res->ai_next) {
565 if ( (
lsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
567 setsockopt(
lsock, SOL_SOCKET, SO_REUSEADDR, &one,
sizeof(one));
570 one = (
family == AF_UNSPEC) ? 0 : 1;
571 if (res->ai_family == AF_INET6)
572 if (setsockopt(
lsock, IPPROTO_IPV6, IPV6_V6ONLY, &one,
sizeof(one)))
573 printf(
" Failed to unset IPV6_V6ONLY: %s.\n", strerror(errno));
574 if (bind(
lsock, res->ai_addr, res->ai_addrlen) < 0) {
575 ERR(
"bind(): %s", strerror(errno));
580 if (listen(
lsock, 16) < 0) {
586 setsockopt(
lsock,SOL_SOCKET,SO_OOBINLINE,&one,
sizeof(
int));
591 freeaddrinfo(reslist);
593 error(
"Listening socket failed.");
612 for (j=0;j<
rules;j++) {
613 if (
rule[j].dir !=
ALL &&
rule[j].dir !=dir)
continue;
615 if ((!memcmp(&
buf[i],
rule[j].from,
rule[j].fs)) && (live[j]!=0)) {
618 printf(
" Applying rule s/%s/%s...\n",
rule[j].forig,
rule[j].torig);
620 if (live[j]==0) printf(
" (rule just expired)\n");
633 if (!changes) printf(
"[*] Forwarding untouched packet of size %d.\n",siz);
634 else printf(
"[*] Done %d replacements, forwarding packet of size %d (orig %d).\n",
635 changes,newsize,siz);
650 if ((rd<0) && (errno!=EAGAIN))
652 DBG(
"[!] server disconnected. (rd err) %s\n",strerror(errno));
657 DBG(
"[!] server disconnected. (rd)\n");
661 printf(
"[+] Caught server -> client packet.\n");
666 DBG(
"[!] client disconnected. (wr)\n");
677 if ((rd<0) && (errno!=EAGAIN))
679 DBG(
"[!] client disconnected. (rd err)\n");
684 DBG(
"[!] client disconnected. (rd)\n");
695 printf(
"[+] Caught client -> server packet.\n");
698 if (write(conn->
fsock,
b2,rd)<=0) {
699 DBG(
"[!] server disconnected. (wr)\n");
708 DBG(
"[!] user interrupt request (%d)\n",getpid());
713 int main(
int argc,
char* argv[]) {
715 in_port_t fixedport = 0;
716 struct sockaddr_storage fixedhost;
717 struct addrinfo hints, *res, *reslist;
720 memset(&fixedhost,
'\0',
sizeof(fixedhost));
721 printf(
"netsed " VERSION " by Julien VdG <julien@silicone.homelinux.org>\n"
722 " based on 0.01c from Michal Zalewski <lcamtuf@ids.pl>\n");
723 setbuffer(stdout,NULL,0);
727 memset(&hints,
'\0',
sizeof(hints));
729 hints.ai_flags = AI_CANONNAME;
730 hints.ai_socktype =
tcp ? SOCK_STREAM : SOCK_DGRAM;
732 if ((ret = getaddrinfo(
rhost,
rport, &hints, &reslist))) {
733 ERR(
"getaddrinfo(): %s\n", gai_strerror(ret));
734 error(
"Impossible to resolve remote address or port.");
737 for (res = reslist; res; res = res->ai_next) {
740 if ( (sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
746 memcpy(&fixedhost, res->ai_addr, res->ai_addrlen);
750 freeaddrinfo(reslist);
752 error(
"Failed in resolving remote host.");
754 if (fixedhost.ss_family && fixedport)
755 printf(
"[+] Using fixed forwarding to %s,%s.\n",
rhost,
rport);
757 printf(
"[+] Using dynamic (transparent proxy) forwarding with fixed port %s.\n",
rport);
758 else if (fixedhost.ss_family)
759 printf(
"[+] Using dynamic (transparent proxy) forwarding with fixed addr %s.\n",
rhost);
761 printf(
"[+] Using dynamic (transparent proxy) forwarding.\n");
765 printf(
"[+] Listening on port %s/%s.\n",
lport, (
tcp)?
"tcp":
"udp");
767 signal(SIGPIPE, SIG_IGN);
770 sigemptyset(&sa.sa_mask);
772 if (sigaction(SIGINT, &sa, NULL) == -1)
error(
"netsed: sigaction() failed");
775 struct sockaddr_storage s;
776 socklen_t l =
sizeof(s);
777 struct sockaddr_storage conho;
779 char ipstr[INET6_ADDRSTRLEN], portstr[12];
783 struct timeval timeout, *ptimeout;
786 FD_SET(
lsock,&rd_set);
793 while(conn != NULL) {
795 FD_SET(conn->
csock, &rd_set);
796 if (nfds < conn->csock) nfds = conn->
csock;
800 if (remain < 0) remain = 0;
801 if (timeout.tv_sec > remain) {
802 timeout.tv_sec = remain;
807 FD_SET(conn->
fsock, &rd_set);
808 if (nfds < conn->fsock) nfds = conn->
fsock;
814 sel=select(nfds+1, &rd_set, (fd_set*)0, (fd_set*)0, ptimeout);
821 DBG(
"[!] select fail! %s\n", strerror(errno));
825 DBG(
"[*] select timeout. now: %d\n",
now);
831 if (FD_ISSET(
lsock, &rd_set)) {
835 csock = accept(
lsock,(
struct sockaddr*)&s,&l);
840 rd = recvfrom(
lsock,
buf,
sizeof(
buf),0,(
struct sockaddr*)&s,&l);
843 while(conn != NULL) {
845 if ((conn->
csl == l) && (0 == memcmp(&s, conn->
csa, l))) {
857 DBG(
"[+] Got incoming datagram from existing connection.\n");
860 ERR(
"recvfrom(): %s", strerror(errno));
867 getnameinfo((
struct sockaddr *) &s, l, ipstr,
sizeof(ipstr),
868 portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
869 printf(
"[+] Got incoming connection from %s,%s", ipstr, portstr);
871 if(NULL == conn)
error(
"netsed: unable to malloc() connection tracker struct");
874 setsockopt(csock,SOL_SOCKET,SO_OOBINLINE,&one,
sizeof(
int));
879 conn->
csa = malloc(l);
880 if(NULL == conn->
csa)
error(
"netsed: unable to malloc() connection tracker sockaddr struct");
881 memcpy(conn->
csa, &s, l);
889 if(NULL == conn->
live)
error(
"netsed: unable to malloc() connection tracker sockaddr struct");
893 #ifndef LINUX_NETFILTER
895 getsockname(csock,(
struct sockaddr*)&s,&l);
898 getsockopt(csock, SOL_IP, SO_ORIGINAL_DST,(
struct sockaddr*)&s,&l);
900 getnameinfo((
struct sockaddr *) &s, l, ipstr,
sizeof(ipstr),
901 portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
902 printf(
" to %s,%s\n", ipstr, portstr);
903 conpo =
get_port((
struct sockaddr *) &s);
905 memcpy(&conho, &s,
sizeof(conho));
907 if (fixedport) conpo=fixedport;
908 if (fixedhost.ss_family)
909 memcpy(&conho, &fixedhost,
sizeof(conho));
912 memcpy(&s, &conho,
sizeof(s));
913 set_port((
struct sockaddr *) &s, conpo);
914 getnameinfo((
struct sockaddr *) &s, l, ipstr,
sizeof(ipstr),
915 portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
916 printf(
"[*] Forwarding connection to %s,%s\n", ipstr, portstr);
919 conn->
fsock = socket(s.ss_family,
tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
921 if (connect(conn->
fsock,(
struct sockaddr*)&s,l)) {
922 printf(
"[!] Cannot connect to remote server, dropping connection.\n");
926 setsockopt(conn->
fsock,SOL_SOCKET,SO_OOBINLINE,&one,
sizeof(
int));
932 if((rd >= 0) && (conn != NULL)) {
939 while(conn != NULL) {
941 if(
tcp && FD_ISSET(conn->
csock, &rd_set)) {
944 if(FD_ISSET(conn->
fsock, &rd_set)) {
948 DBG(
"[!] connection last time: %d, now: %d\n", conn->
time,
now);
950 DBG(
"[!] connection timeout.\n");