source: trunk/minix/commands/simple/tcpd.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 6.6 KB
Line 
1/*
2tcpd.c
3*/
4
5#include <sys/types.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <limits.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <signal.h>
14#include <minix/config.h>
15#include <sys/ioctl.h>
16#include <sys/wait.h>
17#include <net/hton.h>
18#include <net/netlib.h>
19#include <net/gen/in.h>
20#include <net/gen/inet.h>
21#include <net/gen/netdb.h>
22#include <net/gen/tcp.h>
23#include <net/gen/tcp_io.h>
24
25/* This program can be compiled to be paranoid, i.e. check incoming connection
26 * according to an access file, or to trust anyone. The much smaller "trust
27 * 'em" binary will call the paranoid version if the access file exists.
28 */
29
30static char *arg0, *service;
31static unsigned nchildren;
32
33static void report(const char *label)
34{
35 int err= errno;
36
37 fprintf(stderr, "%s %s: %s: %s\n", arg0, service, label, strerror(err));
38 errno= err;
39}
40
41static void sigchld(int sig)
42{
43 while (waitpid(0, NULL, WNOHANG) > 0) {
44 if (nchildren > 0) nchildren--;
45 }
46}
47
48static void release(int *fd)
49{
50 if (*fd != -1) {
51 close(*fd);
52 *fd= -1;
53 }
54}
55
56static void usage(void)
57{
58 fprintf(stderr,
59 "Usage: %s [-d] [-m maxclients] service program [arg ...]\n",
60 arg0);
61 exit(1);
62}
63
64int main(int argc, char **argv)
65{
66 tcpport_t port;
67 int last_failed = 0;
68 struct nwio_tcpcl tcplistenopt;
69 struct nwio_tcpconf tcpconf;
70 struct nwio_tcpopt tcpopt;
71 char *tcp_device;
72 struct servent *servent;
73 int tcp_fd, client_fd, count, r;
74 int pfd[2];
75 unsigned stall= 0;
76 struct sigaction sa;
77 sigset_t chldmask, chldunmask, oldmask;
78 char **progv;
79
80#if !PARANOID
81# define debug 0
82# define max_children ((unsigned) -1)
83 arg0= argv[0];
84
85 /* Switch to the paranoid version of me if there are flags, or if
86 * there is an access file.
87 */
88 if (argv[1][0] == '-' || access(_PATH_SERVACCES, F_OK) == 0) {
89 execv("/usr/bin/tcpdp", argv);
90 report("tcpdp");
91 exit(1);
92 }
93 if (argc < 3) usage();
94 service= argv[1];
95 progv= argv+2;
96
97#else /* PARANOID */
98 int debug, i;
99 unsigned max_children;
100
101 arg0= argv[0];
102 debug= 0;
103 max_children= -1;
104 i= 1;
105 while (i < argc && argv[i][0] == '-') {
106 char *opt= argv[i++] + 1;
107 unsigned long m;
108 char *end;
109
110 if (*opt == '-' && opt[1] == 0) break; /* -- */
111
112 while (*opt != 0) switch (*opt++) {
113 case 'd':
114 debug= 1;
115 break;
116 case 'm':
117 if (*opt == 0) {
118 if (i == argc) usage();
119 opt= argv[i++];
120 }
121 m= strtoul(opt, &end, 10);
122 if (m <= 0 || m > UINT_MAX || *end != 0) usage();
123 max_children= m;
124 opt= "";
125 break;
126 default:
127 usage();
128 }
129 }
130 service= argv[i++];
131 progv= argv+i;
132 if (i >= argc) usage();
133#endif
134
135 /* The interface to start the service on. */
136 if ((tcp_device= getenv("TCP_DEVICE")) == NULL) tcp_device= TCP_DEVICE;
137
138 /* Let SIGCHLD interrupt whatever I'm doing. */
139 sigemptyset(&chldmask);
140 sigaddset(&chldmask, SIGCHLD);
141 sigprocmask(SIG_BLOCK, &chldmask, &oldmask);
142 chldunmask= oldmask;
143 sigdelset(&chldunmask, SIGCHLD);
144 sigemptyset(&sa.sa_mask);
145 sa.sa_flags = 0;
146 sa.sa_handler = sigchld;
147 sigaction(SIGCHLD, &sa, NULL);
148
149 /* Open a socket to the service I'm to serve. */
150 if ((servent= getservbyname(service, "tcp")) == NULL) {
151 unsigned long p;
152 char *end;
153
154 p= strtoul(service, &end, 0);
155 if (p <= 0 || p > 0xFFFF || *end != 0) {
156 fprintf(stderr, "%s: %s: Unknown service\n",
157 arg0, service);
158 exit(1);
159 }
160 port= htons((tcpport_t) p);
161 } else {
162 port= servent->s_port;
163
164 if (debug)
165 {
166 fprintf(stderr, "%s %s: listening to port %u\n",
167 arg0, service, ntohs(port));
168 }
169 }
170
171 /* No client yet. */
172 client_fd= -1;
173
174 while (1) {
175 if ((tcp_fd= open(tcp_device, O_RDWR)) < 0) {
176 report(tcp_device);
177#if 0
178 if (errno == ENOENT || errno == ENODEV
179 || errno == ENXIO) {
180 exit(1);
181 }
182#endif
183 last_failed = 1;
184 goto bad;
185 }
186 if(last_failed)
187 fprintf(stderr, "%s %s: %s: Ok\n",
188 arg0, service, tcp_device);
189 last_failed = 0;
190
191 tcpconf.nwtc_flags= NWTC_LP_SET | NWTC_UNSET_RA | NWTC_UNSET_RP;
192 tcpconf.nwtc_locport= port;
193
194 if (ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf) < 0) {
195 report("Can't configure TCP channel");
196 exit(1);
197 }
198
199 tcpopt.nwto_flags= NWTO_DEL_RST;
200
201 if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) < 0) {
202 report("Can't set TCP options");
203 exit(1);
204 }
205
206 if (client_fd != -1) {
207 /* We have a client, so start a server for it. */
208
209 tcpopt.nwto_flags= 0;
210 (void) ioctl(client_fd, NWIOSTCPOPT, &tcpopt);
211
212 fflush(NULL);
213
214 /* Create a pipe to serve as an error indicator. */
215 if (pipe(pfd) < 0) {
216 report("pipe");
217 goto bad;
218 }
219 (void) fcntl(pfd[1], F_SETFD,
220 fcntl(pfd[1], F_GETFD) | FD_CLOEXEC);
221
222 /* Fork and exec. */
223 switch (fork()) {
224 case -1:
225 report("fork");
226 close(pfd[0]);
227 close(pfd[1]);
228 goto bad;
229 case 0:
230 close(tcp_fd);
231 close(pfd[0]);
232#if PARANOID
233 /* Check if access to this service allowed. */
234 if (ioctl(client_fd, NWIOGTCPCONF, &tcpconf) == 0
235 && tcpconf.nwtc_remaddr != tcpconf.nwtc_locaddr
236 && !servxcheck(tcpconf.nwtc_remaddr, argv[1], NULL)
237 ) {
238 exit(1);
239 }
240#endif
241 sigprocmask(SIG_SETMASK, &oldmask, NULL);
242 dup2(client_fd, 0);
243 dup2(client_fd, 1);
244 close(client_fd);
245 execvp(progv[0], progv);
246 report(progv[0]);
247 write(pfd[1], &errno, sizeof(errno));
248 exit(1);
249 default:
250 nchildren++;
251 release(&client_fd);
252 close(pfd[1]);
253 r= read(pfd[0], &errno, sizeof(errno));
254 close(pfd[0]);
255 if (r != 0) goto bad;
256 break;
257 }
258 }
259
260 while (nchildren >= max_children) {
261 /* Too many clients, wait for one to die off. */
262 sigsuspend(&chldunmask);
263 }
264
265 /* Wait for a new connection. */
266 sigprocmask(SIG_UNBLOCK, &chldmask, NULL);
267
268 tcplistenopt.nwtcl_flags= 0;
269 while (ioctl(tcp_fd, NWIOTCPLISTEN, &tcplistenopt) < 0) {
270 if (errno != EINTR) {
271 if (errno != EAGAIN || debug) {
272 report("Unable to listen");
273 }
274 goto bad;
275 }
276 }
277 sigprocmask(SIG_BLOCK, &chldmask, NULL);
278
279 /* We got a connection. */
280 client_fd= tcp_fd;
281 tcp_fd= -1;
282
283 if (debug && ioctl(client_fd, NWIOGTCPCONF, &tcpconf) == 0) {
284 fprintf(stderr, "%s %s: Connection from %s:%u\n",
285 arg0, service,
286 inet_ntoa(tcpconf.nwtc_remaddr),
287 ntohs(tcpconf.nwtc_remport));
288 }
289 /* All is well, no need to stall. */
290 stall= 0;
291 continue;
292
293 bad:
294 /* All is not well, release resources. */
295 release(&tcp_fd);
296 release(&client_fd);
297
298 /* Wait a bit if this happens more than once. */
299 if (stall != 0) {
300 if (debug) {
301 fprintf(stderr, "%s %s: stalling %u second%s\n",
302 arg0, service,
303 stall, stall == 1 ? "" : "s");
304 }
305 sleep(stall);
306 stall <<= 1;
307 } else {
308 stall= 1;
309 }
310 }
311}
Note: See TracBrowser for help on using the repository browser.