4 * Copyright (c) 2002,2003 Matt Johnston
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
35 #ifdef ENABLE_CLI_REMOTETCPFWD
36 static int newtcpforwarded(struct Channel
* channel
);
38 const struct ChanType cli_chan_tcpremote
= {
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
= {
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
, ...)
68 va_start(param
, format
);
70 if (cli_opts
.exit_on_fwd_failure
) {
71 _dropbear_exit(EXIT_FAILURE
, format
, param
);
73 _dropbear_log(LOG_WARNING
, format
, param
);
80 #ifdef ENABLE_CLI_LOCALTCPFWD
81 void setup_localtcp() {
85 TRACE(("enter setup_localtcp"))
87 for (iter
= cli_opts
.localfwds
->first
; iter
; iter
= iter
->next
) {
88 struct TCPFwdEntry
* fwd
= (struct TCPFwdEntry
*)iter
->item
;
94 if (ret
== DROPBEAR_FAILURE
) {
95 fwd_failed("Failed local port forward %s:%d:%s:%d",
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
;
114 TRACE(("enter cli_localtcp: %d %s %d", listenport
, remoteaddr
,
117 tcpinfo
= (struct TCPListener
*)m_malloc(sizeof(struct TCPListener
));
119 tcpinfo
->sendaddr
= m_strdup(remoteaddr
);
120 tcpinfo
->sendport
= remoteport
;
124 tcpinfo
->listenaddr
= m_strdup(listenaddr
);
128 if (opts
.listen_fwd_all
) {
129 tcpinfo
->listenaddr
= m_strdup("");
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
) {
144 TRACE(("leave cli_localtcp: %d", 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"))
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
);
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
) {
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
);
185 fwd
->listenport
= allocport
;
186 dropbear_log(LOG_INFO
, "Allocated port %d for remote forward to %s:%d",
187 allocport
, fwd
->connectaddr
, fwd
->connectport
);
195 void cli_recv_msg_request_failure() {
197 for (iter
= cli_opts
.remotefwds
->first
; iter
; iter
= iter
->next
) {
198 struct TCPFwdEntry
*fwd
= (struct TCPFwdEntry
*)iter
->item
;
199 if (!fwd
->have_reply
) {
201 fwd_failed("Remote TCP forward request failed (port %d -> %s:%d)",
210 void setup_remotetcp() {
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("");
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) {
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
) {
269 /* We didn't request forwarding on that port */
271 dropbear_log(LOG_INFO
, "Server sent unrequested forward from \"%s:%d\"",
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
;
285 TRACE(("leave newtcpdirect: err %d", err
))
288 #endif /* ENABLE_CLI_REMOTETCPFWD */