sockrw.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /* shttpd - IO on stream 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 "fd.h"
  14. #include <sHT/bugs.h>
  15. #include <sHT/compiler.h>
  16. #include <sHT/nospec.h>
  17. #include <sHT/stream.h>
  18. #include "worker.h"
  19. #include <errno.h>
  20. #include <stddef.h>
  21. #include <stdint.h>
  22. #include <sys/epoll.h>
  23. #include <sys/socket.h>
  24. /* These are sorted in order of expected prevalence */
  25. enum sHT_send_err_type {
  26. sHT_SEND_BLOCKING,
  27. sHT_SEND_GRACEFUL_RESET,
  28. sHT_SEND_BLUNT_RESET,
  29. sHT_SEND_GRACEFUL_CLOSE,
  30. /* like in EINTR */
  31. sHT_SEND_INTERRUPTED,
  32. /* e.g. timeout, connection reset */
  33. sHT_SEND_TRANSIENT,
  34. sHT_SEND_KERNEL_OOM,
  35. /* Anything we didn't expect.
  36. (We expect malicious clients,
  37. but no sHT bugs.)
  38. This should be logged as a warning. */
  39. sHT_SEND_OTHER,
  40. };
  41. __attribute__((const))
  42. static enum sHT_send_err_type
  43. sHT_classify_sentrecv_tcp(int err)
  44. {
  45. switch (err) {
  46. #if EWOULDBLOCK != EAGAIN
  47. case EWOULDBLOCK: /* fallthrough */
  48. #endif
  49. case EAGAIN:
  50. return sHT_SEND_BLOCKING;
  51. case EINTR:
  52. return sHT_SEND_INTERRUPTED;
  53. case ECONNRESET:
  54. return sHT_SEND_GRACEFUL_RESET;
  55. case EPIPE:
  56. return sHT_SEND_GRACEFUL_CLOSE;
  57. case ETIMEDOUT: /* fallthrough */
  58. case EHOSTUNREACH:
  59. return sHT_SEND_BLUNT_RESET;
  60. case ENOBUFS:
  61. /* no busy loops? */
  62. return sHT_SEND_TRANSIENT;
  63. case ENOMEM:
  64. return sHT_SEND_KERNEL_OOM;
  65. default:
  66. return sHT_SEND_OTHER;
  67. }
  68. }
  69. /* True if it should be retried directly, false otherwise. */
  70. __attribute__((nonnull (1, 2)))
  71. static _Bool
  72. sHT_socket_sendrecv_errno(struct sHT_worker *worker, struct sHT_task_stream *task, int err, uint32_t epollflags)
  73. {
  74. /* XXX use err, not errno */
  75. switch (sHT_classify_sentrecv_tcp(errno)) {
  76. case sHT_SEND_BLOCKING:
  77. task->task.epollflags &= ~epollflags;
  78. return 0;
  79. case sHT_SEND_GRACEFUL_CLOSE:
  80. task->stream.flags |= sHT_STREAM_WRITE_EOF;
  81. task->task.epollflags &= ~epollflags;
  82. return 0;
  83. case sHT_SEND_GRACEFUL_RESET:
  84. task->stream.flags |= sHT_STREAM_WRITE_EOF | sHT_STREAM_READ_EOF | sHT_STREAM_RESET_GRACEFUL;
  85. task->task.epollflags &= ~(EPOLLIN | EPOLLOUT);
  86. return 0;
  87. case sHT_SEND_BLUNT_RESET:
  88. task->stream.flags |= sHT_STREAM_WRITE_EOF | sHT_STREAM_READ_EOF | sHT_STREAM_RESET_BLUNT;
  89. task->task.epollflags &= ~(EPOLLIN | EPOLLOUT);
  90. return 0;
  91. case sHT_SEND_INTERRUPTED:
  92. return 1;
  93. case sHT_SEND_TRANSIENT:
  94. /* TODO: may be a good idea to log these too,
  95. as an informational message */
  96. sHT_queue_task(worker->todo, &task->task);
  97. return 0;
  98. case sHT_SEND_KERNEL_OOM:
  99. /* No, I don't like overcommiting.
  100. Killing is better than hanging, though. */
  101. worker->flags |= sHT_WORKER_OOM;
  102. sHT_queue_task(worker->todo, &task->task);
  103. return 0;
  104. case sHT_SEND_OTHER:
  105. sHT_todo("didn't recognise TCP error");
  106. default:
  107. sHT_assert(0);
  108. }
  109. }
  110. void
  111. sHT_socket_sendsome_tcp(struct sHT_worker *worker, struct sHT_task_stream *task)
  112. {
  113. const unsigned char *buf = task->stream.to_write.first;
  114. size_t start = task->stream.to_write.offset;
  115. size_t end = (task->stream.to_write.offset + task->stream.to_write.length) % sHT_PAPER_SIZE;
  116. if (end < start)
  117. end = sHT_PAPER_SIZE;
  118. end = sHT_index_nospec(end, sHT_PAPER_SIZE - start);
  119. do {
  120. /* XXX: speculatively negative sizes? */
  121. ssize_t sent = send(task->stream.fd, buf + start, end - start, MSG_DONTWAIT | MSG_NOSIGNAL);
  122. if (sent < 0)
  123. continue;
  124. /* some data is on the kernel queue, or the NIC ... */
  125. sHT_assert(sent <= task->stream.to_write.length);
  126. task->stream.to_write.offset = (task->stream.to_write.offset + sent) % sHT_PAPER_SIZE;
  127. task->stream.to_write.length -= sent;
  128. return;
  129. } while (sHT_unlikely(sHT_socket_sendrecv_errno(worker, task, errno, EPOLLOUT)));
  130. }
  131. void
  132. sHT_socket_readsome_tcp(struct sHT_worker *worker, struct sHT_task_stream *task)
  133. {
  134. unsigned char *buf = task->stream.has_read.first;
  135. size_t start = (task->stream.has_read.offset + task->stream.has_read.length) % sHT_PAPER_SIZE;
  136. size_t end = task->stream.has_read.offset;
  137. if (end < start)
  138. end = sHT_PAPER_SIZE;
  139. end = sHT_index_nospec(end, sHT_PAPER_SIZE - start);
  140. /* XXX: speculatively negative sizes? */
  141. do {
  142. ssize_t received;
  143. received = recv(task->stream.fd, buf + start, end - start, MSG_DONTWAIT);
  144. if (received < 0)
  145. continue;
  146. sHT_assert(received <= sHT_PAPER_SIZE - task->stream.has_read.length);
  147. task->stream.has_read.length += received;
  148. return;
  149. } while (sHT_unlikely(sHT_socket_sendrecv_errno(worker, task, errno, EPOLLOUT)));
  150. }