netsed
Loading...
Searching...
No Matches
netsed.c
Go to the documentation of this file.
1/*
2 netsed 1.2 (C) 2010-2012 Julien VdG <julien@silicone.homelinux.org>
3 --------------------------------------------------------------------------
4
5 This work is based on the original netsed:
6 netsed 0.01c (C) 2002 Michal Zalewski <lcamtuf@ids.pl>
7
8 Please contact Julien VdG <julien@silicone.homelinux.org> if you encounter
9 any problems with this version.
10 The changes compared to version 0.01c are related in the NEWS file.
11
12
13This program is free software; you can redistribute it and/or
14modify it under the terms of the GNU General Public License
15as published by the Free Software Foundation; either version 2
16of the License, or (at your option) any later version.
17
18This program is distributed in the hope that it will be useful,
19but WITHOUT ANY WARRANTY; without even the implied warranty of
20MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; if not, write to the Free Software
25Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26
27*/
28
29
41
82
86
90
91
92#include <stdio.h>
93#include <unistd.h>
94#include <sys/types.h>
95#include <sys/socket.h>
96#include <sys/select.h>
97#include <sys/wait.h>
98#include <netinet/in.h>
99#include <arpa/inet.h>
100#include <netdb.h>
101#include <fcntl.h>
102#include <string.h>
103#include <errno.h>
104#include <ctype.h>
105#include <stdlib.h>
106#include <signal.h>
107#include <netdb.h>
108#include <time.h>
109
110#ifdef __linux__
114#define LINUX_NETFILTER
115#endif
116
117#ifdef LINUX_NETFILTER
118#include <limits.h>
119#include <linux/netfilter_ipv4.h>
120#endif
121
123#define PARSE_LONG_OPT
124#ifdef PARSE_LONG_OPT
125#include <getopt.h>
126#endif
127
129#define VERSION "1.2"
131#define MAX_BUF 100000
132
134#define ERR(x...) fprintf(stderr,x)
135
136// Uncomment to add a lot of debug information.
137//#define DEBUG
138#ifdef DEBUG
140#define DBG(x...) printf(x)
141#else
143#define DBG(x...)
144#endif
145
147#define UDP_TIMEOUT 30
148
150struct rule_s {
152 char *from;
154 char *to;
156 const char *forig;
158 const char *torig;
160 int fs;
162 int ts;
164 int dir;
165};
166
168enum {
169 ALL = 0,
170 IN = 1,
171 OUT = 2,
172};
173
187
189struct tracker_s {
191 struct sockaddr* csa;
193 socklen_t csl;
195 int csock;
197 int fsock;
199 time_t time;
203 int* live;
204
206 struct tracker_s * n;
207};
208
210time_t now;
211
214
215// Command line parameters are parsed to the following global variables.
216
218int family = AF_UNSPEC;
219
221int tcp;
222
224char* lport;
225
227char* rhost;
229char* rport;
230
234struct rule_s *rule;
238
240struct tracker_s * connections = NULL;
241
243volatile int stop=0;
244
247void short_usage_hints(const char* why) {
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");
251 exit(1);
252}
253
254
257void usage_hints(const char* why) {
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");
265#else
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");
269#endif
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");
297 exit(1);
298}
299
300
304void freetracker (struct tracker_s * conn)
305{
306 if(conn->csa != NULL) { // udp
307 free(conn->csa);
308 } else { // tcp
309 close(conn->csock);
310 }
311 close(conn->fsock);
312 free(conn);
313}
314
317void clean_socks(void)
318{
319 close(lsock);
320 // close all tracker
321 while(connections != NULL) {
322 struct tracker_s * conn = connections;
323 connections = conn->n;
324 freetracker(conn);
325 }
326}
327
328#ifdef __GNUC__
329// avoid gcc from inlining those two function when optimizing, as otherwise
330// the function would break strict-aliasing rules by dereferencing pointers...
331in_port_t get_port(struct sockaddr *sa) __attribute__ ((noinline));
332void set_port(struct sockaddr *sa, in_port_t port) __attribute__ ((noinline));
333#endif
334
337in_port_t get_port(struct sockaddr *sa) {
338 switch (sa->sa_family) {
339 case AF_INET:
340 return ntohs(((struct sockaddr_in *) sa)->sin_port);
341 case AF_INET6:
342 return ntohs(((struct sockaddr_in6 *) sa)->sin6_port);
343 default:
344 return 0;
345 }
346} /* get_port(struct sockaddr *) */
347
351void set_port(struct sockaddr *sa, in_port_t port) {
352 switch (sa->sa_family) {
353 case AF_INET:
354 ((struct sockaddr_in *) sa)->sin_port = htons(port);
355 break;
356 case AF_INET6:
357 ((struct sockaddr_in6 *) sa)->sin6_port = htons(port);
358 default:
359 break;
360 }
361} /* set_port(struct sockaddr *, in_port_t) */
362
366int is_addr_any(struct sockaddr *sa) {
367 switch (sa->sa_family) {
368 case AF_INET:
369 return (((struct sockaddr_in *) sa)->sin_addr.s_addr == htonl(INADDR_ANY));
370 case AF_INET6:
371 return !memcmp(&((struct sockaddr_in6 *) sa)->sin6_addr, &in6addr_any, sizeof(in6addr_any));
372 default:
373 return 0;
374 }
375} /* is_addr_any(struct sockaddr *) */
376
377
379void error(const char* reason) {
380 ERR("[-] Error: %s\n",reason);
381 ERR("netsed: exiting.\n");
382 clean_socks();
383 exit(2);
384}
385
387char hex[]="0123456789ABCDEF";
388
391void shrink_to_binary(struct rule_s* r) {
392 int i;
393
394 r->from=malloc(strlen(r->forig));
395 r->to=malloc(strlen(r->torig));
396 if ((!r->from) || (!r->to)) error("shrink_to_binary: unable to malloc() buffers");
397
398 for (i=0;i<strlen(r->forig);i++) {
399 if (r->forig[i]=='%') {
400 // Have to shrink.
401 i++;
402 if (r->forig[i]=='%') {
403 // '%%' -> '%'
404 r->from[r->fs]='%';
405 r->fs++;
406 } else {
407 int hexval;
408 char* x;
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.");
411 x=strchr(hex,toupper(r->forig[i]));
412 if (!x) error("shrink_to_binary: src pattern: non-hex sequence.");
413 hexval=(x-hex)*16;
414 x=strchr(hex,toupper(r->forig[i+1]));
415 if (!x) error("shrink_to_binary: src pattern: non-hex sequence.");
416 hexval+=(x-hex);
417 r->from[r->fs]=hexval;
418 r->fs++; i++;
419 }
420 } else {
421 // Plaintext case.
422 r->from[r->fs]=r->forig[i];
423 r->fs++;
424 }
425 }
426
427 for (i=0;i<strlen(r->torig);i++) {
428 if (r->torig[i]=='%') {
429 // Have to shrink.
430 i++;
431 if (r->torig[i]=='%') {
432 // '%%' -> '%'
433 r->to[r->ts]='%';
434 r->ts++;
435 } else {
436 int hexval;
437 char* x;
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.");
440 x=strchr(hex,toupper(r->torig[i]));
441 if (!x) error("shrink_to_binary: dst pattern: non-hex sequence.");
442 hexval=(x-hex)*16;
443 x=strchr(hex,toupper(r->torig[i+1]));
444 if (!x) error("shrink_to_binary: dst pattern: non-hex sequence.");
445 hexval+=(x-hex);
446 r->to[r->ts]=hexval;
447 r->ts++; i++;
448 }
449 } else {
450 // Plaintext case.
451 r->to[r->ts]=r->torig[i];
452 r->ts++;
453 }
454 }
455}
456
460void parse_params(int argc,char* argv[]) {
461 int i;
462
463 // parse options, GNU allows us to use long options
464#ifdef PARSE_LONG_OPT
465 static struct option long_options[] = {
466 {"ipv4", 0, 0, '4'},
467 {"ipv6", 0, 0, '6'},
468 {"help", 0, 0, 'h'},
469 {"ipany", 0, &family, AF_UNSPEC},
470 {0, 0, 0, 0}
471 };
472
473 while ((i = getopt_long(argc, argv, "46h", long_options, NULL)) != -1)
474#else
475 while ((i = getopt(argc, argv, "46h")) != -1)
476#endif
477 {
478 switch(i) {
479 case 0: // long option
480 break;
481 case '4':
482 family = AF_INET;
483 break;
484 case '6':
485 family = AF_INET6;
486 break;
487 case 'h':
488 usage_hints(NULL);
489 default:
490 usage_hints("unsupported optional parameter");
491 }
492 }
493
494 // parse remaining positional parameters
495 if (argc<optind+5) short_usage_hints("not enough parameters");
496
497 // protocole
498 if (strcasecmp(argv[optind],"tcp")*strcasecmp(argv[optind],"udp")) short_usage_hints("incorrect protocol");
499 tcp = strncasecmp(argv[optind++], "udp", 3);
500
501 // local port
502 lport = argv[optind++];
503
504 // remote host & port
505 rhost = argv[optind++];
506 rport = argv[optind++];
507
508 // allocate rule arrays, rule number is number of params after 5
509 rule=malloc((argc-optind)*sizeof(struct rule_s));
510 rule_live=malloc((argc-optind)*sizeof(int));
511 // parse rules
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");
517 fs++;
518 ts=strchr(fs,'/');
519 if (!ts) error("missing second '/' in rule");
520 *ts=0;
521 ts++;
522 cs=strchr(ts,'/');
523 if (cs) { *cs=0; cs++; }
524 rule[rules].forig=fs;
525 rule[rules].torig=ts;
526 rule[rules].dir = ALL;
527 /* Is there a direction selector? */
528 if (cs && *cs && strchr("iIoO", *cs)) {
529 rule[rules].dir = (*cs=='i'||*cs=='I') ? IN : OUT;
530 cs++;
531 }
532 if (cs && *cs) /* Only non-trivial quantifiers count. */
533 rule_live[rules]=atoi(cs); else rule_live[rules]=-1;
535// printf("DEBUG: (%s) (%s)\n",rule[rules].from,rule[rules].to);
536 rules++;
537 }
538
539 printf("[+] Loaded %d rule%s...\n", rules, (rules > 1) ? "s" : "");
540
541}
542
548void bind_and_listen(int af, int tcp, const char *portstr) {
549 int ret;
550 struct addrinfo hints, *res, *reslist;
551
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;
556
557 if ((ret = getaddrinfo(NULL, portstr, &hints, &reslist))) {
558 ERR("getaddrinfo(): %s\n", gai_strerror(ret));
559 error("Impossible to resolve listening port.");
560 }
561 /* We have useful addresses. */
562 for (res = reslist; res; res = res->ai_next) {
563 int one = 1;
564
565 if ( (lsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
566 continue;
567 setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
568 //fcntl(lsock,F_SETFL,O_NONBLOCK);
569 /* Make our best to decide on dual-stacked listener. */
570 one = (family == AF_UNSPEC) ? 0 /* All families */ : 1; /* Preconditioned addr */
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));
576 close(lsock);
577 continue;
578 }
579 if (tcp) {
580 if (listen(lsock, 16) < 0) {
581 close(lsock);
582 continue;
583 }
584 } else { // udp
585 int one=1;
586 setsockopt(lsock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int));
587 }
588 /* Successfully bound and now also listening. */
589 break;
590 }
591 freeaddrinfo(reslist);
592 if (res == NULL)
593 error("Listening socket failed.");
594}
595
600
605int sed_the_buffer(int siz, int* live, int dir) {
606 int i=0,j=0;
607 int newsize=0;
608 int changes=0;
609 int gotchange=0;
610 for (i=0;i<siz;) {
611 gotchange=0;
612 for (j=0;j<rules;j++) {
613 if (rule[j].dir != ALL && rule[j].dir !=dir) continue;
614
615 if ((!memcmp(&buf[i],rule[j].from,rule[j].fs)) && (live[j]!=0)) {
616 changes++;
617 gotchange=1;
618 printf(" Applying rule s/%s/%s...\n",rule[j].forig,rule[j].torig);
619 live[j]--;
620 if (live[j]==0) printf(" (rule just expired)\n");
621 memcpy(&b2[newsize],rule[j].to,rule[j].ts);
622 newsize+=rule[j].ts;
623 i+=rule[j].fs;
624 break;
625 }
626 }
627 if (!gotchange) {
628 b2[newsize]=buf[i];
629 newsize++;
630 i++;
631 }
632 }
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);
636 return newsize;
637}
638
639
640// Prototype this function so that the content is in the same order as in
641// previous read_write_sed function. (ease patch and diff)
642void b2server_sed(struct tracker_s * conn, ssize_t rd);
643
647void server2client_sed(struct tracker_s * conn) {
648 ssize_t rd;
649 rd=read(conn->fsock,buf,sizeof(buf));
650 if ((rd<0) && (errno!=EAGAIN))
651 {
652 DBG("[!] server disconnected. (rd err) %s\n",strerror(errno));
653 conn->state = DISCONNECTED;
654 }
655 if (rd == 0) {
656 // nothing read but select said ok, so EOF
657 DBG("[!] server disconnected. (rd)\n");
658 conn->state = DISCONNECTED;
659 }
660 if (rd>0) {
661 printf("[+] Caught server -> client packet.\n");
662 rd=sed_the_buffer(rd, conn->live, IN);
663 conn->time = now;
664 conn->state = ESTABLISHED;
665 if (sendto(conn->csock,b2,rd,0,conn->csa, conn->csl)<=0) {
666 DBG("[!] client disconnected. (wr)\n");
667 conn->state = DISCONNECTED;
668 }
669 }
670}
671
674void client2server_sed(struct tracker_s * conn) {
675 ssize_t rd;
676 rd=read(conn->csock,buf,sizeof(buf));
677 if ((rd<0) && (errno!=EAGAIN))
678 {
679 DBG("[!] client disconnected. (rd err)\n");
680 conn->state = DISCONNECTED;
681 }
682 if (rd == 0) {
683 // nothing read but select said ok, so EOF
684 DBG("[!] client disconnected. (rd)\n");
685 conn->state = DISCONNECTED;
686 }
687 b2server_sed(conn, rd);
688}
689
693void b2server_sed(struct tracker_s * conn, ssize_t rd) {
694 if (rd>0) {
695 printf("[+] Caught client -> server packet.\n");
696 rd=sed_the_buffer(rd, conn->live, OUT);
697 conn->time = now;
698 if (write(conn->fsock,b2,rd)<=0) {
699 DBG("[!] server disconnected. (wr)\n");
700 conn->state = DISCONNECTED;
701 }
702 }
703}
704
706void sig_int(int signo)
707{
708 DBG("[!] user interrupt request (%d)\n",getpid());
709 stop = 1;
710}
711
713int main(int argc,char* argv[]) {
714 int ret;
715 in_port_t fixedport = 0;
716 struct sockaddr_storage fixedhost;
717 struct addrinfo hints, *res, *reslist;
718 struct tracker_s * conn;
719
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);
724
725 parse_params(argc, argv);
726
727 memset(&hints, '\0', sizeof(hints));
728 hints.ai_family = family;
729 hints.ai_flags = AI_CANONNAME;
730 hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM;
731
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.");
735 }
736 /* We have candidates for remote host. */
737 for (res = reslist; res; res = res->ai_next) {
738 int sd = -1;
739
740 if ( (sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
741 continue;
742 /* Has successfully built a socket for this address family. */
743 /* Record the address structure and the port. */
744 fixedport = get_port(res->ai_addr);
745 if (!is_addr_any(res->ai_addr))
746 memcpy(&fixedhost, res->ai_addr, res->ai_addrlen);
747 close(sd);
748 break;
749 }
750 freeaddrinfo(reslist);
751 if (res == NULL)
752 error("Failed in resolving remote host.");
753
754 if (fixedhost.ss_family && fixedport)
755 printf("[+] Using fixed forwarding to %s,%s.\n",rhost,rport);
756 else if (fixedport)
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);
760 else
761 printf("[+] Using dynamic (transparent proxy) forwarding.\n");
762
763 bind_and_listen(fixedhost.ss_family, tcp, lport);
764
765 printf("[+] Listening on port %s/%s.\n", lport, (tcp)?"tcp":"udp");
766
767 signal(SIGPIPE, SIG_IGN);
768 struct sigaction sa;
769 sa.sa_flags = 0;
770 sigemptyset(&sa.sa_mask);
771 sa.sa_handler = sig_int;
772 if (sigaction(SIGINT, &sa, NULL) == -1) error("netsed: sigaction() failed");
773
774 while (!stop) {
775 struct sockaddr_storage s;
776 socklen_t l = sizeof(s);
777 struct sockaddr_storage conho;
778 in_port_t conpo;
779 char ipstr[INET6_ADDRSTRLEN], portstr[12];
780
781 int sel;
782 fd_set rd_set;
783 struct timeval timeout, *ptimeout;
784 int nfds = lsock;
785 FD_ZERO(&rd_set);
786 FD_SET(lsock,&rd_set);
787 timeout.tv_sec = UDP_TIMEOUT+1;
788 timeout.tv_usec = 0;
789 ptimeout = NULL;
790
791 {
792 conn = connections;
793 while(conn != NULL) {
794 if(tcp) {
795 FD_SET(conn->csock, &rd_set);
796 if (nfds < conn->csock) nfds = conn->csock;
797 } else {
798 // adjust timeout to earliest connection end time
799 int remain = UDP_TIMEOUT - (now - conn->time);
800 if (remain < 0) remain = 0;
801 if (timeout.tv_sec > remain) {
802 timeout.tv_sec = remain;
803 // time updated to need to timeout
804 ptimeout = &timeout;
805 }
806 }
807 FD_SET(conn->fsock, &rd_set);
808 if (nfds < conn->fsock) nfds = conn->fsock;
809 // point on next
810 conn = conn->n;
811 }
812 }
813
814 sel=select(nfds+1, &rd_set, (fd_set*)0, (fd_set*)0, ptimeout);
815 time(&now);
816 if (stop)
817 {
818 break;
819 }
820 if (sel < 0) {
821 DBG("[!] select fail! %s\n", strerror(errno));
822 break;
823 }
824 if (sel == 0) {
825 DBG("[*] select timeout. now: %d\n", now);
826 // Here we still have to go through the list to expire some udp
827 // connection if they timed out... But no descriptor will be set.
828 // For tcp, select will not timeout.
829 }
830
831 if (FD_ISSET(lsock, &rd_set)) {
832 int csock=-1;
833 ssize_t rd=-1;
834 if (tcp) {
835 csock = accept(lsock,(struct sockaddr*)&s,&l);
836 } else {
837 // udp does not handle accept, so track connections manually
838 // also set csock if a new connection need to be registered
839 // to share the code with tcp ;)
840 rd = recvfrom(lsock,buf,sizeof(buf),0,(struct sockaddr*)&s,&l);
841 if(rd >= 0) {
842 conn = connections;
843 while(conn != NULL) {
844 // look for existing connections
845 if ((conn->csl == l) && (0 == memcmp(&s, conn->csa, l))) {
846 // found
847 break;
848 }
849 // point on next
850 conn = conn->n;
851 }
852 // not found
853 if(conn == NULL) {
854 // udp 'connection' socket is the listening one
855 csock = lsock;
856 } else {
857 DBG("[+] Got incoming datagram from existing connection.\n");
858 }
859 } else {
860 ERR("recvfrom(): %s", strerror(errno));
861 }
862 }
863
864 // new connection (tcp accept, or udp conn not found)
865 if ((csock)>=0) {
866 int one=1;
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);
870 conn = malloc(sizeof(struct tracker_s));
871 if(NULL == conn) error("netsed: unable to malloc() connection tracker struct");
872 // protocol specific init
873 if (tcp) {
874 setsockopt(csock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int));
875 conn->csa = NULL;
876 conn->csl = 0;
877 conn->state = ESTABLISHED;
878 } else {
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);
882 conn->csl = l;
883 conn->state = UNREPLIED;
884 }
885 conn->csock = csock;
886 conn->time = now;
887
888 conn->live = malloc(rules*sizeof(int));
889 if(NULL == conn->live) error("netsed: unable to malloc() connection tracker sockaddr struct");
890 memcpy(conn->live, rule_live, rules*sizeof(int));
891
892 l = sizeof(s);
893#ifndef LINUX_NETFILTER
894 // was OK for linux 2.2 nat
895 getsockname(csock,(struct sockaddr*)&s,&l);
896#else
897 // for linux 2.4 and later
898 getsockopt(csock, SOL_IP, SO_ORIGINAL_DST,(struct sockaddr*)&s,&l);
899#endif
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);
904
905 memcpy(&conho, &s, sizeof(conho));
906
907 if (fixedport) conpo=fixedport;
908 if (fixedhost.ss_family)
909 memcpy(&conho, &fixedhost, sizeof(conho));
910
911 // forward to addr
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);
917
918 // connect will bind with some dynamic addr/port
919 conn->fsock = socket(s.ss_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
920
921 if (connect(conn->fsock,(struct sockaddr*)&s,l)) {
922 printf("[!] Cannot connect to remote server, dropping connection.\n");
923 freetracker(conn);
924 conn = NULL;
925 } else {
926 setsockopt(conn->fsock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int));
927 conn->n = connections;
928 connections = conn;
929 }
930 }
931 // udp has data process forwarding
932 if((rd >= 0) && (conn != NULL)) {
933 b2server_sed(conn, rd);
934 }
935 } // lsock is set
936 // all other sockets
937 conn = connections;
938 struct tracker_s ** pconn = &connections;
939 while(conn != NULL) {
940 // incoming data ?
941 if(tcp && FD_ISSET(conn->csock, &rd_set)) {
942 client2server_sed(conn);
943 }
944 if(FD_ISSET(conn->fsock, &rd_set)) {
945 server2client_sed(conn);
946 }
947 // timeout ? udp only
948 DBG("[!] connection last time: %d, now: %d\n", conn->time, now);
949 if(!tcp && ((now - conn->time) >= UDP_TIMEOUT)) {
950 DBG("[!] connection timeout.\n");
951 conn->state = TIMEOUT;
952 }
953 if(conn->state >= DISCONNECTED) {
954 // remove it
955 (*pconn)=conn->n;
956 freetracker(conn);
957 conn=(*pconn);
958 } else {
959 // point on next
960 pconn = &(conn->n);
961 conn = conn->n;
962 }
963 }
964 }
965
966 clean_socks();
967 exit(0);
968}
969
970// vim:sw=2:sta:et:
char * rport
Remote Port.
Definition netsed.c:229
@ ALL
Definition netsed.c:169
@ OUT
Definition netsed.c:171
@ IN
Definition netsed.c:170
int main(int argc, char *argv[])
This is main...
Definition netsed.c:713
void short_usage_hints(const char *why)
Display an error message followed by short usage information.
Definition netsed.c:247
#define VERSION
Current version (recovered by Makefile for several release checks)
Definition netsed.c:129
#define MAX_BUF
max size for buffers
Definition netsed.c:131
void usage_hints(const char *why)
Display an error message followed by usage information.
Definition netsed.c:257
void parse_params(int argc, char *argv[])
parse the command line parameters
Definition netsed.c:460
void server2client_sed(struct tracker_s *conn)
Receive a packet or datagram from the server, 'sed' it, send it to the client.
Definition netsed.c:647
time_t now
Store current time (just after select returned).
Definition netsed.c:210
int family
Address family used for parameter resolution.
Definition netsed.c:218
void client2server_sed(struct tracker_s *conn)
Receive a packet from the client, 'sed' it, send it to the server.
Definition netsed.c:674
state_e
Connection state.
Definition netsed.c:175
@ UNREPLIED
udp datagram received by netsed and send to server, no response yet.
Definition netsed.c:177
@ ESTABLISHED
tcp accepted connection or udp 'connection' with a response from server.
Definition netsed.c:179
@ TIMEOUT
udp timeout expired.
Definition netsed.c:185
@ DISCONNECTED
tcp or udp disconnected (detected by an error on read or send).
Definition netsed.c:183
char b2[MAX_BUF]
Buffer containing modified packet or datagram.
Definition netsed.c:599
int lsock
Listening socket.
Definition netsed.c:213
void sig_int(int signo)
Handle SIGINT signal for clean exit.
Definition netsed.c:706
void shrink_to_binary(struct rule_s *r)
Convert the % notation in rules to plain binary data.
Definition netsed.c:391
#define UDP_TIMEOUT
Timeout for udp 'connections' in seconds.
Definition netsed.c:147
void bind_and_listen(int af, int tcp, const char *portstr)
Bind and optionally listen to a socket for netsed server port.
Definition netsed.c:548
char * rhost
Remote Host.
Definition netsed.c:227
struct tracker_s * connections
List of connections.
Definition netsed.c:240
void clean_socks(void)
Close all sockets to use before exit.
Definition netsed.c:317
#define DBG(x...)
Disabled debug prints.
Definition netsed.c:143
int * rule_live
TTL part of the rule as a flat array to be able to copy it in tracker_s::live for each connections.
Definition netsed.c:237
struct rule_s * rule
Array of all rules.
Definition netsed.c:234
char buf[MAX_BUF]
Buffer for receiving a single packet or datagram.
Definition netsed.c:597
void error(const char *reason)
Display an error message and exit.
Definition netsed.c:379
void freetracker(struct tracker_s *conn)
Helper function to free a tracker_s item. csa will be freed if needed, sockets will be closed.
Definition netsed.c:304
#define ERR(x...)
printf to stderr
Definition netsed.c:134
char * lport
Local Port.
Definition netsed.c:224
int rules
Number of rules.
Definition netsed.c:232
char hex[]
Hex digit to parsing the % notation in rules.
Definition netsed.c:387
int sed_the_buffer(int siz, int *live, int dir)
Applies the rules to global buffer buf.
Definition netsed.c:605
void set_port(struct sockaddr *sa, in_port_t port)
Set the port information in a sockaddr for both IPv4 and IPv6.
Definition netsed.c:351
in_port_t get_port(struct sockaddr *sa)
Extract the port information from a sockaddr for both IPv4 and IPv6.
Definition netsed.c:337
volatile int stop
True when SIGINT signal was received.
Definition netsed.c:243
void b2server_sed(struct tracker_s *conn, ssize_t rd)
Send the content of global buffer b2 to the server as packet or datagram.
Definition netsed.c:693
int tcp
TCP or UDP.
Definition netsed.c:221
int is_addr_any(struct sockaddr *sa)
Detect if address in the addr_any value for both IPv4 and IPv6.
Definition netsed.c:366
Rule item.
Definition netsed.c:150
char * from
binary buffer to match.
Definition netsed.c:152
const char * forig
match from the command line.
Definition netsed.c:156
int fs
length of from buffer.
Definition netsed.c:160
int ts
length of to buffer.
Definition netsed.c:162
char * to
binary buffer replacement.
Definition netsed.c:154
const char * torig
replacement from the command line.
Definition netsed.c:158
int dir
direction of rule
Definition netsed.c:164
This structure is used to track information about open connections.
Definition netsed.c:189
time_t time
Last event time, for udp timeout.
Definition netsed.c:199
struct tracker_s * n
chain it !
Definition netsed.c:206
int csock
Connection socket to client.
Definition netsed.c:195
socklen_t csl
size of csa
Definition netsed.c:193
int fsock
Socket to forward to server.
Definition netsed.c:197
int * live
By connection TTL.
Definition netsed.c:203
enum state_e state
Connection state.
Definition netsed.c:201
struct sockaddr * csa
recvfrom information: 'connect' address for udp
Definition netsed.c:191