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