netsed
Loading...
Searching...
No Matches
netsed.c
Go to the documentation of this file.
1/*
2 netsed 1.4 (C) 2010-2025 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.4"
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 tcp = (strncasecmp(argv[optind], "tcp", 4) == 0)?1:0;
499 if ((strncasecmp(argv[optind], "udp", 4) != 0) && !tcp) short_usage_hints("incorrect protocol");
500 optind++;
501
502 // local port
503 lport = argv[optind++];
504
505 // remote host & port
506 rhost = argv[optind++];
507 rport = argv[optind++];
508
509 // allocate rule arrays, rule number is number of params after 5
510 rule=calloc(argc-optind,sizeof(struct rule_s));
511 rule_live=calloc(argc-optind,sizeof(int));
512 // parse rules
513 for (i=optind;i<argc;i++) {
514 char *fs=0, *ts=0, *cs=0;
515 printf("[*] Parsing rule %s...\n",argv[i]);
516 fs=strchr(argv[i],'/');
517 if (!fs) error("missing first '/' in rule");
518 fs++;
519 ts=strchr(fs,'/');
520 if (!ts) error("missing second '/' in rule");
521 *ts=0;
522 ts++;
523 cs=strchr(ts,'/');
524 if (cs) { *cs=0; cs++; }
525 rule[rules].forig=fs;
526 rule[rules].torig=ts;
527 rule[rules].dir = ALL;
528 /* Is there a direction selector? */
529 if (cs && *cs && strchr("iIoO", *cs)) {
530 rule[rules].dir = (*cs=='i'||*cs=='I') ? IN : OUT;
531 cs++;
532 }
533 if (cs && *cs) /* Only non-trivial quantifiers count. */
534 rule_live[rules]=atoi(cs); else rule_live[rules]=-1;
536// printf("DEBUG: (%s) (%s)\n",rule[rules].from,rule[rules].to);
537 rules++;
538 }
539
540 printf("[+] Loaded %d rule%s...\n", rules, (rules > 1) ? "s" : "");
541
542}
543
549void bind_and_listen(int af, int tcp, const char *portstr) {
550 int ret;
551 struct addrinfo hints, *res, *reslist;
552
553 memset(&hints, '\0', sizeof(hints));
554 hints.ai_family = af;
555 hints.ai_flags = AI_PASSIVE;
556 hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM;
557
558 if ((ret = getaddrinfo(NULL, portstr, &hints, &reslist))) {
559 ERR("getaddrinfo(): %s\n", gai_strerror(ret));
560 error("Impossible to resolve listening port.");
561 }
562 /* We have useful addresses. */
563 for (res = reslist; res; res = res->ai_next) {
564 int one = 1;
565
566 if ( (lsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
567 continue;
568 setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
569 //fcntl(lsock,F_SETFL,O_NONBLOCK);
570 /* Make our best to decide on dual-stacked listener. */
571 one = (family == AF_UNSPEC) ? 0 /* All families */ : 1; /* Preconditioned addr */
572 if (res->ai_family == AF_INET6)
573 if (setsockopt(lsock, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)))
574 printf(" Failed to unset IPV6_V6ONLY: %s.\n", strerror(errno));
575 if (bind(lsock, res->ai_addr, res->ai_addrlen) < 0) {
576 ERR("bind(): %s", strerror(errno));
577 close(lsock);
578 continue;
579 }
580 if (tcp) {
581 if (listen(lsock, 16) < 0) {
582 close(lsock);
583 continue;
584 }
585 } else { // udp
586 int one=1;
587 setsockopt(lsock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int));
588 }
589 /* Successfully bound and now also listening. */
590 break;
591 }
592 freeaddrinfo(reslist);
593 if (res == NULL)
594 error("Listening socket failed.");
595}
596
601
606int sed_the_buffer(int siz, int* live, int dir) {
607 int i=0,j=0;
608 int newsize=0;
609 int changes=0;
610 int gotchange=0;
611 for (i=0;i<siz;) {
612 gotchange=0;
613 for (j=0;j<rules;j++) {
614 if (rule[j].dir != ALL && rule[j].dir !=dir) continue;
615
616 if ((!memcmp(&buf[i],rule[j].from,rule[j].fs)) && (live[j]!=0)) {
617 changes++;
618 gotchange=1;
619 printf(" Applying rule s/%s/%s...\n",rule[j].forig,rule[j].torig);
620 live[j]--;
621 if (live[j]==0) printf(" (rule just expired)\n");
622 memcpy(&b2[newsize],rule[j].to,rule[j].ts);
623 newsize+=rule[j].ts;
624 i+=rule[j].fs;
625 break;
626 }
627 }
628 if (!gotchange) {
629 b2[newsize]=buf[i];
630 newsize++;
631 i++;
632 }
633 }
634 if (!changes) printf("[*] Forwarding untouched packet of size %d.\n",siz);
635 else printf("[*] Done %d replacements, forwarding packet of size %d (orig %d).\n",
636 changes,newsize,siz);
637 return newsize;
638}
639
640
641// Prototype this function so that the content is in the same order as in
642// previous read_write_sed function. (ease patch and diff)
643void b2server_sed(struct tracker_s * conn, ssize_t rd);
644
648void server2client_sed(struct tracker_s * conn) {
649 ssize_t rd;
650 rd=read(conn->fsock,buf,sizeof(buf));
651 if ((rd<0) && (errno!=EAGAIN))
652 {
653 DBG("[!] server disconnected. (rd err) %s\n",strerror(errno));
654 conn->state = DISCONNECTED;
655 }
656 if (rd == 0) {
657 // nothing read but select said ok, so EOF
658 DBG("[!] server disconnected. (rd)\n");
659 conn->state = DISCONNECTED;
660 }
661 if (rd>0) {
662 printf("[+] Caught server -> client packet.\n");
663 rd=sed_the_buffer(rd, conn->live, IN);
664 conn->time = now;
665 conn->state = ESTABLISHED;
666 if (sendto(conn->csock,b2,rd,0,conn->csa, conn->csl)<=0) {
667 DBG("[!] client disconnected. (wr)\n");
668 conn->state = DISCONNECTED;
669 }
670 }
671}
672
675void client2server_sed(struct tracker_s * conn) {
676 ssize_t rd;
677 rd=read(conn->csock,buf,sizeof(buf));
678 if ((rd<0) && (errno!=EAGAIN))
679 {
680 DBG("[!] client disconnected. (rd err)\n");
681 conn->state = DISCONNECTED;
682 }
683 if (rd == 0) {
684 // nothing read but select said ok, so EOF
685 DBG("[!] client disconnected. (rd)\n");
686 conn->state = DISCONNECTED;
687 }
688 b2server_sed(conn, rd);
689}
690
694void b2server_sed(struct tracker_s * conn, ssize_t rd) {
695 if (rd>0) {
696 printf("[+] Caught client -> server packet.\n");
697 rd=sed_the_buffer(rd, conn->live, OUT);
698 conn->time = now;
699 if (write(conn->fsock,b2,rd)<=0) {
700 DBG("[!] server disconnected. (wr)\n");
701 conn->state = DISCONNECTED;
702 }
703 }
704}
705
707void sig_int(int signo)
708{
709 DBG("[!] user interrupt request (%d)\n",getpid());
710 stop = 1;
711}
712
714int main(int argc,char* argv[]) {
715 int ret;
716 in_port_t fixedport = 0;
717 struct sockaddr_storage fixedhost;
718 struct addrinfo hints, *res, *reslist;
719 struct tracker_s * conn;
720
721 memset(&fixedhost, '\0', sizeof(fixedhost));
722 printf("netsed " VERSION " by Julien VdG <julien@silicone.homelinux.org>\n"
723 " based on 0.01c from Michal Zalewski <lcamtuf@ids.pl>\n");
724 setbuffer(stdout,NULL,0);
725
726 parse_params(argc, argv);
727
728 memset(&hints, '\0', sizeof(hints));
729 hints.ai_family = family;
730 hints.ai_flags = AI_CANONNAME;
731 hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM;
732
733 if ((ret = getaddrinfo(rhost, rport, &hints, &reslist))) {
734 ERR("getaddrinfo(): %s\n", gai_strerror(ret));
735 error("Impossible to resolve remote address or port.");
736 }
737 /* We have candidates for remote host. */
738 for (res = reslist; res; res = res->ai_next) {
739 int sd = -1;
740
741 if ( (sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
742 continue;
743 /* Has successfully built a socket for this address family. */
744 /* Record the address structure and the port. */
745 fixedport = get_port(res->ai_addr);
746 if (!is_addr_any(res->ai_addr))
747 memcpy(&fixedhost, res->ai_addr, res->ai_addrlen);
748 close(sd);
749 break;
750 }
751 freeaddrinfo(reslist);
752 if (res == NULL)
753 error("Failed in resolving remote host.");
754
755 if (fixedhost.ss_family && fixedport)
756 printf("[+] Using fixed forwarding to %s,%s.\n",rhost,rport);
757 else if (fixedport)
758 printf("[+] Using dynamic (transparent proxy) forwarding with fixed port %s.\n",rport);
759 else if (fixedhost.ss_family)
760 printf("[+] Using dynamic (transparent proxy) forwarding with fixed addr %s.\n",rhost);
761 else
762 printf("[+] Using dynamic (transparent proxy) forwarding.\n");
763
764 bind_and_listen(fixedhost.ss_family, tcp, lport);
765
766 printf("[+] Listening on port %s/%s.\n", lport, (tcp)?"tcp":"udp");
767
768 signal(SIGPIPE, SIG_IGN);
769 struct sigaction sa;
770 sa.sa_flags = 0;
771 sigemptyset(&sa.sa_mask);
772 sa.sa_handler = sig_int;
773 if (sigaction(SIGINT, &sa, NULL) == -1) error("netsed: sigaction() failed");
774
775 while (!stop) {
776 struct sockaddr_storage s;
777 socklen_t l = sizeof(s);
778 struct sockaddr_storage conho;
779 in_port_t conpo;
780 char ipstr[INET6_ADDRSTRLEN], portstr[12];
781
782 int sel;
783 fd_set rd_set;
784 struct timeval timeout, *ptimeout;
785 int nfds = lsock;
786 FD_ZERO(&rd_set);
787 FD_SET(lsock,&rd_set);
788 timeout.tv_sec = UDP_TIMEOUT+1;
789 timeout.tv_usec = 0;
790 ptimeout = NULL;
791
792 {
793 conn = connections;
794 while(conn != NULL) {
795 if(tcp) {
796 FD_SET(conn->csock, &rd_set);
797 if (nfds < conn->csock) nfds = conn->csock;
798 } else {
799 // adjust timeout to earliest connection end time
800 int remain = UDP_TIMEOUT - (now - conn->time);
801 if (remain < 0) remain = 0;
802 if (timeout.tv_sec > remain) {
803 timeout.tv_sec = remain;
804 // time updated to need to timeout
805 ptimeout = &timeout;
806 }
807 }
808 FD_SET(conn->fsock, &rd_set);
809 if (nfds < conn->fsock) nfds = conn->fsock;
810 // point on next
811 conn = conn->n;
812 }
813 }
814
815 sel=select(nfds+1, &rd_set, (fd_set*)0, (fd_set*)0, ptimeout);
816 time(&now);
817 if (stop)
818 {
819 break;
820 }
821 if (sel < 0) {
822 DBG("[!] select fail! %s\n", strerror(errno));
823 break;
824 }
825 if (sel == 0) {
826 DBG("[*] select timeout. now: %d\n", now);
827 // Here we still have to go through the list to expire some udp
828 // connection if they timed out... But no descriptor will be set.
829 // For tcp, select will not timeout.
830 }
831
832 if (FD_ISSET(lsock, &rd_set)) {
833 int csock=-1;
834 ssize_t rd=-1;
835 if (tcp) {
836 csock = accept(lsock,(struct sockaddr*)&s,&l);
837 } else {
838 // udp does not handle accept, so track connections manually
839 // also set csock if a new connection need to be registered
840 // to share the code with tcp ;)
841 rd = recvfrom(lsock,buf,sizeof(buf),0,(struct sockaddr*)&s,&l);
842 if(rd >= 0) {
843 conn = connections;
844 while(conn != NULL) {
845 // look for existing connections
846 if ((conn->csl == l) && (0 == memcmp(&s, conn->csa, l))) {
847 // found
848 break;
849 }
850 // point on next
851 conn = conn->n;
852 }
853 // not found
854 if(conn == NULL) {
855 // udp 'connection' socket is the listening one
856 csock = lsock;
857 } else {
858 DBG("[+] Got incoming datagram from existing connection.\n");
859 }
860 } else {
861 ERR("recvfrom(): %s", strerror(errno));
862 }
863 }
864
865 // new connection (tcp accept, or udp conn not found)
866 if ((csock)>=0) {
867 int one=1;
868 getnameinfo((struct sockaddr *) &s, l, ipstr, sizeof(ipstr),
869 portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
870 printf("[+] Got incoming connection from %s,%s", ipstr, portstr);
871 conn = malloc(sizeof(struct tracker_s));
872 if(NULL == conn) error("netsed: unable to malloc() connection tracker struct");
873 // protocol specific init
874 if (tcp) {
875 setsockopt(csock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int));
876 conn->csa = NULL;
877 conn->csl = 0;
878 conn->state = ESTABLISHED;
879 } else {
880 conn->csa = malloc(l);
881 if(NULL == conn->csa) error("netsed: unable to malloc() connection tracker sockaddr struct");
882 memcpy(conn->csa, &s, l);
883 conn->csl = l;
884 conn->state = UNREPLIED;
885 }
886 conn->csock = csock;
887 conn->time = now;
888
889 conn->live = malloc(rules*sizeof(int));
890 if(NULL == conn->live) error("netsed: unable to malloc() connection tracker sockaddr struct");
891 memcpy(conn->live, rule_live, rules*sizeof(int));
892
893 l = sizeof(s);
894#ifndef LINUX_NETFILTER
895 // was OK for linux 2.2 nat
896 getsockname(csock,(struct sockaddr*)&s,&l);
897#else
898 // for linux 2.4 and later
899 getsockopt(csock, SOL_IP, SO_ORIGINAL_DST,(struct sockaddr*)&s,&l);
900#endif
901 getnameinfo((struct sockaddr *) &s, l, ipstr, sizeof(ipstr),
902 portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
903 printf(" to %s,%s\n", ipstr, portstr);
904 conpo = get_port((struct sockaddr *) &s);
905
906 memcpy(&conho, &s, sizeof(conho));
907
908 if (fixedport) conpo=fixedport;
909 if (fixedhost.ss_family)
910 memcpy(&conho, &fixedhost, sizeof(conho));
911
912 // forward to addr
913 memcpy(&s, &conho, sizeof(s));
914 set_port((struct sockaddr *) &s, conpo);
915 getnameinfo((struct sockaddr *) &s, l, ipstr, sizeof(ipstr),
916 portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
917 printf("[*] Forwarding connection to %s,%s\n", ipstr, portstr);
918
919 // connect will bind with some dynamic addr/port
920 conn->fsock = socket(s.ss_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
921
922 if (connect(conn->fsock,(struct sockaddr*)&s,l)) {
923 printf("[!] Cannot connect to remote server, dropping connection.\n");
924 freetracker(conn);
925 conn = NULL;
926 } else {
927 setsockopt(conn->fsock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int));
928 conn->n = connections;
929 connections = conn;
930 }
931 }
932 // udp has data process forwarding
933 if((rd >= 0) && (conn != NULL)) {
934 b2server_sed(conn, rd);
935 }
936 } // lsock is set
937 // all other sockets
938 conn = connections;
939 struct tracker_s ** pconn = &connections;
940 while(conn != NULL) {
941 // incoming data ?
942 if(tcp && FD_ISSET(conn->csock, &rd_set)) {
943 client2server_sed(conn);
944 }
945 if(FD_ISSET(conn->fsock, &rd_set)) {
946 server2client_sed(conn);
947 }
948 // timeout ? udp only
949 DBG("[!] connection last time: %d, now: %d\n", conn->time, now);
950 if(!tcp && ((now - conn->time) >= UDP_TIMEOUT)) {
951 DBG("[!] connection timeout.\n");
952 conn->state = TIMEOUT;
953 }
954 if(conn->state >= DISCONNECTED) {
955 // remove it
956 (*pconn)=conn->n;
957 freetracker(conn);
958 conn=(*pconn);
959 } else {
960 // point on next
961 pconn = &(conn->n);
962 conn = conn->n;
963 }
964 }
965 }
966
967 clean_socks();
968 exit(0);
969}
970
971// 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:714
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:648
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:675
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:600
int lsock
Listening socket.
Definition netsed.c:213
void sig_int(int signo)
Handle SIGINT signal for clean exit.
Definition netsed.c:707
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:549
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:598
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:606
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:694
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