1/*
2 * Dropbear SSH
3 *
4 * Copyright (c) 2002,2003 Matt Johnston
5 * All rights reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE. */
24
25#include "includes.h"
26#include "options.h"
27#include "dbutil.h"
28#include "tcpfwd.h"
29#include "channel.h"
30#include "runopts.h"
31#include "session.h"
32#include "ssh.h"
33
34#ifdef ENABLE_CLI_REMOTETCPFWD
35static int newtcpforwarded(struct Channel * channel);
36
37const struct ChanType cli_chan_tcpremote = {
38	1, /* sepfds */
39	"forwarded-tcpip",
40	newtcpforwarded,
41	NULL,
42	NULL,
43	NULL
44};
45#endif
46
47#ifdef ENABLE_CLI_LOCALTCPFWD
48static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
49		unsigned int remoteport);
50static const struct ChanType cli_chan_tcplocal = {
51	1, /* sepfds */
52	"direct-tcpip",
53	NULL,
54	NULL,
55	NULL,
56	NULL
57};
58#endif
59
60#ifdef ENABLE_CLI_LOCALTCPFWD
61void setup_localtcp() {
62
63	int ret;
64
65	TRACE(("enter setup_localtcp"))
66
67	if (cli_opts.localfwds == NULL) {
68		TRACE(("cli_opts.localfwds == NULL"))
69	}
70
71	while (cli_opts.localfwds != NULL) {
72		ret = cli_localtcp(cli_opts.localfwds->listenport,
73				cli_opts.localfwds->connectaddr,
74				cli_opts.localfwds->connectport);
75		if (ret == DROPBEAR_FAILURE) {
76			dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
77					cli_opts.localfwds->listenport,
78					cli_opts.localfwds->connectaddr,
79					cli_opts.localfwds->connectport);
80		}
81
82		cli_opts.localfwds = cli_opts.localfwds->next;
83	}
84	TRACE(("leave setup_localtcp"))
85
86}
87
88static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
89		unsigned int remoteport) {
90
91	struct TCPListener* tcpinfo = NULL;
92	int ret;
93
94	TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr,
95				remoteport));
96
97	tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
98
99	tcpinfo->sendaddr = m_strdup(remoteaddr);
100	tcpinfo->sendport = remoteport;
101
102	if (opts.listen_fwd_all) {
103		tcpinfo->listenaddr = m_strdup("");
104	} else {
105		tcpinfo->listenaddr = m_strdup("localhost");
106	}
107	tcpinfo->listenport = listenport;
108
109	tcpinfo->chantype = &cli_chan_tcplocal;
110	tcpinfo->tcp_type = direct;
111
112	ret = listen_tcpfwd(tcpinfo);
113
114	if (ret == DROPBEAR_FAILURE) {
115		m_free(tcpinfo);
116	}
117	TRACE(("leave cli_localtcp: %d", ret))
118	return ret;
119}
120#endif /* ENABLE_CLI_LOCALTCPFWD */
121
122#ifdef  ENABLE_CLI_REMOTETCPFWD
123static void send_msg_global_request_remotetcp(int port) {
124
125	char* listenspec = NULL;
126	TRACE(("enter send_msg_global_request_remotetcp"))
127
128	CHECKCLEARTOWRITE();
129	buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
130	buf_putstring(ses.writepayload, "tcpip-forward", 13);
131	buf_putbyte(ses.writepayload, 0);
132	if (opts.listen_fwd_all) {
133		listenspec = "";
134	} else {
135		listenspec = "localhost";
136	}
137	/* TODO: IPv6? */;
138	buf_putstring(ses.writepayload, listenspec, strlen(listenspec));
139	buf_putint(ses.writepayload, port);
140
141	encrypt_packet();
142
143	TRACE(("leave send_msg_global_request_remotetcp"))
144}
145
146void setup_remotetcp() {
147
148	struct TCPFwdList * iter = NULL;
149
150	TRACE(("enter setup_remotetcp"))
151
152	if (cli_opts.remotefwds == NULL) {
153		TRACE(("cli_opts.remotefwds == NULL"))
154	}
155
156	iter = cli_opts.remotefwds;
157
158	while (iter != NULL) {
159		send_msg_global_request_remotetcp(iter->listenport);
160		iter = iter->next;
161	}
162	TRACE(("leave setup_remotetcp"))
163}
164
165static int newtcpforwarded(struct Channel * channel) {
166
167	unsigned int origport;
168	struct TCPFwdList * iter = NULL;
169	char portstring[NI_MAXSERV];
170	int sock;
171	int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
172
173	/* We don't care what address they connected to */
174	buf_eatstring(ses.payload);
175
176	origport = buf_getint(ses.payload);
177
178	/* Find which port corresponds */
179	iter = cli_opts.remotefwds;
180
181	while (iter != NULL) {
182		if (origport == iter->listenport) {
183			break;
184		}
185		iter = iter->next;
186	}
187
188	if (iter == NULL) {
189		/* We didn't request forwarding on that port */
190		dropbear_log(LOG_INFO, "Server send unrequested port, from port %d",
191										origport);
192		goto out;
193	}
194
195	snprintf(portstring, sizeof(portstring), "%d", iter->connectport);
196	sock = connect_remote(iter->connectaddr, portstring, 1, NULL);
197	if (sock < 0) {
198		TRACE(("leave newtcpdirect: sock failed"))
199		err = SSH_OPEN_CONNECT_FAILED;
200		goto out;
201	}
202
203	ses.maxfd = MAX(ses.maxfd, sock);
204
205	/* We don't set readfd, that will get set after the connection's
206	 * progress succeeds */
207	channel->writefd = sock;
208	channel->initconn = 1;
209
210	err = SSH_OPEN_IN_PROGRESS;
211
212out:
213	TRACE(("leave newtcpdirect: err %d", err))
214	return err;
215}
216#endif /* ENABLE_CLI_REMOTETCPFWD */
217