├── .gitignore ├── CHANGES ├── Makefile ├── README.md ├── getopt.c ├── getopt.h ├── match.c ├── match.h ├── rinetd.8 ├── rinetd.c ├── rinetd.dsp ├── rinetd.dsw ├── rinetd.html ├── rinetd.ncb ├── rinetd.opt └── rinetd.plg /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.exe 3 | rinetd 4 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | Version 0.1: original version. 2 | 3 | Version 0.2: fixed bug when several reads are necessary 4 | on one end or the other before a write flushes them. 5 | Fixed bug which threw away data not yet sent to the 6 | other side on close, when running under Linux. Fixed 7 | associated bugs that probably affected other operating 8 | systems as well. Fixed bug causing long, perhaps 9 | indefinite pauses when a possible connection to a 10 | server socket went away before the accept() call, 11 | resulting in a blocking call. 12 | 13 | Version 0.3: fixed additional bugs relating to 14 | the code previously used only by non-Linux OSes. 15 | This should fix problems such as connections not 16 | going away when they should or connections being 17 | mysteriously closed. Most of that code is now used by 18 | Linux also, so it is likely that rinetd is much closer 19 | to bug-free on non-Linux platforms. Of course, I don't 20 | actually have any to play with it on. 21 | 22 | Version 0.4: added support for kill -1 (SIGHUP) 23 | and specification of service names instead of 24 | port numbers. Removed calls to realloc(), replacing 25 | them with code that should fail gracefully without 26 | crashing the program or breaking existing connections 27 | when another application is hogging memory. 28 | 29 | Version 0.5: added logging in both tab-delimited 30 | and web-server-style formats. No longer exits if 31 | an individual configuration file line generates 32 | an error. Added allow and deny rules. Added 33 | -c command line option to specify a configuration file. 34 | 35 | Version 0.51: fixed failure to check for an open 36 | log file before writing log entries. 37 | 38 | Version 0.52: documentation added regarding the 39 | ability to bind to all IP addresses, if desired, 40 | using the special address 0.0.0.0. 41 | 42 | Version 0.6: ported to Win32. Various compatibility 43 | fixes were made and some minor oversights without 44 | functional consequences were corrected. 45 | 46 | Version 0.61: fixed a bug in 0.6 which completely 47 | broke rinetd under Linux. Oops. 48 | 49 | Version 0.62: fixed a potential buffer overrun; 50 | prior versions failed to reallocate one of the 51 | arrays correctly when reallocating memory to 52 | accommodate more connections. Thanks to 53 | Sam Hocevar. 54 | 55 | 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-DLINUX -g 2 | 3 | 4 | rinetd: rinetd.o match.o 5 | $(CC) rinetd.o match.o -o rinetd 6 | 7 | install: rinetd 8 | install -m 700 rinetd /usr/sbin 9 | install -m 644 rinetd.8 /usr/man/man8 10 | clean: 11 | rm -rf *.o 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OBSOLETE! THIS REPO IS ARCHIVED, USE [Sam Hocevar's lovely and well-maintained version](https://github.com/samhocevar/rinetd). Thanks! 2 | 3 |

Description

4 |

5 | Redirects TCP connections from one IP address and port to another. rinetd 6 | is a single-process server which handles any number of connections to 7 | the address/port pairs specified in the file /etc/rinetd.conf. 8 | Since rinetd runs as a single process using nonblocking I/O, it is 9 | able to redirect a large number of connections without a severe 10 | impact on the machine. This makes it practical to run TCP services 11 | on machines inside an IP masquerading firewall. rinetd does not 12 | redirect FTP, because FTP requires more than one socket. 13 |

14 |

15 | rinetd is typically launched at boot time, using the following syntax: 16 |

17 |

18 | /usr/sbin/rinetd 19 |

20 |

21 | The configuration file is found in the file 22 | /etc/rinetd.conf, unless 23 | another file is specified using the -c command line option. 24 |

25 |

Forwarding Rules

26 |

27 | Most entries in the configuration file are forwarding rules. The 28 | format of a forwarding rule is as follows: 29 |

30 |
 31 | bindaddress bindport connectaddress connectport
 32 | 
33 | For example: 34 |
 35 | 206.125.69.81 80 10.1.1.2 80
 36 | 
37 |

38 | Would redirect all connections to port 80 of the "real" IP address 39 | 206.125.69.81, which could be a virtual interface, through 40 | rinetd to port 80 of the address 10.1.1.2, which would typically 41 | be a machine on the inside of a firewall which has no 42 | direct routing to the outside world. 43 |

44 |

45 | Although responding on individual interfaces rather than on all 46 | interfaces is one of rinetd's primary features, sometimes it is 47 | preferable to respond on all IP addresses that belong to the server. 48 | In this situation, the special IP address 0.0.0.0 49 | can be used. For example: 50 |

51 |
 52 | 0.0.0.0 23 10.1.1.2 23
 53 | 
54 |

55 | Would redirect all connections to port 23, for all IP addresses 56 | assigned to the server. This is the default behavior for most 57 | other programs. 58 |

59 |

60 | Service names can be specified instead of port numbers. On most systems, 61 | service names are defined in the file /etc/services. 62 |

63 |

64 | Both IP addresses and hostnames are accepted for 65 | bindaddress and connectaddress. 66 |

67 |

Allow and Deny Rules

68 |

69 | Configuration files can also contain allow and deny rules. 70 |

71 |

72 | Allow rules which appear before the first forwarding rule are 73 | applied globally: if at least one global allow rule exists, 74 | and the address of a new connection does not 75 | satisfy at least one of the global allow rules, that connection 76 | is immediately rejected, regardless of any other rules. 77 |

78 |

79 | Allow rules which appear after a specific forwarding rule apply 80 | to that forwarding rule only. If at least one allow rule 81 | exists for a particular forwarding rule, and the address of a new 82 | connection does not satisfy at least one of the allow rules 83 | for that forwarding rule, that connection is immediately 84 | rejected, regardless of any other rules. 85 |

86 |

87 | Deny rules which appear before the first forwarding rule are 88 | applied globally: if the address of a new connection satisfies 89 | any of the global allow rules, that connection 90 | is immediately rejected, regardless of any other rules. 91 |

92 |

93 | Deny rules which appear after a specific forwarding rule apply 94 | to that forwarding rule only. If the address of a new 95 | connection satisfies any of the deny rules for that forwarding rule, 96 | that connection is immediately rejected, regardless of any other rules. 97 |

98 |

99 | The format of an allow rule is as follows: 100 |

101 |
102 | allow pattern
103 | 
104 |

105 | Patterns can contain the following characters: 0, 1, 2, 3, 4, 5, 106 | 6, 7, 8, 9, . (period), ?, and *. The ? wildcard matches any one 107 | character. The * wildcard matches any number of characters, including 108 | zero. 109 |

110 |

111 | For example: 112 |

113 |
114 | allow 206.125.69.*
115 | 
116 |

117 | This allow rule matches all IP addresses in the 206.125.69 class C domain. 118 |

119 |

120 | Host names are NOT permitted in allow and deny rules. The performance 121 | cost of looking up IP addresses to find their corresponding names 122 | is prohibitive. Since rinetd is a single process server, all other 123 | connections would be forced to pause during the address lookup. 124 |

125 |

Logging

126 |

127 | rinetd is able to produce a log file in either of two formats: 128 | tab-delimited and web server-style "common log format." 129 |

130 |

131 | By default, rinetd does not produce a log file. To activate logging, add 132 | the following line to the configuration file: 133 |

134 |
135 | logfile log-file-location
136 | 
137 |

138 | Example: 139 |

140 |
141 | logfile /var/log/rinetd.log
142 | 
143 |

144 | By default, rinetd logs in a simple tab-delimited format containing 145 | the following information: 146 |

147 |

148 | Date and time
149 | Client address
150 | 151 | Listening host 152 |
153 | Listening port 154 |
155 | Forwarded-to host 156 |
157 | Forwarded-to port 158 |
159 | Bytes received from client 160 |
161 | Bytes sent to client 162 |
163 | Result message 164 |

165 |

166 | To activate web server-style "common log format" logging, 167 | add the following line to the configuration file: 168 |

169 |
170 | logcommon
171 | 
172 |

Command line options

173 |

174 | The -c command line option is used to specify an alternate 175 | configuration file. 176 |

177 |

178 | The -h command line option produces a short help message. 179 |

180 |

181 | The -v command line option displays the version number. 182 |

183 |

Reinitializing rinetd

184 |

185 | The kill -1 signal (SIGHUP) can be used to cause rinetd 186 | to reload its configuration file without interrupting existing 187 | connections. Under Linux(tm) the process id 188 | is saved in the file /var/run/rinetd.pid 189 | to facilitate the kill -HUP. An alternate 190 | file name can be provided by using the pidlogfile 191 | configuration file option. 192 |

193 |

Bugs

194 |

195 | The server redirected to is not able to identify the host the 196 | client really came from. This cannot be corrected; however, 197 | the log produced by rinetd provides a way to obtain this 198 | information. Under Unix, sockets would theoretically lose data when closed 199 | with SO_LINGER turned off, but in Linux this is not the case 200 | (kernel source comments support this belief on my part). On non-Linux Unix 201 | platforms, alternate code which uses a different trick to work around 202 | blocking close() is provided, but this code is untested. 203 |

204 |

205 | The logging is inadequate. The duration of the connection should be logged. 206 |

207 |

License

208 |

209 | Copyright (c) 1997-2019 Thomas Boutell. 210 | This software is released for free use under the terms of 211 | the GNU General Public License, version 2 or higher. 212 |

213 |

Thanks

214 |

215 | Thanks are due to Bill Davidsen, Libor Pechachek, Sascha Ziemann, 216 | Joel S. Noble, the Apache Group, and many others who have contributed 217 | advice, encouragement and/or source code to this and other open 218 | software projects. 219 |

220 | -------------------------------------------------------------------------------- /getopt.c: -------------------------------------------------------------------------------- 1 | /* THIS IS HERE FOR WIN32's BENEFIT ONLY. */ 2 | 3 | /* Getopt for GNU. 4 | NOTE: getopt is now part of the C library, so if you don't know what 5 | "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu 6 | before changing it! 7 | 8 | Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 9 | Free Software Foundation, Inc. 10 | 11 | This program is free software; you can redistribute it and/or modify it 12 | under the terms of the GNU General Public License as published by the 13 | Free Software Foundation; either version 2, or (at your option) any 14 | later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with this program; if not, write to the Free Software 23 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include "config.h" 27 | #endif 28 | 29 | #ifndef __STDC__ 30 | # ifndef const 31 | # define const 32 | # endif 33 | #endif 34 | 35 | /* This tells Alpha OSF/1 not to define a getopt prototype in . */ 36 | #ifndef _NO_PROTO 37 | #define _NO_PROTO 38 | #endif 39 | 40 | #include 41 | 42 | /* Comment out all this code if we are using the GNU C Library, and are not 43 | actually compiling the library itself. This code is part of the GNU C 44 | Library, but also included in many other GNU distributions. Compiling 45 | and linking in this code is a waste when using the GNU C library 46 | (especially if it is a shared library). Rather than having every GNU 47 | program understand `configure --with-gnu-libc' and omit the object files, 48 | it is simpler to just do this in the source for each such file. */ 49 | 50 | #if defined (_LIBC) || !defined (__GNU_LIBRARY__) 51 | 52 | 53 | /* This needs to come after some library #include 54 | to get __GNU_LIBRARY__ defined. */ 55 | #ifdef __GNU_LIBRARY__ 56 | /* Don't include stdlib.h for non-GNU C libraries because some of them 57 | contain conflicting prototypes for getopt. */ 58 | #include 59 | #endif /* GNU C library. */ 60 | 61 | /* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a 62 | long-named option. Because this is not POSIX.2 compliant, it is 63 | being phased out. */ 64 | /* #define GETOPT_COMPAT */ 65 | 66 | /* This version of `getopt' appears to the caller like standard Unix `getopt' 67 | but it behaves differently for the user, since it allows the user 68 | to intersperse the options with the other arguments. 69 | 70 | As `getopt' works, it permutes the elements of ARGV so that, 71 | when it is done, all the options precede everything else. Thus 72 | all application programs are extended to handle flexible argument order. 73 | 74 | Setting the environment variable POSIXLY_CORRECT disables permutation. 75 | Then the behavior is completely standard. 76 | 77 | GNU application programs can use a third alternative mode in which 78 | they can distinguish the relative order of options and other arguments. */ 79 | 80 | #include "getopt.h" 81 | 82 | /* For communication from `getopt' to the caller. 83 | When `getopt' finds an option that takes an argument, 84 | the argument value is returned here. 85 | Also, when `ordering' is RETURN_IN_ORDER, 86 | each non-option ARGV-element is returned here. */ 87 | 88 | char *optarg = 0; 89 | 90 | /* Index in ARGV of the next element to be scanned. 91 | This is used for communication to and from the caller 92 | and for communication between successive calls to `getopt'. 93 | 94 | On entry to `getopt', zero means this is the first call; initialize. 95 | 96 | When `getopt' returns EOF, this is the index of the first of the 97 | non-option elements that the caller should itself scan. 98 | 99 | Otherwise, `optind' communicates from one call to the next 100 | how much of ARGV has been scanned so far. */ 101 | 102 | /* XXX 1003.2 says this must be 1 before any call. */ 103 | int optind = 0; 104 | 105 | /* The next char to be scanned in the option-element 106 | in which the last option character we returned was found. 107 | This allows us to pick up the scan where we left off. 108 | 109 | If this is zero, or a null string, it means resume the scan 110 | by advancing to the next ARGV-element. */ 111 | 112 | static char *nextchar; 113 | 114 | /* Callers store zero here to inhibit the error message 115 | for unrecognized options. */ 116 | 117 | int opterr = 1; 118 | 119 | /* Set to an option character which was unrecognized. 120 | This must be initialized on some systems to avoid linking in the 121 | system's own getopt implementation. */ 122 | 123 | #define BAD_OPTION '\0' 124 | int optopt = BAD_OPTION; 125 | 126 | /* Describe how to deal with options that follow non-option ARGV-elements. 127 | 128 | If the caller did not specify anything, 129 | the default is REQUIRE_ORDER if the environment variable 130 | POSIXLY_CORRECT is defined, PERMUTE otherwise. 131 | 132 | REQUIRE_ORDER means don't recognize them as options; 133 | stop option processing when the first non-option is seen. 134 | This is what Unix does. 135 | This mode of operation is selected by either setting the environment 136 | variable POSIXLY_CORRECT, or using `+' as the first character 137 | of the list of option characters. 138 | 139 | PERMUTE is the default. We permute the contents of ARGV as we scan, 140 | so that eventually all the non-options are at the end. This allows options 141 | to be given in any order, even with programs that were not written to 142 | expect this. 143 | 144 | RETURN_IN_ORDER is an option available to programs that were written 145 | to expect options and other ARGV-elements in any order and that care about 146 | the ordering of the two. We describe each non-option ARGV-element 147 | as if it were the argument of an option with character code 1. 148 | Using `-' as the first character of the list of option characters 149 | selects this mode of operation. 150 | 151 | The special argument `--' forces an end of option-scanning regardless 152 | of the value of `ordering'. In the case of RETURN_IN_ORDER, only 153 | `--' can cause `getopt' to return EOF with `optind' != ARGC. */ 154 | 155 | static enum 156 | { 157 | REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER 158 | } ordering; 159 | 160 | #ifdef __GNU_LIBRARY__ 161 | /* We want to avoid inclusion of string.h with non-GNU libraries 162 | because there are many ways it can cause trouble. 163 | On some systems, it contains special magic macros that don't work 164 | in GCC. */ 165 | #include 166 | #define my_index strchr 167 | #define my_strlen strlen 168 | #else 169 | 170 | /* Avoid depending on library functions or files 171 | whose names are inconsistent. */ 172 | 173 | #if __STDC__ || defined(PROTO) 174 | extern char *getenv(const char *name); 175 | extern int strcmp (const char *s1, const char *s2); 176 | extern int strncmp(const char *s1, const char *s2, int n); 177 | 178 | static int my_strlen(const char *s); 179 | static char *my_index (const char *str, int chr); 180 | #else 181 | extern char *getenv (); 182 | #endif 183 | 184 | static int 185 | my_strlen (str) 186 | const char *str; 187 | { 188 | int n = 0; 189 | while (*str++) 190 | n++; 191 | return n; 192 | } 193 | 194 | static char * 195 | my_index (str, chr) 196 | const char *str; 197 | int chr; 198 | { 199 | while (*str) 200 | { 201 | if (*str == chr) 202 | return (char *) str; 203 | str++; 204 | } 205 | return 0; 206 | } 207 | 208 | #endif /* GNU C library. */ 209 | 210 | /* Handle permutation of arguments. */ 211 | 212 | /* Describe the part of ARGV that contains non-options that have 213 | been skipped. `first_nonopt' is the index in ARGV of the first of them; 214 | `last_nonopt' is the index after the last of them. */ 215 | 216 | static int first_nonopt; 217 | static int last_nonopt; 218 | 219 | /* Exchange two adjacent subsequences of ARGV. 220 | One subsequence is elements [first_nonopt,last_nonopt) 221 | which contains all the non-options that have been skipped so far. 222 | The other is elements [last_nonopt,optind), which contains all 223 | the options processed since those non-options were skipped. 224 | 225 | `first_nonopt' and `last_nonopt' are relocated so that they describe 226 | the new indices of the non-options in ARGV after they are moved. 227 | 228 | To perform the swap, we first reverse the order of all elements. So 229 | all options now come before all non options, but they are in the 230 | wrong order. So we put back the options and non options in original 231 | order by reversing them again. For example: 232 | original input: a b c -x -y 233 | reverse all: -y -x c b a 234 | reverse options: -x -y c b a 235 | reverse non options: -x -y a b c 236 | */ 237 | 238 | #if __STDC__ || defined(PROTO) 239 | static void exchange (char **argv); 240 | #endif 241 | 242 | static void 243 | exchange (argv) 244 | char **argv; 245 | { 246 | char *temp, **first, **last; 247 | 248 | /* Reverse all the elements [first_nonopt, optind) */ 249 | first = &argv[first_nonopt]; 250 | last = &argv[optind-1]; 251 | while (first < last) { 252 | temp = *first; *first = *last; *last = temp; first++; last--; 253 | } 254 | /* Put back the options in order */ 255 | first = &argv[first_nonopt]; 256 | first_nonopt += (optind - last_nonopt); 257 | last = &argv[first_nonopt - 1]; 258 | while (first < last) { 259 | temp = *first; *first = *last; *last = temp; first++; last--; 260 | } 261 | 262 | /* Put back the non options in order */ 263 | first = &argv[first_nonopt]; 264 | last_nonopt = optind; 265 | last = &argv[last_nonopt-1]; 266 | while (first < last) { 267 | temp = *first; *first = *last; *last = temp; first++; last--; 268 | } 269 | } 270 | 271 | /* Scan elements of ARGV (whose length is ARGC) for option characters 272 | given in OPTSTRING. 273 | 274 | If an element of ARGV starts with '-', and is not exactly "-" or "--", 275 | then it is an option element. The characters of this element 276 | (aside from the initial '-') are option characters. If `getopt' 277 | is called repeatedly, it returns successively each of the option characters 278 | from each of the option elements. 279 | 280 | If `getopt' finds another option character, it returns that character, 281 | updating `optind' and `nextchar' so that the next call to `getopt' can 282 | resume the scan with the following option character or ARGV-element. 283 | 284 | If there are no more option characters, `getopt' returns `EOF'. 285 | Then `optind' is the index in ARGV of the first ARGV-element 286 | that is not an option. (The ARGV-elements have been permuted 287 | so that those that are not options now come last.) 288 | 289 | OPTSTRING is a string containing the legitimate option characters. 290 | If an option character is seen that is not listed in OPTSTRING, 291 | return BAD_OPTION after printing an error message. If you set `opterr' to 292 | zero, the error message is suppressed but we still return BAD_OPTION. 293 | 294 | If a char in OPTSTRING is followed by a colon, that means it wants an arg, 295 | so the following text in the same ARGV-element, or the text of the following 296 | ARGV-element, is returned in `optarg'. Two colons mean an option that 297 | wants an optional arg; if there is text in the current ARGV-element, 298 | it is returned in `optarg', otherwise `optarg' is set to zero. 299 | 300 | If OPTSTRING starts with `-' or `+', it requests different methods of 301 | handling the non-option ARGV-elements. 302 | See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. 303 | 304 | Long-named options begin with `--' instead of `-'. 305 | Their names may be abbreviated as long as the abbreviation is unique 306 | or is an exact match for some defined option. If they have an 307 | argument, it follows the option name in the same ARGV-element, separated 308 | from the option name by a `=', or else the in next ARGV-element. 309 | When `getopt' finds a long-named option, it returns 0 if that option's 310 | `flag' field is nonzero, the value of the option's `val' field 311 | if the `flag' field is zero. 312 | 313 | The elements of ARGV aren't really const, because we permute them. 314 | But we pretend they're const in the prototype to be compatible 315 | with other systems. 316 | 317 | LONGOPTS is a vector of `struct option' terminated by an 318 | element containing a name which is zero. 319 | 320 | LONGIND returns the index in LONGOPT of the long-named option found. 321 | It is only valid when a long-named option has been found by the most 322 | recent call. 323 | 324 | If LONG_ONLY is nonzero, '-' as well as '--' can introduce 325 | long-named options. */ 326 | 327 | int 328 | _getopt_internal (argc, argv, optstring, longopts, longind, long_only) 329 | int argc; 330 | char *const *argv; 331 | const char *optstring; 332 | const struct option *longopts; 333 | int *longind; 334 | int long_only; 335 | { 336 | int option_index; 337 | 338 | optarg = 0; 339 | 340 | /* Initialize the internal data when the first call is made. 341 | Start processing options with ARGV-element 1 (since ARGV-element 0 342 | is the program name); the sequence of previously skipped 343 | non-option ARGV-elements is empty. */ 344 | 345 | if (optind == 0) 346 | { 347 | first_nonopt = last_nonopt = optind = 1; 348 | 349 | nextchar = NULL; 350 | 351 | /* Determine how to handle the ordering of options and nonoptions. */ 352 | 353 | if (optstring[0] == '-') 354 | { 355 | ordering = RETURN_IN_ORDER; 356 | ++optstring; 357 | } 358 | else if (optstring[0] == '+') 359 | { 360 | ordering = REQUIRE_ORDER; 361 | ++optstring; 362 | } 363 | else if (getenv ("POSIXLY_CORRECT") != NULL) 364 | ordering = REQUIRE_ORDER; 365 | else 366 | ordering = PERMUTE; 367 | } 368 | 369 | if (nextchar == NULL || *nextchar == '\0') 370 | { 371 | if (ordering == PERMUTE) 372 | { 373 | /* If we have just processed some options following some non-options, 374 | exchange them so that the options come first. */ 375 | 376 | if (first_nonopt != last_nonopt && last_nonopt != optind) 377 | exchange ((char **) argv); 378 | else if (last_nonopt != optind) 379 | first_nonopt = optind; 380 | 381 | /* Now skip any additional non-options 382 | and extend the range of non-options previously skipped. */ 383 | 384 | while (optind < argc 385 | && (argv[optind][0] != '-' || argv[optind][1] == '\0') 386 | #ifdef GETOPT_COMPAT 387 | && (longopts == NULL 388 | || argv[optind][0] != '+' || argv[optind][1] == '\0') 389 | #endif /* GETOPT_COMPAT */ 390 | ) 391 | optind++; 392 | last_nonopt = optind; 393 | } 394 | 395 | /* Special ARGV-element `--' means premature end of options. 396 | Skip it like a null option, 397 | then exchange with previous non-options as if it were an option, 398 | then skip everything else like a non-option. */ 399 | 400 | if (optind != argc && !strcmp (argv[optind], "--")) 401 | { 402 | optind++; 403 | 404 | if (first_nonopt != last_nonopt && last_nonopt != optind) 405 | exchange ((char **) argv); 406 | else if (first_nonopt == last_nonopt) 407 | first_nonopt = optind; 408 | last_nonopt = argc; 409 | 410 | optind = argc; 411 | } 412 | 413 | /* If we have done all the ARGV-elements, stop the scan 414 | and back over any non-options that we skipped and permuted. */ 415 | 416 | if (optind == argc) 417 | { 418 | /* Set the next-arg-index to point at the non-options 419 | that we previously skipped, so the caller will digest them. */ 420 | if (first_nonopt != last_nonopt) 421 | optind = first_nonopt; 422 | return EOF; 423 | } 424 | 425 | /* If we have come to a non-option and did not permute it, 426 | either stop the scan or describe it to the caller and pass it by. */ 427 | 428 | if ((argv[optind][0] != '-' || argv[optind][1] == '\0') 429 | #ifdef GETOPT_COMPAT 430 | && (longopts == NULL 431 | || argv[optind][0] != '+' || argv[optind][1] == '\0') 432 | #endif /* GETOPT_COMPAT */ 433 | ) 434 | { 435 | if (ordering == REQUIRE_ORDER) 436 | return EOF; 437 | optarg = argv[optind++]; 438 | return 1; 439 | } 440 | 441 | /* We have found another option-ARGV-element. 442 | Start decoding its characters. */ 443 | 444 | nextchar = (argv[optind] + 1 445 | + (longopts != NULL && argv[optind][1] == '-')); 446 | } 447 | 448 | if (longopts != NULL 449 | && ((argv[optind][0] == '-' 450 | && (argv[optind][1] == '-' || long_only)) 451 | #ifdef GETOPT_COMPAT 452 | || argv[optind][0] == '+' 453 | #endif /* GETOPT_COMPAT */ 454 | )) 455 | { 456 | const struct option *p; 457 | char *s = nextchar; 458 | int exact = 0; 459 | int ambig = 0; 460 | const struct option *pfound = NULL; 461 | int indfound = 0; 462 | 463 | while (*s && *s != '=') 464 | s++; 465 | 466 | /* Test all options for either exact match or abbreviated matches. */ 467 | for (p = longopts, option_index = 0; p->name; 468 | p++, option_index++) 469 | if (!strncmp (p->name, nextchar, s - nextchar)) 470 | { 471 | if (s - nextchar == my_strlen (p->name)) 472 | { 473 | /* Exact match found. */ 474 | pfound = p; 475 | indfound = option_index; 476 | exact = 1; 477 | break; 478 | } 479 | else if (pfound == NULL) 480 | { 481 | /* First nonexact match found. */ 482 | pfound = p; 483 | indfound = option_index; 484 | } 485 | else 486 | /* Second nonexact match found. */ 487 | ambig = 1; 488 | } 489 | 490 | if (ambig && !exact) 491 | { 492 | if (opterr) 493 | fprintf (stderr, "%s: option `%s' is ambiguous\n", 494 | argv[0], argv[optind]); 495 | nextchar += my_strlen (nextchar); 496 | optind++; 497 | return BAD_OPTION; 498 | } 499 | 500 | if (pfound != NULL) 501 | { 502 | option_index = indfound; 503 | optind++; 504 | if (*s) 505 | { 506 | /* Don't test has_arg with >, because some C compilers don't 507 | allow it to be used on enums. */ 508 | if (pfound->has_arg) 509 | optarg = s + 1; 510 | else 511 | { 512 | if (opterr) 513 | { 514 | if (argv[optind - 1][1] == '-') 515 | /* --option */ 516 | fprintf (stderr, 517 | "%s: option `--%s' doesn't allow an argument\n", 518 | argv[0], pfound->name); 519 | else 520 | /* +option or -option */ 521 | fprintf (stderr, 522 | "%s: option `%c%s' doesn't allow an argument\n", 523 | argv[0], argv[optind - 1][0], pfound->name); 524 | } 525 | nextchar += my_strlen (nextchar); 526 | return BAD_OPTION; 527 | } 528 | } 529 | else if (pfound->has_arg == 1) 530 | { 531 | if (optind < argc) 532 | optarg = argv[optind++]; 533 | else 534 | { 535 | if (opterr) 536 | fprintf (stderr, "%s: option `%s' requires an argument\n", 537 | argv[0], argv[optind - 1]); 538 | nextchar += my_strlen (nextchar); 539 | return optstring[0] == ':' ? ':' : BAD_OPTION; 540 | } 541 | } 542 | nextchar += my_strlen (nextchar); 543 | if (longind != NULL) 544 | *longind = option_index; 545 | if (pfound->flag) 546 | { 547 | *(pfound->flag) = pfound->val; 548 | return 0; 549 | } 550 | return pfound->val; 551 | } 552 | /* Can't find it as a long option. If this is not getopt_long_only, 553 | or the option starts with '--' or is not a valid short 554 | option, then it's an error. 555 | Otherwise interpret it as a short option. */ 556 | if (!long_only || argv[optind][1] == '-' 557 | #ifdef GETOPT_COMPAT 558 | || argv[optind][0] == '+' 559 | #endif /* GETOPT_COMPAT */ 560 | || my_index (optstring, *nextchar) == NULL) 561 | { 562 | if (opterr) 563 | { 564 | if (argv[optind][1] == '-') 565 | /* --option */ 566 | fprintf (stderr, "%s: unrecognized option `--%s'\n", 567 | argv[0], nextchar); 568 | else 569 | /* +option or -option */ 570 | fprintf (stderr, "%s: unrecognized option `%c%s'\n", 571 | argv[0], argv[optind][0], nextchar); 572 | } 573 | nextchar = (char *) ""; 574 | optind++; 575 | return BAD_OPTION; 576 | } 577 | } 578 | 579 | /* Look at and handle the next option-character. */ 580 | 581 | { 582 | char c = *nextchar++; 583 | char *temp = my_index (optstring, c); 584 | 585 | /* Increment `optind' when we start to process its last character. */ 586 | if (*nextchar == '\0') 587 | ++optind; 588 | 589 | if (temp == NULL || c == ':') 590 | { 591 | if (opterr) 592 | { 593 | #if 0 594 | if (c < 040 || c >= 0177) 595 | fprintf (stderr, "%s: unrecognized option, character code 0%o\n", 596 | argv[0], c); 597 | else 598 | fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); 599 | #else 600 | /* 1003.2 specifies the format of this message. */ 601 | fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); 602 | #endif 603 | } 604 | optopt = c; 605 | return BAD_OPTION; 606 | } 607 | if (temp[1] == ':') 608 | { 609 | if (temp[2] == ':') 610 | { 611 | /* This is an option that accepts an argument optionally. */ 612 | if (*nextchar != '\0') 613 | { 614 | optarg = nextchar; 615 | optind++; 616 | } 617 | else 618 | optarg = 0; 619 | nextchar = NULL; 620 | } 621 | else 622 | { 623 | /* This is an option that requires an argument. */ 624 | if (*nextchar != '\0') 625 | { 626 | optarg = nextchar; 627 | /* If we end this ARGV-element by taking the rest as an arg, 628 | we must advance to the next element now. */ 629 | optind++; 630 | } 631 | else if (optind == argc) 632 | { 633 | if (opterr) 634 | { 635 | #if 0 636 | fprintf (stderr, "%s: option `-%c' requires an argument\n", 637 | argv[0], c); 638 | #else 639 | /* 1003.2 specifies the format of this message. */ 640 | fprintf (stderr, "%s: option requires an argument -- %c\n", 641 | argv[0], c); 642 | #endif 643 | } 644 | optopt = c; 645 | if (optstring[0] == ':') 646 | c = ':'; 647 | else 648 | c = BAD_OPTION; 649 | } 650 | else 651 | /* We already incremented `optind' once; 652 | increment it again when taking next ARGV-elt as argument. */ 653 | optarg = argv[optind++]; 654 | nextchar = NULL; 655 | } 656 | } 657 | return c; 658 | } 659 | } 660 | 661 | int 662 | getopt (argc, argv, optstring) 663 | int argc; 664 | char *const *argv; 665 | const char *optstring; 666 | { 667 | return _getopt_internal (argc, argv, optstring, 668 | (const struct option *) 0, 669 | (int *) 0, 670 | 0); 671 | } 672 | 673 | int 674 | getopt_long (argc, argv, options, long_options, opt_index) 675 | int argc; 676 | char *const *argv; 677 | const char *options; 678 | const struct option *long_options; 679 | int *opt_index; 680 | { 681 | return _getopt_internal (argc, argv, options, long_options, opt_index, 0); 682 | } 683 | 684 | #endif /* _LIBC or not __GNU_LIBRARY__. */ 685 | 686 | #ifdef TEST 687 | 688 | /* Compile with -DTEST to make an executable for use in testing 689 | the above definition of `getopt'. */ 690 | 691 | int 692 | main (argc, argv) 693 | int argc; 694 | char **argv; 695 | { 696 | int c; 697 | int digit_optind = 0; 698 | 699 | while (1) 700 | { 701 | int this_option_optind = optind ? optind : 1; 702 | 703 | c = getopt (argc, argv, "abc:d:0123456789"); 704 | if (c == EOF) 705 | break; 706 | 707 | switch (c) 708 | { 709 | case '0': 710 | case '1': 711 | case '2': 712 | case '3': 713 | case '4': 714 | case '5': 715 | case '6': 716 | case '7': 717 | case '8': 718 | case '9': 719 | if (digit_optind != 0 && digit_optind != this_option_optind) 720 | printf ("digits occur in two different argv-elements.\n"); 721 | digit_optind = this_option_optind; 722 | printf ("option %c\n", c); 723 | break; 724 | 725 | case 'a': 726 | printf ("option a\n"); 727 | break; 728 | 729 | case 'b': 730 | printf ("option b\n"); 731 | break; 732 | 733 | case 'c': 734 | printf ("option c with value `%s'\n", optarg); 735 | break; 736 | 737 | case BAD_OPTION: 738 | break; 739 | 740 | default: 741 | printf ("?? getopt returned character code 0%o ??\n", c); 742 | } 743 | } 744 | 745 | if (optind < argc) 746 | { 747 | printf ("non-option ARGV-elements: "); 748 | while (optind < argc) 749 | printf ("%s ", argv[optind++]); 750 | printf ("\n"); 751 | } 752 | 753 | exit (0); 754 | } 755 | 756 | #endif /* TEST */ 757 | -------------------------------------------------------------------------------- /getopt.h: -------------------------------------------------------------------------------- 1 | /* THIS IS HERE FOR WIN32's BENEFIT ONLY. */ 2 | 3 | /* Declarations for getopt. 4 | Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. 5 | 6 | This program is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by the 8 | Free Software Foundation; either version 2, or (at your option) any 9 | later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ 19 | 20 | #ifndef _GETOPT_H 21 | #define _GETOPT_H 1 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | /* For communication from `getopt' to the caller. 28 | When `getopt' finds an option that takes an argument, 29 | the argument value is returned here. 30 | Also, when `ordering' is RETURN_IN_ORDER, 31 | each non-option ARGV-element is returned here. */ 32 | 33 | extern char *optarg; 34 | 35 | /* Index in ARGV of the next element to be scanned. 36 | This is used for communication to and from the caller 37 | and for communication between successive calls to `getopt'. 38 | 39 | On entry to `getopt', zero means this is the first call; initialize. 40 | 41 | When `getopt' returns EOF, this is the index of the first of the 42 | non-option elements that the caller should itself scan. 43 | 44 | Otherwise, `optind' communicates from one call to the next 45 | how much of ARGV has been scanned so far. */ 46 | 47 | extern int optind; 48 | 49 | /* Callers store zero here to inhibit the error message `getopt' prints 50 | for unrecognized options. */ 51 | 52 | extern int opterr; 53 | 54 | /* Set to an option character which was unrecognized. */ 55 | 56 | extern int optopt; 57 | 58 | /* Describe the long-named options requested by the application. 59 | The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector 60 | of `struct option' terminated by an element containing a name which is 61 | zero. 62 | 63 | The field `has_arg' is: 64 | no_argument (or 0) if the option does not take an argument, 65 | required_argument (or 1) if the option requires an argument, 66 | optional_argument (or 2) if the option takes an optional argument. 67 | 68 | If the field `flag' is not NULL, it points to a variable that is set 69 | to the value given in the field `val' when the option is found, but 70 | left unchanged if the option is not found. 71 | 72 | To have a long-named option do something other than set an `int' to 73 | a compiled-in constant, such as set a value from `optarg', set the 74 | option's `flag' field to zero and its `val' field to a nonzero 75 | value (the equivalent single-letter option character, if there is 76 | one). For long options that have a zero `flag' field, `getopt' 77 | returns the contents of the `val' field. */ 78 | 79 | struct option 80 | { 81 | #if __STDC__ 82 | const char *name; 83 | #else 84 | char *name; 85 | #endif 86 | /* has_arg can't be an enum because some compilers complain about 87 | type mismatches in all the code that assumes it is an int. */ 88 | int has_arg; 89 | int *flag; 90 | int val; 91 | }; 92 | 93 | /* Names for the values of the `has_arg' field of `struct option'. */ 94 | 95 | #define no_argument 0 96 | #define required_argument 1 97 | #define optional_argument 2 98 | 99 | #if __STDC__ || defined(PROTO) 100 | #if defined(__GNU_LIBRARY__) 101 | /* Many other libraries have conflicting prototypes for getopt, with 102 | differences in the consts, in stdlib.h. To avoid compilation 103 | errors, only prototype getopt for the GNU C library. */ 104 | extern int getopt (int argc, char *const *argv, const char *shortopts); 105 | #endif /* not __GNU_LIBRARY__ */ 106 | extern int getopt_long (int argc, char *const *argv, const char *shortopts, 107 | const struct option *longopts, int *longind); 108 | extern int getopt_long_only (int argc, char *const *argv, 109 | const char *shortopts, 110 | const struct option *longopts, int *longind); 111 | 112 | /* Internal only. Users should not call this directly. */ 113 | extern int _getopt_internal (int argc, char *const *argv, 114 | const char *shortopts, 115 | const struct option *longopts, int *longind, 116 | int long_only); 117 | #else /* not __STDC__ */ 118 | extern int getopt (); 119 | extern int getopt_long (); 120 | extern int getopt_long_only (); 121 | 122 | extern int _getopt_internal (); 123 | #endif /* not __STDC__ */ 124 | 125 | #ifdef __cplusplus 126 | } 127 | #endif 128 | 129 | #endif /* _GETOPT_H */ 130 | -------------------------------------------------------------------------------- /match.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "match.h" 4 | 5 | int match(char *sorig, char *p) 6 | { 7 | return matchBody(sorig, p, 0); 8 | } 9 | 10 | int matchNoCase(char *sorig, char *p) 11 | { 12 | return matchBody(sorig, p, 1); 13 | } 14 | 15 | #define CASE(x) (nocase ? tolower(x) : (x)) 16 | 17 | int matchBody(char *sorig, char *p, int nocase) 18 | { 19 | static int dummy = 0; 20 | /* Algorithm: 21 | 22 | Word separator: *. End-of-string 23 | is considered to be a word constituent. 24 | ? is similarly considered to be a specialized 25 | word constituent. 26 | 27 | Match the word to the current position in s. 28 | Empty words automatically succeed. 29 | 30 | If the word matches s, and the word 31 | and s contain end-of-string at that 32 | point, return success. 33 | 34 | \ escapes the next character, including \ itself (6.0). 35 | 36 | For each *: 37 | 38 | Find the next occurrence of the next word 39 | and advance beyond it in both p and s. 40 | If the next word ends in end-of-string 41 | and is found successfully, return success, 42 | otherwise advance past the *. 43 | 44 | If the word is not found, return failure. 45 | 46 | If the next word is empty, advance past the *. 47 | 48 | Behavior of ?: advance one character in s and p. 49 | 50 | Addendum: consider the | character to be a logical OR 51 | separating distinct patterns. */ 52 | 53 | char *s = sorig; 54 | int escaped = 0; 55 | if (strstr(p, "WS-0000")) { 56 | if (strstr(s, "ws_ftp_pro.html")) { 57 | dummy = 1; 58 | } 59 | } 60 | while (1) { 61 | char *word; 62 | int wordLen; 63 | int wordPos; 64 | if (escaped) { 65 | /* This is like the default case, 66 | except that | doesn't end the pattern. */ 67 | escaped = 0; 68 | if ((*s == '\0') && (*p == '\0')) { 69 | return 1; 70 | } 71 | if (CASE(*p) != CASE(*s)) { 72 | goto nextPattern; 73 | } 74 | p++; 75 | s++; 76 | continue; 77 | } 78 | switch(*p) { 79 | case '\\': 80 | /* Escape the next character. */ 81 | escaped = 1; 82 | p++; 83 | continue; 84 | case '*': 85 | /* Find the next occurrence of the next word 86 | and advance beyond it in both p and s. 87 | If the next word ends in end-of-string 88 | and is found successfully, return success, 89 | otherwise advance past the *. 90 | 91 | If the word is not found, return failure. 92 | 93 | If the next word is empty, advance. */ 94 | p++; 95 | wordLen = 0; 96 | word = p; 97 | while (1) { 98 | if ((*p) == '*') { 99 | break; 100 | } 101 | wordLen++; 102 | if ((*p == '\0') || (*p == '|')) { 103 | break; 104 | } 105 | p++; 106 | } 107 | wordPos = 0; 108 | while (1) { 109 | if (wordPos == wordLen) { 110 | if ((*p == '\0') || (*p == '|')) { 111 | return 1; 112 | } 113 | break; 114 | } 115 | if ((((CASE(*s)) == CASE(word[wordPos])) || 116 | ((*s == '\0') && 117 | (word[wordPos] == '|'))) || 118 | (((*s != '\0') && (*s != '|')) && 119 | (word[wordPos] == '?'))) 120 | { 121 | wordPos++; 122 | s++; 123 | } else { 124 | s -= wordPos; 125 | if (!(*s)) { 126 | goto nextPattern; 127 | } 128 | s++; 129 | wordPos = 0; 130 | } 131 | } 132 | break; 133 | case '?': 134 | p++; 135 | s++; 136 | break; 137 | default: 138 | if ((*s == '\0') && ((*p == '\0') || 139 | (*p == '|'))) { 140 | return 1; 141 | } 142 | if (CASE(*p) != CASE(*s)) { 143 | goto nextPattern; 144 | } 145 | p++; 146 | s++; 147 | break; 148 | } 149 | continue; 150 | nextPattern: 151 | while (1) { 152 | if (*p == '\0') { 153 | return 0; 154 | } 155 | if (*p == '|') { 156 | p++; 157 | s = sorig; 158 | break; 159 | } 160 | p++; 161 | } 162 | } 163 | } 164 | 165 | #ifdef TEST_MATCH 166 | 167 | #include 168 | #include 169 | #include 170 | 171 | int main(int argc, char *argv[]) 172 | { 173 | char s[1024]; 174 | if (argc != 2) { 175 | fprintf(stderr, "Usage: match pattern\n"); 176 | return 1; 177 | } 178 | while (1) { 179 | if (!fgets(s, sizeof(s), stdin)) { 180 | break; 181 | } 182 | while (isspace(s[strlen(s) - 1])) { 183 | s[strlen(s) - 1] = '\0'; 184 | } 185 | printf("%s --> %s\n", s, argv[1]); 186 | if (match(s, argv[1])) { 187 | printf("Match\n"); 188 | } else { 189 | printf("No Match\n"); 190 | } 191 | } 192 | } 193 | 194 | #endif /* TEST_MATCH */ 195 | 196 | -------------------------------------------------------------------------------- /match.h: -------------------------------------------------------------------------------- 1 | #ifndef MATCH_H 2 | #define MATCH_H 1 3 | 4 | extern int match(char *s, char *p); 5 | extern int matchNoCase(char *s, char *p); 6 | extern int matchBody(char *s, char *p, int nocase); 7 | 8 | #endif /* MATCH_H */ 9 | 10 | -------------------------------------------------------------------------------- /rinetd.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1997, 1998, 1999, Thomas Boutell and Boutell.Com, Inc. 2 | .\" This software is released for free use under the terms of 3 | .\" the GNU Public License, version 2 or higher. 4 | .\" 5 | .Dd February 18, 1999 6 | .Dt RINETD 8 7 | .Os LINUX 8 | .Sh NAME 9 | .Nm rinetd 10 | .Nd internet 11 | .Dq redirection server 12 | .Sh SYNOPSIS 13 | .Nm /usr/sbin/rinetd 14 | .Sh VERSION 15 | Version 0.62, 04/14/2003. 16 | .Sh DESCRIPTION 17 | .Nm rinetd 18 | redirects TCP connections from one IP address and port to another. rinetd 19 | is a single-process server which handles any number of connections to 20 | the address/port pairs specified in the file /etc/rinetd.conf. 21 | Since rinetd runs as a single process using nonblocking I/O, it is 22 | able to redirect a large number of connections without a severe 23 | impact on the machine. This makes it practical to run TCP services 24 | on machines inside an IP masquerading firewall. rinetd does not 25 | redirect FTP, because FTP requires more than one socket. 26 | .Pp 27 | rinetd is typically launched at boot time, using the following syntax: 28 | .Pp 29 | /usr/sbin/rinetd 30 | .Pp 31 | The configuration file is found in the file /etc/rinetd.conf, unless 32 | another file is specified using the -c command line option. 33 | .Sh FORWARDING RULES 34 | Most entries in the configuration file are forwarding rules. The 35 | format of a forwarding rule is as follows: 36 | .Pp 37 | bindaddress bindport connectaddress connectport 38 | .Pp 39 | For example: 40 | .Pp 41 | 206.125.69.81 80 10.1.1.2 80 42 | .Pp 43 | Would redirect all connections to port 80 of the "real" IP address 44 | 206.125.69.81, which could be a virtual interface, through 45 | rinetd to port 80 of the address 10.1.1.2, which would typically 46 | be a machine on the inside of a firewall which has no 47 | direct routing to the outside world. 48 | .Pp 49 | Although responding on individual interfaces rather than on all 50 | interfaces is one of rinetd's primary features, sometimes it is 51 | preferable to respond on all IP addresses that belong to the server. 52 | In this situation, the special IP address 0.0.0.0 53 | can be used. For example: 54 | .Pp 55 | 0.0.0.0 23 10.1.1.2 23 56 | .Pp 57 | Would redirect all connections to port 23, for all IP addresses 58 | assigned to the server. This is the default behavior for most 59 | other programs. 60 | .Pp 61 | Service names can be specified instead of port numbers. On most systems, 62 | service names are defined in the file /etc/services. 63 | .Pp 64 | Both IP addresses and hostnames are accepted for 65 | bindaddress and connectaddress. 66 | .Pp 67 | .Sh ALLOW AND DENY RULES 68 | Configuration files can also contain allow and deny rules. 69 | .Pp 70 | Allow rules which appear before the first forwarding rule are 71 | applied globally: if at least one global allow rule exists, 72 | and the address of a new connection does not 73 | satisfy at least one of the global allow rules, that connection 74 | is immediately rejected, regardless of any other rules. 75 | .Pp 76 | Allow rules which appear after a specific forwarding rule apply 77 | to that forwarding rule only. If at least one allow rule 78 | exists for a particular forwarding rule, and the address of a new 79 | connection does not satisfy at least one of the allow rules 80 | for that forwarding rule, that connection is immediately 81 | rejected, regardless of any other rules. 82 | .Pp 83 | Deny rules which appear before the first forwarding rule are 84 | applied globally: if the address of a new connection satisfies 85 | any of the global allow rules, that connection 86 | is immediately rejected, regardless of any other rules. 87 | .Pp 88 | Deny rules which appear after a specific forwarding rule apply 89 | to that forwarding rule only. If the address of a new 90 | connection satisfies any of the deny rules for that forwarding rule, 91 | that connection is immediately rejected, regardless of any other rules. 92 | .Pp 93 | The format of an allow rule is as follows: 94 | .Pp 95 | allow pattern 96 | .Pp 97 | Patterns can contain the following characters: 0, 1, 2, 3, 4, 5, 98 | 6, 7, 8, 9, . (period), ?, and *. The ? wildcard matches any one 99 | character. The * wildcard matches any number of characters, including 100 | zero. 101 | .Pp 102 | For example: 103 | .Pp 104 | allow 206.125.69.* 105 | .Pp 106 | This allow rule matches all IP addresses in the 206.125.69 class C domain. 107 | .Pp 108 | Host names are NOT permitted in allow and deny rules. The performance 109 | cost of looking up IP addresses to find their corresponding names 110 | is prohibitive. Since rinetd is a single process server, all other 111 | connections would be forced to pause during the address lookup. 112 | .Pp 113 | .Sh LOGGING 114 | rinetd is able to produce a log file in either of two formats: 115 | tab-delimited and web server-style "common log format." 116 | .Pp 117 | By default, rinetd does not produce a log file. To activate logging, add 118 | the following line to the configuration file: 119 | .Pp 120 | logfile log-file-location 121 | .Pp 122 | Example: logfile /var/log/rinetd.log 123 | .Pp 124 | By default, rinetd logs in a simple tab-delimited format containing 125 | the following information: 126 | .Pp 127 | Date and time 128 | .Pp 129 | Client address 130 | .Pp 131 | Listening host 132 | .Pp 133 | Listening port 134 | .Pp 135 | Forwarded-to host 136 | .Pp 137 | Forwarded-to port 138 | .Pp 139 | Bytes received from client 140 | .Pp 141 | Bytes sent to client 142 | .Pp 143 | Result message 144 | .Pp 145 | To activate web server-style "common log format" logging, 146 | add the following line to the configuration file: 147 | .Pp 148 | logcommon 149 | .Sh COMMAND LINE OPTIONS 150 | The -c command line option is used to specify an alternate 151 | configuration file. 152 | .Pp 153 | The -h command line option produces a short help message. 154 | .Pp 155 | The -v command line option displays the version number. 156 | .Sh REINITIALIZING RINETD 157 | The kill -1 signal (SIGHUP) can be used to cause rinetd 158 | to reload its configuration file without interrupting existing 159 | connections. 160 | Under Linux\(tm the process id is saved in the file \fI/var/run/rinetd.pid\fR 161 | to facilitate the kill -HUP. An alternate 162 | filename can be provided by using the pidlogfile 163 | configuration file option. 164 | 165 | .Sh LIMITATIONS 166 | rinetd redirects TCP connections only. There is 167 | no support for UDP. rinetd only redirects protocols which 168 | use a single TCP socket. This rules out FTP. 169 | .Sh BUGS 170 | The server redirected to is not able to identify the host the 171 | client really came from. This cannot be corrected; however, 172 | the log produced by rinetd provides a way to obtain this 173 | information. Under Unix, Sockets would theoretically lose data when closed 174 | with SO_LINGER turned off, but in Linux this is not the case (kernel 175 | source comments support this belief on my part). On non-Linux Unix platforms, 176 | alternate code which uses a different trick to work around blocking close() 177 | is provided, but this code is untested. The logging is inadequate. 178 | The duration of each connection should be logged. 179 | .Sh LICENSE 180 | Copyright (c) 1997, 1998, 1999, Thomas Boutell and Boutell.Com, Inc. 181 | This software is released for free use under the terms of 182 | the GNU Public License, version 2 or higher. NO WARRANTY 183 | IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. 184 | .Sh THANKS 185 | Thanks are due to Bill Davidsen, Libor Pechachek, Sascha Ziemann, the 186 | Apache Group, and many others who have contributed advice 187 | and/or source code to this and other free software projects. 188 | -------------------------------------------------------------------------------- /rinetd.c: -------------------------------------------------------------------------------- 1 | #define VERSION "0.62" 2 | 3 | #ifdef WIN32 4 | #include 5 | #include 6 | #include "getopt.h" 7 | #else 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #define INVALID_SOCKET (-1) 16 | #include 17 | #endif /* WIN32 */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #ifndef WIN32 28 | /* Windows sockets compatibility defines */ 29 | #define INVALID_SOCKET (-1) 30 | #define SOCKET_ERROR (-1) 31 | int closesocket(int s); 32 | 33 | int closesocket(int s) { 34 | return close(s); 35 | } 36 | #define ioctlsocket ioctl 37 | #define MAKEWORD(a, b) 38 | #define WSAStartup(a, b) (0) 39 | #define WSACleanup() 40 | #ifdef __MAC__ 41 | /* The constants for these are a little screwy in the prelinked 42 | MSL GUSI lib and we can't rebuild it, so roll with it */ 43 | #define WSAEWOULDBLOCK EWOULDBLOCK 44 | #define WSAEAGAIN EAGAIN 45 | #define WSAEINPROGRESS EINPROGRESS 46 | #else 47 | #define WSAEWOULDBLOCK EWOULDBLOCK 48 | #define WSAEAGAIN EAGAIN 49 | #define WSAEINPROGRESS EINPROGRESS 50 | #endif /* __MAC__ */ 51 | #define WSAEINTR EINTR 52 | #define SOCKET int 53 | #define GetLastError() (errno) 54 | typedef struct { 55 | int dummy; 56 | } WSADATA; 57 | 58 | void Sleep(long ms); 59 | 60 | void Sleep(long ms) 61 | { 62 | struct timeval tv; 63 | tv.tv_sec = ms / 1000; 64 | tv.tv_usec = ms * 1000; 65 | select(0, 0, 0, 0, &tv); 66 | } 67 | #else 68 | /* WIN32 doesn't really have WSAEAGAIN */ 69 | #ifndef WSAEAGAIN 70 | #define WSAEAGAIN WSAEWOULDBLOCK 71 | #endif 72 | #endif /* WIN32 */ 73 | 74 | #ifndef TRUE 75 | #define TRUE 1 76 | #endif 77 | 78 | #ifndef FALSE 79 | #define FALSE 0 80 | #endif 81 | 82 | #ifdef DEBUG 83 | #define PERROR perror 84 | #else 85 | #define PERROR(x) 86 | #endif /* DEBUG */ 87 | 88 | /* We've got to get FIONBIO from somewhere. Try the Solaris location 89 | if it isn't defined yet by the above includes. */ 90 | #ifndef FIONBIO 91 | #include 92 | #endif /* FIONBIO */ 93 | 94 | #include "match.h" 95 | 96 | SOCKET *seFds = 0; 97 | /* In network order, for network purposes */ 98 | struct in_addr *seLocalAddrs = 0; 99 | unsigned short *seLocalPorts = 0; 100 | /* In ASCII and local byte order, for logging purposes */ 101 | char **seFromHosts; 102 | int *seFromPorts; 103 | char **seToHosts; 104 | int *seToPorts; 105 | 106 | /* Offsets into list of allow and deny rules. Any rules 107 | prior to globalAllowRules and globalDenyRules are global rules. */ 108 | 109 | int *seAllowRules = 0; 110 | int *seAllowRulesTotal = 0; 111 | int globalAllowRules = 0; 112 | int *seDenyRules = 0; 113 | int *seDenyRulesTotal = 0; 114 | int globalDenyRules = 0; 115 | 116 | SOCKET *reFds = 0; 117 | SOCKET *loFds = 0; 118 | unsigned char *reAddresses = 0; 119 | int *coInputRPos = 0; 120 | int *coInputWPos = 0; 121 | int *coOutputRPos = 0; 122 | int *coOutputWPos = 0; 123 | int *coClosed = 0; 124 | int *coClosing = 0; 125 | int *reClosed = 0; 126 | int *loClosed = 0; 127 | int *coBytesInput = 0; 128 | int *coBytesOutput = 0; 129 | int *coLog = 0; 130 | int *coSe = 0; 131 | char **coInput = 0; 132 | char **coOutput = 0; 133 | char **allowRules = 0; 134 | char **denyRules = 0; 135 | int *denyRulesFor = 0; 136 | int seTotal = 0; 137 | int coTotal = 0; 138 | int allowRulesTotal = 0; 139 | int denyRulesTotal = 0; 140 | int maxfd = 0; 141 | char *logFileName = 0; 142 | char *pidLogFileName = 0; 143 | int logFormatCommon = 0; 144 | FILE *logFile = 0; 145 | 146 | /* If 'newsize' bytes can be allocated, *data is set to point 147 | to them, the previous data is copied, and 1 is returned. 148 | If 'size' bytes cannot be allocated, *data is UNCHANGED, 149 | and 0 is returned. */ 150 | 151 | #define SAFE_REALLOC(x, y, z) safeRealloc((void **) (x), (y), (z)) 152 | 153 | int safeRealloc(void **data, int oldsize, int newsize); 154 | 155 | /* 156 | se: (se)rver sockets 157 | re: (re)mote sockets 158 | lo: (lo)cal sockets (being redirected to) 159 | co: connections 160 | */ 161 | 162 | #define bufferSpace 1024 163 | 164 | void readConfiguration(); 165 | 166 | /* Signal handlers */ 167 | void plumber(int s); 168 | void hup(int s); 169 | void term(int s); 170 | 171 | void initArrays(void); 172 | void RegisterPID(void); 173 | 174 | void selectLoop(void); 175 | 176 | void log(int i, int coSe, int result); 177 | 178 | int getAddress(char *host, struct in_addr *iaddr); 179 | 180 | char *logMessages[] = { 181 | "done-local-closed", 182 | "done-remote-closed", 183 | "accept-failed -", 184 | 0, 185 | "local-socket-failed -", 186 | 0, 187 | "local-bind-failed -", 188 | 0, 189 | "local-connect-failed -", 190 | 0, 191 | "not-allowed", 192 | 0, 193 | "denied", 194 | 0 195 | }; 196 | 197 | #define logDone 0 198 | #define logAcceptFailed 2 199 | #define logLocalSocketFailed 4 200 | #define logLocalBindFailed 6 201 | #define logLocalConnectFailed 8 202 | #define logNotAllowed 10 203 | #define logDenied 12 204 | 205 | #define logLocalClosedFirst 0 206 | #define logRemoteClosedFirst 1 207 | 208 | /* Option parsing */ 209 | 210 | typedef struct _rinetd_options RinetdOptions; 211 | struct _rinetd_options 212 | { 213 | char *conf_file; 214 | }; 215 | 216 | RinetdOptions options = { 217 | "/etc/rinetd.conf" 218 | }; 219 | 220 | int readArgs (int argc, 221 | char **argv, 222 | RinetdOptions *options); 223 | 224 | int main(int argc, char *argv[]) 225 | { 226 | WSADATA wsaData; 227 | int result = WSAStartup(MAKEWORD(1, 1), &wsaData); 228 | if (result != 0) { 229 | fprintf(stderr, "Your computer was not connected " 230 | "to the Internet at the time that " 231 | "this program was launched, or you " 232 | "do not have a 32-bit " 233 | "connection to the Internet."); 234 | exit(1); 235 | } 236 | readArgs(argc, argv, &options); 237 | #ifndef WIN32 238 | #ifndef DEBUG 239 | if (!fork()) { 240 | if (!fork()) { 241 | #endif /* DEBUG */ 242 | signal(SIGPIPE, plumber); 243 | signal(SIGHUP, hup); 244 | #endif /* WIN32 */ 245 | signal(SIGTERM, term); 246 | initArrays(); 247 | readConfiguration(); 248 | RegisterPID(); 249 | selectLoop(); 250 | #ifndef WIN32 251 | #ifndef DEBUG 252 | } else { 253 | exit(0); 254 | } 255 | } else { 256 | exit(0); 257 | } 258 | #endif /* DEBUG */ 259 | #endif /* WIN32 */ 260 | return 0; 261 | } 262 | 263 | int getConfLine(FILE *in, char *line, int space, int *lnum); 264 | 265 | int patternBad(char *pattern); 266 | 267 | void readConfiguration(void) 268 | { 269 | FILE *in; 270 | char line[16384]; 271 | int lnum = 0; 272 | int i; 273 | int ai; 274 | int di; 275 | if (seFds) { 276 | /* Close existing server sockets. */ 277 | for (i = 0; (i < seTotal); i++) { 278 | if (seFds[i] != -1) { 279 | closesocket(seFds[i]); 280 | free(seFromHosts[i]); 281 | free(seToHosts[i]); 282 | } 283 | } 284 | /* Free memory associated with previous set. */ 285 | free(seFds); 286 | free(seLocalAddrs); 287 | free(seLocalPorts); 288 | free(seFromHosts); 289 | free(seFromPorts); 290 | free(seToHosts); 291 | free(seToPorts); 292 | free(seAllowRules); 293 | free(seDenyRules); 294 | free(seAllowRulesTotal); 295 | free(seDenyRulesTotal); 296 | } 297 | seTotal = 0; 298 | if (allowRules) { 299 | /* Forget existing allow rules. */ 300 | for (i = 0; (i < allowRulesTotal); i++) { 301 | free(allowRules[i]); 302 | } 303 | /* Free memory associated with previous set. */ 304 | free(allowRules); 305 | globalAllowRules = 0; 306 | } 307 | allowRulesTotal = 0; 308 | if (denyRules) { 309 | /* Forget existing deny rules. */ 310 | for (i = 0; (i < denyRulesTotal); i++) { 311 | free(denyRules[i]); 312 | } 313 | /* Free memory associated with previous set. */ 314 | free(denyRules); 315 | globalDenyRules = 0; 316 | } 317 | denyRulesTotal = 0; 318 | if (logFileName) { 319 | free(logFileName); 320 | logFileName = 0; 321 | } 322 | if (pidLogFileName) { 323 | free(pidLogFileName); 324 | pidLogFileName = 0; 325 | } 326 | /* 1. Count the non-comment lines of each type and 327 | allocate space for the data. */ 328 | in = fopen(options.conf_file, "r"); 329 | if (!in) { 330 | fprintf(stderr, "rinetd: can't open %s\n", options.conf_file); 331 | exit(1); 332 | } 333 | while (1) { 334 | char *t = 0; 335 | if (!getConfLine(in, line, sizeof(line), &lnum)) { 336 | break; 337 | } 338 | t = strtok(line, " \t\r\n"); 339 | if (!strcmp(t, "logfile")) { 340 | continue; 341 | } else if (!strcmp(t, "pidlogfile")) { 342 | continue; 343 | } else if (!strcmp(t, "logcommon")) { 344 | continue; 345 | } else if (!strcmp(t, "allow")) { 346 | allowRulesTotal++; 347 | } else if (!strcmp(t, "deny")) { 348 | denyRulesTotal++; 349 | } else { 350 | /* A regular forwarding rule */ 351 | seTotal++; 352 | } 353 | } 354 | fclose(in); 355 | seFds = (SOCKET *) malloc(sizeof(int) * seTotal); 356 | if (!seFds) { 357 | goto lowMemory; 358 | } 359 | seLocalAddrs = (struct in_addr *) malloc(sizeof(struct in_addr) * 360 | seTotal); 361 | if (!seLocalAddrs) { 362 | goto lowMemory; 363 | } 364 | seLocalPorts = (unsigned short *) 365 | malloc(sizeof(unsigned short) * seTotal); 366 | if (!seLocalPorts) { 367 | goto lowMemory; 368 | } 369 | seFromHosts = (char **) 370 | malloc(sizeof(char *) * seTotal); 371 | if (!seFromHosts) { 372 | goto lowMemory; 373 | } 374 | seFromPorts = (int *) 375 | malloc(sizeof(int) * seTotal); 376 | if (!seFromPorts) { 377 | goto lowMemory; 378 | } 379 | seToHosts = (char **) 380 | malloc(sizeof(char *) * seTotal); 381 | if (!seToHosts) { 382 | goto lowMemory; 383 | } 384 | seToPorts = (int *) 385 | malloc(sizeof(int) * seTotal); 386 | if (!seToPorts) { 387 | goto lowMemory; 388 | } 389 | allowRules = (char **) 390 | malloc(sizeof(char *) * allowRulesTotal); 391 | if (!allowRules) { 392 | goto lowMemory; 393 | } 394 | denyRules = (char **) 395 | malloc(sizeof(char *) * denyRulesTotal); 396 | if (!denyRules) { 397 | goto lowMemory; 398 | } 399 | seAllowRules = (int *) 400 | malloc(sizeof(int) * seTotal); 401 | if (!seAllowRules) { 402 | goto lowMemory; 403 | } 404 | seAllowRulesTotal = (int *) 405 | malloc(sizeof(int) * seTotal); 406 | if (!seAllowRulesTotal) { 407 | goto lowMemory; 408 | } 409 | seDenyRules = (int *) 410 | malloc(sizeof(int) * seTotal); 411 | if (!seDenyRules) { 412 | goto lowMemory; 413 | } 414 | seDenyRulesTotal = (int *) 415 | malloc(sizeof(int) * seTotal); 416 | if (!seDenyRulesTotal) { 417 | goto lowMemory; 418 | } 419 | /* 2. Make a second pass to configure them. */ 420 | i = 0; 421 | ai = 0; 422 | di = 0; 423 | lnum = 0; 424 | in = fopen(options.conf_file, "r"); 425 | if (!in) { 426 | goto lowMemory; 427 | } 428 | if (seTotal > 0) { 429 | seAllowRulesTotal[i] = 0; 430 | seDenyRulesTotal[i] = 0; 431 | } 432 | while (1) { 433 | char *bindAddress; 434 | unsigned short bindPort; 435 | char *connectAddress; 436 | char *bindPortS; 437 | char *connectPortS; 438 | unsigned short connectPort; 439 | struct in_addr iaddr; 440 | struct sockaddr_in saddr; 441 | struct servent *service; 442 | int j; 443 | if (!getConfLine(in, line, sizeof(line), &lnum)) { 444 | break; 445 | } 446 | bindAddress = strtok(line, " \t\r\n"); 447 | if (!bindAddress) { 448 | fprintf(stderr, "rinetd: no bind address specified " 449 | "on line %d.\n", lnum); 450 | continue; 451 | } 452 | if (!strcmp(bindAddress, "allow")) { 453 | char *pattern = strtok(0, " \t\r\n"); 454 | if (!pattern) { 455 | fprintf(stderr, "rinetd: nothing to allow " 456 | "specified on line %d.\n", lnum); 457 | continue; 458 | } 459 | if (patternBad(pattern)) { 460 | fprintf(stderr, "rinetd: illegal allow or " 461 | "deny pattern. Only digits, ., and\n" 462 | "the ? and * wild cards are allowed. " 463 | "For performance reasons, rinetd\n" 464 | "does not look up complete " 465 | "host names.\n"); 466 | continue; 467 | } 468 | 469 | allowRules[ai] = malloc(strlen(pattern) + 1); 470 | if (!allowRules[ai]) { 471 | goto lowMemory; 472 | } 473 | strcpy(allowRules[ai], pattern); 474 | if (i > 0) { 475 | if (seAllowRulesTotal[i - 1] == 0) { 476 | seAllowRules[i - 1] = ai; 477 | } 478 | seAllowRulesTotal[i - 1]++; 479 | } else { 480 | globalAllowRules++; 481 | } 482 | ai++; 483 | } else if (!strcmp(bindAddress, "deny")) { 484 | char *pattern = strtok(0, " \t\r\n"); 485 | if (!pattern) { 486 | fprintf(stderr, "rinetd: nothing to deny " 487 | "specified on line %d.\n", lnum); 488 | continue; 489 | } 490 | denyRules[di] = malloc(strlen(pattern) + 1); 491 | if (!denyRules[di]) { 492 | goto lowMemory; 493 | } 494 | strcpy(denyRules[di], pattern); 495 | if (i > 0) { 496 | if (seDenyRulesTotal[i - 1] == 0) { 497 | seDenyRules[i - 1] = di; 498 | } 499 | seDenyRulesTotal[i - 1]++; 500 | } else { 501 | globalDenyRules++; 502 | } 503 | di++; 504 | } else if (!strcmp(bindAddress, "logfile")) { 505 | char *nt = strtok(0, " \t\r\n"); 506 | if (!nt) { 507 | fprintf(stderr, "rinetd: no log file name " 508 | "specified on line %d.\n", lnum); 509 | continue; 510 | } 511 | logFileName = malloc(strlen(nt) + 1); 512 | if (!logFileName) { 513 | goto lowMemory; 514 | } 515 | strcpy(logFileName, nt); 516 | } else if (!strcmp(bindAddress, "pidlogfile")) { 517 | char *nt = strtok(0, " \t\r\n"); 518 | if (!nt) { 519 | fprintf(stderr, "rinetd: no PID log file name " 520 | "specified on line %d.\n", lnum); 521 | continue; 522 | } 523 | pidLogFileName = malloc(strlen(nt) + 1); 524 | if (!pidLogFileName) { 525 | goto lowMemory; 526 | } 527 | strcpy(pidLogFileName, nt); 528 | } else if (!strcmp(bindAddress, "logcommon")) { 529 | logFormatCommon = 1; 530 | } else { 531 | /* A regular forwarding rule. */ 532 | bindPortS = strtok(0, " \t\r\n"); 533 | if (!bindPortS) { 534 | fprintf(stderr, "rinetd: no bind port " 535 | "specified on line %d.\n", lnum); 536 | continue; 537 | } 538 | service = getservbyname(bindPortS, "tcp"); 539 | if (service) { 540 | bindPort = ntohs(service->s_port); 541 | } else { 542 | bindPort = atoi(bindPortS); 543 | } 544 | if ((bindPort == 0) || (bindPort >= 65536)) { 545 | fprintf(stderr, "rinetd: bind port missing " 546 | "or out of range on line %d.\n", lnum); 547 | continue; 548 | } 549 | connectAddress = strtok(0, " \t\r\n"); 550 | if (!connectAddress) { 551 | fprintf(stderr, "rinetd: no connect address " 552 | "specified on line %d.\n", lnum); 553 | continue; 554 | } 555 | connectPortS = strtok(0, " \t\r\n"); 556 | if (!connectPortS) { 557 | fprintf(stderr, "rinetd: no connect port " 558 | "specified on line %d.\n", lnum); 559 | continue; 560 | } 561 | service = getservbyname(connectPortS, "tcp"); 562 | if (service) { 563 | connectPort = ntohs(service->s_port); 564 | } else { 565 | connectPort = atoi(connectPortS); 566 | } 567 | if ((connectPort == 0) || (connectPort >= 65536)) { 568 | fprintf(stderr, "rinetd: bind port missing " 569 | "or out of range on line %d.\n", lnum); 570 | continue; 571 | } 572 | /* Turn all of this stuff into reasonable addresses */ 573 | if (!getAddress(bindAddress, &iaddr)) { 574 | fprintf(stderr, "rinetd: host %s could not be " 575 | "resolved on line %d.\n", 576 | bindAddress, lnum); 577 | continue; 578 | } 579 | /* Make a server socket */ 580 | seFds[i] = socket(PF_INET, SOCK_STREAM, 0); 581 | if (seFds[i] == INVALID_SOCKET) { 582 | fprintf(stderr, "rinetd: couldn't create " 583 | "server socket!\n"); 584 | seFds[i] = -1; 585 | continue; 586 | } 587 | #ifndef WIN32 588 | if (seFds[i] > maxfd) { 589 | maxfd = seFds[i]; 590 | } 591 | #endif 592 | saddr.sin_family = AF_INET; 593 | memcpy(&saddr.sin_addr, &iaddr, sizeof(iaddr)); 594 | saddr.sin_port = htons(bindPort); 595 | j = 1; 596 | setsockopt(seFds[i], SOL_SOCKET, SO_REUSEADDR, 597 | (const char *) &j, sizeof(j)); 598 | if (bind(seFds[i], (struct sockaddr *) 599 | &saddr, sizeof(saddr)) == SOCKET_ERROR) 600 | { 601 | /* Warn -- don't exit. */ 602 | fprintf(stderr, "rinetd: couldn't bind to " 603 | "address %s port %d\n", 604 | bindAddress, bindPort); 605 | closesocket(seFds[i]); 606 | seFds[i] = INVALID_SOCKET; 607 | continue; 608 | } 609 | if (listen(seFds[i], 5) == SOCKET_ERROR) { 610 | /* Warn -- don't exit. */ 611 | fprintf(stderr, "rinetd: couldn't listen to " 612 | "address %s port %d\n", 613 | bindAddress, bindPort); 614 | closesocket(seFds[i]); 615 | seFds[i] = INVALID_SOCKET; 616 | continue; 617 | } 618 | ioctlsocket(seFds[i], FIONBIO, &j); 619 | if (!getAddress(connectAddress, &iaddr)) { 620 | /* Warn -- don't exit. */ 621 | fprintf(stderr, "rinetd: host %s could not be " 622 | "resolved on line %d.\n", 623 | bindAddress, lnum); 624 | closesocket(seFds[i]); 625 | seFds[i] = INVALID_SOCKET; 626 | continue; 627 | } 628 | seLocalAddrs[i] = iaddr; 629 | seLocalPorts[i] = htons(connectPort); 630 | seFromHosts[i] = malloc(strlen(bindAddress) + 1); 631 | if (!seFromHosts[i]) { 632 | goto lowMemory; 633 | } 634 | strcpy(seFromHosts[i], bindAddress); 635 | seFromPorts[i] = bindPort; 636 | seToHosts[i] = malloc(strlen(connectAddress) + 1); 637 | if (!seToHosts[i]) { 638 | goto lowMemory; 639 | } 640 | strcpy(seToHosts[i], connectAddress); 641 | seToPorts[i] = connectPort; 642 | i++; 643 | if (i < seTotal) { 644 | seAllowRulesTotal[i] = 0; 645 | seDenyRulesTotal[i] = 0; 646 | } 647 | } 648 | } 649 | if (i maxfd) { 1026 | maxfd = nfd; 1027 | } 1028 | #endif /* WIN32 */ 1029 | j = 1; 1030 | ioctlsocket(nfd, FIONBIO, &j); 1031 | j = 0; 1032 | #ifndef WIN32 1033 | setsockopt(nfd, SOL_SOCKET, SO_LINGER, &j, sizeof(j)); 1034 | #endif 1035 | for (j = 0; (j < coTotal); j++) { 1036 | if (coClosed[j]) { 1037 | index = j; 1038 | break; 1039 | } 1040 | } 1041 | if (index == -1) { 1042 | o = coTotal; 1043 | coTotal *= 2; 1044 | if (!SAFE_REALLOC(&reFds, sizeof(int) * o, 1045 | sizeof(SOCKET) * coTotal)) 1046 | { 1047 | goto shortage; 1048 | } 1049 | if (!SAFE_REALLOC(&loFds, sizeof(int) * o, 1050 | sizeof(SOCKET) * coTotal)) 1051 | { 1052 | goto shortage; 1053 | } 1054 | if (!SAFE_REALLOC(&coInputRPos, 1055 | sizeof(int) * o, sizeof(int) * coTotal)) 1056 | { 1057 | goto shortage; 1058 | } 1059 | if (!SAFE_REALLOC(&coInputWPos, 1060 | sizeof(int) * o, sizeof(int) * coTotal)) 1061 | { 1062 | goto shortage; 1063 | } 1064 | if (!SAFE_REALLOC(&coOutputRPos, 1065 | sizeof(int) * o, sizeof(int) * coTotal)) 1066 | { 1067 | goto shortage; 1068 | } 1069 | if (!SAFE_REALLOC(&coOutputWPos, sizeof(int) * o, 1070 | sizeof(int) * coTotal)) 1071 | { 1072 | goto shortage; 1073 | } 1074 | if (!SAFE_REALLOC(&coClosed, sizeof(int) * o, 1075 | sizeof(int) * coTotal)) 1076 | { 1077 | goto shortage; 1078 | } 1079 | if (!SAFE_REALLOC(&coClosing, sizeof(int) * o, 1080 | sizeof(int) * coTotal)) 1081 | { 1082 | goto shortage; 1083 | } 1084 | if (!SAFE_REALLOC(&reClosed, sizeof(int) * o, 1085 | sizeof(int) * coTotal)) 1086 | { 1087 | goto shortage; 1088 | } 1089 | if (!SAFE_REALLOC(&loClosed, sizeof(int) * o, 1090 | sizeof(int) * coTotal)) 1091 | { 1092 | goto shortage; 1093 | } 1094 | if (!SAFE_REALLOC(&coLog, sizeof(int) * o, 1095 | sizeof(int) * coTotal)) 1096 | { 1097 | goto shortage; 1098 | } 1099 | if (!SAFE_REALLOC(&coSe, sizeof(int) * o, 1100 | sizeof(int) * coTotal)) 1101 | { 1102 | goto shortage; 1103 | } 1104 | if (!SAFE_REALLOC(&coBytesInput, sizeof(int) * o, 1105 | sizeof(int) * coTotal)) 1106 | { 1107 | goto shortage; 1108 | } 1109 | if (!SAFE_REALLOC(&reAddresses, 4 * o, 1110 | 4 * coTotal)) 1111 | { 1112 | goto shortage; 1113 | } 1114 | if (!SAFE_REALLOC(&coBytesOutput, sizeof(int) * o, 1115 | sizeof(int) * coTotal)) 1116 | { 1117 | goto shortage; 1118 | } 1119 | if (!SAFE_REALLOC(&coInput, sizeof(char *) * o, 1120 | sizeof(char *) * coTotal)) 1121 | { 1122 | goto shortage; 1123 | } 1124 | if (!SAFE_REALLOC(&coOutput, sizeof(char *) * o, 1125 | sizeof(char *) * coTotal)) 1126 | { 1127 | goto shortage; 1128 | } 1129 | for (j = o; (j < coTotal); j++) { 1130 | coClosed[j] = 1; 1131 | coInput[j] = (char *) 1132 | malloc(sizeof(char) * bufferSpace); 1133 | if (!coInput[j]) { 1134 | int k; 1135 | for (k = o; (k < j); k++) { 1136 | free(coInput[k]); 1137 | free(coOutput[k]); 1138 | } 1139 | goto shortage; 1140 | } 1141 | coOutput[j] = (char *) 1142 | malloc(sizeof(char) * bufferSpace); 1143 | if (!coOutput[j]) { 1144 | int k; 1145 | free(coInput[j]); 1146 | for (k = o; (k < j); k++) { 1147 | free(coInput[k]); 1148 | free(coOutput[k]); 1149 | } 1150 | goto shortage; 1151 | } 1152 | } 1153 | index = o; 1154 | } 1155 | coInputRPos[index] = 0; 1156 | coInputWPos[index] = 0; 1157 | coOutputRPos[index] = 0; 1158 | coOutputWPos[index] = 0; 1159 | coClosed[index] = 0; 1160 | coClosing[index] = 0; 1161 | reClosed[index] = 0; 1162 | loClosed[index] = 0; 1163 | reFds[index] = nfd; 1164 | coBytesInput[index] = 0; 1165 | coBytesOutput[index] = 0; 1166 | coLog[index] = 0; 1167 | coSe[index] = i; 1168 | sin = (struct sockaddr_in *) &addr; 1169 | memcpy(address, &(sin->sin_addr.s_addr), 4); 1170 | memcpy(reAddresses + index * 4, address, 4); 1171 | /* Now, do we want to accept this connection? 1172 | Format it for comparison to a pattern. */ 1173 | sprintf(addressText, "%d.%d.%d.%d", 1174 | address[0], address[1], address[2], address[3]); 1175 | /* 1. Check global allow rules. If there are no 1176 | global allow rules, it's presumed OK at 1177 | this step. If there are any, and it doesn't 1178 | match at least one, kick it out. */ 1179 | if (globalAllowRules) { 1180 | int good = 0; 1181 | for (j = 0; (j < globalAllowRules); j++) { 1182 | if (match(addressText, allowRules[j])) { 1183 | good = 1; 1184 | break; 1185 | } 1186 | } 1187 | if (!good) { 1188 | refuse(index, logNotAllowed); 1189 | return; 1190 | } 1191 | } 1192 | /* 2. Check global deny rules. If it matches 1193 | any of the global deny rules, kick it out. */ 1194 | if (globalDenyRules) { 1195 | for (j = 0; (j < globalDenyRules); j++) { 1196 | if (match(addressText, denyRules[j])) { 1197 | refuse(index, logDenied); 1198 | } 1199 | } 1200 | } 1201 | /* 3. Check allow rules specific to this forwarding rule. 1202 | If there are none, it's OK. If there are any, 1203 | it must match at least one. */ 1204 | if (seAllowRulesTotal[i]) { 1205 | int good = 0; 1206 | for (j = 0; (j < seAllowRulesTotal[i]); j++) { 1207 | if (match(addressText, 1208 | allowRules[seAllowRules[i] + j])) { 1209 | good = 1; 1210 | break; 1211 | } 1212 | } 1213 | if (!good) { 1214 | refuse(index, logNotAllowed); 1215 | return; 1216 | } 1217 | } 1218 | /* 2. Check deny rules specific to this forwarding rule. If 1219 | it matches any of the deny rules, kick it out. */ 1220 | if (seDenyRulesTotal[i]) { 1221 | for (j = 0; (j < seDenyRulesTotal[i]); j++) { 1222 | if (match(addressText, 1223 | denyRules[seDenyRules[i] + j])) { 1224 | refuse(index, logDenied); 1225 | } 1226 | } 1227 | } 1228 | /* Now open a connection to the local server. 1229 | This, too, is nonblocking. Why wait 1230 | for anything when you don't have to? */ 1231 | openLocalFd(i, index); 1232 | return; 1233 | shortage: 1234 | fprintf(stderr, "rinetd: not enough memory to " 1235 | "add slots. Currently %d slots.\n", o); 1236 | /* Go back to the previous total number of slots */ 1237 | coTotal = o; 1238 | } 1239 | 1240 | void openLocalFd(int se, int i) 1241 | { 1242 | int j; 1243 | struct sockaddr_in saddr; 1244 | loFds[i] = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 1245 | if (loFds[i] == INVALID_SOCKET) { 1246 | closesocket(reFds[i]); 1247 | reClosed[i] = 1; 1248 | loClosed[i] = 1; 1249 | coClosed[i] = 1; 1250 | log(i, coSe[i], logLocalSocketFailed); 1251 | return; 1252 | } 1253 | #ifndef WIN32 1254 | if (loFds[i] > maxfd) { 1255 | maxfd = loFds[i]; 1256 | } 1257 | #endif /* WIN32 */ 1258 | /* Bind the local socket */ 1259 | saddr.sin_family = AF_INET; 1260 | saddr.sin_port = INADDR_ANY; 1261 | saddr.sin_addr.s_addr = 0; 1262 | if (bind(loFds[i], (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR) { 1263 | closesocket(loFds[i]); 1264 | closesocket(reFds[i]); 1265 | reClosed[i] = 1; 1266 | loClosed[i] = 1; 1267 | coClosed[i] = 1; 1268 | log(i, coSe[i], logLocalBindFailed); 1269 | return; 1270 | } 1271 | memset(&saddr, 0, sizeof(struct sockaddr_in)); 1272 | saddr.sin_family = AF_INET; 1273 | memcpy(&saddr.sin_addr, &seLocalAddrs[se], sizeof(struct in_addr)); 1274 | saddr.sin_port = seLocalPorts[se]; 1275 | #ifndef WIN32 1276 | #ifdef LINUX 1277 | j = 0; 1278 | setsockopt(loFds[i], SOL_SOCKET, SO_LINGER, &j, sizeof(j)); 1279 | #else 1280 | j = 1024; 1281 | setsockopt(loFds[i], SOL_SOCKET, SO_SNDBUF, &j, sizeof(j)); 1282 | #endif /* LINUX */ 1283 | #endif /* WIN32 */ 1284 | j = 1; 1285 | ioctlsocket(loFds[i], FIONBIO, &j); 1286 | if (connect(loFds[i], (struct sockaddr *)&saddr, 1287 | sizeof(struct sockaddr_in)) == INVALID_SOCKET) 1288 | { 1289 | if ((GetLastError() != WSAEINPROGRESS) && 1290 | (GetLastError() != WSAEWOULDBLOCK)) 1291 | { 1292 | PERROR("rinetd: connect"); 1293 | closesocket(loFds[i]); 1294 | closesocket(reFds[i]); 1295 | reClosed[i] = 1; 1296 | loClosed[i] = 1; 1297 | coClosed[i] = 1; 1298 | log(i, coSe[i], logLocalConnectFailed); 1299 | return; 1300 | } 1301 | } 1302 | } 1303 | 1304 | int getAddress(char *host, struct in_addr *iaddr) 1305 | { 1306 | char *p = host; 1307 | int ishost = 0; 1308 | while (*p) { 1309 | if (!(isdigit(*p) || ((*p) == '.'))) { 1310 | ishost = 1; 1311 | break; 1312 | } 1313 | p++; 1314 | } 1315 | if (ishost) { 1316 | struct hostent *h; 1317 | h = gethostbyname(host); 1318 | if (!h) { 1319 | return 0; 1320 | } 1321 | memcpy( 1322 | (void *) &iaddr->s_addr, 1323 | (void *) h->h_addr, 1324 | 4); 1325 | return 1; 1326 | } else { 1327 | iaddr->s_addr = inet_addr(host); 1328 | return 1; 1329 | } 1330 | } 1331 | 1332 | #ifndef WIN32 1333 | void plumber(int s) 1334 | { 1335 | /* Just reinstall */ 1336 | signal(SIGPIPE, plumber); 1337 | } 1338 | 1339 | void hup(int s) 1340 | { 1341 | /* Learn the new rules */ 1342 | readConfiguration(); 1343 | /* And reinstall the signal handler */ 1344 | signal(SIGHUP, hup); 1345 | } 1346 | #endif /* WIN32 */ 1347 | 1348 | int safeRealloc(void **data, int oldsize, int newsize) 1349 | { 1350 | void *newData = malloc(newsize + 1); 1351 | if (!newData) { 1352 | return 0; 1353 | } 1354 | if (newsize < oldsize) { 1355 | memcpy(newData, *data, newsize); 1356 | } else { 1357 | memcpy(newData, *data, oldsize); 1358 | } 1359 | *data = newData; 1360 | return 1; 1361 | } 1362 | 1363 | void RegisterPID(void) 1364 | { 1365 | FILE *pid_file; 1366 | char *pid_file_name = "/var/run/rinetd.pid"; 1367 | if (pidLogFileName) { 1368 | pid_file_name = pidLogFileName; 1369 | } 1370 | /* add other systems with wherever they register processes */ 1371 | #if defined(LINUX) 1372 | pid_file = fopen(pid_file_name, "w"); 1373 | if (pid_file == NULL) { 1374 | /* non-fatal, non-Linux may lack /var/run... */ 1375 | fprintf(stderr, "rinetd: Couldn't write to " 1376 | "%s. PID was not logged.\n", pid_file_name); 1377 | } else { 1378 | /* error checking deliberately omitted */ 1379 | fprintf(pid_file, "%d\n", getpid()); 1380 | fclose(pid_file); 1381 | } 1382 | #endif /* LINUX */ 1383 | } 1384 | 1385 | unsigned char nullAddress[4] = { 0, 0, 0, 0 }; 1386 | 1387 | struct tm *get_gmtoff(int *tz); 1388 | 1389 | void log(int i, int coSe, int result) 1390 | { 1391 | unsigned char *reAddress; 1392 | int bytesOutput; 1393 | int bytesInput; 1394 | /* Bit of borrowing from Apache logging module here, 1395 | thanks folks */ 1396 | int timz; 1397 | struct tm *t; 1398 | char tstr[1024]; 1399 | char sign; 1400 | if (!log) { 1401 | return; 1402 | } 1403 | t = get_gmtoff(&timz); 1404 | sign = (timz < 0 ? '-' : '+'); 1405 | if (timz < 0) { 1406 | timz = -timz; 1407 | } 1408 | strftime(tstr, sizeof(tstr), "%d/%b/%Y:%H:%M:%S ", t); 1409 | 1410 | if (i != -1) { 1411 | reAddress = reAddresses + i * 4; 1412 | bytesOutput = coBytesOutput[i]; 1413 | bytesInput = coBytesInput[i]; 1414 | } else { 1415 | reAddress = nullAddress; 1416 | bytesOutput = 0; 1417 | bytesInput = 0; 1418 | } 1419 | if (logFile) { 1420 | if (logFormatCommon) { 1421 | /* Fake a common log format log file in a way that 1422 | most web analyzers can do something interesting with. 1423 | We lie and say the protocol is HTTP because we don't 1424 | want the web analyzer to reject the line. We also 1425 | lie and claim success (code 200) because we don't 1426 | want the web analyzer to ignore the line as an 1427 | error and not analyze the "URL." We put a result 1428 | message into our "URL" instead. The last field 1429 | is an extra, giving the number of input bytes, 1430 | after several placeholders meant to fill the 1431 | positions frequently occupied by user agent, 1432 | referrer, and server name information. */ 1433 | fprintf(logFile, "%d.%d.%d.%d - - " 1434 | "[%s %c%.2d%.2d] " 1435 | "\"GET /rinetd-services/%s/%d/%s/%d/%s HTTP/1.0\" " 1436 | "200 %d - - - %d\n", 1437 | reAddress[0], 1438 | reAddress[1], 1439 | reAddress[2], 1440 | reAddress[3], 1441 | tstr, 1442 | sign, 1443 | timz / 60, 1444 | timz % 60, 1445 | seFromHosts[coSe], seFromPorts[coSe], 1446 | seToHosts[coSe], seToPorts[coSe], 1447 | logMessages[result], 1448 | bytesOutput, 1449 | bytesInput); 1450 | } else { 1451 | /* Write an rinetd-specific log entry with a 1452 | less goofy format. */ 1453 | fprintf(logFile, "%s\t%d.%d.%d.%d\t%s\t%d\t%s\t%d\t%d" 1454 | "\t%d\t%s\n", 1455 | tstr, 1456 | reAddress[0], 1457 | reAddress[1], 1458 | reAddress[2], 1459 | reAddress[3], 1460 | seFromHosts[coSe], seFromPorts[coSe], 1461 | seToHosts[coSe], seToPorts[coSe], 1462 | bytesInput, 1463 | bytesOutput, 1464 | logMessages[result]); 1465 | } 1466 | } 1467 | } 1468 | 1469 | int readArgs (int argc, 1470 | char **argv, 1471 | RinetdOptions *options) 1472 | { 1473 | int c; 1474 | 1475 | while (1) { 1476 | int option_index = 0; 1477 | static struct option long_options[] = { 1478 | {"conf-file", 1, 0, 'c'}, 1479 | {"help", 0, 0, 'h'}, 1480 | {"version", 0, 0, 'v'}, 1481 | {0, 0, 0, 0} 1482 | }; 1483 | c = getopt_long (argc, argv, "c:shv", 1484 | long_options, &option_index); 1485 | if (c == -1) { 1486 | break; 1487 | } 1488 | switch (c) { 1489 | case 'c': 1490 | options->conf_file = malloc(strlen(optarg) + 1); 1491 | if (!options->conf_file) { 1492 | fprintf(stderr, "Not enough memory to " 1493 | "launch rinetd.\n"); 1494 | exit(1); 1495 | } 1496 | strcpy(options->conf_file, optarg); 1497 | break; 1498 | case 'h': 1499 | printf("Usage: rinetd [OPTION]\n" 1500 | " -c, --conf-file FILE read configuration " 1501 | "from FILE\n" 1502 | " -h, --help display this help\n" 1503 | " -v, --version display version " 1504 | "number\n\n"); 1505 | printf("Most options are controlled through the\n" 1506 | "configuration file. See the rinetd(8)\n" 1507 | "manpage for more information.\n"); 1508 | exit (0); 1509 | case 'v': 1510 | printf ("rinetd %s\n", VERSION); 1511 | exit (0); 1512 | case '?': 1513 | default: 1514 | exit (1); 1515 | } 1516 | } 1517 | return 0; 1518 | } 1519 | 1520 | /* get_gmtoff was borrowed from Apache. Thanks folks. */ 1521 | 1522 | struct tm *get_gmtoff(int *tz) { 1523 | time_t tt = time(NULL); 1524 | struct tm gmt; 1525 | struct tm *t; 1526 | int days, hours, minutes; 1527 | 1528 | /* Assume we are never more than 24 hours away. */ 1529 | gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */ 1530 | t = localtime(&tt); /* buffer... so be careful */ 1531 | days = t->tm_yday - gmt.tm_yday; 1532 | hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) 1533 | + t->tm_hour - gmt.tm_hour); 1534 | minutes = hours * 60 + t->tm_min - gmt.tm_min; 1535 | *tz = minutes; 1536 | return t; 1537 | } 1538 | 1539 | int patternBad(char *pattern) 1540 | { 1541 | char *p = pattern; 1542 | while (*p) { 1543 | if (isdigit(*p) || ((*p) == '?') || ((*p) == '*') || 1544 | ((*p) == '.')) 1545 | { 1546 | p++; 1547 | } 1548 | return 0; 1549 | } 1550 | return 1; 1551 | } 1552 | 1553 | void refuse(int index, int logCode) 1554 | { 1555 | closesocket(reFds[index]); 1556 | reClosed[index] = 1; 1557 | loClosed[index] = 1; 1558 | coClosed[index] = 1; 1559 | log(index, coSe[index], logCode); 1560 | } 1561 | 1562 | void term(int s) 1563 | { 1564 | /* Obey the request, but first flush the log */ 1565 | if (logFile) { 1566 | fclose(logFile); 1567 | } 1568 | exit(0); 1569 | } 1570 | 1571 | -------------------------------------------------------------------------------- /rinetd.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="rinetd" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 5.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103 6 | 7 | CFG=rinetd - Win32 Debug 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "rinetd.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "rinetd.mak" CFG="rinetd - Win32 Debug" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "rinetd - Win32 Release" (based on "Win32 (x86) Console Application") 21 | !MESSAGE "rinetd - Win32 Debug" (based on "Win32 (x86) Console Application") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP Scc_ProjName "" 26 | # PROP Scc_LocalPath "" 27 | CPP=cl.exe 28 | RSC=rc.exe 29 | 30 | !IF "$(CFG)" == "rinetd - Win32 Release" 31 | 32 | # PROP BASE Use_MFC 0 33 | # PROP BASE Use_Debug_Libraries 0 34 | # PROP BASE Output_Dir "Release" 35 | # PROP BASE Intermediate_Dir "Release" 36 | # PROP BASE Target_Dir "" 37 | # PROP Use_MFC 0 38 | # PROP Use_Debug_Libraries 0 39 | # PROP Output_Dir "Release" 40 | # PROP Intermediate_Dir "Release" 41 | # PROP Ignore_Export_Lib 0 42 | # PROP Target_Dir "" 43 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 44 | # ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 45 | # ADD BASE RSC /l 0x409 /d "NDEBUG" 46 | # ADD RSC /l 0x409 /d "NDEBUG" 47 | BSC32=bscmake.exe 48 | # ADD BASE BSC32 /nologo 49 | # ADD BSC32 /nologo 50 | LINK32=link.exe 51 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 52 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386 53 | 54 | !ELSEIF "$(CFG)" == "rinetd - Win32 Debug" 55 | 56 | # PROP BASE Use_MFC 0 57 | # PROP BASE Use_Debug_Libraries 1 58 | # PROP BASE Output_Dir "Debug" 59 | # PROP BASE Intermediate_Dir "Debug" 60 | # PROP BASE Target_Dir "" 61 | # PROP Use_MFC 0 62 | # PROP Use_Debug_Libraries 1 63 | # PROP Output_Dir "Debug" 64 | # PROP Intermediate_Dir "Debug" 65 | # PROP Ignore_Export_Lib 0 66 | # PROP Target_Dir "" 67 | # ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 68 | # ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 69 | # ADD BASE RSC /l 0x409 /d "_DEBUG" 70 | # ADD RSC /l 0x409 /d "_DEBUG" 71 | BSC32=bscmake.exe 72 | # ADD BASE BSC32 /nologo 73 | # ADD BSC32 /nologo 74 | LINK32=link.exe 75 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept 76 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept 77 | 78 | !ENDIF 79 | 80 | # Begin Target 81 | 82 | # Name "rinetd - Win32 Release" 83 | # Name "rinetd - Win32 Debug" 84 | # Begin Source File 85 | 86 | SOURCE=.\getopt.c 87 | # End Source File 88 | # Begin Source File 89 | 90 | SOURCE=.\match.c 91 | # End Source File 92 | # Begin Source File 93 | 94 | SOURCE=.\rinetd.c 95 | # End Source File 96 | # End Target 97 | # End Project 98 | -------------------------------------------------------------------------------- /rinetd.dsw: -------------------------------------------------------------------------------- 1 | Microsoft Developer Studio Workspace File, Format Version 5.00 2 | # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! 3 | 4 | ############################################################################### 5 | 6 | Project: "rinetd"=.\rinetd.dsp - Package Owner=<4> 7 | 8 | Package=<5> 9 | {{{ 10 | }}} 11 | 12 | Package=<4> 13 | {{{ 14 | }}} 15 | 16 | ############################################################################### 17 | 18 | Global: 19 | 20 | Package=<5> 21 | {{{ 22 | }}} 23 | 24 | Package=<3> 25 | {{{ 26 | }}} 27 | 28 | ############################################################################### 29 | 30 | -------------------------------------------------------------------------------- /rinetd.html: -------------------------------------------------------------------------------- 1 |

rinetd: a user-mode port redirect utility

2 |

Description

3 |

4 | Redirects TCP connections from one IP address and port to another. rinetd 5 | is a single-process server which handles any number of connections to 6 | the address/port pairs specified in the file /etc/rinetd.conf. 7 | Since rinetd runs as a single process using nonblocking I/O, it is 8 | able to redirect a large number of connections without a severe 9 | impact on the machine. This makes it practical to run TCP services 10 | on machines inside an IP masquerading firewall. rinetd does not 11 | redirect FTP, because FTP requires more than one socket. 12 |

13 |

14 | rinetd is typically launched at boot time, using the following syntax: 15 |

16 |

17 | /usr/sbin/rinetd 18 |

19 |

20 | The configuration file is found in the file 21 | /etc/rinetd.conf, unless 22 | another file is specified using the -c command line option. 23 |

24 |

Forwarding Rules

25 |

26 | Most entries in the configuration file are forwarding rules. The 27 | format of a forwarding rule is as follows: 28 |

29 |
 30 | bindaddress bindport connectaddress connectport
 31 | 
32 | For example: 33 |
 34 | 206.125.69.81 80 10.1.1.2 80
 35 | 
36 |

37 | Would redirect all connections to port 80 of the "real" IP address 38 | 206.125.69.81, which could be a virtual interface, through 39 | rinetd to port 80 of the address 10.1.1.2, which would typically 40 | be a machine on the inside of a firewall which has no 41 | direct routing to the outside world. 42 |

43 |

44 | Although responding on individual interfaces rather than on all 45 | interfaces is one of rinetd's primary features, sometimes it is 46 | preferable to respond on all IP addresses that belong to the server. 47 | In this situation, the special IP address 0.0.0.0 48 | can be used. For example: 49 |

50 |
 51 | 0.0.0.0 23 10.1.1.2 23
 52 | 
53 |

54 | Would redirect all connections to port 23, for all IP addresses 55 | assigned to the server. This is the default behavior for most 56 | other programs. 57 |

58 |

59 | Service names can be specified instead of port numbers. On most systems, 60 | service names are defined in the file /etc/services. 61 |

62 |

63 | Both IP addresses and hostnames are accepted for 64 | bindaddress and connectaddress. 65 |

66 |

Allow and Deny Rules

67 |

68 | Configuration files can also contain allow and deny rules. 69 |

70 |

71 | Allow rules which appear before the first forwarding rule are 72 | applied globally: if at least one global allow rule exists, 73 | and the address of a new connection does not 74 | satisfy at least one of the global allow rules, that connection 75 | is immediately rejected, regardless of any other rules. 76 |

77 |

78 | Allow rules which appear after a specific forwarding rule apply 79 | to that forwarding rule only. If at least one allow rule 80 | exists for a particular forwarding rule, and the address of a new 81 | connection does not satisfy at least one of the allow rules 82 | for that forwarding rule, that connection is immediately 83 | rejected, regardless of any other rules. 84 |

85 |

86 | Deny rules which appear before the first forwarding rule are 87 | applied globally: if the address of a new connection satisfies 88 | any of the global allow rules, that connection 89 | is immediately rejected, regardless of any other rules. 90 |

91 |

92 | Deny rules which appear after a specific forwarding rule apply 93 | to that forwarding rule only. If the address of a new 94 | connection satisfies any of the deny rules for that forwarding rule, 95 | that connection is immediately rejected, regardless of any other rules. 96 |

97 |

98 | The format of an allow rule is as follows: 99 |

100 |
101 | allow pattern
102 | 
103 |

104 | Patterns can contain the following characters: 0, 1, 2, 3, 4, 5, 105 | 6, 7, 8, 9, . (period), ?, and *. The ? wildcard matches any one 106 | character. The * wildcard matches any number of characters, including 107 | zero. 108 |

109 |

110 | For example: 111 |

112 |
113 | allow 206.125.69.*
114 | 
115 |

116 | This allow rule matches all IP addresses in the 206.125.69 class C domain. 117 |

118 |

119 | Host names are NOT permitted in allow and deny rules. The performance 120 | cost of looking up IP addresses to find their corresponding names 121 | is prohibitive. Since rinetd is a single process server, all other 122 | connections would be forced to pause during the address lookup. 123 |

124 |

Logging

125 |

126 | rinetd is able to produce a log file in either of two formats: 127 | tab-delimited and web server-style "common log format." 128 |

129 |

130 | By default, rinetd does not produce a log file. To activate logging, add 131 | the following line to the configuration file: 132 |

133 |
134 | logfile log-file-location
135 | 
136 |

137 | Example: 138 |

139 |
140 | logfile /var/log/rinetd.log
141 | 
142 |

143 | By default, rinetd logs in a simple tab-delimited format containing 144 | the following information: 145 |

146 |

147 | Date and time
148 | Client address
149 | 150 | Listening host 151 |
152 | Listening port 153 |
154 | Forwarded-to host 155 |
156 | Forwarded-to port 157 |
158 | Bytes received from client 159 |
160 | Bytes sent to client 161 |
162 | Result message 163 |

164 |

165 | To activate web server-style "common log format" logging, 166 | add the following line to the configuration file: 167 |

168 |
169 | logcommon
170 | 
171 |

Command line options

172 |

173 | The -c command line option is used to specify an alternate 174 | configuration file. 175 |

176 |

177 | The -h command line option produces a short help message. 178 |

179 |

180 | The -v command line option displays the version number. 181 |

182 |

Reinitializing rinetd

183 |

184 | The kill -1 signal (SIGHUP) can be used to cause rinetd 185 | to reload its configuration file without interrupting existing 186 | connections. Under Linux(tm) the process id 187 | is saved in the file /var/run/rinetd.pid 188 | to facilitate the kill -HUP. An alternate 189 | file name can be provided by using the pidlogfile 190 | configuration file option. 191 |

192 |

Bugs

193 |

194 | The server redirected to is not able to identify the host the 195 | client really came from. This cannot be corrected; however, 196 | the log produced by rinetd provides a way to obtain this 197 | information. Under Unix, sockets would theoretically lose data when closed 198 | with SO_LINGER turned off, but in Linux this is not the case 199 | (kernel source comments support this belief on my part). On non-Linux Unix 200 | platforms, alternate code which uses a different trick to work around 201 | blocking close() is provided, but this code is untested. 202 |

203 |

204 | The logging is inadequate. The duration of the connection should be logged. 205 |

206 |

License

207 |

208 | Copyright (c) 1997-2019, Thomas Boutell. 209 | This software is released for free use under the terms of 210 | the GNU General Public License, version 2 or higher. 211 |

212 |

Thanks

213 |

214 | Thanks are due to Bill Davidsen, Libor Pechachek, Sascha Ziemann, 215 | Joel S. Noble, the Apache Group, and many others who have contributed 216 | advice, encouragement and/or source code to this and other open 217 | software projects. 218 |

219 | -------------------------------------------------------------------------------- /rinetd.ncb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boutell/rinetd/e2fbc1559be2931a218e1dec51dcae75a30a7bce/rinetd.ncb -------------------------------------------------------------------------------- /rinetd.opt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boutell/rinetd/e2fbc1559be2931a218e1dec51dcae75a30a7bce/rinetd.opt -------------------------------------------------------------------------------- /rinetd.plg: -------------------------------------------------------------------------------- 1 | --------------------Configuration: rinetd - Win32 Debug-------------------- 2 | Begining build with project "g:\rinetd\rinetd.dsp", at root. 3 | Active configuration is Win32 (x86) Console Application (based on Win32 (x86) Console Application) 4 | 5 | Project's tools are: 6 | "32-bit C/C++ Compiler for 80x86" with flags "/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Debug/rinetd.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /c " 7 | "Win32 Resource Compiler" with flags "/l 0x409 /d "_DEBUG" " 8 | "Browser Database Maker" with flags "/nologo /o"Debug/rinetd.bsc" " 9 | "COFF Linker for 80x86" with flags "kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/rinetd.pdb" /debug /machine:I386 /out:"Debug/rinetd.exe" /pdbtype:sept " 10 | "Custom Build" with flags "" 11 | "" with flags "" 12 | 13 | Creating temp file "c:\windows\TEMP\RSP10D4.TMP" with contents 16 | Creating command line "cl.exe @c:\windows\TEMP\RSP10D4.TMP" 17 | Creating temp file "c:\windows\TEMP\RSP10D5.TMP" with contents 21 | Creating command line "link.exe @c:\windows\TEMP\RSP10D5.TMP" 22 | Compiling... 23 | rinetd.c 24 | G:\rinetd\rinetd.c(965) : warning C4101: 'arg' : unreferenced local variable 25 | G:\rinetd\rinetd.c(992) : warning C4101: 'arg' : unreferenced local variable 26 | G:\rinetd\rinetd.c(1367) : warning C4101: 'pid_file' : unreferenced local variable 27 | Linking... 28 | LINK : LNK6004: Debug/rinetd.exe not found or not built by the last incremental link; performing full link 29 | 30 | 31 | 32 | rinetd.exe - 0 error(s), 3 warning(s) 33 | --------------------------------------------------------------------------------