1 /*
2 * Copyright (C) 1995, 1996 Karl-Johan Johnsson.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <signal.h>
10 #include <errno.h>
11 #include <sys/time.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <netdb.h>
16 #include <sys/wait.h>
17
18 #undef False
19 #define False 0
20 #undef True
21 #define True 1
22
23 #define DEFAULT_REMOTE_PORT 119
24
25 /*
26 * Define this if <unistd.h> doesn't declare getopt() and friends.
27 */
28 #define BOGUS_UNISTD
29
perror_exit(const char * msg)30 static void perror_exit(const char *msg)
31 {
32 perror(msg);
33 _exit(1);
34 }
35
xrealloc(void * ptr,size_t size)36 static void* xrealloc(void *ptr, size_t size)
37 {
38 if (!ptr)
39 ptr = malloc(size); /* Hack for broken C libraries */
40 else
41 ptr = realloc(ptr, size);
42
43 if (!ptr)
44 perror_exit("malloc");
45
46 return ptr;
47 }
48
49 static struct in_addr *allowed_hosts = NULL;
50 static long n_hosts = 0;
51 static long n_alloced = 0;
52
allow_host(char * host)53 static void allow_host(char *host)
54 {
55 struct hostent *hent;
56
57 hent = gethostbyname(host);
58 if (!hent) {
59 fprintf(stderr, "No such host: %s\n", host);
60 return;
61 }
62
63 if (n_hosts >= n_alloced)
64 allowed_hosts = xrealloc(allowed_hosts,
65 (n_alloced = 2 * (n_alloced + 1)) *
66 sizeof allowed_hosts[0]);
67
68 memcpy(&allowed_hosts[n_hosts++], hent->h_addr, hent->h_length);
69 }
70
is_allowed(struct sockaddr_in * addr)71 static int is_allowed(struct sockaddr_in *addr)
72 {
73 long i;
74
75 for (i = 0 ; i < n_hosts ; i++)
76 if (memcmp(&addr->sin_addr, &allowed_hosts[i],
77 sizeof addr->sin_addr) == 0)
78 return True;
79
80 return False;
81 }
82
get_remote_host(char * host)83 static struct sockaddr_in get_remote_host(char *host)
84 {
85 struct sockaddr_in addr;
86 struct hostent *hent;
87 char *c;
88
89 memset(&addr, 0, sizeof addr);
90 addr.sin_family = PF_INET;
91 addr.sin_port = htons(DEFAULT_REMOTE_PORT);
92 c = strchr(host, ':');
93 if (c) {
94 *c++ = '\0';
95 /* FIXME: error check? */
96 addr.sin_port = htons(atoi(c));
97 }
98
99 hent = gethostbyname(host);
100 if (!hent) {
101 fprintf(stderr, "No such host: %s\n", host);
102 _exit(1);
103 }
104
105 memcpy(&addr.sin_addr, hent->h_addr, hent->h_length);
106
107 return addr;
108 }
109
do_rw(int from,int to)110 static int do_rw(int from, int to)
111 {
112 static char buffer[16384];
113 char *c;
114 long i, j, n;
115
116 do {
117 n = read(from, buffer, sizeof buffer);
118 } while (n < 0 && errno == EINTR);
119
120 c = buffer;
121 i = n;
122 while (i > 0) {
123 j = write(to, c, i);
124 if (j < 0)
125 if (errno == EINTR)
126 continue;
127 else
128 return -1;
129 if (j == 0)
130 return 0;
131 c += j;
132 i -= j;
133 }
134
135 return n;
136 }
137
open_serv_socket(struct sockaddr_in * serv_addr)138 static int open_serv_socket(struct sockaddr_in *serv_addr)
139 {
140 int ss, tmp;
141
142 ss = socket(PF_INET, SOCK_STREAM, 0);
143 if (ss < 0)
144 return -1;
145
146 do {
147 tmp = connect(ss, (struct sockaddr *)serv_addr, sizeof *serv_addr);
148 } while (tmp < 0 && errno == EINTR);
149
150 if (tmp < 0) {
151 perror("connect");
152 close(ss);
153 return -1;
154 }
155
156 return ss;
157 }
158
child(struct sockaddr_in * serv_addr,int cs)159 static int child(struct sockaddr_in *serv_addr, int cs)
160 {
161 fd_set fds;
162 int max, ss, tmp = 0;
163
164 ss = open_serv_socket(serv_addr);
165 if (ss < 0)
166 return -1;
167
168 FD_ZERO(&fds);
169 FD_SET(cs, &fds);
170 FD_SET(ss, &fds);
171 max = (cs > ss ? cs : ss) + 1;
172
173 for (;;) {
174 fd_set rd_fds = fds;
175
176 tmp = select(max, &rd_fds, NULL, NULL, NULL);
177 if (tmp < 0)
178 if (errno == EINTR)
179 continue;
180 else
181 break;
182
183 if (FD_ISSET(cs, &rd_fds)) {
184 tmp = do_rw(cs, ss);
185 if (tmp <= 0)
186 break;
187 }
188
189 if (FD_ISSET(ss, &rd_fds)) {
190 tmp = do_rw(ss, cs);
191 if (tmp <= 0)
192 break;
193 }
194 }
195
196 if (tmp < 0)
197 perror("read/write");
198
199 close(ss);
200
201 return tmp;
202 }
203
doit(struct sockaddr_in * serv_addr,int port,int single)204 static void doit(struct sockaddr_in *serv_addr, int port, int single)
205 {
206 struct sockaddr_in local_addr, cli_addr;
207 int as;
208 int yes = 1;
209
210 memset(&local_addr, 0, sizeof local_addr);
211 local_addr.sin_family = PF_INET;
212 local_addr.sin_port = port;
213
214 as = socket(PF_INET, SOCK_STREAM, 0);
215 if (as < 0)
216 perror_exit("socket");
217 if (setsockopt(as, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof yes) < 0)
218 perror_exit("setsockopt(SO_REUSEADDR)");
219 if (bind(as, (struct sockaddr *)&local_addr, sizeof local_addr) < 0)
220 perror_exit("bind");
221 if (listen(as, 5) < 0)
222 perror_exit("listen");
223
224 for (;;) {
225 int cs, len;
226
227 do {
228 len = sizeof cli_addr;
229 cs = accept(as, (struct sockaddr *)&cli_addr, &len);
230 } while (cs < 0 && errno == EINTR);
231
232 if (cs < 0)
233 perror_exit("accept");
234
235 if (!is_allowed(&cli_addr)) {
236 static char deny[] = "502 You have no permission to talk!\r\n";
237
238 if (serv_addr->sin_port == htons(119))
239 write(cs, deny, sizeof deny - 1);
240 shutdown(cs, 2);
241 close(cs);
242 continue;
243 }
244
245 if (single)
246 child(serv_addr, cs);
247 else
248 switch (fork()) {
249 case -1:
250 perror_exit("fork");
251 break;
252 case 0:
253 if (child(serv_addr, cs) < 0)
254 _exit(1);
255 _exit(0);
256 default:
257 break;
258 }
259
260 close(cs);
261 }
262 }
263
264 /************************************************************************/
265
sig_chld(int no)266 static void sig_chld(int no)
267 {
268 while (waitpid(-1, NULL, WNOHANG) > 0);
269 }
270
install_sig_handlers(void)271 static void install_sig_handlers(void)
272 {
273 struct sigaction sig_act;
274
275 sig_act.sa_handler = sig_chld;
276 sig_act.sa_flags = 0;
277 sigfillset(&sig_act.sa_mask);
278 if (sigaction(SIGCHLD, &sig_act, NULL) < 0)
279 perror_exit("sigaction(SIGCHLD)");
280
281 sig_act.sa_handler = SIG_IGN;
282 sig_act.sa_flags = 0;
283 sigemptyset(&sig_act.sa_mask);
284 if (sigaction(SIGPIPE, &sig_act, NULL) < 0)
285 perror_exit("sigaction(SIGPIPE)");
286 }
287
usage(char * name)288 static void usage(char *name)
289 {
290 fprintf(stderr, "Usage: %s -p port [-s] -h host [...] remotehost:port\n",
291 name);
292 _exit(1);
293 }
294
main(int argc,char * argv[])295 int main(int argc, char *argv[])
296 {
297 struct sockaddr_in serv_addr;
298 int single = False;
299 int port = 0;
300 int c;
301 #ifdef BOGUS_UNISTD
302 extern int getopt(int, char *const[], const char*);
303 extern char *optarg;
304 extern int optind, opterr;
305 #endif
306
307 freopen("/dev/null", "r", stdin);
308 freopen("/dev/null", "w", stdout);
309
310 install_sig_handlers();
311
312 opterr = 0;
313 while ((c = getopt(argc, argv, "p:h:s")) != -1)
314 switch (c) {
315 case 'p': /* port to listen on */
316 if (sscanf(optarg, "%d", &port) != 1 || port < 0)
317 usage(argv[0]);
318 port = htons(port);
319 break;
320 case 's':
321 single = True;
322 break;
323 case 'h':
324 allow_host(optarg);
325 break;
326 default:
327 usage(argv[0]);
328 break;
329 }
330
331 if (optind != argc - 1 || port == 0)
332 usage(argv[0]);
333
334 if (n_hosts <= 0) {
335 fprintf(stderr, "No allowed hosts!\n");
336 _exit(1);
337 }
338
339 serv_addr = get_remote_host(argv[optind]);
340 doit(&serv_addr, port, single);
341
342 return 0;
343 }
344