summaryrefslogtreecommitdiffhomepage
path: root/tcpfwd-direct.c
blob: b6112839201edc5e09cc62551096ce3b35c3020a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "includes.h"
#include "session.h"
#include "dbutil.h"
#include "channel.h"
#include "tcpfwd-direct.h"
#include "runopts.h"

#ifndef DISABLE_TCPFWD_DIRECT
static int newtcpdirect(struct Channel * channel);
static int newtcp(const char * host, int port);

const struct ChanType chan_tcpdirect = {
	1, /* sepfds */
	"direct-tcpip",
	newtcpdirect, /* init */
	NULL, /* checkclose */
	NULL, /* reqhandler */
	NULL /* closehandler */
};


/* Called upon creating a new direct tcp channel (ie we connect out to an
 * address */
static int newtcpdirect(struct Channel * channel) {

	unsigned char* desthost = NULL;
	unsigned int destport;
	unsigned char* orighost = NULL;
	unsigned int origport;
	char portstring[6];
	int sock;
	int len;
	int ret = DROPBEAR_FAILURE;

	if (opts.nolocaltcp) {
		TRACE(("leave newtcpdirect: local tcp forwarding disabled"));
		goto out;
	}

	desthost = buf_getstring(ses.payload, &len);
	if (len > MAX_HOST_LEN) {
		TRACE(("leave newtcpdirect: desthost too long"));
		goto out;
	}

	destport = buf_getint(ses.payload);
	
	orighost = buf_getstring(ses.payload, &len);
	if (len > MAX_HOST_LEN) {
		TRACE(("leave newtcpdirect: orighost too long"));
		goto out;
	}

	origport = buf_getint(ses.payload);

	/* best be sure */
	if (origport > 65535 || destport > 65535) {
		TRACE(("leave newtcpdirect: port > 65535"));
		goto out;
	}

	snprintf(portstring, sizeof(portstring), "%d", destport);
	sock = connect_remote(desthost, portstring, 1, NULL);
	if (sock < 0) {
		TRACE(("leave newtcpdirect: sock failed"));
		goto out;
	}

	ses.maxfd = MAX(ses.maxfd, sock);

	/* Note that infd is actually the "outgoing" direction on the
	 * tcp connection, vice versa for outfd.
	 * We don't set outfd, that will get set after the connection's
	 * progress succeeds */
	channel->infd = sock;
	channel->initconn = 1;
	
	ret = DROPBEAR_SUCCESS;

out:
	m_free(desthost);
	m_free(orighost);
	TRACE(("leave newtcpdirect: ret %d", ret));
	return ret;
}

/* Initiate a new TCP connection - this is non-blocking, so the socket
 * returned will need to be checked for success when it is first written.
 * Similarities with OpenSSH's connect_to() are not coincidental.
 * Returns -1 on failure */
#if 0
static int newtcp(const char * host, int port) {

	int sock = -1;
	char portstring[6];
	struct addrinfo *res = NULL, *ai;
	int val;

	struct addrinfo hints;

	TRACE(("enter newtcp"));

	memset(&hints, 0, sizeof(hints));
	/* TCP, either ip4 or ip6 */
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_family = PF_UNSPEC;

	snprintf(portstring, sizeof(portstring), "%d", port);
	if (getaddrinfo(host, portstring, &hints, &res) != 0) {
		if (res) {
			freeaddrinfo(res);
		}
		TRACE(("leave newtcp: failed getaddrinfo"));
		return -1;
	}

	/* Use the first socket that works */
	for (ai = res; ai != NULL; ai = ai->ai_next) {
		
		if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) {
			continue;
		}

		sock = socket(ai->ai_family, SOCK_STREAM, 0);
		if (sock < 0) {
			TRACE(("TCP socket() failed"));
			continue;
		}

		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
			close(sock);
			TRACE(("TCP non-blocking failed"));
			continue;
		}

		/* non-blocking, so it might return without success (EINPROGRESS) */
		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
			if (errno == EINPROGRESS) {
				TRACE(("connect in progress"));
			} else {
				close(sock);
				TRACE(("TCP connect failed"));
				continue;
			}
		} 
		break;
	}

	freeaddrinfo(res);
	
	if (ai == NULL) {
		return -1;
	}

	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
	return sock;
}
#endif
#endif /* DISABLE_TCPFWD_DIRECT */