1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2015-2018 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, The ProFTPD Project team and other respective
20 * copyright holders give permission to link this program with OpenSSL, and
21 * distribute the resulting executable, without including the source code for
22 * OpenSSL in the source distribution.
23 */
24
25 /* FTP ASCII conversions. */
26
27 #include "conf.h"
28
pr_ascii_ftp_from_crlf(pool * p,char * in,size_t inlen,char ** out,size_t * outlen)29 int pr_ascii_ftp_from_crlf(pool *p, char *in, size_t inlen, char **out,
30 size_t *outlen) {
31 char *src, *dst;
32 size_t rem;
33 int adj;
34
35 (void) p;
36
37 if (in == NULL ||
38 out == NULL ||
39 outlen == NULL) {
40 errno = EINVAL;
41 return -1;
42 }
43
44 if (inlen == 0) {
45 *outlen = inlen;
46 return 0;
47 }
48
49 src = in;
50 rem = inlen;
51 dst = *out;
52 adj = 0;
53
54 while (rem--) {
55 if (*src != '\r') {
56 *dst++ = *src++;
57 (*outlen)++;
58
59 } else {
60 if (rem == 0) {
61 /* copy, but save it for later */
62 adj++;
63 *dst++ = *src++;
64
65 } else {
66 if (*(src+1) == '\n') {
67 /* Skip the CR. */
68 src++;
69
70 } else {
71 *dst++ = *src++;
72 (*outlen)++;
73 }
74 }
75 }
76 }
77
78 return adj;
79 }
80
81 static int have_dangling_cr = FALSE;
82
83 /* This function rewrites the contents of the given buffer, making sure that
84 * each LF has a preceding CR, as required by RFC959.
85 */
pr_ascii_ftp_to_crlf(pool * p,char * in,size_t inlen,char ** out,size_t * outlen)86 int pr_ascii_ftp_to_crlf(pool *p, char *in, size_t inlen, char **out,
87 size_t *outlen) {
88 register unsigned int i = 0, j = 0;
89 char *dst = NULL, *src;
90 size_t src_len, lf_pos;
91
92 if (p == NULL ||
93 in == NULL ||
94 out == NULL ||
95 outlen == NULL) {
96 errno = EINVAL;
97 return -1;
98 }
99
100 if (inlen == 0) {
101 *out = in;
102 return 0;
103 }
104
105 src = in;
106 src_len = lf_pos = inlen;
107
108 /* First, determine the position of the first bare LF. */
109 if (have_dangling_cr == FALSE &&
110 src[0] == '\n') {
111 lf_pos = 0;
112 goto found_lf;
113 }
114
115 for (i = 1; i < src_len; i++) {
116 if (src[i] == '\n' &&
117 src[i-1] != '\r') {
118 lf_pos = i;
119 break;
120 }
121 }
122
123 found_lf:
124 /* If the last character in the buffer is CR, then we have a dangling CR.
125 * The first character in the next buffer could be an LF, and without
126 * this flag, that LF would be treated as a bare LF, thus resulting in
127 * an added extraneous CR in the stream.
128 */
129 have_dangling_cr = (src[src_len-1] == '\r') ? TRUE : FALSE;
130
131 if (lf_pos == src_len) {
132 /* No translation needed. */
133 *outlen = inlen;
134
135 dst = malloc(inlen);
136 if (dst == NULL) {
137 pr_log_pri(PR_LOG_ALERT, "Out of memory!");
138 exit(1);
139 }
140
141 memcpy(dst, in, inlen);
142 *out = dst;
143
144 return 0;
145 }
146
147 /* Assume the worst: a block containing only LF characters, needing twice
148 * the size for holding the corresponding CRs.
149 */
150 dst = malloc(src_len * 2);
151 if (dst == NULL) {
152 pr_log_pri(PR_LOG_ALERT, "Out of memory!");
153 exit(1);
154 }
155
156 if (lf_pos > 0) {
157 memcpy(dst, src, lf_pos);
158 i = j = lf_pos;
159
160 } else {
161 dst[0] = '\r';
162 dst[1] = '\n';
163 i = 2;
164 j = 1;
165 }
166
167 while (j < src_len) {
168 if (src[j] == '\n' &&
169 src[j-1] != '\r') {
170 dst[i++] = '\r';
171 }
172
173 dst[i++] = src[j++];
174 }
175 pr_signals_handle();
176
177 *outlen = i;
178 *out = dst;
179
180 return i - j;
181 }
182
pr_ascii_ftp_reset(void)183 void pr_ascii_ftp_reset(void) {
184 have_dangling_cr = FALSE;
185 }
186