dropbear 2016.73
[tomato.git] / release / src / router / dropbear / cli-tcpfwd.c
blob03b4b926f04f89220454b959d7618b81ccacdf4d
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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. */
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 #include "netio.h"
35 #ifdef ENABLE_CLI_REMOTETCPFWD
36 static int newtcpforwarded(struct Channel * channel);
38 const struct ChanType cli_chan_tcpremote = {
39 1, /* sepfds */
40 "forwarded-tcpip",
41 newtcpforwarded,
42 NULL,
43 NULL,
44 NULL
46 #endif
48 #ifdef ENABLE_CLI_LOCALTCPFWD
49 static int cli_localtcp(const char* listenaddr,
50 unsigned int listenport,
51 const char* remoteaddr,
52 unsigned int remoteport);
53 static const struct ChanType cli_chan_tcplocal = {
54 1, /* sepfds */
55 "direct-tcpip",
56 tcp_prio_inithandler,
57 NULL,
58 NULL,
59 NULL
61 #endif
63 #ifdef ENABLE_CLI_ANYTCPFWD
64 static void fwd_failed(const char* format, ...) ATTRIB_PRINTF(1,2);
65 static void fwd_failed(const char* format, ...)
67 va_list param;
68 va_start(param, format);
70 if (cli_opts.exit_on_fwd_failure) {
71 _dropbear_exit(EXIT_FAILURE, format, param);
72 } else {
73 _dropbear_log(LOG_WARNING, format, param);
76 va_end(param);
78 #endif
80 #ifdef ENABLE_CLI_LOCALTCPFWD
81 void setup_localtcp() {
82 m_list_elem *iter;
83 int ret;
85 TRACE(("enter setup_localtcp"))
87 for (iter = cli_opts.localfwds->first; iter; iter = iter->next) {
88 struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item;
89 ret = cli_localtcp(
90 fwd->listenaddr,
91 fwd->listenport,
92 fwd->connectaddr,
93 fwd->connectport);
94 if (ret == DROPBEAR_FAILURE) {
95 fwd_failed("Failed local port forward %s:%d:%s:%d",
96 fwd->listenaddr,
97 fwd->listenport,
98 fwd->connectaddr,
99 fwd->connectport);
102 TRACE(("leave setup_localtcp"))
106 static int cli_localtcp(const char* listenaddr,
107 unsigned int listenport,
108 const char* remoteaddr,
109 unsigned int remoteport) {
111 struct TCPListener* tcpinfo = NULL;
112 int ret;
114 TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr,
115 remoteport));
117 tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
119 tcpinfo->sendaddr = m_strdup(remoteaddr);
120 tcpinfo->sendport = remoteport;
122 if (listenaddr)
124 tcpinfo->listenaddr = m_strdup(listenaddr);
126 else
128 if (opts.listen_fwd_all) {
129 tcpinfo->listenaddr = m_strdup("");
130 } else {
131 tcpinfo->listenaddr = m_strdup("localhost");
134 tcpinfo->listenport = listenport;
136 tcpinfo->chantype = &cli_chan_tcplocal;
137 tcpinfo->tcp_type = direct;
139 ret = listen_tcpfwd(tcpinfo);
141 if (ret == DROPBEAR_FAILURE) {
142 m_free(tcpinfo);
144 TRACE(("leave cli_localtcp: %d", ret))
145 return ret;
147 #endif /* ENABLE_CLI_LOCALTCPFWD */
149 #ifdef ENABLE_CLI_REMOTETCPFWD
150 static void send_msg_global_request_remotetcp(const char *addr, int port) {
152 TRACE(("enter send_msg_global_request_remotetcp"))
154 CHECKCLEARTOWRITE();
155 buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
156 buf_putstring(ses.writepayload, "tcpip-forward", 13);
157 buf_putbyte(ses.writepayload, 1); /* want_reply */
158 buf_putstring(ses.writepayload, addr, strlen(addr));
159 buf_putint(ses.writepayload, port);
161 encrypt_packet();
163 TRACE(("leave send_msg_global_request_remotetcp"))
166 /* The only global success/failure messages are for remotetcp.
167 * Since there isn't any identifier in these messages, we have to rely on them
168 * being in the same order as we sent the requests. This is the ordering
169 * of the cli_opts.remotefwds list.
170 * If the requested remote port is 0 the listen port will be
171 * dynamically allocated by the server and the port number will be returned
172 * to client and the port number reported to the user. */
173 void cli_recv_msg_request_success() {
174 /* We just mark off that we have received the reply,
175 * so that we can report failure for later ones. */
176 m_list_elem * iter = NULL;
177 for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
178 struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
179 if (!fwd->have_reply) {
180 fwd->have_reply = 1;
181 if (fwd->listenport == 0) {
182 /* The server should let us know which port was allocated if we requested port 0 */
183 int allocport = buf_getint(ses.payload);
184 if (allocport > 0) {
185 fwd->listenport = allocport;
186 dropbear_log(LOG_INFO, "Allocated port %d for remote forward to %s:%d",
187 allocport, fwd->connectaddr, fwd->connectport);
190 return;
195 void cli_recv_msg_request_failure() {
196 m_list_elem *iter;
197 for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
198 struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
199 if (!fwd->have_reply) {
200 fwd->have_reply = 1;
201 fwd_failed("Remote TCP forward request failed (port %d -> %s:%d)",
202 fwd->listenport,
203 fwd->connectaddr,
204 fwd->connectport);
205 return;
210 void setup_remotetcp() {
211 m_list_elem *iter;
212 TRACE(("enter setup_remotetcp"))
214 for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
215 struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
216 if (!fwd->listenaddr)
218 /* we store the addresses so that we can compare them
219 when the server sends them back */
220 if (opts.listen_fwd_all) {
221 fwd->listenaddr = m_strdup("");
222 } else {
223 fwd->listenaddr = m_strdup("localhost");
226 send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport);
229 TRACE(("leave setup_remotetcp"))
232 static int newtcpforwarded(struct Channel * channel) {
234 char *origaddr = NULL;
235 unsigned int origport;
236 m_list_elem * iter = NULL;
237 struct TCPFwdEntry *fwd;
238 char portstring[NI_MAXSERV];
239 int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
241 origaddr = buf_getstring(ses.payload, NULL);
242 origport = buf_getint(ses.payload);
244 /* Find which port corresponds. First try and match address as well as port,
245 in case they want to forward different ports separately ... */
246 for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
247 fwd = (struct TCPFwdEntry*)iter->item;
248 if (origport == fwd->listenport
249 && strcmp(origaddr, fwd->listenaddr) == 0) {
250 break;
254 if (!iter)
256 /* ... otherwise try to generically match the only forwarded port
257 without address (also handles ::1 vs 127.0.0.1 vs localhost case).
258 rfc4254 is vague about the definition of "address that was connected" */
259 for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
260 fwd = (struct TCPFwdEntry*)iter->item;
261 if (origport == fwd->listenport) {
262 break;
268 if (iter == NULL) {
269 /* We didn't request forwarding on that port */
270 cleantext(origaddr);
271 dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"",
272 origaddr, origport);
273 goto out;
276 snprintf(portstring, sizeof(portstring), "%u", fwd->connectport);
277 channel->conn_pending = connect_remote(fwd->connectaddr, portstring, channel_connect_done, channel);
279 channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
281 err = SSH_OPEN_IN_PROGRESS;
283 out:
284 m_free(origaddr);
285 TRACE(("leave newtcpdirect: err %d", err))
286 return err;
288 #endif /* ENABLE_CLI_REMOTETCPFWD */