"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "ftp_data_connection.c" see the
Fossies "Dox" file reference documentation.
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * Original Copyright (c) 2005 Covalent Technologies
19 *
20 * FTP Protocol module for Apache 2.0
21 */
22
23 #include "mod_ftp.h"
24 #include "ftp_internal.h"
25 #include "apr_portable.h"
26
27 #if APR_HAVE_SYS_SOCKET_H
28 #include <sys/socket.h>
29 #endif
30 #ifdef HAVE_NETINET_IP_H
31 #include <netinet/ip.h>
32 #endif
33
34 /*
35 * ftp_reset_dataconn: Close any data channel listen/connect socket and
36 * clear all data channel state from ftp_connection
37 */
38 void ftp_reset_dataconn(ftp_connection *fc)
39 {
40 if (fc->csock) {
41 apr_socket_close(fc->csock);
42 fc->csock = NULL;
43 }
44 fc->clientsa = NULL;
45 fc->passive_created = -1;
46 apr_pool_clear(fc->data_pool);
47 }
48
49 /*
50 * ftp_open_datasock: If we are in passive mode we accept and return a
51 * socket. If we are in active mode, a socket is
52 * created based on the fc->clientsa sockaddr and
53 * then returned.
54 *
55 * Arguments: r - The request.
56 *
57 * Returns: apr_status_t
58 */
59 static apr_status_t ftp_open_datasock(request_rec *r)
60 {
61 ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
62 ftp_connection *fc = ftp_get_module_config(r->connection->conn_config);
63
64 apr_interval_time_t timeout;
65 apr_pollfd_t pollset[2];
66 apr_socket_t *s;
67 apr_status_t rv, res;
68 int n;
69 #ifdef HAVE_SOL_IP_H
70 int sd, sopt;
71 #endif
72
73 /*
74 * handle err condition when the creation of the socket had failed, this
75 * will occur if a PORT command has failed
76 */
77 if (!fc->csock) {
78 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
79 "Nonexistant connection");
80 return APR_EGENERAL;
81 }
82
83 if (fc->passive_created != -1) {
84 pollset[0].desc_type = APR_POLL_SOCKET;
85 pollset[0].desc.s = fc->csock;
86 pollset[0].reqevents = APR_POLLIN;
87 pollset[1].desc_type = APR_POLL_SOCKET;
88 pollset[1].desc.s = fc->cntlsock;
89 pollset[1].reqevents = (APR_POLLIN | APR_POLLPRI);
90
91 /*
92 * since it is possible to hang in accept(), poll both the control
93 * and client sockets waiting for activity.
94 */
95 apr_socket_timeout_get(fc->csock, &timeout); /* likely to be -1 */
96 res = apr_poll(pollset, 2, &n, timeout);
97
98 if (res == APR_SUCCESS) {
99 if (pollset[0].rtnevents & APR_POLLIN) {
100 /* activity on client socket, fall through to accept() */
101 ;
102 }
103 else if (pollset[1].rtnevents & (APR_POLLIN | APR_POLLPRI)) {
104 /*
105 * command channel has activity. since we can only do a
106 * single read ahead operation, no use in looping here if the
107 * command is not an ABOR. bail out and let command
108 * processing occur as normal.
109 */
110 ap_log_error(APLOG_MARK, APLOG_ERR, res, r->server,
111 "Activity on control channel while waiting "
112 "for client connect, processing command");
113
114 /* manual cleanup, no need to close the socket */
115 fc->csock = NULL;
116 fc->passive_created = -1;
117 return APR_ECONNRESET;
118 }
119 }
120 else {
121 /*
122 * not much we can do, one of our sockets was likely disconnected
123 */
124 fc->csock = NULL;
125 fc->passive_created = -1;
126 return APR_EGENERAL;
127 }
128
129 /* activity on client socket, attempt an accept() */
130 rv = apr_socket_accept(&s, fc->csock, fc->data_pool);
131
132 res = apr_socket_close(fc->csock);
133 fc->csock = NULL;
134 fc->passive_created = -1;
135
136 /* check csock closure first */
137 if (res != APR_SUCCESS) {
138 ap_log_error(APLOG_MARK, APLOG_ERR, res, r->server,
139 "Couldn't close passive connection");
140 }
141
142 /* retest the apr_accept result from above */
143 if (rv != APR_SUCCESS) {
144 return rv;
145 }
146 fc->datasock = s;
147 }
148 else {
149 if (fc->clientsa) {
150 int tries;
151 for (tries = 0;; tries++) {
152 rv = apr_socket_connect(fc->csock, fc->clientsa);
153 if (rv == APR_SUCCESS) {
154 break;
155 }
156 if (!APR_STATUS_IS_EAGAIN(rv) || tries > FTP_MAX_TRIES) {
157 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
158 "Couldn't connect to client");
159 apr_socket_close(fc->csock);
160 fc->csock = NULL;
161 fc->passive_created = -1;
162 return rv;
163 }
164 apr_sleep(tries * APR_USEC_PER_SEC);
165 }
166 }
167 else {
168 return APR_EGENERAL;
169 }
170 fc->datasock = fc->csock;
171 fc->csock = NULL;
172 fc->passive_created = -1;
173 }
174
175 #ifdef HAVE_SOL_IP_H
176 sopt = IPTOS_THROUGHPUT;
177 if (((apr_os_sock_get(&sd, fc->datasock)) == APR_SUCCESS) &&
178 #ifdef HAVE_SOL_IP
179 (setsockopt(sd, SOL_IP, IP_TOS, &sopt, sizeof(sopt)) < 0)) {
180 #else
181 (setsockopt(sd, IPPROTO_IP, IP_TOS, &sopt, sizeof(sopt)) < 0)) {
182 #endif
183 ap_log_error(APLOG_MARK, APLOG_ERR, errno, r->server,
184 "Failed to set TOS priority");
185 }
186 #endif
187
188 rv = apr_socket_opt_set(fc->datasock, APR_SO_LINGER,
189 APR_MAX_SECS_TO_LINGER);
190 if (rv != APR_SUCCESS) {
191 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
192 "Couldn't set APR_SO_LINGER socket option");
193 }
194 rv = apr_socket_opt_set(fc->datasock, APR_SO_REUSEADDR, 1);
195 if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
196 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
197 "Couldn't set APR_SO_REUSEADDR socket option");
198 }
199
200 /* Set the default data connection timeout value */
201 rv = apr_socket_timeout_set(fc->datasock,
202 fsc->timeout_data * APR_USEC_PER_SEC);
203 if (rv != APR_SUCCESS) {
204 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
205 "Couldn't set socket timeout");
206 }
207
208 return APR_SUCCESS;
209 }
210
211 /*
212 * ftp_open_dataconn: Creates the appropriate data channel
213 * and initializes the
214 *
215 * Arguments: r - The request.
216 * write - Open for write-to-client only
217 *
218 * Returns: apr_status_t
219 */
220 conn_rec *ftp_open_dataconn(request_rec *r, int write_not_read)
221 {
222 ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
223 ftp_connection *fc = ftp_get_module_config(r->connection->conn_config);
224 conn_rec *cdata;
225 ap_filter_t *f;
226
227 if (ftp_open_datasock(r) != APR_SUCCESS) {
228 return NULL;
229 }
230
231 cdata = ap_run_create_connection(r->pool, r->server, fc->datasock,
232 r->connection->id, r->connection->sbh,
233 r->connection->bucket_alloc);
234
235 ftp_set_module_config(cdata->conn_config, fc);
236
237 ap_run_pre_connection(cdata, fc->datasock);
238
239 /*
240 * Open data connection only if the connection is from the client's IP
241 * address. All other PASV connection attempts are denied, unless
242 * disabled using:
243 *
244 * FTPOptions AllowProxyPASV
245 */
246 if (fc->clientsa == NULL) { /* Only check PASV, never PORT connections */
247 if (!(fsc->options & FTP_OPT_ALLOWPROXYPASV)) {
248 if (strcmp(fc->connection->remote_ip, cdata->remote_ip) != 0) {
249 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
250 "PASV data connection attempt from %s "
251 "doesn't match the client IP %s",
252 cdata->remote_ip, fc->connection->remote_ip);
253 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
254 "PASV data connection attempt denied, "
255 "not configured to AllowProxyPASV");
256 apr_socket_close(fc->datasock);
257 fc->datasock = NULL;
258 return NULL;
259 }
260 }
261 fc->passive_created = 0;
262 }
263
264 if (write_not_read) {
265 /*
266 * We need the network-level poll filter to watch both the control
267 * incoming command and writable data port conditions
268 */
269 fc->filter_mask += FTP_NEED_DATA_OUT;
270 }
271 else {
272 /*
273 * Once Apache has inserted the core i/o filters, we must insert our
274 * idea of a core socket, based on our own ftp_datasock bucket,
275 * instead of the socket_bucket. This will capture any abort command
276 * on the control socket while actually reading from the data socket.
277 *
278 * Insert this bucket type only for read connections
279 */
280 for (f = cdata->input_filters; f; f = f->next) {
281 if (strcasecmp(f->frec->name, "CORE_IN") == 0) {
282 core_net_rec *net = f->ctx;
283 apr_bucket *e;
284
285 net->in_ctx = apr_pcalloc(fc->data_pool, sizeof(*net->in_ctx));
286 net->in_ctx->b = apr_brigade_create(fc->data_pool,
287 f->c->bucket_alloc);
288 net->in_ctx->tmpbb =
289 apr_brigade_create(net->in_ctx->b->p,
290 net->in_ctx->b->bucket_alloc);
291
292 /* seed the brigade with our client data+control sockets */
293 e = ftp_bucket_datasock_create(fc, f->c->bucket_alloc);
294 APR_BRIGADE_INSERT_TAIL(net->in_ctx->b, e);
295 break;
296 }
297 }
298 }
299
300 /*
301 * We initalize the data connection here by adding/removing the SSL/TLS
302 * filters. We then invoke ftp_ssl_init immediately for writers, since
303 * negotation would normally occur on the first socket read (and we won't
304 * be reading from that socket.)
305 */
306 if (fc->prot == FTP_PROT_CLEAR) {
307 for (f = cdata->output_filters; f; f = f->next) {
308 if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
309 ap_remove_output_filter(f);
310 }
311 }
312 for (f = cdata->input_filters; f; f = f->next) {
313 if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
314 ap_remove_input_filter(f);
315 }
316 }
317 }
318 else if ((fc->prot == FTP_PROT_PRIVATE) && write_not_read) {
319 if (ftp_ssl_init(cdata) != APR_SUCCESS) {
320 /*
321 * In case of failure within ssl_init, return no data connection
322 */
323 apr_socket_close(fc->datasock);
324 fc->datasock = NULL;
325 return NULL;
326 }
327 }
328
329 /*
330 * We now need to remove the NET_TIME filter to allow
331 * use to control timeouts ourselves.
332 */
333 for (f = cdata->input_filters; f; f = f->next) {
334 if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
335 ap_remove_input_filter(f);
336 }
337 }
338
339 return cdata;
340 }