redir-2.2.1/ 40755 0 0 0 7031500577 10752 5ustar sammyrootredir-2.2.1/COPYING100644 0 0 43127 6617442256 12141 0ustar sammyroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. redir-2.2.1/Makefile100644 0 0 2767 7031477753 12534 0ustar sammyroot### user configuration section # if your system lacks getopt_long(), remove the comment from this line GETOPT_OBJS = # getopt/getopt.o getopt/getopt1.o # if your system lacks strrchr() or strdup(), edit this line STR_CFLAGS = # -DNEED_STRRCHR -DNEED_STRDUP # if you would like support for TCP wrappers (and have libwrap.a # installed), remove these comments. WRAP_CFLAGS = # -DUSE_TCP_WRAPPERS WRAP_LIBS = # -lwrap # if your system needs any additional libraries (solaris, for example, # needs the ones commented out below), edit this line. EXTRA_LIBS = #-lnsl -lsocket # add additional compiler flags here. Some useful ones are: # # -DNO_SHAPER (doesn't compile in traffic shaping code) # -DNO_FTP (doesn't compile in FTP redirection support) EXTRA_CFLAGS = # -DNO_SHAPER -DNO_FTP ### end of user configuration section # redir requires gcc. if you're lucky, another compiler might work. CC = gcc # if your system lacks getopt_long, remove the comment from this line OBJS = redir.o $(GETOPT_OBJS) CFLAGS = -O2 -Wall $(STR_CFLAGS) $(WRAP_CFLAGS) $(EXTRA_CFLAGS) LDFLAGS = -s # solaris, and others, may also need these libraries to link # also edit here if you're using the TCP wrappers code LIBS = $(WRAP_LIBS) $(EXTRA_LIBS) # this line should build under os/2 using syslog from # http://r350.ee.ntu.edu.tw/~hcchu/os2/ports/dev # submitted by: Doug LaRue (dlarue@nosc.mil) # LIBS = -lsyslog -lsocket all: redir clean: rm -f $(OBJS) redir core redir: ${OBJS} ${CC} ${LDFLAGS} -o redir ${OBJS} ${LIBS} redir-2.2.1/README100644 0 0 7273 7031475526 11745 0ustar sammyroot Redir v.2.2.1 Redir is a port redirector. It's functionally basically consists of the ability to listen for TCP connections on a given port, and, when it recieves a connection, to then connect to a given destination address/port, and pass data between them. It finds most of its applications in traversing firewalls, but, of course, there are other uses. Consult the man page, or run with no options for usage information. Please check the Makefile to see if you need to make any changes to get it to compile correctly on your particular unix flavor. If you would like to remove support for some extended options (for the sake of speed, code size, whatever), change the EXTRA_CFLAGS line in the makefile. The following are supported: -DNO_SHAPER (doesn't compile in traffic shaping code) -DNO_FTP (doesn't compile in FTP redirection support) Please submit patches/comments, etc to: Sam Creasey (sammy@oh.verio.com). ----- I'm thinking of eventually doing a version which never forks, but does one big-honking-select-loop, which probably wouldn't be much of a bother, and would save a good chunk of ram, but then an FD limit becomes quite a real possibility. perhaps an #ifdef selecting the old or new code would help this... though, really, this is a known problem with a lot of proxies, and it doesn't seem to hurt too bad. depends on the MAX_FDS (or whatever that define is. FD_MAX?) on your machine. Wow. The authorship/maintnence for this thing has REALLY gotten mangled. Credits should, logically, go to the following people: Nigel Metheringham For taking the code I wrote and making it into a more stable sensible, and usable package. Sam Creasey Original author, but this package would be vastly inferior without Nigel's modifications. Thomas Osterried Added the --bind-addr patch. 22 June, 1999 - Sam Creasey Bug reports/patches/comments/etc please now to Current versions can be found at http://oh.verio.com/~sammy/hacks redir is distributed under the terms of the GNU Public Licence, version 2 or later, which was distributed with this source archive in the file COPYING. the files in the getopt/ directory are taken from GNU source code distributed under the same license. These particular files were copied from the GNU package gawk-3.0.3, because it happened to be sitting on my drive at the time. ======================================================================= Redir v.0.7 redir is a tcp port redirector for unix. It can run under inetd or stand alone (in which case it handles multiple connections). Its 8 bit clean, not limited to line mode, is small and light. If you want access control run it under xinetd, or inetd with tcp wrappers. Or you could use the tcp wrapper library to extend it and do fancy access control - if so please let me know. redir is released under GPL. Nigel Metheringham Nigel.Metheringham@ThePLAnet.net 30 June, 1996 ======================================================================= [Original readme from version 0.5] If you liked daemon, you'll LOVE redir! Redir, the fully functional (but only in line mode) port redirector for unix! (yeah! WOOOO!). Basically, it's like tredir. But hacked from daemon. And poorly written. But, hey, it dodges firewalls, and THAT's the important part. I think. Oh, fuck it. Look, it's useful. Good for dynamic IP, too. Trust me, it is. usage: redir [remote-host] listen_port connect_port The syntax is a little clumsy, but it works. compile with make redir or gcc redir.c -o redir comments/bugs/flames to sammy@freenet.akron.oh.us (please, write if you use the program!) redir-2.2.1/getopt/ 40755 0 0 0 6617442303 12255 5ustar sammyrootredir-2.2.1/getopt/getopt.c100644 0 0 70050 6617432701 14043 0ustar sammyroot/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc. This file is part of the GNU C Library. Its master source is NOT part of the C library, however. The master source lives in /gd/gnu/lib. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #ifdef HAVE_CONFIG_H #include #endif #if !defined (__STDC__) || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #include #endif /* GNU C library. */ #ifdef VMS #include #if HAVE_STRING_H - 0 #include #endif #endif #if defined (WIN32) && !defined (__CYGWIN32__) /* It's not Unix, really. See? Capital letters. */ #include #define getpid() GetCurrentProcessId() #endif #ifndef _ /* This is for other GNU distributions with internationalized messages. When compiling libc, the _ macro is predefined. */ #ifdef HAVE_LIBINTL_H # include # define _(msgid) gettext (msgid) #else # define _(msgid) (msgid) #endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #else /* Avoid depending on library functions or files whose names are inconsistent. */ char *getenv (); static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ #if !defined (__STDC__) || !__STDC__ /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen (const char *); #endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ static const char *nonoption_flags; static int nonoption_flags_len; static int original_argc; static char *const *original_argv; /* Make sure the environment variable bash 2.0 puts in the environment is valid for the getopt call we must make sure that the ARGV passed to getopt is that one passed to the process. */ static void store_args (int argc, char *const *argv) __attribute__ ((unused)); static void store_args (int argc, char *const *argv) { /* XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ original_argc = argc; original_argv = argv; } text_set_element (__libc_subinit, store_args); #endif /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ #if defined (__STDC__) && __STDC__ static void exchange (char **); #endif static void exchange (argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ #if defined (__STDC__) && __STDC__ static const char *_getopt_initialize (int, char *const *, const char *); #endif static const char * _getopt_initialize (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind = 1; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #ifdef _LIBC if (posixly_correct == NULL && argc == original_argc && argv == original_argv) { /* Bash 2.0 puts a special variable in the environment for each command it runs, specifying which ARGV elements are the results of file name wildcard expansion and therefore should not be considered as options. */ char var[100]; sprintf (var, "_%d_GNU_nonoption_argv_flags_", getpid ()); nonoption_flags = getenv (var); if (nonoption_flags == NULL) nonoption_flags_len = 0; else nonoption_flags_len = strlen (nonoption_flags); } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { optarg = NULL; if (!__getopt_initialized || optind == 0) { optstring = _getopt_initialize (argc, argv, optstring); optind = 1; /* Don't scan ARGV[0], the program name. */ __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #ifdef _LIBC #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && nonoption_flags[optind] == '1')) #else #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, _("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, _("%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); nextchar += strlen (nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); else /* +option or -option */ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); else fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) fprintf (stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #endif /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ redir-2.2.1/getopt/getopt.h100644 0 0 11123 6617432701 14044 0ustar sammyroot/* Declarations for getopt. Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. This file is part of the GNU C Library. Its master source is NOT part of the C library, however. The master source lives in /gd/gnu/lib. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if defined (__STDC__) && __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if defined (__STDC__) && __STDC__ #ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int argc, char *const *argv, const char *shortopts); #else /* not __GNU_LIBRARY__ */ extern int getopt (); #endif /* __GNU_LIBRARY__ */ extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ extern int getopt (); extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); #endif /* __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ redir-2.2.1/getopt/getopt1.c100644 0 0 11053 6617432701 14122 0ustar sammyroot/* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. This file is part of the GNU C Library. Its master source is NOT part of the C library, however. The master source lives in /gd/gnu/lib. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "getopt.h" #if !defined (__STDC__) || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #endif #ifndef NULL #define NULL 0 #endif int getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } #endif /* Not ELIDE_CODE. */ #ifdef TEST #include int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ redir-2.2.1/redir.c100644 0 0 67424 7031477576 12371 0ustar sammyroot/* $Id$ * * redir - a utility for redirecting tcp connections * * Author: Nigel Metheringham * Nigel.Metheringham@ThePLAnet.net * * Based on, but much modified from, code originally written by * sammy@freenet.akron.oh.us - original header is below. * * redir is released under the GNU General Public license, * version 2, or at your discretion, any later version. * */ /* * redir is currently maintained by Sam Creasey (sammy@oh.verio.com). * Please send patches, etc. there. * */ /* 980601: dl9sau * added some nice new features: * * --bind_addr=my.other.ip.address * forces to use my.other.ip.address for the outgoing connection * * you can also specify, that redir listens not on all IP addresses of * your system but only for the given one, i.e.: * if my host has the addresses * irc.thishost.my.domain and mail.thishost.my.domain * but you want that your users do connect for the irc redir service * only on irc.thishost.my.domain, then do it this way: * redir irc.fu-berlin.de irc.thishost.mydomain:6667 6667 * my need was that: * addr1.first.domain 6667 redirects to irc.first.net port 6667 * and addr2.second.domain 6667 redirects to irc.second.net port 6667 * while addr1 and addr2 are the same maschine and the ports can be equal. * * enjoy it! * - thomas , * * btw: i tried without success implementing code for the following scenario: * redir --force_addr irc.fu-berlin.de 6667 6667 * if "--force_addr" is given and a user connects to my system, that address * of my system will be used on the outgoing connection that the user * connected to. * i was not successful to determine, to which of my addresses the user * has connected. */ /* 990320 added support for ftp connection done by the client, now this code * should work for all ftp clients. * * - harald */ /* 991221 added options to simulate a slow connection and to limit * bandwidth. * * - Emmanuel Chantréau */ #define VERSION "2.2.1" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_TCP_WRAPPERS #include #endif #define debug(x) if (dodebug) fprintf(stderr, x) #define debug1(x,y) if (dodebug) fprintf(stderr, x, y) /* let's set up some globals... */ int dodebug = 0; int dosyslog = 0; unsigned char reuse_addr = 1; unsigned char linger_opt = 0; char * bind_addr = NULL; struct sockaddr_in addr_out; int timeout = 0; #ifndef NO_FTP int ftp = 0; #endif int transproxy = 0; #ifndef NO_SHAPER int max_bandwidth = 0; int random_wait = 0; int wait_in_out=3; /* bit 0: wait for "in", bit 1: wait for "out" */ int wait_in=1; int wait_out=1; #endif unsigned int bufsize=4096; char *connect_str = NULL; /* CONNECT string passed to proxy */ char * ident = NULL; #ifndef NO_FTP /* what ftp to redirect */ #define FTP_PORT 1 #define FTP_PASV 2 #endif #ifdef USE_TCP_WRAPPERS struct request_info request; int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* USE_TCP_WRAPPERS */ #ifdef NEED_STRRCHR #define strrchr rindex #endif /* NEED_STRRCHR */ #define REDIR_IN 1 #define REDIR_OUT 0 /* prototype anything needing it */ void do_accept(int servsock, struct sockaddr_in *target); int bindsock(char *addr, int port, int fail); #ifndef NO_SHAPER /* Used in this program to write something in a socket, it has the same parameters and return value as "write", but with the flag "in": true if it's the "in" socket and false if it's the "out" socket */ static inline ssize_t redir_write (int fd, const void *buf, size_t size, int in) { ssize_t result; int wait; wait=in ? wait_in : wait_out; if( random_wait > 0 && wait) { fd_set empty; struct timeval waitbw; /* for bandwidth */ int rand_time; FD_ZERO(&empty); rand_time=rand()%(random_wait*2); debug1("random wait: %u\n", rand_time); waitbw.tv_sec=rand_time/1000; waitbw.tv_usec=rand_time%1000; select (1, &empty, NULL, NULL, &waitbw); } result=write(fd, buf, size); if( max_bandwidth > 0 && wait) { fd_set empty; unsigned long bits; struct timeval waitbw; /* for bandwidth */ FD_ZERO(&empty); /* wait to be sure tu be below the allowed bandwidth */ bits=size*8; debug1("bandwidth wait: %lu\n", 1000*bits/max_bandwidth); waitbw.tv_sec=bits/max_bandwidth; waitbw.tv_usec=(1000*(bits%max_bandwidth))/max_bandwidth; select (1, &empty, NULL, NULL, &waitbw); } return result; } #else /* macro if traffic shaper is disabled */ #define redir_write(fd, buf, size, in) write(fd, buf,size) #endif #ifdef NEED_STRDUP char * strdup(char * str) { char * result; if (result = (char *) malloc(strlen(str) + 1)) strcpy(result, str); return result; } #endif /* NEED_STRDUP */ void redir_usage(char *name) { fprintf(stderr,"usage:\n"); fprintf(stderr, "\t%s --lport= --cport= [options]\n", name); fprintf(stderr, "\t%s --inetd --cport=\n", name); fprintf(stderr, "\n\tOptions are:-\n"); fprintf(stderr, "\t\t--lport=\t\tport to listen on\n"); fprintf(stderr, "\t\t--laddr=IP\t\taddress of interface to listen on\n"); fprintf(stderr, "\t\t--cport=\t\tport to connect to\n"); fprintf(stderr, "\t\t--caddr=\t\tremote host to connect to\n"); fprintf(stderr, "\t\t--inetd\t\trun from inetd\n"); fprintf(stderr, "\t\t--debug\t\toutput debugging info\n"); fprintf(stderr, "\t\t--timeout=\tset timeout to n seconds\n"); fprintf(stderr, "\t\t--syslog\tlog messages to syslog\n"); fprintf(stderr, "\t\t--name=\ttag syslog messages with 'str'\n"); fprintf(stderr, "\t\t--connect=\tCONNECT string passed to proxy server\n"); #ifdef USE_TCP_WRAPPERS fprintf(stderr, "\t\t \tAlso used as service name for TCP wrappers\n"); #endif /* USE_TCP_WRAPPERS */ fprintf(stderr, "\t\t--bind_addr=IP\tbind() outgoing IP to given addr\n"); #ifndef NO_FTP fprintf(stderr, "\t\t--ftp=\t\tredirect ftp connections\n"); fprintf(stderr, "\t\t\twhere type is either port, pasv, both\n"); #endif fprintf(stderr, "\t\t--transproxy\trun in linux's transparent proxy mode\n"); #ifndef NO_SHAPER /* options for bandwidth */ fprintf(stderr, "\t\t--bufsize=\tsize of the buffer\n"); fprintf(stderr, "\t\t--maxbandwidth=\tlimit the bandwidth\n"); fprintf(stderr, "\t\t--random_wait=\twait before each packet\n"); fprintf(stderr, "\t\t--wait_in_out=\t1 wait for in, 2 out, 3 in&out\n"); /* end options for bandwidth */ #endif fprintf(stderr, "\n\tVersion %s.\n", VERSION); exit(2); } void parse_args(int argc, char * argv[], char ** target_addr, int * target_port, char ** local_addr, int * local_port, int * timeout, int * dodebug, int * inetd, int * dosyslog, char ** bind_addr, #ifndef NO_FTP int * ftp, #endif int *transproxy, #ifndef NO_SHAPER unsigned int * bufsize, int * max_bandwidth, int * random_wait, int * wait_in_out, #endif char **connect_str) { static struct option long_options[] = { {"lport", required_argument, 0, 'l'}, {"laddr", required_argument, 0, 'a'}, {"cport", required_argument, 0, 'r'}, {"caddr", required_argument, 0, 'c'}, {"bind_addr", required_argument, 0, 'b'}, {"debug", no_argument, 0, 'd'}, {"timeout", required_argument, 0, 't'}, {"inetd", no_argument, 0, 'i'}, {"ident", required_argument, 0, 'n'}, {"name", required_argument, 0, 'n'}, {"syslog", no_argument, 0, 's'}, {"ftp", required_argument, 0, 'f'}, {"transproxy", no_argument, 0, 'p'}, {"connect", required_argument, 0, 'x'}, {"bufsize", required_argument, 0, 'z'}, {"max_bandwidth", required_argument, 0, 'm'}, {"random_wait", required_argument, 0, 'w'}, {"wait_in_out", required_argument, 0, 'o'}, {0,0,0,0} /* End marker */ }; int option_index = 0; extern int optind; int opt; struct servent *portdesc; char *lport = NULL; char *tport = NULL; #ifndef NO_FTP char *ftp_type = NULL; #endif *local_addr = NULL; *target_addr = NULL; *target_port = 0; *local_port = 0; while ((opt = getopt_long(argc, argv, "disfpn:t:b:a:l:r:c:x:z:m:w:o:", long_options, &option_index)) != -1) { switch (opt) { case 'x': *connect_str = optarg; break; case 'a': *local_addr = optarg; break; case 'l': lport = optarg; break; case 'r': tport = optarg; break; case 'c': *target_addr = optarg; break; case 'b': *bind_addr = optarg; break; case 'd': (*dodebug)++; break; case 't': *timeout = atol(optarg); break; case 'i': (*inetd)++; break; case 'n': /* This is the ident which is added to syslog messages */ ident = optarg; break; case 's': (*dosyslog)++; break; #ifndef NO_FTP case 'f': ftp_type = optarg; if(!ftp_type) { redir_usage(argv[0]); exit(1); } break; #endif case 'p': (*transproxy)++; break; #ifndef NO_SHAPER case 'z': *bufsize = (unsigned int)atol(optarg); break; case 'm': *max_bandwidth = atol(optarg); break; case 'w': *random_wait = atol(optarg); break; case 'o': *wait_in_out = atol(optarg); wait_in=*wait_in_out & 1; wait_out=*wait_in_out & 2; break; #endif default: redir_usage(argv[0]); exit(1); break; } } if(tport == NULL) { redir_usage(argv[0]); exit(1); } if ((portdesc = getservbyname(tport, "tcp")) != NULL) { *target_port = ntohs(portdesc->s_port); } else { *target_port = atol(tport); } /* only check local port if not running from inetd */ if(!(*inetd)) { if(lport == NULL) { redir_usage(argv[0]); exit(1); } if ((portdesc = getservbyname(lport, "tcp")) != NULL) *local_port = ntohs(portdesc->s_port); else *local_port = atol(lport); } /* if *inetd */ if (!ident) { if ((ident = (char *) strrchr(argv[0], '/'))) { ident++; } else { ident = argv[0]; } } #ifndef NO_FTP /* some kind of ftp being forwarded? */ if(ftp_type) { if(!strncasecmp(ftp_type, "port", 4)) *ftp = FTP_PORT; else if(!strncasecmp(ftp_type, "pasv", 4)) *ftp = FTP_PASV; else if(!strncasecmp(ftp_type, "both", 4)) *ftp = FTP_PORT | FTP_PASV; else { redir_usage(argv[0]); exit(1); } } #endif openlog(ident, LOG_PID, LOG_DAEMON); return; } #ifndef NO_FTP /* with the --ftp option, this one changes passive mode replies from the ftp server to point to a new redirector which we spawn, now it also change the PORT commando when the client accept the dataconnection */ void ftp_clean(int send, char *buf, unsigned long *bytes, int ftpsrv) { char *port_start; int rporthi, lporthi; int lportlo, rportlo; int lport, rport; int remip[4]; int localsock; int socksize = sizeof(struct sockaddr_in); struct sockaddr_in newsession; struct sockaddr_in sockname; if (ftpsrv == 0) { /* is this a port commando ? */ if(strncmp(buf, "PORT", 4)) { redir_write(send, buf, (*bytes), REDIR_OUT); return; } /* parse the old address out of the buffer */ port_start = strchr(buf, ' '); sscanf(port_start, " %d,%d,%d,%d,%d,%d", &remip[0], &remip[1], &remip[2], &remip[3], &rporthi, &rportlo); } else { /* is this a passive mode return ? */ if(strncmp(buf, "227", 3)) { redir_write(send, buf, (*bytes), REDIR_OUT); return; } /* parse the old address out of the buffer */ port_start = strchr(buf, '('); sscanf(port_start, "(%d,%d,%d,%d,%d,%d", &remip[0], &remip[1], &remip[2], &remip[3], &rporthi, &rportlo); } /* get the outside interface so we can listen */ if(getsockname(send, (struct sockaddr *)&sockname, &socksize) != 0) { perror("getsockname"); exit(1); } rport = (rporthi << 8) | rportlo; /* we need to listen on a port for the incoming connection. we will use the port 0, so let the system pick one. */ localsock = bindsock(inet_ntoa(sockname.sin_addr), 0, 1); /* get the real info */ if(getsockname(localsock, (struct sockaddr *)&sockname, &socksize) < 0) { perror("getsockname"); if (dosyslog) syslog(LOG_ERR, "getsockname failed: %m"); exit(1); } lport = ntohs(sockname.sin_port); lporthi=(lport >> 8 ) & 0xff; lportlo=lport & 0xff; /* check to see if we bound */ if(localsock == -1) { fprintf(stderr, "ftp: unable to bind new listening address\n"); exit(1); } if (ftpsrv == 0) { /* send the new port and ipaddress to the server */ (*bytes) = sprintf(buf, "PORT %d,%d,%d,%d,%d,%d\n", sockname.sin_addr.s_addr & 0xff, (sockname.sin_addr.s_addr >> 8) & 0xff, (sockname.sin_addr.s_addr >> 16) & 0xff, sockname.sin_addr.s_addr >> 24, lporthi, lportlo); } else { /* send the new port and ipaddress to the client */ (*bytes) = sprintf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\n", sockname.sin_addr.s_addr & 0xff, (sockname.sin_addr.s_addr >> 8) & 0xff, (sockname.sin_addr.s_addr >> 16) & 0xff, sockname.sin_addr.s_addr >> 24, lporthi, lportlo); } newsession.sin_port = htons(rport); newsession.sin_family = AF_INET; newsession.sin_addr.s_addr = remip[0] | (remip[1] << 8) | (remip[2] << 16) | (remip[3] << 24); debug1("ftpdata server ip: %s\n", inet_ntoa(newsession.sin_addr)); debug1("ftpdata server port: %d\n", rport); debug1("listening for ftpdata on port %d\n", lport); debug1("listening for ftpdata on addr %s\n", inet_ntoa(sockname.sin_addr)); /* now that we're bound and listening, we can safely send the new string without fear of them getting a connection refused. */ redir_write(send, buf, (*bytes), REDIR_OUT); /* make a new process to handle the dataconnection correctly, for the PASV mode this isn't a problem because after sending the PASV command, the data connection, get active. For the PORT command the server must send a success, if starting here with the copyloop the success command never arrive the client.*/ switch(fork()) { case -1: /* Error */ syslog(LOG_ERR, "Couldn't fork: %m"); _exit(1); case 0: /* Child */ { /* turn off ftp checking while the data connection is active */ ftp = 0; do_accept(localsock, &newsession); close(localsock); _exit(0); } default: /* Parent */ { close(localsock); } } return; } #endif void copyloop(int insock, int outsock, int timeout_secs) { fd_set iofds; fd_set c_iofds; int max_fd; /* Maximum numbered fd used */ struct timeval timeout; unsigned long bytes; unsigned long bytes_in = 0; unsigned long bytes_out = 0; unsigned int start_time, end_time; char buf[bufsize]; /* Record start time */ start_time = (unsigned int) time(NULL); /* Set up timeout */ timeout.tv_sec = timeout_secs; timeout.tv_usec = 0; /* file descriptor bits */ FD_ZERO(&iofds); FD_SET(insock, &iofds); FD_SET(outsock, &iofds); if (insock > outsock) { max_fd = insock; } else { max_fd = outsock; } debug1("Entering copyloop() - timeout is %d\n", timeout_secs); while(1) { (void) memcpy(&c_iofds, &iofds, sizeof(iofds)); if (select(max_fd + 1, &c_iofds, (fd_set *)0, (fd_set *)0, (timeout_secs ? &timeout : NULL)) <= 0) { /* syslog(LLEV,"connection timeout: %d sec",timeout.tv_sec);*/ break; } if(FD_ISSET(insock, &c_iofds)) { if((bytes = read(insock, buf, sizeof(buf))) <= 0) break; #ifndef NO_FTP if (ftp & FTP_PORT) /* if we're correcting FTP, lookup for a PORT commando in the buffer, if yes change this and establish a new redirector for the data */ ftp_clean(outsock, buf, &bytes,0); else #endif if(redir_write(outsock, buf, bytes, REDIR_OUT) != bytes) break; bytes_out += bytes; } if(FD_ISSET(outsock, &c_iofds)) { if((bytes = read(outsock, buf, sizeof(buf))) <= 0) break; /* if we're correcting for PASV on ftp redirections, then fix buf and bytes to have the new address, among other things */ #ifndef NO_FTP if(ftp & FTP_PASV) ftp_clean(insock, buf, &bytes,1); else #endif if(redir_write(insock, buf, bytes, REDIR_IN) != bytes) break; bytes_in += bytes; } } debug("Leaving main copyloop\n"); /* setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(insock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); setsockopt(outsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(outsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); */ shutdown(insock,0); shutdown(outsock,0); close(insock); close(outsock); debug("copyloop - sockets shutdown and closed\n"); end_time = (unsigned int) time(NULL); debug1("copyloop - connect time: %8d seconds\n", end_time - start_time); debug1("copyloop - transfer in: %8ld bytes\n", bytes_in); debug1("copyloop - transfer out: %8ld bytes\n", bytes_out); if (dosyslog) { syslog(LOG_NOTICE, "disconnect %d secs, %ld in %ld out", (end_time - start_time), bytes_in, bytes_out); } return; } void doproxyconnect(int socket) { char buf[128]; int x; /* write CONNECT string to proxy */ sprintf((char *) &buf, "CONNECT %s HTTP/1.0\n\n", connect_str); x = write(socket, (char *) &buf, strlen(buf)); if (x < 1) { perror("doproxyconnect: failed"); exit(1); } /* now read result */ x = read(socket, (char *) &buf, sizeof(buf)); if (x < 1) { perror("doproxyconnect: failed reading fra proxy"); exit(1); } /* no more error checking for now -- something should be added later */ /* HTTP/1.0 200 Connection established */ } /* lwait for a connection and move into copyloop... again, ftp redir will call this, so we don't dupilcate it. */ void do_accept(int servsock, struct sockaddr_in *target) { int clisock; int targetsock; struct sockaddr_in client; int clientlen = sizeof(client); int accept_errno; debug("top of accept loop\n"); if ((clisock = accept(servsock, (struct sockaddr *) &client, &clientlen)) < 0) { accept_errno = errno; perror("server: accept"); if (dosyslog) syslog(LOG_ERR, "accept failed: %m"); /* determine if this error is fatal */ switch(accept_errno) { /* non-fatal errors */ case EHOSTUNREACH: case ECONNRESET: case ETIMEDOUT: return; /* all other errors assumed fatal */ default: exit(1); } } debug1("peer IP is %s\n", inet_ntoa(client.sin_addr)); debug1("peer socket is %d\n", client.sin_port); /* * Double fork here so we don't have to wait later * This detaches us from our parent so that the parent * does not need to pick up dead kids later. * * This needs to be done before the hosts_access stuff, because * extended hosts_access options expect to be run from a child. */ switch(fork()) { case -1: /* Error */ perror("(server) fork"); if (dosyslog) syslog(LOG_ERR, "(server) fork failed: %m"); _exit(1); case 0: /* Child */ break; default: /* Parent */ { int status; /* Wait for child (who has forked off grandchild) */ (void) wait(&status); /* Close sockets to prevent confusion */ close(clisock); return; } } /* We are now the first child. Fork again and exit */ switch(fork()) { case -1: /* Error */ perror("(child) fork"); if (dosyslog) syslog(LOG_ERR, "(child) fork failed: %m"); _exit(1); case 0: /* Child */ break; default: /* Parent */ _exit(0); } /* We are now the grandchild */ #ifdef USE_TCP_WRAPPERS request_init(&request, RQ_DAEMON, ident, RQ_FILE, clisock, 0); sock_host(&request); sock_hostname(&request); sock_hostaddr(&request); if (!hosts_access(&request)) { refuse(&request); _exit(0); } if (dosyslog) syslog(LOG_INFO, "accepted connect from %s", eval_client(&request)); #endif /* USE_TCP_WRAPPERS */ if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("target: socket"); if (dosyslog) syslog(LOG_ERR, "socket failed: %m"); _exit(1); } if(transproxy) { memcpy(&addr_out, &client, sizeof(struct sockaddr_in)); addr_out.sin_port = 0; } if (bind_addr || transproxy) { /* this only makes sense if an outgoing IP addr has been forced; * at this point, we have a valid targetsock to bind() to.. */ /* also, if we're in transparent proxy mode, this option never makes sense */ if (bind(targetsock, (struct sockaddr *) &addr_out, sizeof(struct sockaddr_in)) < 0) { perror("bind_addr: cannot bind to forcerd outgoing addr"); /* the port parameter fetch the really port we are listening, it should only be different if the input value is 0 (let the system pick a port) */ if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); _exit(1); } debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr)); } if (connect(targetsock, (struct sockaddr *) target, sizeof(struct sockaddr_in)) < 0) { perror("target: connect"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); _exit(1); } debug1("connected to %s\n", inet_ntoa(target->sin_addr)); /* thanks to Anders Vannman for the fix to make proper syslogging happen here... */ if (dosyslog) { char tmp1[20], tmp2[20]; strcpy(tmp1, inet_ntoa(client.sin_addr)); strcpy(tmp2, inet_ntoa(target->sin_addr)); syslog(LOG_NOTICE, "connecting %s/%d to %s/%d", tmp1, client.sin_port, tmp2, target->sin_port); } /* do proxy stuff */ if (connect_str) doproxyconnect(targetsock); #ifndef NO_SHAPER /* initialise random number if necessary */ if( random_wait > 0 ) { srand(getpid()); } #endif copyloop(clisock, targetsock, timeout); exit(0); /* Exit after copy */ } /* bind to a new socket, we do this out here because passive-fixups are going to call it too, and there's no sense dupliciting the code. */ /* fail is true if we should just return a -1 on error, false if we should bail. */ int bindsock(char *addr, int port, int fail) { int servsock; struct sockaddr_in server; /* * Get a socket to work with. This socket will * be in the Internet domain, and will be a * stream socket. */ if ((servsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { if(fail) { return -1; } else { perror("server: socket"); if (dosyslog) syslog(LOG_ERR, "socket failed: %m"); exit(1); } } memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(port); if (addr != NULL) { struct hostent *hp; debug1("listening on %s\n", addr); if ((hp = gethostbyname(addr)) == NULL) { fprintf(stderr, "%s: cannot resolve hostname.\n", addr); exit(1); } memcpy(&server.sin_addr, hp->h_addr, hp->h_length); } else { debug("local IP is default\n"); server.sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); } setsockopt(servsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(servsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); /* * Try to bind the address to the socket. */ if (bind(servsock, (struct sockaddr *) &server, sizeof(server)) < 0) { if(fail) { close(servsock); return -1; } else { perror("server: bind"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); exit(1); } } /* * Listen on the socket. */ if (listen(servsock, 10) < 0) { if(fail) { close(servsock); return -1; } else { perror("server: listen"); if (dosyslog) syslog(LOG_ERR, "listen failed: %m"); exit(1); } } return servsock; } int main(int argc, char *argv[]) { struct sockaddr_in target; char *target_addr; int target_port; char *local_addr; int local_port; int inetd = 0; char * target_ip; char * ip_to_target; debug("parse args\n"); parse_args(argc, argv, &target_addr, &target_port, &local_addr, &local_port, &timeout, &dodebug, &inetd, &dosyslog, &bind_addr, #ifndef NO_FTP &ftp, #endif &transproxy, #ifndef NO_SHAPER &bufsize, &max_bandwidth, &random_wait, &wait_in_out, #endif &connect_str); /* Set up target */ target.sin_family = AF_INET; target.sin_port = htons(target_port); if (target_addr != NULL) { struct hostent *hp; debug1("target is %s\n", target_addr); if ((hp = gethostbyname(target_addr)) == NULL) { fprintf(stderr, "%s: host unknown.\n", target_addr); exit(1); } memcpy(&target.sin_addr, hp->h_addr, hp->h_length); } else { debug("target is default\n"); target.sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); } target_ip = strdup(inet_ntoa(target.sin_addr)); debug1("target IP address is %s\n", target_ip); debug1("target port is %d\n", target_port); /* Set up outgoing IP addr (optional); * we have to wait for bind until targetsock = socket() is done */ if (bind_addr && !transproxy) { struct hostent *hp; fprintf(stderr, "bind_addr is %s\n", bind_addr); addr_out.sin_family = AF_INET; addr_out.sin_port = 0; if ((hp = gethostbyname(bind_addr)) == NULL) { fprintf(stderr, "%s: cannot resolve forced outgoing IP address.\n", bind_addr); exit(1); } memcpy(&addr_out.sin_addr, hp->h_addr, hp->h_length); ip_to_target = strdup(inet_ntoa(addr_out.sin_addr)); debug1("IP address for target is %s\n", ip_to_target); } if (inetd) { int targetsock; struct sockaddr_in client; int client_size = sizeof(client); #ifdef USE_TCP_WRAPPERS request_init(&request, RQ_DAEMON, ident, RQ_FILE, 0, 0); sock_host(&request); sock_hostname(&request); sock_hostaddr(&request); if (!hosts_access(&request)) refuse(&request); #endif /* USE_TCP_WRAPPERS */ if (!getpeername(0, (struct sockaddr *) &client, &client_size)) { debug1("peer IP is %s\n", inet_ntoa(client.sin_addr)); debug1("peer socket is %d\n", client.sin_port); } if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("target: socket"); if (dosyslog) syslog(LOG_ERR, "targetsock failed: %m"); exit(1); } if(transproxy) { memcpy(&addr_out, &client, sizeof(struct sockaddr_in)); addr_out.sin_port = 0; } if (bind_addr || transproxy) { /* this only makes sense if an outgoing IP addr has been forced; * at this point, we have a valid targetsock to bind() to.. */ if (bind(targetsock, (struct sockaddr *) &addr_out, sizeof(addr_out)) < 0) { perror("bind_addr: cannot bind to forcerd outgoing addr"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); exit(1); } debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr)); } if (connect(targetsock, (struct sockaddr *) &target, sizeof(target)) < 0) { perror("target: connect"); if (dosyslog) syslog(LOG_ERR, "connect failed: %m"); exit(1); } if (dosyslog) { syslog(LOG_NOTICE, "connecting %s/%d to %s/%d", inet_ntoa(client.sin_addr), client.sin_port, target_ip, target.sin_port); } /* Just start copying - one side of the loop is stdin - 0 */ copyloop(0, targetsock, timeout); } else { int servsock; if(local_addr) servsock = bindsock(local_addr, local_port, 0); else servsock = bindsock(NULL, local_port, 0); /* * Accept connections. When we accept one, ns * will be connected to the client. client will * contain the address of the client. */ while (1) do_accept(servsock, &target); } /* this should really never be reached */ exit(0); } redir-2.2.1/redir.man100644 0 0 6365 7031500010 12641 0ustar sammyroot.PU .TH REDIR 1 local .SH NAME redir \- redirect tcp connections .SH SYNOPSIS .ll +8 .B redir .RB [ \--laddr=incoming.ip.address ] .RB [ \--caddr=host ] .RB [ \--debug ] .RB [ \--syslog .RB [ \--name=str ] .RB [ \--timeout=n ] .RB [ \--bind_addr=my.other.ip.address ] .RB [ \--ftp=type ] .RB [ \--transproxy ] .RB [ \--connect=host:port ] .I --lport=port .I --cport=port .RB [ \--bufsize=n ] .RB [ \--maxbandwidth=n ] .RB [ \--random_wait=n ] .RB [ \--wait_in_out=n ] .ll -8 .br .B redir .RB \--inetd .RB [ \--caddr=host ] .RB [ \--debug ] .RB [ \--syslog .RB [ \--name=str ] .RB [ \--timeout=n ] .RB [ \--ftp=type ] .RB [ \--transproxy ] .RB [ \--connect=host:port ] .I --cport=port .RB [ \--bufsize=n ] .RB [ \--maxbandwidth=n ] .RB [ \--random_wait=n ] .RB [ \--wait_in_out=n ] .ll -8 .br .SH DESCRIPTION .I Redir redirects tcp connections coming in to a local port to a specified address/port combination. .PP It may be run either from inetd or as a standalone daemon. Depending on how redir was compiled, not all options may be available. .SH OPTIONS .TP .B \--lport Specifies port to listen for connections on (when not running from inetd) .TP .B \--laddr IP address to bind to when listening for connections (when not running from inetd) .TP .B \--cport Specifies port to connect to. .TP .B \--caddr Specifies remote host to connect to. (localhost if omitted) .TP .B \--inetd Run as a process started from inetd, with the connection passed as stdin and stdout on startup. .TP .B \--debug Write debug output to stderr or syslog. .TP .B \--name Specify program name to be used for TCP wrapper checks and syslog logging. .TP .B --timeout Timeout and close the connection after n seconds on inactivity. .TP .B \--syslog Log information to syslog. .TP .B \--bind_addr Forces redir to pick a specific address/interface to bind to when it listens for incoming connections. .TP .B \--ftp When using redir for an FTP server, this will cause redir to also redirect ftp connections. Type should be specified as either "port", "pasv", or "both", to specify what type of FTP connection to handle. Note that --transproxy often makes one or the other (generally port) undesirable. .TP .B \--transproxy On a linux system with transparany proxying enables, causes redir to make connections appear as if they had come from their true origin. (see transproxy.txt in the source archive) .TP .B \--connect Redirects connections through an HTTP proxy which supports the CONNECT command. Specify the address and port of the proxy using --caddr and --cport. --connect requires the hostname and port which the HTTP proxy will be asked to connect to. .TP .B \--bufsize n Set the bufsize (defaut 4096) in bytes. Can be used combined with --maxbandwidth or --random_wait to simulate a slow connection. .TP .B \--maxbandwidth n Reduce the bandwidth to be no more than n bits/sec. The algorithme is basic, the goal is to simulate a slow connection, so there is no pic acceptance. .TP .B \--random_wait n Wait between 0 and 2 x n milliseconds before each "packet". A "packet" is a bloc of data read in one time by redir. A "packet" size is always less than the bufsize (see also --bufsize). .TP .B \--wait_in_out n Apply --maxbandwidth and --random_wait for input if n=1, output if n=2 and both if n=3. .SH "SEE ALSO" inetd(1) redir-2.2.1/transproxy.txt100644 0 0 7702 6733744606 14062 0ustar sammyroot Transparent proxying support: (with much thanks to Bernd Eckenfels, who has been maintaing redir for debian, and pointed out to me that this could be done at all.) Most semi-recent versions of the linux kernel have an option which can be used with IP firewalls entitled "transparent proxying". Basically, it allows one to add rules with ipfwadm which will redirect all connections to certain hosts, on certain ports, to a port on the firewall machine. An convienent upshot of this feature is that, when it is enabled, a program running as root may explicitly specify the outgoing address to be used when making a connection to just about anything it pleases, which allows us to, when redirecting a connection, make the connection to the destination machine appear as if it were coming from the system which connected to redir. Also quite convenient is the fact that the program doing this does not actually have to be run using transparent proxy firewalling rules, it simply has to be compiled into the kernel. The net effect of it all is the --transproxy flag, which will use this to make connections "look right" in terms of their originating IP, as long as redir is running on a linux system with this feature compiled into its kernel. (please don't ask me about kernel compiling issues, unless you're sure you have this option turned on, your kernel is otherwise installed/working correctly, and --transproxy isn't operating) Note the following side effects: 1) Use of --transproxy will cause the --bind-addr option to have no effect. Not really a problem, as using them together wouldn't make any sense in the first place. 2) For redirection with --transproxy to work at all, the connection to redir must pass through the firewall. The following example should illustrate this: Let's say that there's a firewall machine running with the internal IP 10.0.0.1, and a netmask of 255.0.0.0 (that is, inside network is considered to encompass the entire 10.0.0.0 network). All machines inside the network are configured to use 10.0.0.1 as their gateway address. We want toredirect all connections to the firewall on port 2323 to port 23 on 10.0.0.2, and we'd like to use --transproxy, so we run: redir --transproxy 10.0.0.2 23 2323 Case 1: Connection from the outside world. Let's say someone at address 111.111.111.111 telnets to port 2323, on the external ip address of the firewall machine. Now, as all traffic from inside the firewall to 111.111.111.111 must always pass through the firewall, in any situation, this will work. Case 2: Connection from somewhere on the internal network. Now, someone at 10.0.0.3 wants to connect to the same service, but, rather than telnetting to port 23 on 10.0.0.2, they've telnetted to the port 2323 on 10.0.0.1 (or the external IP of the firewall, doesn't matter). This won't work. This is because when the destination machine (10.0.0.2) saw the connection appearing to come from 10.0.0.3, it then expects the real 10.0.0.3 to be talking to it, which is, in fact not the case. By contrast, in case 1, 10.0.0.2, regardless of the external address, expected these packets to come from the gateway host, which was, in fact, the case. For this reason, internal hosts will be unable to make connections through a redir running with --transproxy enabled. This cannot properly be fixed by redir itself, as far as I can tell, except for using a workaround which would examine the system's routing tables, and then disable the effects of --transproxy when a connection is made from a host in the same routing block as redir's destination. This doesn't sound particularly worthwhile, given that there's no need for an internal machine to hit the redirector at all. Patches will be accepted from someone who bothers to do it, however. Hopefully, you now have a clear understanding of how to use this feature. Questions can be directed to sammy@oh.verio.com. -- Sam Creasey (11/2/98) redir-2.2.1/contrib/ 40755 0 0 0 7025706725 12420 5ustar sammyrootredir-2.2.1/contrib/redir-ha.c100644 0 0 72764 7025705723 14411 0ustar sammyroot/* $Id$ * * redir - a utility for redirecting tcp connections * * Author: Nigel Metheringham * Nigel.Metheringham@ThePLAnet.net * * Based on, but much modified from, code originally written by * sammy@freenet.akron.oh.us - original header is below. * * redir is released under the GNU General Public license, * version 2, or at your discretion, any later version. * */ /* Redir, the code to which is below, is actually a horrible hack of my * other cool network utility, daemon, which is actually a horrible hack * of ora's using C sample code, 12.2.c. But, hey, they do something. * (and that's the key.) * -- Sammy (sammy@freenet.akron.oh.us) */ /* oh, incidentally, Sammy is now sammy@oh.verio.com */ /* 980601: dl9sau * added some nice new features: * * --bind_addr=my.other.ip.address * forces to use my.other.ip.address for the outgoing connection * * you can also specify, that redir listens not on all IP addresses of * your system but only for the given one, i.e.: * if my host has the addresses * irc.thishost.my.domain and mail.thishost.my.domain * but you want that your users do connect for the irc redir service * only on irc.thishost.my.domain, then do it this way: * redir irc.fu-berlin.de irc.thishost.mydomain:6667 6667 * my need was that: * addr1.first.domain 6667 redirects to irc.first.net port 6667 * and addr2.second.domain 6667 redirects to irc.second.net port 6667 * while addr1 and addr2 are the same maschine and the ports can be equal. * * enjoy it! * - thomas , * * btw: i tried without success implementing code for the following scenario: * redir --force_addr irc.fu-berlin.de 6667 6667 * if "--force_addr" is given and a user connects to my system, that address * of my system will be used on the outgoing connection that the user * connected to. * i was not successful to determine, to which of my addresses the user * has connected. */ /* Peter Skliarouk (skliaroukp@geocites.com) added possibility to automatically recognize failed services(hosts) and to continue maintain connections to "survived" services(hosts) in round-robin manner. If program recognized that an service failed it would try to connect to it every predefined number of connections BUGS: 1) Program accepts one port for all targets thus SERVICES=SERVERS (I just need to change the way redir takes service arguments to something servername:port) 2) No inetd support 3) No ftp support 4) One force addr for everyone (and transparent redir). */ #define VERSION "2.1.2" #define MAXSERVERS 10 // MAX number of servers #define ATTEMPTS 1000 // In case of failure attempt to reconnect every #define READY 0 // Service is ready #define EMPTYSTRING ((char*)NULL) // empty string #define YES 1 #define NO 0 #define NOTinitialized 0 #define FAILED -1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_TCP_WRAPPERS #include #endif #define debug(x) if (dodebug) fprintf(stderr, x) #define debug1(x,y) if (dodebug) fprintf(stderr, x, y) #define AND && #define OR || /* let's set up some globals... */ int dodebug = 0; int dosyslog = 0; unsigned char reuse_addr = 1; unsigned char linger_opt = 0; int isForceAddr; char * ForceAddr; struct sockaddr_in AddrOut; int timeout = 0; int ftp = 0; int isTransProxy; char * ident = NULL; struct Service_ Service[MAXSERVERS]; int CurrentService; #ifdef USE_TCP_WRAPPERS struct request_info request; int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* USE_TCP_WRAPPERS */ #ifdef NEED_STRRCHR #define strrchr rindex #endif /* NEED_STRRCHR */ /* prototype anything needing it */ //void do_accept(int servsock, struct sockaddr_in *target); struct Service_{ // if it is NULL we know there is no more services in the array. char *sHostName; // later I should add PORTNUMBER - see bugs. int iPort; int ServiceTimeOut; struct sockaddr_in Socket; // int InUse; }; //void do_accept(int servsock, struct Service_ *Service); void do_accept(int servsock); int bindsock(char *addr, int port, int fail); #ifdef NEED_STRDUP char * strdup(char * str) { char * result; if (result = (char *) malloc(strlen(str) + 1)) strcpy(result, str); return result; } #endif /* NEED_STRDUP */ void redir_usage(char *name) { fprintf(stderr,"usage:\n"); fprintf(stderr, "\t%s --lport= --cport= [options]\n", name); fprintf(stderr, "\t%s --inetd --cport=\n", name); fprintf(stderr, "\n\tOptions are:-\n"); fprintf(stderr, "\t\t--lport=\t\tport to listen on\n"); fprintf(stderr, "\t\t--laddr=IP\t\taddress of interface to listen on\n"); fprintf(stderr, "\t\t--cport=\t\tport to connect to\n"); fprintf(stderr, "\t\t--caddr=\t\tremote host to connect to\n"); fprintf(stderr, "\t\t--inetd\t\trun from inetd\n"); fprintf(stderr, "\t\t--debug\t\toutput debugging info\n"); fprintf(stderr, "\t\t--timeout=\tset timeout to n seconds\n"); fprintf(stderr, "\t\t--syslog\tlog messages to syslog\n"); fprintf(stderr, "\t\t--name=\ttag syslog messages with 'str'\n"); #ifdef USE_TCP_WRAPPERS fprintf(stderr, "\t\t \tAlso used as service name for TCP wrappers\n"); #endif /* USE_TCP_WRAPPERS */ fprintf(stderr, "\t\t--bind_addr=IP\tbind() outgoing IP to given addr\n"); fprintf(stderr, "\t\t--ftp\t\tredirect passive ftp connections\n"); fprintf(stderr, "\t\t--transproxy\trun in linux's transparent proxy mode\n"); fprintf(stderr, "\n\tVersion %s - $Id$\n", VERSION); exit(2); } void parse_args(int argc, char * argv[], struct Service_ * Services, // int * target_port, char ** local_addr, int * local_port, int * timeout, int * dodebug, int * inetd, int * dosyslog, char ** bind_addr, int * ftp, int * transproxy) { static struct option long_options[] = { {"lport", required_argument, 0, 'l'}, {"laddr", required_argument, 0, 'a'}, {"cport", required_argument, 0, 'r'}, {"caddr", required_argument, 0, 'c'}, {"bind_addr", required_argument, 0, 'b'}, {"debug", no_argument, 0, 'd'}, {"timeout", required_argument, 0, 't'}, {"inetd", no_argument, 0, 'i'}, {"ident", required_argument, 0, 'n'}, {"name", required_argument, 0, 'n'}, {"syslog", no_argument, 0, 's'}, {"ftp", no_argument, 0, 'f'}, {"transproxy", no_argument, 0, 'p'}, {0,0,0,0} /* End marker */ }; int option_index = 0; int tmp_port ; int nService; extern int optind; int opt; struct servent *portdesc; char *lport = NULL; char *tport = NULL; int currentService ; *local_addr = NULL; // *target_addr = NULL; // *target_port = 0; *local_port = 0; nService =0; while ((opt = getopt_long(argc, argv, "disfpn:t:b:a:l:r:c:", long_options, &option_index)) != -1) { switch (opt) { case 'a': *local_addr = optarg; break; case 'l': lport = optarg; break; case 'r': tport = optarg; break; case 'c': { Services[nService].sHostName = optarg; nService++; break; } case 'b': *bind_addr = optarg; isForceAddr= YES; break; case 'd': (*dodebug)++; break; case 't': *timeout = atol(optarg); break; case 'i': (*inetd)++; break; case 'n': /* This is the ident which is added to syslog messages */ ident = optarg; break; case 's': (*dosyslog)++; break; case 'f': (*ftp)++; break; case 'p': (*transproxy)++; break; default: redir_usage(argv[0]); exit(1); break; } } if(tport == NULL) { redir_usage(argv[0]); exit(1); } tmp_port = 0; portdesc = getservbyname(tport, "tcp"); if ( portdesc != NULL) // succesfull lookup of service name { tmp_port = ntohs(portdesc->s_port); } else { tmp_port=atol(tport); } for(currentService = 0; currentServices_port); else *local_port = atol(lport); } /* if *inetd */ if (!ident) { if ((ident = (char *) strrchr(argv[0], '/'))) { ident++; } else { ident = argv[0]; } } openlog(ident, LOG_PID, LOG_DAEMON); return; } /* with the --ftp option, this one changes passive mode replies from the ftp server to point to a new redirector which we spawn */ void ftp_clean(int send, char *buf, unsigned long *bytes) { char *port_start; int rporthi, lporthi; int lportlo, rportlo; int lport, rport; int remip[4]; int localsock; int socksize = sizeof(struct sockaddr_in); int i; struct sockaddr_in newsession; struct sockaddr_in sockname; /* is this a passive mode return ? */ if(strncmp(buf, "227", 3)) { write(send, buf, (*bytes)); return; } /* get the outside interface so we can listen */ if(getsockname(send, (struct sockaddr *)&sockname, &socksize) != 0) { perror("getsockname"); if (dosyslog) syslog(LOG_ERR, "getsockname failed: %m"); exit(1); } /* parse the old address out of the buffer */ port_start = strchr(buf, '('); sscanf(port_start, "(%d,%d,%d,%d,%d,%d", &remip[0], &remip[1], &remip[2], &remip[3], &rporthi, &rportlo); /* we need to listen on a port for the incoming connection. we'll use this strategy. start at 32768 for hi byte, then sweep using the low byte of our pid and try to bind 5 times. if we can't bind to any of those ports, fail */ lporthi = 0x80; lportlo = getpid() & 0xf0; rport = (rporthi << 8) | rportlo; for(i = 0, localsock = -1; ((localsock == -1) && (i < 5)); i++, lportlo++) { lport = ((lporthi << 8) | lportlo) +1; /* weird off by 1 bug */ localsock = bindsock(inet_ntoa(sockname.sin_addr), lport, 1); } /* check to see if we bound */ if(localsock == -1) { fprintf(stderr, "ftp: unable to bind new listening address\n"); exit(1); } (*bytes) = sprintf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\n", sockname.sin_addr.s_addr & 0xff, (sockname.sin_addr.s_addr >> 8) & 0xff, (sockname.sin_addr.s_addr >> 16) & 0xff, sockname.sin_addr.s_addr >> 24, lporthi, lportlo); newsession.sin_port = htons(rport); newsession.sin_family = AF_INET; newsession.sin_addr.s_addr = remip[0] | (remip[1] << 8) | (remip[2] << 16) | (remip[3] << 24); debug1("ftp server ip: %s\n", inet_ntoa(newsession.sin_addr)); debug1("ftp server port: %d\n", rport); debug1("listening on port %d\n", lport); debug1("listening on addr %s\n", inet_ntoa(sockname.sin_addr)); /* now that we're bound and listening, we can safely send the new passive string without fear of them getting a connection refused. */ write(send, buf, (*bytes)); /* turn off ftp checking while the data connection is active */ ftp = 0; ////////////////////////// /// Warning: the high-avialablility technique is not /// applicable here, so this part of code is commented out /// until further reviews. // do_accept(localsock, &newsession); close(localsock); ftp = 1; return; } void copyloop(int insock, int outsock, int timeout_secs) { fd_set iofds; fd_set c_iofds; int max_fd; /* Maximum numbered fd used */ struct timeval timeout; unsigned long bytes; unsigned long bytes_in = 0; unsigned long bytes_out = 0; unsigned int start_time, end_time; char buf[4096]; /* Record start time */ start_time = (unsigned int) time(NULL); /* Set up timeout */ timeout.tv_sec = timeout_secs; timeout.tv_usec = 0; /* file descriptor bits */ FD_ZERO(&iofds); FD_SET(insock, &iofds); FD_SET(outsock, &iofds); if (insock > outsock) { max_fd = insock; } else { max_fd = outsock; } debug1("Entering copyloop() - timeout is %d\n", timeout_secs); while(1) { (void) memcpy(&c_iofds, &iofds, sizeof(iofds)); if (select(max_fd + 1, &c_iofds, (fd_set *)0, (fd_set *)0, (timeout_secs ? &timeout : NULL)) <= 0) { /* syslog(LLEV,"connection timeout: %d sec",timeout.tv_sec);*/ break; } if(FD_ISSET(insock, &c_iofds)) { if((bytes = read(insock, buf, sizeof(buf))) <= 0) break; if(write(outsock, buf, bytes) != bytes) break; bytes_out += bytes; } if(FD_ISSET(outsock, &c_iofds)) { if((bytes = read(outsock, buf, sizeof(buf))) <= 0) break; /* if we're correcting for PASV on ftp redirections, then fix buf and bytes to have the new address, among other things */ if(ftp) ftp_clean(insock, buf, &bytes); else if(write(insock, buf, bytes) != bytes) break; bytes_in += bytes; } } debug("Leaving main copyloop\n"); /* setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(insock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); setsockopt(outsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(outsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); */ shutdown(insock,0); shutdown(outsock,0); close(insock); close(outsock); debug("copyloop - sockets shutdown and closed\n"); end_time = (unsigned int) time(NULL); debug1("copyloop - connect time: %8d seconds\n", end_time - start_time); debug1("copyloop - transfer in: %8ld bytes\n", bytes_in); debug1("copyloop - transfer out: %8ld bytes\n", bytes_out); if (dosyslog) { syslog(LOG_NOTICE, "disconnect %d secs, %ld in %ld out", (end_time - start_time), bytes_in, bytes_out); } return; } // Socket initialization for connection to real server. int InitializeSocket ( int isTransProxy, int isForceAddr, struct sockaddr_in *AddrOut, struct sockaddr_in *AddrClient ) { int targetsock; // int val; targetsock = socket(AF_INET, SOCK_STREAM, 0); // set non-block mode // val = 0; // ioctl(targetsock,BLOCKING,&val); if (targetsock==FAILED) { if (dodebug) { perror("target: socket"); } if (dosyslog) { syslog(LOG_ERR, "socket failed: %m"); } exit(1); } if(isTransProxy==YES) { memcpy(AddrOut, AddrClient, sizeof(struct sockaddr_in)); AddrOut->sin_port = 0; // To choose port yourself. } if ((isForceAddr==YES) OR (isTransProxy==YES)) { /* this only makes sense if an outgoing IP addr has been forced; at this point, we have a valid targetsock to bind() to.. also, if we're in transparent proxy mode, this option never makes sense */ if (bind(targetsock, (struct sockaddr *) AddrOut, sizeof(struct sockaddr_in)) < 0) { perror("bind_addr: cannot bind to forced outgoing addr"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); _exit(1); } debug1("outgoing IP is %s\n", inet_ntoa(AddrOut->sin_addr)); } return(targetsock); } /* lwait for a connection and move into copyloop... again, passive ftp's will call this, so we don't dupilcate it. */ //do_accept(int servsock, struct sockaddr_in *target) void do_accept(int servsock) { int clisock; int targetsock; struct sockaddr_in client; int clientlen = sizeof(client); struct sockaddr_in * target; int isConnect; int ServiceToTry; int isConnected; int isMoreServices; // struct Service_ *Service // global structure int SonCurrentService; int isVacantSocket; debug("top of accept loop\n"); if ((clisock = accept(servsock, (struct sockaddr *) &client, &clientlen)) < 0) { perror("server: accept"); if (dosyslog) { syslog(LOG_ERR, "accept failed: %m"); } exit(1); } ////////// ///////// setsockopt(targetsock,SOL_SOCKET,SOCKET_KEEPALIVE,&val) debug1("peer IP is %s\n", inet_ntoa(client.sin_addr)); debug1("peer socket is %d\n", client.sin_port); /* CurrentService is global variable which shows number of service to which last connection attempt was made */ /* The algorithm is: Get request Find first service not "in use" (nobody tries to connect to it) Mark the service "in use" Fork to serve the request (telling to grandchild to connect to that service) Grandchild: if connection failed - mark the service as "failed", try next service not "in use" */ // Looking for service not "in use" ServiceToTry=CurrentService; isMoreServices=YES; // to avoid deadlock isVacantSocket=NO; while((isMoreServices==YES) AND (isVacantSocket==NO)) { if (Service[ServiceToTry].sHostName==EMPTYSTRING) { debug("Reached end of service's list\n"); ServiceToTry=0; continue; } if (Service[ServiceToTry].ServiceTimeOut>0) { Service[ServiceToTry].ServiceTimeOut--; /* if (Service[ServiceToTry].ServiceTimeOut==0) { #pragma _CRI guard Service[ServiceToTry].InUse=NO; #pragma _CRI endguard } */ ServiceToTry++; if (ServiceToTry==CurrentService) isMoreServices=NO; continue; } /* if (Service[ServiceToTry].InUse==YES) { ServiceToTry++; // we should not set isMoreServices to NO if it failed - // could be this is the only Service left and it has huge queue // of connections to serve... if (ServiceToTry==CurrentService) { // let's sleep for a second... sleep(1); } continue; } */ // Found vacant socket :-) CurrentService++; // next time try to use another service if (Service[CurrentService].sHostName==EMPTYSTRING) { debug("Reached end of service's list\n"); CurrentService=0; } debug1("Next service will be: %d\n",CurrentService); isVacantSocket=YES; } if (isMoreServices==NO) { if (dosyslog) syslog(LOG_ERR, "No available services!"); debug("No avialable services!\n"); return; } assert(isVacantSocket==YES); /* * Double fork here so we don't have to wait later * This detaches us from our parent so that the parent * does not need to pick up dead kids later. * * This needs to be done before the hosts_access stuff, because * extended hosts_access options expect to be run from a child. */ switch(fork()) { case -1: /* Error */ perror("(server) fork"); if (dosyslog) syslog(LOG_ERR, "(server) fork failed: %m"); _exit(1); case 0: /* Child */ break; default: /* Parent */ { int status; /* Wait for child (who has forked off grandchild) */ (void) wait(&status); /* Close sockets to prevent confusion */ close(clisock); return; } } /* We are now the first child. Fork again and exit */ switch(fork()) { case -1: /* Error */ perror("(child) fork"); if (dosyslog) syslog(LOG_ERR, "(child) fork failed: %m"); _exit(1); case 0: /* Child */ break; default: /* Parent */ _exit(0); } /* We are now the grandchild */ /* Grandchild: if connection to ServiceToTry failed - mark the service as "failed", try next service not "in use" */ #ifdef USE_TCP_WRAPPERS request_init(&request, RQ_DAEMON, ident, RQ_FILE, clisock, 0); sock_host(&request); sock_hostname(&request); sock_hostaddr(&request); if (!hosts_access(&request)) { refuse(&request); _exit(0); } if (dosyslog){ syslog(LOG_INFO, "accepted connect from %s", eval_client(&request)); } #endif /* USE_TCP_WRAPPERS */ targetsock=NOTinitialized; target=NULL; SonCurrentService=ServiceToTry; isMoreServices=YES; // to avoid deadlock in trying the services isConnected=NO; while((isMoreServices==YES) AND (isConnected==NO)) { if (Service[ServiceToTry].sHostName==EMPTYSTRING) { debug("Son: Reached end of service's list\n"); ServiceToTry=0; continue; } if (targetsock==NOTinitialized) { targetsock=InitializeSocket(isTransProxy,isForceAddr,&AddrOut,&client); } if (Service[ServiceToTry].ServiceTimeOut>0) { ServiceToTry++; if (ServiceToTry==SonCurrentService) sleep(1); continue; } // Trying to connect... target=&(Service[ServiceToTry].Socket); // let's mark the socket "in use" isConnect=connect(targetsock, (struct sockaddr *) target, sizeof(struct sockaddr_in)); ////////////////////////////////////// // Keep Alive - setsockopt(targetsock,SOL_SOCKET,SOCKET_KEEPALIVE,&val) if (isConnect==FAILED) { // we need to rebuild socket close(targetsock); targetsock=NOTinitialized; // perror("target: connect"); if (dosyslog) syslog(LOG_ERR, "target: connect failed: %m"); debug1("Service %d failed\n",ServiceToTry); Service[ServiceToTry].ServiceTimeOut=ATTEMPTS; ServiceToTry++; if (ServiceToTry==SonCurrentService) { // We couldn't just terminate the thread - we have // client on hold... :-( // Let's sleep and after that will see what we could do. sleep(1); } continue; } // Connection established. isConnected=YES; } debug1("connected to %s\n", inet_ntoa(target->sin_addr)); /* thanks to Anders Vannman for the fix to make proper syslogging happen here... */ if (dosyslog) { char tmp1[20], tmp2[20]; strcpy(tmp1, inet_ntoa(client.sin_addr)); strcpy(tmp2, inet_ntoa(target->sin_addr)); syslog(LOG_NOTICE, "connecting %s/%d to %s/%d", tmp1, client.sin_port, tmp2, target->sin_port); } copyloop(clisock, targetsock, timeout); exit(0); /* Exit after copy */ } /* bind to a new socket, we do this out here because passive-fixups are going to call it too, and there's no sense dupliciting the code. */ /* fail is true if we should just return a -1 on error, false if we should bail. */ int bindsock(char *addr, int port, int fail) { int servsock; struct sockaddr_in server; /* * Get a socket to work with. This socket will * be in the Internet domain, and will be a * stream socket. */ if ((servsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { if(fail) { return -1; } else { perror("server: socket"); if (dosyslog) syslog(LOG_ERR, "socket failed: %m"); exit(1); } } server.sin_family = AF_INET; server.sin_port = htons(port); if (addr != NULL) { struct hostent *hp; debug1("listening on %s\n", addr); if ((hp = gethostbyname(addr)) == NULL) { fprintf(stderr, "%s: cannot resolve hostname.\n", addr); exit(1); } memcpy(&server.sin_addr, hp->h_addr, hp->h_length); } else { debug("local IP is default\n"); server.sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); } setsockopt(servsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(servsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); /* * Try to bind the address to the socket. */ if (bind(servsock, (struct sockaddr *) &server, sizeof(server)) < 0) { if(fail) { close(servsock); return -1; } else { perror("server: bind"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); exit(1); } } /* * Listen on the socket. */ if (listen(servsock, 10000) < 0) { if(fail) { close(servsock); return -1; } else { perror("server: listen"); if (dosyslog) syslog(LOG_ERR, "listen failed: %m"); exit(1); } } return servsock; } void BuildSocket(struct sockaddr_in *Socket,char *sHostName ,int iPort ) { char* target_ip ; Socket->sin_family = AF_INET; Socket->sin_port = htons(iPort); if (sHostName != NULL) { struct hostent *hp; debug1("target is %s\n", sHostName); if ((hp = gethostbyname(sHostName)) == NULL) { fprintf(stderr, "%s: host unknown.\n", sHostName); exit(1); } memcpy(&Socket->sin_addr, hp->h_addr, hp->h_length); } else { debug("target is localhost\n"); Socket->sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); } if (dodebug) { target_ip = NULL; target_ip = strdup(inet_ntoa(Socket->sin_addr)); if(target_ip) { debug1("target IP address is %s\n", target_ip); debug1("target port is %d\n", iPort); } } } void BuildServersSockets(struct Service_* Service){ // Seting up Service's sockets int nService=0; while(Service[nService].sHostName != EMPTYSTRING){ BuildSocket(&(Service[nService].Socket),Service[nService].sHostName,Service[nService].iPort); nService++; } } void InitServices(struct Service_* Service) { int nService; if(Service) { for(nService=0; nServiceh_addr, hp->h_length); ip_to_target = strdup(inet_ntoa(AddrOut.sin_addr)); debug1("IP address for target is %s\n", ip_to_target); } if (inetd) { ////////////////////// /// Warning!!! /// INETD part not affected by the patch. /// It probably doesn't work at all!!!! ////////////////////// int targetsock; struct sockaddr_in client; int client_size = sizeof(client); exit(1); // to prevent catastrophes #ifdef USE_TCP_WRAPPERS request_init(&request, RQ_DAEMON, ident, RQ_FILE, 0, 0); sock_host(&request); sock_hostname(&request); sock_hostaddr(&request); if (!hosts_access(&request)) refuse(&request); #endif /* USE_TCP_WRAPPERS */ if (!getpeername(0, (struct sockaddr *) &client, &client_size)) { debug1("peer IP is %s\n", inet_ntoa(client.sin_addr)); debug1("peer socket is %d\n", client.sin_port); } if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("target: socket"); if (dosyslog) syslog(LOG_ERR, "targetsock failed: %m"); exit(1); } if(isTransProxy) { memcpy(&AddrOut, &client, sizeof(struct sockaddr_in)); AddrOut.sin_port = 0; } if ((isForceAddr==YES) OR (isTransProxy==YES)) { /* this only makes sense if an outgoing IP addr has been forced; * at this point, we have a valid targetsock to bind() to.. */ if (bind(targetsock, (struct sockaddr *) &AddrOut, sizeof(AddrOut)) < 0) { perror("bind_addr: cannot bind to forcerd outgoing addr"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); exit(1); } debug1("outgoing IP is %s\n", inet_ntoa(AddrOut.sin_addr)); } /////////////////// /// Warning!!! /// The following code is completely commented out /// because the whole INETD part was not patched. /* if (connect(targetsock, (struct sockaddr *) &target, sizeof(target)) < 0) { perror("target: connect"); if (dosyslog) syslog(LOG_ERR, "connect failed: %m"); exit(1); } if (dosyslog) { syslog(LOG_NOTICE, "connecting %s/%d to %s/%d", inet_ntoa(client.sin_addr), client.sin_port, target_ip, target.sin_port); } */ /* Just start copying - one side of the loop is stdin - 0 */ copyloop(0, targetsock, timeout); /* End of inetd invocation */ } else { /* if it is standalone daemon - continue here */ /* Let's listen for incoming connections */ int servsock; if(local_addr) servsock = bindsock(local_addr, local_port, 0); else servsock = bindsock(NULL, local_port, 0); /* * Accept connections. When we accept one, ns * will be connected to the client. client will * contain the address of the client. */ CurrentService=0; // global variable while (1) { // do_accept(servsock, &target); do_accept(servsock); // Service is global structure } } /* this should really never be reached */ exit(0); } redir-2.2.1/contrib/redir-wrapper100644 0 0 4553 7025706531 15225 0ustar sammyroot#!/bin/bash ##### v1.0.0 ##### written by Daniel Roesen ##### Configuration (change as needed) ###################### CONFIG=/usr/local/etc/redir-wrapper.conf # our configuration file REDIR=redir # redir executable name REDIR_PATH=/usr/local/sbin # path where redir resides PING=ping ##### End of user configuration ############################# MYNAME=`basename $0` # name of this script LOGCRITIC="logger -s -p daemon.crit -t ${MYNAME}[$$]" LOGERROR="logger -s -p daemon.err -t ${MYNAME}[$$]" LOGNOTICE="logger -s -p daemon.notice -t ${MYNAME}[$$]" OPTTEMP=/tmp/${MYNAME}-options # name of the temporary option file IFS=${IFS}: # append ":" to the IFS RET=0 # return-code ############################################################# killall -q redir # shutdown any running redirectors rm -f $OPTTEMP # remove temporary file ### ### reading the config file ### if [ ! -f $CONFIG ]; then $LOGCRITIC "$CONFIG not found, no redirectors started!" exit 1 fi ### ### get default options ### grep "^option " $CONFIG | while read JUNK OPTNAME OPTPARAM do case $OPTNAME in debug) echo -n " --debug" >>$OPTTEMP ;; timeout) echo -n " --timeout="$OPTPARAM >>$OPTTEMP ;; syslog) echo -n " --syslog" >>$OPTTEMP ;; identity) echo -n " --bind_addr=$OPTPARAM" >>$OPTTEMP ;; ftp) echo -n " --ftp" >>$OPTTEMP ;; transproxy) echo -n " --transproxy" >>$OPTTEMP ;; *) $LOGERROR "unknown option \"$OPTNAME\", ignored." RET=1 ;; esac done ### ### start redirectors ### grep "^redir" $CONFIG | while read JUNK NAME FROMADDR FROMPORT TOADDR TOPORT OPTIONS do # build argument list REDIR_ARGS="--laddr=${FROMADDR} --lport=${FROMPORT}" REDIR_ARGS=$REDIR_ARGS" --caddr=${TOADDR} --cport=${TOPORT}" REDIR_ARGS=$REDIR_ARGS"`cat ${OPTTEMP}` ${OPTIONS}" REDIR_ARGS=$REDIR_ARGS" --name=${REDIR}-${NAME}" # start the redirector $LOGNOTICE "starting redirector \"${NAME}\"..." $REDIR_PATH/$REDIR $REDIR_ARGS & # MYPID=$! # # ps $! >/dev/null # # if [ $? = "0" ] # then # $LOGNOTICE "...succeeeded!" # else # $LOGCRITIC "redirector \"${NAME}\" failed!" # RET=1 # fi done ### remove temporary files rm $OPTTEMP # remove temporary option file exit $RET redir-2.2.1/contrib/README.redir-ha100644 0 0 1603 7025706475 15071 0ustar sammyroot redir-ha.c is a high-availability version of redir 2.1 sent to me by Peter Skliarouk (skliaroukp@geocities.com). The most fundamental difference is that the --caddr option will now accept multiple hosts, so that load can be balanced, or that one faulted server can be avoided. This has not been integrated into the redir 2.2 source for a couple of reasons. First, I think that the level of complexity which is involved in this version of the source is quite difficult to maintain. Also, the redir 2.2 source was significantly different from 2.1 that it would have had to be patched in manually. :) But, for those of you who would like this functionality (and, I agree, it's a pretty slick feature), I felt I should finally get the code released. To use: Replace redir.c in the top-level source directory with redir-ha.c. Compile. This will produce a redir 2.1 binary with the HA mods. redir-2.2.1/contrib/redir-wrapper.conf100644 0 0 1625 7025706531 16146 0ustar sammyroot# # redir-wrapper Configuration file # # general options # --------------- # Syntax: option [] # # available options: # debug turn redir debugging on (--debug) # timeout set timeout to seconds (--timeout) # syslog log messages to syslog (--syslog) # ftp redirect passive ftp connections (--ftp) # transproxy run in transparent proxy mode (--transproxy) # identity set specific outgoing interface (--bind_addr) # is either a hostname or an IP address # option syslog option identity charon # # redirector definitions # ---------------------- # # Syntax: redir : : [] # # defines a symbolic name for this redirector used in logging # and are either a hostname or an IP address # redir nntp www:119 netgate:119 redir pop3 ftp:110 netgate:110 redir-2.2.1/contrib/README.redir-wrapper100644 0 0 425 7025706725 16140 0ustar sammyrootredir-wrapper is a shell script sent to be by Daniel Roesen which simplifies the task of starting multiple redir instances using a configuration file in the form of redir-wrapper.conf. Consult the Configuration section in redir-wrapper to use. redir-2.2.1/CHANGES100644 0 0 5760 7031474215 12051 0ustar sammyroot----- Changes for 2.2.1 ----- 2.2.1 fixes a bug in do_accept() where non-fatal error codes returned by accept() would cause redir to terminate entirely. I had recieved reports of this behavior but was unable to find it until sammy.net had to handle the load of the redir 2.2 update using redir. :) All non-fatal error codes might not be covered. But it "got better". 2.2.1 integrates a patch by Emmanuel Chantréau which provides traffic shaping functionality to redir. Interesting stuff. I've not tested this in detail personally. 2.2.1 adds the ability to compile redir with lesser functionality for speed. This is documented in the README. ----- Changes for 2.2 ----- 2.2 adds support for redirecting PORT mode ftp connections in addition to PASV mode ftp redirection. Thus --ftp is now split into --ftp={port,pasv,both} to determine what should be redirected. The original version of this patch was submitted by Harald Holzer . 2.2 adds the --connect option, which is useful if you're bouncing your connections through an HTTP proxy server. Use as --connect=host:port, and this will be the CONNECT line sent to the proxy. ----- Changes for 2.1 ----- 2.1 is just a bugfix release, fixing a problem with ftp redirection, and adds/fixes various logging messages. Also a fix for some of the TCP wrappers code. ----- Changes for 2.0 ----- 2.0 has changed the command line syntax! You're going to have to change how you call redir in order to upgrade, but not by all that much. We now use --options for everything, instead of having the rather wonky "if you've got this thing here, something happens" method used before. We apologize for the inconvenience, but this is really a lot less brain damaged. 2.0 now includes support for using TCP wrappers, thanks to a patch submitted by Damien Miller . The --name option now sets the TCP wrapper service name as well as the syslog program name, making it possible to run multiple instances of redir with different access controls. Edit the Makefile to enable TCP wrappers. 2.0 now actually implements --transproxy when running from inetd. 2.0 has cleaned up the --ftp support, at least a little. There are probably still improvements to be made here, but, alas. ----- Changes for 1.2 ----- 1.2 now should compile and run correctly on systems which lack getopt_long. 1.2 adds the option --transproxy, which, when run as super-user on a linux system which has had transparent proxying compiled into it's kernel, will make connections seem as if they had come from their true origin. see transproxy.txt for further discussion of this option. ----- Changes for 1.1 ----- 1.1 adds the option --ftp, which, when redirecting a port to an FTP server, will, when the server wants to initiate a passive connection, redirect another port for that connection. ----- Changes for 1.0 ----- 1.0 adds the option --bind-addr, which can force it to bind to a specific address or interface when making outgoing connections.