PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/usr.sbin/faithd/tcp.c

https://gitlab.com/tlevine/DragonFlyBSD
C | 305 lines | 219 code | 28 blank | 58 comment | 47 complexity | 6662eec7c02ca3e01e352b87d169eea7 MD5 | raw file
  1. /* $KAME: tcp.c,v 1.8 2001/11/21 07:40:22 itojun Exp $ */
  2. /*
  3. * Copyright (C) 1997 and 1998 WIDE Project.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the project nor the names of its contributors
  15. * may be used to endorse or promote products derived from this software
  16. * without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
  22. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  24. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  25. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  27. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * $FreeBSD: src/usr.sbin/faithd/tcp.c,v 1.1.2.3 2002/04/28 05:40:29 suz Exp $
  31. * $DragonFly: src/usr.sbin/faithd/tcp.c,v 1.5 2007/05/18 17:05:12 dillon Exp $
  32. */
  33. #include <sys/param.h>
  34. #include <sys/types.h>
  35. #include <sys/socket.h>
  36. #include <sys/ioctl.h>
  37. #include <sys/time.h>
  38. #include <sys/wait.h>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <syslog.h>
  43. #include <unistd.h>
  44. #include <errno.h>
  45. #include <fcntl.h>
  46. #include <signal.h>
  47. #include <netinet/in.h>
  48. #include <arpa/inet.h>
  49. #include <netdb.h>
  50. #include "faithd.h"
  51. static char tcpbuf[16*1024];
  52. /* bigger than MSS and may be lesser than window size */
  53. static int tblen, tboff, oob_exists;
  54. static fd_set readfds, writefds, exceptfds;
  55. static char atmark_buf[2];
  56. static pid_t cpid = (pid_t)0;
  57. static pid_t ppid = (pid_t)0;
  58. volatile time_t child_lastactive = (time_t)0;
  59. static time_t parent_lastactive = (time_t)0;
  60. static void sig_ctimeout(int);
  61. static void sig_child(int);
  62. static void notify_inactive(void);
  63. static void notify_active(void);
  64. static void send_data(int, int, const char *, int);
  65. static void relay(int, int, const char *, int);
  66. /*
  67. * Inactivity timer:
  68. * - child side (ppid != 0) will send SIGUSR1 to parent every (FAITH_TIMEOUT/4)
  69. * second if traffic is active. if traffic is inactive, don't send SIGUSR1.
  70. * - parent side (ppid == 0) will check the last SIGUSR1 it have seen.
  71. */
  72. static void
  73. sig_ctimeout(int sig)
  74. {
  75. /* parent side: record notification from the child */
  76. if (dflag)
  77. syslog(LOG_DEBUG, "activity timer from child");
  78. child_lastactive = time(NULL);
  79. }
  80. /* parent will terminate if child dies. */
  81. static void
  82. sig_child(int sig)
  83. {
  84. int status;
  85. pid_t pid;
  86. pid = wait3(&status, WNOHANG, NULL);
  87. if (pid && WEXITSTATUS(status))
  88. syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
  89. exit_success("terminate connection due to child termination");
  90. }
  91. static void
  92. notify_inactive(void)
  93. {
  94. time_t t;
  95. /* only on parent side... */
  96. if (ppid)
  97. return;
  98. /* parent side should check for timeout. */
  99. t = time(NULL);
  100. if (dflag) {
  101. syslog(LOG_DEBUG, "parent side %sactive, child side %sactive",
  102. (FAITH_TIMEOUT < t - parent_lastactive) ? "in" : "",
  103. (FAITH_TIMEOUT < t - child_lastactive) ? "in" : "");
  104. }
  105. if (FAITH_TIMEOUT < t - child_lastactive
  106. && FAITH_TIMEOUT < t - parent_lastactive) {
  107. /* both side timeouted */
  108. signal(SIGCHLD, SIG_DFL);
  109. kill(cpid, SIGTERM);
  110. wait(NULL);
  111. exit_failure("connection timeout");
  112. /* NOTREACHED */
  113. }
  114. }
  115. static void
  116. notify_active(void)
  117. {
  118. if (ppid) {
  119. /* child side: notify parent of active traffic */
  120. time_t t;
  121. t = time(NULL);
  122. if (FAITH_TIMEOUT / 4 < t - child_lastactive) {
  123. if (kill(ppid, SIGUSR1) < 0) {
  124. exit_failure("terminate connection due to parent termination");
  125. /* NOTREACHED */
  126. }
  127. child_lastactive = t;
  128. }
  129. } else {
  130. /* parent side */
  131. parent_lastactive = time(NULL);
  132. }
  133. }
  134. static void
  135. send_data(int s_rcv, int s_snd, const char *service, int direction)
  136. {
  137. int cc;
  138. if (oob_exists) {
  139. cc = send(s_snd, atmark_buf, 1, MSG_OOB);
  140. if (cc == -1)
  141. goto retry_or_err;
  142. oob_exists = 0;
  143. FD_SET(s_rcv, &exceptfds);
  144. }
  145. for (; tboff < tblen; tboff += cc) {
  146. cc = write(s_snd, tcpbuf + tboff, tblen - tboff);
  147. if (cc < 0)
  148. goto retry_or_err;
  149. }
  150. #ifdef DEBUG
  151. if (tblen) {
  152. if (tblen >= sizeof(tcpbuf))
  153. tblen = sizeof(tcpbuf) - 1;
  154. tcpbuf[tblen] = '\0';
  155. syslog(LOG_DEBUG, "from %s (%dbytes): %s",
  156. direction == 1 ? "client" : "server", tblen, tcpbuf);
  157. }
  158. #endif /* DEBUG */
  159. tblen = 0; tboff = 0;
  160. FD_CLR(s_snd, &writefds);
  161. FD_SET(s_rcv, &readfds);
  162. return;
  163. retry_or_err:
  164. if (errno != EAGAIN)
  165. exit_failure("writing relay data failed: %s", strerror(errno));
  166. FD_SET(s_snd, &writefds);
  167. }
  168. static void
  169. relay(int s_rcv, int s_snd, const char *service, int direction)
  170. {
  171. int atmark, error, maxfd;
  172. struct timeval tv;
  173. fd_set oreadfds, owritefds, oexceptfds;
  174. FD_ZERO(&readfds);
  175. FD_ZERO(&writefds);
  176. FD_ZERO(&exceptfds);
  177. fcntl(s_snd, F_SETFD, O_NONBLOCK);
  178. oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds;
  179. FD_SET(s_rcv, &readfds);
  180. FD_SET(s_rcv, &exceptfds);
  181. oob_exists = 0;
  182. maxfd = (s_rcv > s_snd) ? s_rcv : s_snd;
  183. for (;;) {
  184. tv.tv_sec = FAITH_TIMEOUT / 4;
  185. tv.tv_usec = 0;
  186. oreadfds = readfds;
  187. owritefds = writefds;
  188. oexceptfds = exceptfds;
  189. error = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv);
  190. if (error == -1) {
  191. if (errno == EINTR)
  192. continue;
  193. exit_failure("select: %s", strerror(errno));
  194. } else if (error == 0) {
  195. readfds = oreadfds;
  196. writefds = owritefds;
  197. exceptfds = oexceptfds;
  198. notify_inactive();
  199. continue;
  200. }
  201. /* activity notification */
  202. notify_active();
  203. if (FD_ISSET(s_rcv, &exceptfds)) {
  204. error = ioctl(s_rcv, SIOCATMARK, &atmark);
  205. if (error != -1 && atmark == 1) {
  206. int cc;
  207. oob_read_retry:
  208. cc = read(s_rcv, atmark_buf, 1);
  209. if (cc == 1) {
  210. FD_CLR(s_rcv, &exceptfds);
  211. FD_SET(s_snd, &writefds);
  212. oob_exists = 1;
  213. } else if (cc == -1) {
  214. if (errno == EINTR)
  215. goto oob_read_retry;
  216. exit_failure("reading oob data failed"
  217. ": %s",
  218. strerror(errno));
  219. }
  220. }
  221. }
  222. if (FD_ISSET(s_rcv, &readfds)) {
  223. relaydata_read_retry:
  224. tblen = read(s_rcv, tcpbuf, sizeof(tcpbuf));
  225. tboff = 0;
  226. switch (tblen) {
  227. case -1:
  228. if (errno == EINTR)
  229. goto relaydata_read_retry;
  230. exit_failure("reading relay data failed: %s",
  231. strerror(errno));
  232. /* NOTREACHED */
  233. case 0:
  234. /* to close opposite-direction relay process */
  235. shutdown(s_snd, SHUT_RD);
  236. close(s_rcv);
  237. close(s_snd);
  238. exit_success("terminating %s relay", service);
  239. /* NOTREACHED */
  240. default:
  241. FD_CLR(s_rcv, &readfds);
  242. FD_SET(s_snd, &writefds);
  243. break;
  244. }
  245. }
  246. if (FD_ISSET(s_snd, &writefds))
  247. send_data(s_rcv, s_snd, service, direction);
  248. }
  249. }
  250. void
  251. tcp_relay(int s_src, int s_dst, const char *service)
  252. {
  253. syslog(LOG_INFO, "starting %s relay", service);
  254. child_lastactive = parent_lastactive = time(NULL);
  255. cpid = fork();
  256. switch (cpid) {
  257. case -1:
  258. exit_failure("tcp_relay: can't fork grand child: %s",
  259. strerror(errno));
  260. /* NOTREACHED */
  261. case 0:
  262. /* child process: relay going traffic */
  263. ppid = getppid();
  264. /* this is child so reopen log */
  265. closelog();
  266. openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
  267. relay(s_src, s_dst, service, 1);
  268. /* NOTREACHED */
  269. default:
  270. /* parent process: relay coming traffic */
  271. ppid = (pid_t)0;
  272. signal(SIGUSR1, sig_ctimeout);
  273. signal(SIGCHLD, sig_child);
  274. relay(s_dst, s_src, service, 0);
  275. /* NOTREACHED */
  276. }
  277. }