1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/time.h>
5 #include <time.h>
6 #include <syslog.h>
7 #include <sys/types.h>
8 #include <sys/uio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <netinet/in.h>
14 #include <arpa/ftp.h>
15 #include <sys/socket.h>
16 #include <sys/uio.h>
17 #include <sys/syscall.h>
18 #include <string.h>
19 #include <errno.h>
20 
21 #include "smbftpd.h"
22 #include "cmd.h"
23 #include "restrict.h"
24 #include "ssl.h"
25 
26 #ifdef	HAVE_LINUX_SENDFILE
27 #include <sys/sendfile.h>
28 #endif
29 
30 extern smbftpd_session_t smbftpd_session;
31 
send_data_ascii_mode(FILE * instr,FILE * outstr)32 static off_t send_data_ascii_mode(FILE *instr, FILE *outstr)
33 {
34 	struct timeval tvsince;
35 	off_t byte_count = 0;
36 	int c, cp;
37 
38 	if (smbftpd_session.max_download_rate > 0) {
39 		gettimeofday(&tvsince, NULL);
40 	}
41 
42 	cp = EOF;
43 	for (;;) {
44 		c = getc(instr);
45 		if (sigurg_received()) {
46 			if (check_oob()) {
47 				return -1;
48 			}
49 		} else if (c == EOF && ferror(instr))
50 			goto file_err;
51 		if (c == EOF) {
52 			if (ferror(instr)) {	/* resume after OOB */
53 				clearerr(instr);
54 				continue;
55 			}
56 			if (feof(instr))	/* EOF */
57 				break;
58 			syslog(LOG_ERR, "Internal: impossible condition"
59 				   " on file after getc()");
60 			goto file_err;
61 		}
62 
63 		if (c == '\n' && cp != '\r') {
64 			START_UNSAFE;
65 			smbftpd_socket_putc('\r', outstr, 1);
66 			END_UNSAFE;
67 			if (sigurg_received()) {
68 				if (check_oob()) {
69 					return -1;
70 				}
71 			} else if (ferror(outstr))
72 				goto data_err;
73 			byte_count++;
74 		}
75 		START_UNSAFE;
76 		smbftpd_socket_putc(c, outstr, 1);
77 		END_UNSAFE;
78 		if (sigurg_received()) {
79 			if (check_oob()) {
80 				return -1;
81 			}
82 		} else if (ferror(outstr))
83 			goto data_err;
84 		cp = c;
85 		byte_count++;
86 		if (smbftpd_session.max_download_rate > 0) {
87 			transfer_rate_throttle(byte_count, &tvsince, smbftpd_session.max_download_rate);
88 		}
89 	}
90 
91 	if (smbftpd_socket_fflush(outstr, 1) == EOF)
92 		goto data_err;
93 
94 	reply_noformat(226, "Transfer complete.");
95 	return byte_count;
96 
97 data_err:
98 	reply(426, "Data connection: %s.", strerror(errno));
99 	return -1;
100 
101 file_err:
102 	reply(551, "Error on input file: %s.", strerror(errno));
103 	return -1;
104 }
105 
106 /*
107  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
108  * encapsulation of the data subject to Mode, Structure, and Type.
109  *
110  * NB: Form isn't handled.
111  */
send_data_biary_mode(FILE * instr,FILE * outstr,off_t blksize,off_t filesize,int isreg)112 static off_t send_data_biary_mode(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
113 {
114 	struct timeval tvsince;
115 	int filefd, netfd;
116 	char *buf;
117 	off_t cnt, byte_count = 0;
118 #ifdef	HAVE_SENDFILE
119 	off_t offset;
120 	int err;
121 	cnt = offset = 0;
122 #endif
123 
124 	if (smbftpd_session.max_download_rate > 0) {
125 		gettimeofday(&tvsince, NULL);
126 	}
127 	if (blksize < NET_BUF_SIZE) {
128 		blksize = NET_BUF_SIZE;
129 	}
130 
131 	/*
132 	 * isreg is only set if we are not doing restart and we
133 	 * are sending a regular file
134 	 */
135 	netfd = fileno(outstr);
136 	filefd = fileno(instr);
137 
138 	if (smbftpd_session.max_download_rate > 0 || smbftpd_session.ssl_ctrl.ssl_data_active_flag || !isreg) {
139 		goto oldway;
140 	}
141 
142 #ifdef	HAVE_SENDFILE
143 	while (filesize > 0) {
144 #ifndef	HAVE_LINUX_SENDFILE
145 
146 		err = sendfile(filefd, netfd, offset, 0, NULL, &cnt, 0);
147 
148 		/*
149 		 * Calculate byte_count before OOB processing.
150 		 * It can be used in myoob() later.
151 		 */
152 		byte_count += cnt;
153 		offset += cnt;
154 		filesize -= cnt;
155 
156 		if (sigurg_received()) {
157 			if (check_oob()) {
158 				return -1;
159 			}
160 		} else if (err == -1) {
161 			if (errno != EINTR && cnt == 0 && offset == 0)
162 				goto oldway;
163 			goto data_err;
164 		}
165 		if (err == -1)	{/* resume after OOB */
166 			continue;
167 		}
168 
169 		/*
170 		 * We hit the EOF prematurely.
171 		 * Perhaps the file was externally truncated.
172 		 */
173 		if (cnt == 0) {
174 			reply_noformat(226, "Transfer finished due to premature end of file.");
175 			return -1;
176 		}
177 #else
178 		size_t  count = 0x7FFFFFFF;
179 
180 		if (filesize < count) {
181 			count = filesize;
182 		}
183 #ifdef __NR_sendfile64
184 		/* directly syscall to avoid glibc */
185 		err = syscall(__NR_sendfile64, netfd, filefd, &offset, count);
186 #else
187 		err = sendfile64(netfd, filefd, &offset, count);
188 #endif
189 		if (sigurg_received()) {
190 			if (check_oob()) {
191 				return -1;
192 			}
193 		} else if (err == -1) {
194 			if (offset == 0) {
195 				goto oldway;
196 			}
197 			goto data_err;
198 		}
199 		if (err == -1)	{/* resume after OOB */
200 			continue;
201 		}
202 
203 		filesize -= err;
204 		byte_count += err;
205 #endif
206 	}
207 	reply_noformat(226, "Transfer complete.");
208 	return byte_count;
209 #endif /* HAVE_SENDFILE */
210 
211 
212 oldway:
213 	if ((buf = malloc((u_int)blksize)) == NULL) {
214 		reply_noformat(451, "Ran out of memory.");
215 		return -1;
216 	}
217 
218 	for (;;) {
219 		int len;
220 		char *bp;
221 
222 		cnt = read(filefd, buf, blksize);
223 		if (sigurg_received()) {
224 			if (check_oob()) {
225 				free(buf);
226 				return -1;
227 			}
228 		} else if (cnt < 0) {
229 			free(buf);
230 			goto file_err;
231 		}
232 		if (cnt < 0){	/* resume after OOB */
233 			continue;
234 		}
235 
236 		if (cnt == 0)	/* EOF */
237 			break;
238 
239 		for (len = cnt, bp = buf; len > 0;) {
240 #ifdef WITH_SSL
241 			if (smbftpd_session.ssl_ctrl.ssl_data_active_flag) {
242 				cnt = ssl_write(ssl_data_con, bp, len);
243 			} else
244 #endif /* WITH_SSL */
245 				cnt = write(netfd, bp, len);
246 			if (sigurg_received()) {
247 				if (check_oob()) {
248 					free(buf);
249 					return -1;
250 				}
251 			} else if (cnt < 0) {
252 				free(buf);
253 				goto data_err;
254 			}
255 			if (cnt <= 0)
256 				continue;
257 
258 			len -= cnt;
259 			bp += cnt;
260 			byte_count += cnt;
261 			if (smbftpd_session.max_download_rate > 0) {
262 				transfer_rate_throttle(byte_count, &tvsince, smbftpd_session.max_download_rate);
263 			}
264 		}
265 	}
266 	free(buf);
267 	reply_noformat(226, "Transfer complete.");
268 	return byte_count;
269 
270 data_err:
271 	reply(426, "Data connection: %s.", strerror(errno));
272 	return -1;
273 
274 file_err:
275 	reply(551, "Error on input file: %s.", strerror(errno));
276 	return -1;
277 }
278 
279 /**
280  * Download file from FTP server. (RETR)
281  *
282  * We will open data connection and send file to client.
283  *
284  * @param file   The filename to send
285  * @param restart_point
286  *               Restart point of resume download. This is file offset.
287  */
cmd_retr(const char * file,off_t restart_point)288 void cmd_retr(const char *file, off_t restart_point)
289 {
290 	FILE *fin = NULL, *dout;
291 	const smbftpd_valid_share_t *share;
292 	struct stat st;
293 	char *real_path;
294 	off_t byte_count;
295 	time_t tstart, tend;
296 
297 	real_path = smbftpd_get_realpath(smbftpd_session.valid_shares, file, 0);
298 	if (NULL == real_path) {
299 		reply_fs2client(550,"%s: Permission deny", file);
300 		return;
301 	}
302 	if (smbftpd_session.mode == MODE_SMB) {
303 		share = smbftpd_get_share_by_path(smbftpd_session.valid_shares, real_path);
304 		if (!share) {
305 			reply_fs2client(550,"%s: Permission deny", file);
306 			return;
307 		}
308 		if (share->disable_download) {
309 			reply_noformat(500, "RETR command disabled.");
310 			return;
311 		}
312 	}
313 
314 	fin = fopen(real_path, "r");
315 	st.st_size = 0;
316 
317 	if (fin == NULL) {
318 		if (errno != 0) {
319 			reply_fs2client(550, "%s: %s.", file, strerror(errno));
320 			LOGCMD("get", real_path);
321 		}
322 		return;
323 	}
324 	byte_count = -1;
325 	if ((fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
326 		reply_fs2client(550, "%s: not a plain file.", file);
327 		goto done;
328 	}
329 	if (restart_point) {
330 		if (smbftpd_session.transfer_type == TYPE_A) {
331 			off_t i, n;
332 			int c;
333 
334 			n = restart_point;
335 			i = 0;
336 			while (i++ < n) {
337 				if ((c=getc(fin)) == EOF) {
338 					reply_fs2client(550, "%s: %s.", file, strerror(errno));
339 					goto done;
340 				}
341 				if (c == '\n')
342 					i++;
343 			}
344 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
345 			reply_fs2client(550, "%s: %s.", file, strerror(errno));
346 			goto done;
347 		}
348 	}
349 	dout = dataconn(file, st.st_size, "w");
350 	if (dout == NULL)
351 		goto done;
352 	time(&tstart);
353 
354 	STARTXFER;
355 
356 	switch (smbftpd_session.transfer_type) {
357 	case TYPE_A:
358 		byte_count = send_data_ascii_mode(fin, dout);
359 		break;
360 	case TYPE_I:
361 	case TYPE_L:
362 		byte_count = send_data_biary_mode(fin, dout, st.st_blksize, st.st_size,
363 										  restart_point == 0 && S_ISREG(st.st_mode));
364 		break;
365 	default:
366 		reply(550, "Unimplemented TYPE %d in send_data.", smbftpd_session.transfer_type);
367 	}
368 
369 	ENDXFER;
370 
371 	time(&tend);
372 
373 	dataconnclose(dout);
374 done:
375 	LOGBYTES("get", real_path, byte_count);
376 	if (fin) {
377 		fclose(fin);
378 	}
379 
380 	if (byte_count >= 0) {
381 		smbftpd_xferlog_write("get", real_path, byte_count, tstart, tend);
382 		smbftpd_session.byte_downloaded += byte_count;
383 	}
384 
385 }
386 
387