netsed
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 
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License
15 as published by the Free Software Foundation; either version 2
16 of the License, or (at your option) any later version.
17 
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, 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__
111 #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"
130 #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
139 #define DBG(x...) printf(x)
141 #else
142 #define DBG(x...)
144 #endif
145 
147 #define UDP_TIMEOUT 30
148 
150 struct 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 
168 enum {
169  ALL = 0,
170  IN = 1,
171  OUT = 2,
172 };
173 
175 enum state_e {
185  TIMEOUT
186 };
187 
189 struct tracker_s {
191  struct sockaddr* csa;
193  socklen_t csl;
195  int csock;
197  int fsock;
199  time_t time;
201  enum state_e state;
203  int* live;
204 
206  struct tracker_s * n;
207 };
208 
210 time_t now;
211 
213 int lsock;
214 
215 // Command line parameters are parsed to the following global variables.
216 
218 int family = AF_UNSPEC;
219 
221 int tcp;
222 
224 char* lport;
225 
227 char* rhost;
229 char* rport;
230 
232 int rules;
234 struct rule_s *rule;
238 
240 struct tracker_s * connections = NULL;
241 
243 volatile int stop=0;
244 
247 void 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 
257 void 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 
304 void 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 
317 void 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...
331 in_port_t get_port(struct sockaddr *sa) __attribute__ ((noinline));
332 void set_port(struct sockaddr *sa, in_port_t port) __attribute__ ((noinline));
333 #endif
334 
337 in_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 
351 void 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 
366 int 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 
379 void error(const char* reason) {
380  ERR("[-] Error: %s\n",reason);
381  ERR("netsed: exiting.\n");
382  clean_socks();
383  exit(2);
384 }
385 
387 char hex[]="0123456789ABCDEF";
388 
391 void 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 
460 void 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 
548 void 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 
597 char buf[MAX_BUF];
599 char b2[MAX_BUF];
600 
605 int 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)
642 void b2server_sed(struct tracker_s * conn, ssize_t rd);
643 
647 void 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 
674 void 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 
693 void 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 
706 void sig_int(int signo)
707 {
708  DBG("[!] user interrupt request (%d)\n",getpid());
709  stop = 1;
710 }
711 
713 int 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:
tracker_s::csa
struct sockaddr * csa
recvfrom information: 'connect' address for udp
Definition: netsed.c:191
IN
@ IN
Definition: netsed.c:170
UDP_TIMEOUT
#define UDP_TIMEOUT
Timeout for udp 'connections' in seconds.
Definition: netsed.c:147
ERR
#define ERR(x...)
printf to stderr
Definition: netsed.c:134
rule_s::ts
int ts
length of to buffer.
Definition: netsed.c:162
get_port
in_port_t get_port(struct sockaddr *sa)
Extract the port information from a sockaddr for both IPv4 and IPv6.
Definition: netsed.c:337
rule_live
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
TIMEOUT
@ TIMEOUT
udp timeout expired.
Definition: netsed.c:185
now
time_t now
Store current time (just after select returned).
Definition: netsed.c:210
b2
char b2[MAX_BUF]
Buffer containing modified packet or datagram.
Definition: netsed.c:599
family
int family
Address family used for parameter resolution.
Definition: netsed.c:218
rule_s
Rule item.
Definition: netsed.c:150
usage_hints
void usage_hints(const char *why)
Display an error message followed by usage information.
Definition: netsed.c:257
rule_s::forig
const char * forig
match from the command line.
Definition: netsed.c:156
sig_int
void sig_int(int signo)
Handle SIGINT signal for clean exit.
Definition: netsed.c:706
VERSION
#define VERSION
Current version (recovered by Makefile for several release checks)
Definition: netsed.c:129
rule_s::fs
int fs
length of from buffer.
Definition: netsed.c:160
ESTABLISHED
@ ESTABLISHED
tcp accepted connection or udp 'connection' with a response from server.
Definition: netsed.c:179
rule
struct rule_s * rule
Array of all rules.
Definition: netsed.c:234
client2server_sed
void client2server_sed(struct tracker_s *conn)
Receive a packet from the client, 'sed' it, send it to the server.
Definition: netsed.c:674
rules
int rules
Number of rules.
Definition: netsed.c:232
stop
volatile int stop
True when SIGINT signal was received.
Definition: netsed.c:243
DISCONNECTED
@ DISCONNECTED
tcp or udp disconnected (detected by an error on read or send).
Definition: netsed.c:183
error
void error(const char *reason)
Display an error message and exit.
Definition: netsed.c:379
sed_the_buffer
int sed_the_buffer(int siz, int *live, int dir)
Applies the rules to global buffer buf.
Definition: netsed.c:605
rport
char * rport
Remote Port.
Definition: netsed.c:229
rule_s::dir
int dir
direction of rule
Definition: netsed.c:164
tracker_s::fsock
int fsock
Socket to forward to server.
Definition: netsed.c:197
tracker_s::n
struct tracker_s * n
chain it !
Definition: netsed.c:206
lport
char * lport
Local Port.
Definition: netsed.c:224
set_port
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
server2client_sed
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
rule_s::from
char * from
binary buffer to match.
Definition: netsed.c:152
is_addr_any
int is_addr_any(struct sockaddr *sa)
Detect if address in the addr_any value for both IPv4 and IPv6.
Definition: netsed.c:366
clean_socks
void clean_socks(void)
Close all sockets to use before exit.
Definition: netsed.c:317
tracker_s::state
enum state_e state
Connection state.
Definition: netsed.c:201
rule_s::to
char * to
binary buffer replacement.
Definition: netsed.c:154
parse_params
void parse_params(int argc, char *argv[])
parse the command line parameters
Definition: netsed.c:460
tracker_s
This structure is used to track information about open connections.
Definition: netsed.c:189
buf
char buf[MAX_BUF]
Buffer for receiving a single packet or datagram.
Definition: netsed.c:597
main
int main(int argc, char *argv[])
This is main...
Definition: netsed.c:713
tracker_s::live
int * live
By connection TTL.
Definition: netsed.c:203
state_e
state_e
Connection state.
Definition: netsed.c:175
tracker_s::time
time_t time
Last event time, for udp timeout.
Definition: netsed.c:199
connections
struct tracker_s * connections
List of connections.
Definition: netsed.c:240
tracker_s::csock
int csock
Connection socket to client.
Definition: netsed.c:195
OUT
@ OUT
Definition: netsed.c:171
hex
char hex[]
Hex digit to parsing the % notation in rules.
Definition: netsed.c:387
b2server_sed
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
DBG
#define DBG(x...)
Disabled debug prints.
Definition: netsed.c:143
ALL
@ ALL
Definition: netsed.c:169
lsock
int lsock
Listening socket.
Definition: netsed.c:213
UNREPLIED
@ UNREPLIED
udp datagram received by netsed and send to server, no response yet.
Definition: netsed.c:177
rhost
char * rhost
Remote Host.
Definition: netsed.c:227
rule_s::torig
const char * torig
replacement from the command line.
Definition: netsed.c:158
shrink_to_binary
void shrink_to_binary(struct rule_s *r)
Convert the % notation in rules to plain binary data.
Definition: netsed.c:391
tracker_s::csl
socklen_t csl
size of csa
Definition: netsed.c:193
MAX_BUF
#define MAX_BUF
max size for buffers
Definition: netsed.c:131
tcp
int tcp
TCP or UDP.
Definition: netsed.c:221
bind_and_listen
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
short_usage_hints
void short_usage_hints(const char *why)
Display an error message followed by short usage information.
Definition: netsed.c:247
freetracker
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