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

/usr.sbin/popa3d/standalone.c

https://bitbucket.org/kmv/aeriebsd-src
C | 331 lines | 252 code | 55 blank | 24 comment | 50 complexity | 7d9a0a68b6065f25807dffb0bb221c64 MD5 | raw file
  1. /*
  2. * Standalone POP server: accepts connections, checks the anti-flood limits,
  3. * logs and starts the actual POP sessions.
  4. */
  5. #include "params.h"
  6. #if POP_STANDALONE
  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <signal.h>
  12. #include <syslog.h>
  13. #include <time.h>
  14. #include <errno.h>
  15. #include <netdb.h>
  16. #include <poll.h>
  17. #include <sys/times.h>
  18. #include <sys/types.h>
  19. #include <sys/wait.h>
  20. #include <sys/socket.h>
  21. #include <netinet/in.h>
  22. #include <arpa/inet.h>
  23. #if DAEMON_LIBWRAP
  24. #include <tcpd.h>
  25. int allow_severity = SYSLOG_PRI_LO;
  26. int deny_severity = SYSLOG_PRI_HI;
  27. #endif
  28. /*
  29. * These are defined in pop_root.c.
  30. */
  31. extern int log_error(char *s);
  32. extern int do_pop_startup(void);
  33. extern int do_pop_session(void);
  34. extern int af;
  35. typedef volatile sig_atomic_t va_int;
  36. /*
  37. * Active POP sessions. Those that were started within the last MIN_DELAY
  38. * seconds are also considered active (regardless of their actual state),
  39. * to allow for limiting the logging rate without throwing away critical
  40. * information about sessions that we could have allowed to proceed.
  41. */
  42. static struct {
  43. char addr[NI_MAXHOST]; /* Source IP address */
  44. va_int pid; /* PID of the server, or 0 for none */
  45. clock_t start; /* When the server was started */
  46. clock_t log; /* When we've last logged a failure */
  47. } sessions[MAX_SESSIONS];
  48. static va_int child_blocked; /* We use blocking to avoid races */
  49. static va_int child_pending; /* Are any dead children waiting? */
  50. int handle(int);
  51. /*
  52. * SIGCHLD handler.
  53. */
  54. static void handle_child(int signum)
  55. {
  56. int saved_errno;
  57. pid_t pid;
  58. int i;
  59. saved_errno = errno;
  60. if (child_blocked)
  61. child_pending = 1;
  62. else {
  63. child_pending = 0;
  64. while ((pid = waitpid(0, NULL, WNOHANG)) > 0)
  65. for (i = 0; i < MAX_SESSIONS; i++)
  66. if (sessions[i].pid == pid) {
  67. sessions[i].pid = 0;
  68. break;
  69. }
  70. }
  71. signal(SIGCHLD, handle_child);
  72. errno = saved_errno;
  73. }
  74. #if DAEMON_LIBWRAP
  75. static void check_access(int sock)
  76. {
  77. struct request_info request;
  78. request_init(&request,
  79. RQ_DAEMON, DAEMON_LIBWRAP_IDENT,
  80. RQ_FILE, sock,
  81. 0);
  82. fromhost(&request);
  83. if (!hosts_access(&request)) {
  84. /* refuse() shouldn't return... */
  85. refuse(&request);
  86. /* ...but just in case */
  87. exit(1);
  88. }
  89. }
  90. #endif
  91. #if POP_OPTIONS
  92. int do_standalone(void)
  93. #else
  94. int main(void)
  95. #endif
  96. {
  97. int error, i, n, true = 1;
  98. struct pollfd *pfds;
  99. struct addrinfo hints, *res, *res0;
  100. char sbuf[NI_MAXSERV];
  101. if (do_pop_startup()) return 1;
  102. snprintf(sbuf, sizeof(sbuf), "%u", DAEMON_PORT);
  103. memset(&hints, 0, sizeof(hints));
  104. hints.ai_socktype = SOCK_STREAM;
  105. hints.ai_family = af;
  106. hints.ai_flags = AI_PASSIVE;
  107. error = getaddrinfo(NULL, sbuf, &hints, &res0);
  108. if (error)
  109. return log_error("getaddrinfo");
  110. i = 0;
  111. for (res = res0; res; res = res->ai_next)
  112. i++;
  113. pfds = calloc(i, sizeof(pfds[0]));
  114. if (!pfds) {
  115. freeaddrinfo(res0);
  116. return log_error("malloc");
  117. }
  118. i = 0;
  119. for (res = res0; res; res = res->ai_next) {
  120. if ((pfds[i].fd = socket(res->ai_family, res->ai_socktype,
  121. res->ai_protocol)) < 0)
  122. continue;
  123. if (setsockopt(pfds[i].fd, SOL_SOCKET, SO_REUSEADDR,
  124. (void *)&true, sizeof(true))) {
  125. close(pfds[i].fd);
  126. continue;
  127. }
  128. #ifdef IPV6_V6ONLY
  129. if (res->ai_family == AF_INET6)
  130. (void)setsockopt(pfds[i].fd, IPPROTO_IPV6, IPV6_V6ONLY,
  131. (void *)&true, sizeof(true));
  132. #endif
  133. if (bind(pfds[i].fd, res->ai_addr, res->ai_addrlen)) {
  134. close(pfds[i].fd);
  135. continue;
  136. }
  137. if (listen(pfds[i].fd, MAX_BACKLOG)) {
  138. close(pfds[i].fd);
  139. continue;
  140. }
  141. pfds[i].events = POLLIN;
  142. i++;
  143. }
  144. freeaddrinfo(res0);
  145. if (i == 0)
  146. return log_error("socket");
  147. n = i;
  148. chdir("/");
  149. setsid();
  150. switch (fork()) {
  151. case -1:
  152. return log_error("fork");
  153. case 0:
  154. break;
  155. default:
  156. return 0;
  157. }
  158. setsid();
  159. child_blocked = 1;
  160. child_pending = 0;
  161. signal(SIGCHLD, handle_child);
  162. memset((void *)sessions, 0, sizeof(sessions));
  163. while (1) {
  164. child_blocked = 0;
  165. if (child_pending) raise(SIGCHLD);
  166. i = poll(pfds, n, INFTIM);
  167. if (i < 0) {
  168. if (errno == EINTR || errno == EAGAIN)
  169. continue;
  170. return log_error("poll");
  171. }
  172. for (i = 0; i < n; i++)
  173. if (pfds[i].revents & POLLIN)
  174. handle(pfds[i].fd);
  175. }
  176. }
  177. int
  178. handle(int sock)
  179. {
  180. clock_t now, log;
  181. int new;
  182. char hbuf[NI_MAXHOST];
  183. struct sockaddr_storage addr;
  184. socklen_t addrlen;
  185. pid_t pid;
  186. struct tms buf;
  187. int error;
  188. int j, n, i;
  189. log = 0;
  190. new = 0;
  191. addrlen = sizeof(addr);
  192. new = accept(sock, (struct sockaddr *)&addr, &addrlen);
  193. /*
  194. * I wish there was a portable way to classify errno's... In this case,
  195. * it appears to be better to risk eating up the CPU on a fatal error
  196. * rather than risk terminating the entire service because of a minor
  197. * temporary error having to do with one particular connection attempt.
  198. */
  199. if (new < 0)
  200. return -1;
  201. error = getnameinfo((struct sockaddr *)&addr, addrlen,
  202. hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
  203. if (error) {
  204. syslog(SYSLOG_PRI_HI,
  205. "could not get host address");
  206. close(new);
  207. return -1;
  208. }
  209. now = times(&buf);
  210. if (!now)
  211. now = 1;
  212. child_blocked = 1;
  213. j = -1;
  214. n = 0;
  215. for (i = 0; i < MAX_SESSIONS; i++) {
  216. if (sessions[i].start > now)
  217. sessions[i].start = 0;
  218. if (sessions[i].pid ||
  219. (sessions[i].start &&
  220. now - sessions[i].start < MIN_DELAY * CLK_TCK)) {
  221. if (strcmp(sessions[i].addr, hbuf) == 0)
  222. if (++n >= MAX_SESSIONS_PER_SOURCE)
  223. break;
  224. } else if (j < 0)
  225. j = i;
  226. }
  227. if (n >= MAX_SESSIONS_PER_SOURCE) {
  228. if (!sessions[i].log ||
  229. now < sessions[i].log ||
  230. now - sessions[i].log >= MIN_DELAY * CLK_TCK) {
  231. syslog(SYSLOG_PRI_HI,
  232. "%s: per source limit reached",
  233. hbuf);
  234. sessions[i].log = now;
  235. }
  236. close(new);
  237. return -1;
  238. }
  239. if (j < 0) {
  240. if (!log ||
  241. now < log || now - log >= MIN_DELAY * CLK_TCK) {
  242. syslog(SYSLOG_PRI_HI,
  243. "%s: sessions limit reached", hbuf);
  244. log = now;
  245. }
  246. close(new);
  247. return -1;
  248. }
  249. switch ((pid = fork())) {
  250. case -1:
  251. syslog(SYSLOG_PRI_ERROR, "%s: fork: %m", hbuf);
  252. close(new);
  253. return -1;
  254. case 0:
  255. #if DAEMON_LIBWRAP
  256. check_access(new);
  257. #endif
  258. syslog(SYSLOG_PRI_LO, "Session from %s",
  259. hbuf);
  260. if (dup2(new, 0) < 0 || dup2(new, 1) < 0 || dup2(new, 2) < 0) {
  261. log_error("dup2");
  262. _exit(1);
  263. }
  264. closefrom(3);
  265. _exit(do_pop_session());
  266. default:
  267. close(new);
  268. strlcpy(sessions[j].addr, hbuf,
  269. sizeof(sessions[j].addr));
  270. sessions[j].pid = (va_int)pid;
  271. sessions[j].start = now;
  272. sessions[j].log = 0;
  273. return 0;
  274. }
  275. }
  276. #endif