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