123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- /* shttpd - accepting connections from passive sockets
- Copyright (C) 2018 Ariadne Devos
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "worker.h"
- #include <sHT/stream.h>
- #include <sHT/accept.h>
- #include <sHT/bugs.h>
- #include <errno.h>
- #include <stddef.h>
- #include <sys/epoll.h>
- #include <sys/socket.h>
- static const struct sHT_bug_option sHT_accept_bugs[] =
- { { .code = EBADFD, .msg = "passive socket not a fd" },
- { .code = EINVAL, .msg = "socket not listening" },
- { .code = ENOTSOCK, .msg = "not a socket" } };
- static int
- sHT_sys_accept(int fd, int flags)
- {
- int new_fd = accept4(fd, NULL, NULL, flags);
- if (new_fd < 0)
- new_fd = -errno;
- return new_fd;
- }
- __attribute__((nonnull (1, 2)))
- static _Bool
- sHT_try_accept(struct sHT_worker *worker, struct sHT_task_accept *task)
- {
- /* It is called a subtask or child task, but sHT has no concept
- of any such DAG. */
- struct sHT_task_stream *subtask = sHT_accept_subtask(worker, task);
- if (subtask == NULL)
- return 1;
- int fd;
- retry:
- /* XXX: use errno correctly */
- fd = sHT_sys_accept(task->accept.fd, SOCK_NONBLOCK | SOCK_CLOEXEC);
- if (fd >= 0) {
- /* Only set this field. It is up to sHT_accept_subtask and
- sHT_accept_logic to fill in other fields. */
- subtask->stream.fd = fd;
- sHT_accept_logic(worker, task, subtask);
- return 0;
- }
- /* TODO: possibly unportable, assumes Linux
- Probably, only a few cases should be added. */
- int err = errno;
- switch(err) {
- #if EAGAIN != EWOULDBLOCK
- case EAGAIN: /* fallthrough */
- #endif
- case EWOULDBLOCK:
- task->task.epollflags &= ~EPOLLIN;
- sHT_accept_abort(worker, task, subtask);
- return 1;
- case EMFILE:
- worker->flags |= sHT_WORKER_LIMIT_FDS;
- return 1;
- case ENFILE:
- /* not that we can really do anything about it,
- but provide a diagnostic anyway */
- worker->flags |= sHT_WORKER_SYSTEM_FDS;
- return 1;
- case ENOBUFS: /* fallthrough */
- case ENOMEM:
- worker->flags |= sHT_WORKER_OOM;
- return 1;
- case EINTR:
- goto retry;
- case EPROTO: /* fallthrough */
- case ECONNABORTED: /* fallthrough */
- /* firewall rules */
- case EPERM: /* fallthrough */
- return 1;
- /* Linux accept4(2): Various Linux kernels can return other
- errors ... */
- case ENOSR: /* fallthrough */
- case ESOCKTNOSUPPORT: /* fallthrough */
- case EPROTONOSUPPORT: /* fallthrough */
- case ETIMEDOUT:
- return 1;
- /* From Linux accept4(2) Error handling: */
- case ENETDOWN: /* fallthrough */
- case ENOPROTOOPT: /* fallthrough */
- case EHOSTDOWN: /* fallthrough */
- case ENONET: /* fallthrough */
- case EHOSTUNREACH: /* fallthrough */
- case EOPNOTSUPP: /* fallthrough */
- case ENETUNREACH:
- return 1;
- default:
- sHT_bug(sHT_accept_bugs, err);
- return 0;
- }
- }
- void
- sHT_accept_task(struct sHT_worker *worker, struct sHT_task_accept *task)
- {
- if (!(task->task.epollflags & EPOLLIN))
- /* TODO: other values */
- return;
- /* Something you should now when debugging:
- if N connections are active,
- you have allocated enough tasks, papers, etc. to handle N
- connections,
- but there is a connection waiting,
- sHT_try_accept will nevertheless try to accept it,
- but as there are not enough resources, sHT_TASK_* will be set.
- So make sure that, in test cases, you allow an error message in
- this case (which I did not). */
- for (int i = 0; i < task->accept.max_accept_iterations; i++)
- if (sHT_try_accept(worker, task))
- return;
- }
|