accept.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /* shttpd - accepting connections from passive sockets
  2. Copyright (C) 2018 Ariadne Devos
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. #include "worker.h"
  14. #include <sHT/stream.h>
  15. #include <sHT/accept.h>
  16. #include <sHT/bugs.h>
  17. #include <errno.h>
  18. #include <stddef.h>
  19. #include <sys/epoll.h>
  20. #include <sys/socket.h>
  21. static const struct sHT_bug_option sHT_accept_bugs[] =
  22. { { .code = EBADFD, .msg = "passive socket not a fd" },
  23. { .code = EINVAL, .msg = "socket not listening" },
  24. { .code = ENOTSOCK, .msg = "not a socket" } };
  25. static int
  26. sHT_sys_accept(int fd, int flags)
  27. {
  28. int new_fd = accept4(fd, NULL, NULL, flags);
  29. if (new_fd < 0)
  30. new_fd = -errno;
  31. return new_fd;
  32. }
  33. __attribute__((nonnull (1, 2)))
  34. static _Bool
  35. sHT_try_accept(struct sHT_worker *worker, struct sHT_task_accept *task)
  36. {
  37. /* It is called a subtask or child task, but sHT has no concept
  38. of any such DAG. */
  39. struct sHT_task_stream *subtask = sHT_accept_subtask(worker, task);
  40. if (subtask == NULL)
  41. return 1;
  42. int fd;
  43. retry:
  44. /* XXX: use errno correctly */
  45. fd = sHT_sys_accept(task->accept.fd, SOCK_NONBLOCK | SOCK_CLOEXEC);
  46. if (fd >= 0) {
  47. /* Only set this field. It is up to sHT_accept_subtask and
  48. sHT_accept_logic to fill in other fields. */
  49. subtask->stream.fd = fd;
  50. sHT_accept_logic(worker, task, subtask);
  51. return 0;
  52. }
  53. /* TODO: possibly unportable, assumes Linux
  54. Probably, only a few cases should be added. */
  55. int err = errno;
  56. switch(err) {
  57. #if EAGAIN != EWOULDBLOCK
  58. case EAGAIN: /* fallthrough */
  59. #endif
  60. case EWOULDBLOCK:
  61. task->task.epollflags &= ~EPOLLIN;
  62. sHT_accept_abort(worker, task, subtask);
  63. return 1;
  64. case EMFILE:
  65. worker->flags |= sHT_WORKER_LIMIT_FDS;
  66. return 1;
  67. case ENFILE:
  68. /* not that we can really do anything about it,
  69. but provide a diagnostic anyway */
  70. worker->flags |= sHT_WORKER_SYSTEM_FDS;
  71. return 1;
  72. case ENOBUFS: /* fallthrough */
  73. case ENOMEM:
  74. worker->flags |= sHT_WORKER_OOM;
  75. return 1;
  76. case EINTR:
  77. goto retry;
  78. case EPROTO: /* fallthrough */
  79. case ECONNABORTED: /* fallthrough */
  80. /* firewall rules */
  81. case EPERM: /* fallthrough */
  82. return 1;
  83. /* Linux accept4(2): Various Linux kernels can return other
  84. errors ... */
  85. case ENOSR: /* fallthrough */
  86. case ESOCKTNOSUPPORT: /* fallthrough */
  87. case EPROTONOSUPPORT: /* fallthrough */
  88. case ETIMEDOUT:
  89. return 1;
  90. /* From Linux accept4(2) Error handling: */
  91. case ENETDOWN: /* fallthrough */
  92. case ENOPROTOOPT: /* fallthrough */
  93. case EHOSTDOWN: /* fallthrough */
  94. case ENONET: /* fallthrough */
  95. case EHOSTUNREACH: /* fallthrough */
  96. case EOPNOTSUPP: /* fallthrough */
  97. case ENETUNREACH:
  98. return 1;
  99. default:
  100. sHT_bug(sHT_accept_bugs, err);
  101. return 0;
  102. }
  103. }
  104. void
  105. sHT_accept_task(struct sHT_worker *worker, struct sHT_task_accept *task)
  106. {
  107. if (!(task->task.epollflags & EPOLLIN))
  108. /* TODO: other values */
  109. return;
  110. /* Something you should now when debugging:
  111. if N connections are active,
  112. you have allocated enough tasks, papers, etc. to handle N
  113. connections,
  114. but there is a connection waiting,
  115. sHT_try_accept will nevertheless try to accept it,
  116. but as there are not enough resources, sHT_TASK_* will be set.
  117. So make sure that, in test cases, you allow an error message in
  118. this case (which I did not). */
  119. for (int i = 0; i < task->accept.max_accept_iterations; i++)
  120. if (sHT_try_accept(worker, task))
  121. return;
  122. }