Home | History | Annotate | Line # | Download | only in faithd
tcp.c revision 1.4
      1  1.4  itojun /*	$NetBSD: tcp.c,v 1.4 2001/02/15 17:58:55 itojun Exp $	*/
      2  1.4  itojun /*	$KAME: tcp.c,v 1.5 2000/09/29 03:48:31 sakane 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.1  itojun static int tblen, tboff, oob_exists;
     58  1.1  itojun static fd_set readfds, writefds, exceptfds;
     59  1.1  itojun static char atmark_buf[2];
     60  1.1  itojun static pid_t cpid = (pid_t)0;
     61  1.1  itojun static pid_t ppid = (pid_t)0;
     62  1.1  itojun static time_t child_lastactive = (time_t)0;
     63  1.1  itojun static time_t parent_lastactive = (time_t)0;
     64  1.1  itojun 
     65  1.1  itojun static void sig_ctimeout __P((int));
     66  1.1  itojun static void sig_child __P((int));
     67  1.1  itojun static void notify_inactive __P((void));
     68  1.1  itojun static void notify_active __P((void));
     69  1.1  itojun static void send_data __P((int, int, const char *, int));
     70  1.1  itojun static void relay __P((int, int, const char *, int));
     71  1.1  itojun 
     72  1.1  itojun /*
     73  1.1  itojun  * Inactivity timer:
     74  1.1  itojun  * - child side (ppid != 0) will send SIGUSR1 to parent every (FAITH_TIMEOUT/4)
     75  1.1  itojun  *   second if traffic is active.  if traffic is inactive, don't send SIGUSR1.
     76  1.1  itojun  * - parent side (ppid == 0) will check the last SIGUSR1 it have seen.
     77  1.1  itojun  */
     78  1.1  itojun static void
     79  1.1  itojun sig_ctimeout(int sig)
     80  1.1  itojun {
     81  1.1  itojun 	/* parent side: record notification from the child */
     82  1.1  itojun 	if (dflag)
     83  1.1  itojun 		syslog(LOG_DEBUG, "activity timer from child");
     84  1.1  itojun 	child_lastactive = time(NULL);
     85  1.1  itojun }
     86  1.1  itojun 
     87  1.1  itojun /* parent will terminate if child dies. */
     88  1.1  itojun static void
     89  1.1  itojun sig_child(int sig)
     90  1.1  itojun {
     91  1.1  itojun 	int status;
     92  1.1  itojun 	pid_t pid;
     93  1.1  itojun 
     94  1.1  itojun 	pid = wait3(&status, WNOHANG, (struct rusage *)0);
     95  1.3  itojun 	if (pid && WEXITSTATUS(status))
     96  1.1  itojun 		syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
     97  1.3  itojun 	exit_success("terminate connection due to child termination");
     98  1.1  itojun }
     99  1.1  itojun 
    100  1.1  itojun static void
    101  1.1  itojun notify_inactive()
    102  1.1  itojun {
    103  1.1  itojun 	time_t t;
    104  1.1  itojun 
    105  1.1  itojun 	/* only on parent side... */
    106  1.1  itojun 	if (ppid)
    107  1.1  itojun 		return;
    108  1.1  itojun 
    109  1.1  itojun 	/* parent side should check for timeout. */
    110  1.1  itojun 	t = time(NULL);
    111  1.1  itojun 	if (dflag) {
    112  1.1  itojun 		syslog(LOG_DEBUG, "parent side %sactive, child side %sactive",
    113  1.1  itojun 			(FAITH_TIMEOUT < t - parent_lastactive) ? "in" : "",
    114  1.1  itojun 			(FAITH_TIMEOUT < t - child_lastactive) ? "in" : "");
    115  1.1  itojun 	}
    116  1.1  itojun 
    117  1.1  itojun 	if (FAITH_TIMEOUT < t - child_lastactive
    118  1.1  itojun 	 && FAITH_TIMEOUT < t - parent_lastactive) {
    119  1.1  itojun 		/* both side timeouted */
    120  1.1  itojun 		signal(SIGCHLD, SIG_DFL);
    121  1.1  itojun 		kill(cpid, SIGTERM);
    122  1.1  itojun 		wait(NULL);
    123  1.1  itojun 		exit_failure("connection timeout");
    124  1.1  itojun 		/* NOTREACHED */
    125  1.1  itojun 	}
    126  1.1  itojun }
    127  1.1  itojun 
    128  1.1  itojun static void
    129  1.1  itojun notify_active()
    130  1.1  itojun {
    131  1.1  itojun 	if (ppid) {
    132  1.1  itojun 		/* child side: notify parent of active traffic */
    133  1.1  itojun 		time_t t;
    134  1.1  itojun 		t = time(NULL);
    135  1.1  itojun 		if (FAITH_TIMEOUT / 4 < t - child_lastactive) {
    136  1.1  itojun 			if (kill(ppid, SIGUSR1) < 0) {
    137  1.1  itojun 				exit_failure("terminate connection due to parent termination");
    138  1.1  itojun 				/* NOTREACHED */
    139  1.1  itojun 			}
    140  1.1  itojun 			child_lastactive = t;
    141  1.1  itojun 		}
    142  1.1  itojun 	} else {
    143  1.1  itojun 		/* parent side */
    144  1.1  itojun 		parent_lastactive = time(NULL);
    145  1.1  itojun 	}
    146  1.1  itojun }
    147  1.1  itojun 
    148  1.1  itojun static void
    149  1.1  itojun send_data(int s_rcv, int s_snd, const char *service, int direction)
    150  1.1  itojun {
    151  1.1  itojun 	int cc;
    152  1.1  itojun 
    153  1.1  itojun 	if (oob_exists) {
    154  1.1  itojun 		cc = send(s_snd, atmark_buf, 1, MSG_OOB);
    155  1.1  itojun 		if (cc == -1)
    156  1.1  itojun 			goto retry_or_err;
    157  1.1  itojun 		oob_exists = 0;
    158  1.1  itojun 		FD_SET(s_rcv, &exceptfds);
    159  1.1  itojun 	}
    160  1.1  itojun 
    161  1.1  itojun 	for (; tboff < tblen; tboff += cc) {
    162  1.1  itojun 		cc = write(s_snd, tcpbuf + tboff, tblen - tboff);
    163  1.1  itojun 		if (cc < 0)
    164  1.1  itojun 			goto retry_or_err;
    165  1.1  itojun 	}
    166  1.1  itojun #ifdef DEBUG
    167  1.1  itojun 	if (tblen) {
    168  1.1  itojun 		if (tblen >= sizeof(tcpbuf))
    169  1.1  itojun 			tblen = sizeof(tcpbuf) - 1;
    170  1.1  itojun 	    	tcpbuf[tblen] = '\0';
    171  1.1  itojun 		syslog(LOG_DEBUG, "from %s (%dbytes): %s",
    172  1.1  itojun 		       direction == 1 ? "client" : "server", tblen, tcpbuf);
    173  1.1  itojun 	}
    174  1.1  itojun #endif /* DEBUG */
    175  1.1  itojun 	tblen = 0; tboff = 0;
    176  1.1  itojun 	FD_CLR(s_snd, &writefds);
    177  1.1  itojun 	FD_SET(s_rcv, &readfds);
    178  1.1  itojun 	return;
    179  1.1  itojun     retry_or_err:
    180  1.1  itojun 	if (errno != EAGAIN)
    181  1.1  itojun 		exit_failure("writing relay data failed: %s", ERRSTR);
    182  1.1  itojun 	FD_SET(s_snd, &writefds);
    183  1.1  itojun }
    184  1.1  itojun 
    185  1.1  itojun static void
    186  1.1  itojun relay(int s_rcv, int s_snd, const char *service, int direction)
    187  1.1  itojun {
    188  1.1  itojun 	int atmark, error, maxfd;
    189  1.1  itojun 	struct timeval tv;
    190  1.1  itojun 	fd_set oreadfds, owritefds, oexceptfds;
    191  1.1  itojun 
    192  1.1  itojun 	FD_ZERO(&readfds);
    193  1.1  itojun 	FD_ZERO(&writefds);
    194  1.1  itojun 	FD_ZERO(&exceptfds);
    195  1.1  itojun 	fcntl(s_snd, F_SETFD, O_NONBLOCK);
    196  1.1  itojun 	oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds;
    197  1.4  itojun 	FD_SET(s_rcv, &readfds);
    198  1.4  itojun 	FD_SET(s_rcv, &exceptfds);
    199  1.1  itojun 	oob_exists = 0;
    200  1.1  itojun 	maxfd = (s_rcv > s_snd) ? s_rcv : s_snd;
    201  1.1  itojun 
    202  1.1  itojun 	for (;;) {
    203  1.1  itojun 		tv.tv_sec = FAITH_TIMEOUT / 4;
    204  1.1  itojun 		tv.tv_usec = 0;
    205  1.1  itojun 		oreadfds = readfds;
    206  1.1  itojun 		owritefds = writefds;
    207  1.1  itojun 		oexceptfds = exceptfds;
    208  1.1  itojun 		error = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv);
    209  1.1  itojun 		if (error == -1) {
    210  1.1  itojun 			if (errno == EINTR)
    211  1.1  itojun 				continue;
    212  1.1  itojun 			exit_failure("select: %s", ERRSTR);
    213  1.1  itojun 		} else if (error == 0) {
    214  1.1  itojun 			readfds = oreadfds;
    215  1.1  itojun 			writefds = owritefds;
    216  1.1  itojun 			exceptfds = oexceptfds;
    217  1.1  itojun 			notify_inactive();
    218  1.1  itojun 			continue;
    219  1.1  itojun 		}
    220  1.1  itojun 
    221  1.1  itojun 		/* activity notification */
    222  1.1  itojun 		notify_active();
    223  1.1  itojun 
    224  1.1  itojun 		if (FD_ISSET(s_rcv, &exceptfds)) {
    225  1.1  itojun 			error = ioctl(s_rcv, SIOCATMARK, &atmark);
    226  1.1  itojun 			if (error != -1 && atmark == 1) {
    227  1.1  itojun 				int cc;
    228  1.1  itojun 			    oob_read_retry:
    229  1.1  itojun 				cc = read(s_rcv, atmark_buf, 1);
    230  1.1  itojun 				if (cc == 1) {
    231  1.1  itojun 					FD_CLR(s_rcv, &exceptfds);
    232  1.1  itojun 					FD_SET(s_snd, &writefds);
    233  1.1  itojun 					oob_exists = 1;
    234  1.1  itojun 				} else if (cc == -1) {
    235  1.1  itojun 					if (errno == EINTR)
    236  1.1  itojun 						goto oob_read_retry;
    237  1.1  itojun 					exit_failure("reading oob data failed"
    238  1.1  itojun 						     ": %s",
    239  1.1  itojun 						     ERRSTR);
    240  1.1  itojun 				}
    241  1.1  itojun 			}
    242  1.1  itojun 		}
    243  1.1  itojun 		if (FD_ISSET(s_rcv, &readfds)) {
    244  1.1  itojun 		    relaydata_read_retry:
    245  1.1  itojun 			tblen = read(s_rcv, tcpbuf, sizeof(tcpbuf));
    246  1.1  itojun 			tboff = 0;
    247  1.1  itojun 
    248  1.1  itojun 			switch (tblen) {
    249  1.1  itojun 			case -1:
    250  1.1  itojun 				if (errno == EINTR)
    251  1.1  itojun 					goto relaydata_read_retry;
    252  1.1  itojun 				exit_failure("reading relay data failed: %s",
    253  1.1  itojun 					     ERRSTR);
    254  1.1  itojun 				/* NOTREACHED */
    255  1.1  itojun 			case 0:
    256  1.1  itojun 				/* to close opposite-direction relay process */
    257  1.1  itojun 				shutdown(s_snd, 0);
    258  1.1  itojun 
    259  1.1  itojun 				close(s_rcv);
    260  1.1  itojun 				close(s_snd);
    261  1.1  itojun 				exit_success("terminating %s relay", service);
    262  1.1  itojun 				/* NOTREACHED */
    263  1.1  itojun 			default:
    264  1.1  itojun 				FD_CLR(s_rcv, &readfds);
    265  1.1  itojun 				FD_SET(s_snd, &writefds);
    266  1.1  itojun 				break;
    267  1.1  itojun 			}
    268  1.1  itojun 		}
    269  1.1  itojun 		if (FD_ISSET(s_snd, &writefds))
    270  1.1  itojun 			send_data(s_rcv, s_snd, service, direction);
    271  1.1  itojun 	}
    272  1.1  itojun }
    273  1.1  itojun 
    274  1.1  itojun void
    275  1.1  itojun tcp_relay(int s_src, int s_dst, const char *service)
    276  1.1  itojun {
    277  1.1  itojun 	syslog(LOG_INFO, "starting %s relay", service);
    278  1.1  itojun 
    279  1.1  itojun 	child_lastactive = parent_lastactive = time(NULL);
    280  1.1  itojun 
    281  1.1  itojun 	cpid = fork();
    282  1.1  itojun 	switch (cpid) {
    283  1.1  itojun 	case -1:
    284  1.1  itojun 		exit_failure("tcp_relay: can't fork grand child: %s", ERRSTR);
    285  1.1  itojun 		/* NOTREACHED */
    286  1.1  itojun 	case 0:
    287  1.1  itojun 		/* child process: relay going traffic */
    288  1.1  itojun 		ppid = getppid();
    289  1.1  itojun 		/* this is child so reopen log */
    290  1.1  itojun 		closelog();
    291  1.1  itojun 		openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
    292  1.1  itojun 		relay(s_src, s_dst, service, 1);
    293  1.1  itojun 		/* NOTREACHED */
    294  1.1  itojun 	default:
    295  1.1  itojun 		/* parent process: relay coming traffic */
    296  1.1  itojun 		ppid = (pid_t)0;
    297  1.1  itojun 		signal(SIGUSR1, sig_ctimeout);
    298  1.1  itojun 		signal(SIGCHLD, sig_child);
    299  1.1  itojun 		relay(s_dst, s_src, service, 0);
    300  1.1  itojun 		/* NOTREACHED */
    301  1.1  itojun 	}
    302  1.1  itojun }
    303