PageRenderTime 38ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

commands/tcpd/tcpd.c

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