1 /* Proxytunnel - (C) 2001-2008 Jos Visser / Mark Janssen    */
2 /* Contact:                  josv@osp.nl / maniac@maniac.nl */
3 
4 /*
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19 
20 /* io.c */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 
29 #include "proxytunnel.h"
30 #include "io.h"
31 
32 #define ACTIVE 1
33 #define CLOSED 0
34 
35 /*
36  * Read one line of data from the tunnel. Line is terminated by a
37  * newline character. Result is stored in buf.
38  */
readline(PTSTREAM * pts)39 int readline(PTSTREAM *pts) {
40 	char *p = buf;
41 	char c = 0;
42 	int i = 0;
43 
44 	/* Read one character at a time into buf, until a newline is encountered. */
45 	while ( c != 10 && ( i < SIZE - 1 ) ) {
46 		if( stream_read( pts, &c ,1) <= 0) {
47 			my_perror( "Socket read error" );
48 			exit( 1 );
49 		}
50 
51 		*p = c;
52 		p++;
53 		i++;
54 	}
55 
56 	*p = 0;
57 
58 	if( args_info.verbose_flag ) {
59 		/* Copy line of data into dstr without trailing newline */
60 		char *dstr = malloc(strlen(buf) + 1);
61 		strncpy( dstr, buf, strlen(buf));
62 		if (strcmp(dstr, ""))
63 			message( " <- %s\n", dstr );
64 	}
65 	return strlen( buf );
66 }
67 
68 /*
69  * Bond stream1 and stream2 together; any data received in stream1 is relayed
70  * to stream2, and vice-versa.
71  */
cpio(PTSTREAM * stream1,PTSTREAM * stream2)72 void cpio(PTSTREAM *stream1, PTSTREAM *stream2) {
73 	fd_set readfds;
74 	fd_set writefds;
75 	fd_set exceptfds;
76 	int in_max_fd, out_max_fd, max_fd;
77 
78 	/* Find the biggest file descriptor for select() */
79 	in_max_fd = MAX(stream_get_incoming_fd(stream1), stream_get_incoming_fd(stream2));
80 	out_max_fd = MAX(stream_get_outgoing_fd(stream1), stream_get_outgoing_fd(stream2));
81 	max_fd = MAX(in_max_fd, out_max_fd);
82 
83 	/* We are never interested in sockets being available for write */
84 	FD_ZERO( &writefds );
85 
86 
87 	/* experimental timeout */
88 	struct timeval select_timeout;
89 	select_timeout.tv_sec = 30; /* should be fine */
90 	select_timeout.tv_usec = 0;
91 
92 	if( args_info.verbose_flag )
93 		message( "\nTunnel established.\n" );
94 
95         int stream_status = ACTIVE;
96 	while( stream_status == ACTIVE ) {
97 		/* Clear the interesting socket sets */
98 		FD_ZERO( &readfds );
99 		FD_ZERO( &exceptfds );
100 
101 		/* We want to know whether stream1 or stream2 is ready for reading */
102 		FD_SET( stream_get_incoming_fd(stream1), &readfds );
103 		FD_SET( stream_get_incoming_fd(stream2), &readfds );
104 
105 		/* And we want to know about exceptional conditions on either stream */
106 		FD_SET( stream_get_incoming_fd(stream1), &exceptfds );
107 		FD_SET( stream_get_outgoing_fd(stream1), &exceptfds );
108 		FD_SET( stream_get_incoming_fd(stream2), &exceptfds );
109 		FD_SET( stream_get_outgoing_fd(stream2), &exceptfds );
110 
111                 /* reset the timeout, since select() does modify this struct! */
112                 select_timeout.tv_sec = 30;
113                 select_timeout.tv_usec = 0;
114 
115 		/* Wait/timeout something happens on the registered sockets/files */
116 		int number_of_fds_ready;
117 		number_of_fds_ready = select( max_fd + 1, &readfds, &writefds, &exceptfds, &select_timeout );
118 		if ( number_of_fds_ready < 0 ) {
119 			perror("select error");
120 			exit(1);
121 		}
122 
123 		if (number_of_fds_ready > 0) {
124 			/* Is stream1 ready for read? If so, copy a block of data
125 			 * from stream1 to stream2. Or else if stream2
126 			 * is ready for read, copy a block of data from the
127 			 * stream2 to stream1. Otherwise an exceptional condition
128 			 * is flagged and the program is terminated.
129 			 */
130 			if ( FD_ISSET( stream_get_incoming_fd(stream1), &readfds ) ) {
131 				if ( stream_copy(stream1, stream2 ) )
132 					stream_status = CLOSED;
133 			} else if( FD_ISSET( stream_get_incoming_fd(stream2), &readfds ) ) {
134 				if( stream_copy(stream2, stream1 ) )
135 					stream_status = CLOSED;
136 			} else {
137 				my_perror( "Exceptional condition" );
138 				stream_status = CLOSED;
139 			}
140 		}
141 	}
142 	closeall();
143 }
144