1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33
34 #include <netinet/in.h>
35 #include <arpa/tftp.h>
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <unistd.h>
44
45 #include "tftp-file.h"
46 #include "tftp-utils.h"
47
48 static FILE *file;
49 static int convert;
50
51 static char convbuffer[66000];
52 static int gotcr = 0;
53
54 static size_t
convert_from_net(char * buffer,size_t count)55 convert_from_net(char *buffer, size_t count)
56 {
57 size_t i, n;
58
59 /*
60 * Convert all CR/LF to LF and all CR,NUL to CR
61 */
62
63 n = 0;
64 for (i = 0; i < count; i++) {
65
66 if (gotcr == 0) {
67 convbuffer[n++] = buffer[i];
68 gotcr = (buffer[i] == '\r');
69 continue;
70 }
71
72 /* CR, NULL -> CR */
73 if (buffer[i] == '\0') {
74 gotcr = 0;
75 continue;
76 }
77
78 /* CR, LF -> LF */
79 if (buffer[i] == '\n') {
80 if (n == 0) {
81 if (ftell(file) != 0) {
82 int r = fseek(file, -1, SEEK_END);
83 assert(r == 0);
84 convbuffer[n++] = '\n';
85 } else {
86 /* This shouldn't happen */
87 tftp_log(LOG_ERR,
88 "Received LF as first character");
89 abort();
90 }
91 } else
92 convbuffer[n-1] = '\n';
93 gotcr = 0;
94 continue;
95 }
96
97 /* Everything else just accept as is */
98 convbuffer[n++] = buffer[i];
99 gotcr = (buffer[i] == '\r');
100 continue;
101 }
102
103 return fwrite(convbuffer, 1, n, file);
104 }
105
106 static size_t
convert_to_net(char * buffer,size_t count,int init)107 convert_to_net(char *buffer, size_t count, int init)
108 {
109 size_t i;
110 static size_t n = 0, in = 0;
111 static int newline = -1;
112
113 if (init) {
114 newline = -1;
115 n = 0;
116 in = 0;
117 return 0 ;
118 }
119
120 /*
121 * Convert all LF to CR,LF and all CR to CR,NUL
122 */
123 i = 0;
124
125 if (newline != -1) {
126 buffer[i++] = newline;
127 newline = -1;
128 }
129
130 while (i < count) {
131 if (n == in) {
132 /* When done we're done */
133 if (feof(file)) break;
134
135 /* Otherwise read another bunch */
136 in = fread(convbuffer, 1, count, file);
137 if (in == 0) break;
138 n = 0;
139 }
140
141 /* CR -> CR,NULL */
142 if (convbuffer[n] == '\r') {
143 buffer[i++] = '\r';
144 buffer[i++] = '\0';
145 n++;
146 continue;
147 }
148
149 /* LF -> CR,LF */
150 if (convbuffer[n] == '\n') {
151 buffer[i++] = '\r';
152 buffer[i++] = '\n';
153 n++;
154 continue;
155 }
156
157 buffer[i++] = convbuffer[n++];
158 }
159
160 if (i > count) {
161 /*
162 * Whoops... that isn't allowed (but it will happen
163 * when there is a CR or LF at the end of the buffer)
164 */
165 newline = buffer[i-1];
166 }
167
168 if (i < count) {
169 /* We are done! */
170 return i;
171 } else
172 return count;
173
174 }
175
176 int
write_init(int fd,FILE * f,const char * mode)177 write_init(int fd, FILE *f, const char *mode)
178 {
179
180 if (f == NULL) {
181 file = fdopen(fd, "w");
182 if (file == NULL) {
183 int en = errno;
184 tftp_log(LOG_ERR, "fdopen() failed: %s",
185 strerror(errno));
186 return en;
187 }
188 } else
189 file = f;
190 convert = !strcmp(mode, "netascii");
191 return 0;
192 }
193
194 size_t
write_file(char * buffer,int count)195 write_file(char *buffer, int count)
196 {
197
198 if (convert == 0)
199 return fwrite(buffer, 1, count, file);
200
201 return convert_from_net(buffer, count);
202 }
203
204 int
write_close(void)205 write_close(void)
206 {
207
208 if (fclose(file) != 0) {
209 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
210 return 1;
211 }
212 return 0;
213 }
214
215 off_t
tell_file(void)216 tell_file(void)
217 {
218
219 return ftello(file);
220 }
221
222 int
seek_file(off_t offset)223 seek_file(off_t offset)
224 {
225
226 return fseeko(file, offset, SEEK_SET);
227 }
228
229 int
read_init(int fd,FILE * f,const char * mode)230 read_init(int fd, FILE *f, const char *mode)
231 {
232
233 convert_to_net(NULL, 0, 1);
234 if (f == NULL) {
235 file = fdopen(fd, "r");
236 if (file == NULL) {
237 int en = errno;
238 tftp_log(LOG_ERR, "fdopen() failed: %s",
239 strerror(errno));
240 return en;
241 }
242 } else
243 file = f;
244 convert = !strcmp(mode, "netascii");
245 return 0;
246 }
247
248 size_t
read_file(char * buffer,int count)249 read_file(char *buffer, int count)
250 {
251
252 if (convert == 0)
253 return fread(buffer, 1, count, file);
254
255 return convert_to_net(buffer, count, 0);
256 }
257
258 int
read_close(void)259 read_close(void)
260 {
261
262 if (fclose(file) != 0) {
263 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
264 return 1;
265 }
266 return 0;
267 }
268
269
270 /* When an error has occurred, it is possible that the two sides
271 * are out of synch. Ie: that what I think is the other side's
272 * response to packet N is really their response to packet N-1.
273 *
274 * So, to try to prevent that, we flush all the input queued up
275 * for us on the network connection on our host.
276 *
277 * We return the number of packets we flushed (mostly for reporting
278 * when trace is active).
279 */
280
281 int
synchnet(int peer)282 synchnet(int peer) /* socket to flush */
283 {
284 int i, j = 0;
285 char rbuf[MAXPKTSIZE];
286 struct sockaddr_storage from;
287 socklen_t fromlen;
288
289 while (1) {
290 (void) ioctl(peer, FIONREAD, &i);
291 if (i) {
292 j++;
293 fromlen = sizeof from;
294 (void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
295 (struct sockaddr *)&from, &fromlen);
296 } else {
297 return(j);
298 }
299 }
300 }
301