1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
30
31 #include <sys/types.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35
36 #include <netinet/in.h>
37 #include <arpa/tftp.h>
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
46
47 #include "tftp-file.h"
48 #include "tftp-utils.h"
49
50 static FILE *file;
51 static int convert;
52
53 static char convbuffer[66000];
54 static int gotcr = 0;
55
56 static size_t
convert_from_net(char * buffer,size_t count)57 convert_from_net(char *buffer, size_t count)
58 {
59 size_t i, n;
60
61 /*
62 * Convert all CR/LF to LF and all CR,NUL to CR
63 */
64
65 n = 0;
66 for (i = 0; i < count; i++) {
67
68 if (gotcr == 0) {
69 convbuffer[n++] = buffer[i];
70 gotcr = (buffer[i] == '\r');
71 continue;
72 }
73
74 /* CR, NULL -> CR */
75 if (buffer[i] == '\0') {
76 gotcr = 0;
77 continue;
78 }
79
80 /* CR, LF -> LF */
81 if (buffer[i] == '\n') {
82 if (n == 0) {
83 if (ftell(file) != 0) {
84 int r = fseek(file, -1, SEEK_END);
85 assert(r == 0);
86 convbuffer[n++] = '\n';
87 } else {
88 /* This shouldn't happen */
89 tftp_log(LOG_ERR,
90 "Received LF as first character");
91 abort();
92 }
93 } else
94 convbuffer[n-1] = '\n';
95 gotcr = 0;
96 continue;
97 }
98
99 /* Everything else just accept as is */
100 convbuffer[n++] = buffer[i];
101 gotcr = (buffer[i] == '\r');
102 continue;
103 }
104
105 return fwrite(convbuffer, 1, n, file);
106 }
107
108 static size_t
convert_to_net(char * buffer,size_t count,int init)109 convert_to_net(char *buffer, size_t count, int init)
110 {
111 size_t i;
112 static size_t n = 0, in = 0;
113 static int newline = -1;
114
115 if (init) {
116 newline = -1;
117 n = 0;
118 in = 0;
119 return 0 ;
120 }
121
122 /*
123 * Convert all LF to CR,LF and all CR to CR,NUL
124 */
125 i = 0;
126
127 if (newline != -1) {
128 buffer[i++] = newline;
129 newline = -1;
130 }
131
132 while (i < count) {
133 if (n == in) {
134 /* When done we're done */
135 if (feof(file)) break;
136
137 /* Otherwise read another bunch */
138 in = fread(convbuffer, 1, count, file);
139 if (in == 0) break;
140 n = 0;
141 }
142
143 /* CR -> CR,NULL */
144 if (convbuffer[n] == '\r') {
145 buffer[i++] = '\r';
146 buffer[i++] = '\0';
147 n++;
148 continue;
149 }
150
151 /* LF -> CR,LF */
152 if (convbuffer[n] == '\n') {
153 buffer[i++] = '\r';
154 buffer[i++] = '\n';
155 n++;
156 continue;
157 }
158
159 buffer[i++] = convbuffer[n++];
160 }
161
162 if (i > count) {
163 /*
164 * Whoops... that isn't allowed (but it will happen
165 * when there is a CR or LF at the end of the buffer)
166 */
167 newline = buffer[i-1];
168 }
169
170 if (i < count) {
171 /* We are done! */
172 return i;
173 } else
174 return count;
175
176 }
177
178 int
write_init(int fd,FILE * f,const char * mode)179 write_init(int fd, FILE *f, const char *mode)
180 {
181
182 if (f == NULL) {
183 file = fdopen(fd, "w");
184 if (file == NULL) {
185 int en = errno;
186 tftp_log(LOG_ERR, "fdopen() failed: %s",
187 strerror(errno));
188 return en;
189 }
190 } else
191 file = f;
192 convert = !strcmp(mode, "netascii");
193 return 0;
194 }
195
196 size_t
write_file(char * buffer,int count)197 write_file(char *buffer, int count)
198 {
199
200 if (convert == 0)
201 return fwrite(buffer, 1, count, file);
202
203 return convert_from_net(buffer, count);
204 }
205
206 int
write_close(void)207 write_close(void)
208 {
209
210 if (fclose(file) != 0) {
211 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
212 return 1;
213 }
214 return 0;
215 }
216
217 off_t
tell_file(void)218 tell_file(void)
219 {
220
221 return ftello(file);
222 }
223
224 int
seek_file(off_t offset)225 seek_file(off_t offset)
226 {
227
228 return fseeko(file, offset, SEEK_SET);
229 }
230
231 int
read_init(int fd,FILE * f,const char * mode)232 read_init(int fd, FILE *f, const char *mode)
233 {
234
235 convert_to_net(NULL, 0, 1);
236 if (f == NULL) {
237 file = fdopen(fd, "r");
238 if (file == NULL) {
239 int en = errno;
240 tftp_log(LOG_ERR, "fdopen() failed: %s",
241 strerror(errno));
242 return en;
243 }
244 } else
245 file = f;
246 convert = !strcmp(mode, "netascii");
247 return 0;
248 }
249
250 size_t
read_file(char * buffer,int count)251 read_file(char *buffer, int count)
252 {
253
254 if (convert == 0)
255 return fread(buffer, 1, count, file);
256
257 return convert_to_net(buffer, count, 0);
258 }
259
260 int
read_close(void)261 read_close(void)
262 {
263
264 if (fclose(file) != 0) {
265 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
266 return 1;
267 }
268 return 0;
269 }
270
271
272 /* When an error has occurred, it is possible that the two sides
273 * are out of synch. Ie: that what I think is the other side's
274 * response to packet N is really their response to packet N-1.
275 *
276 * So, to try to prevent that, we flush all the input queued up
277 * for us on the network connection on our host.
278 *
279 * We return the number of packets we flushed (mostly for reporting
280 * when trace is active).
281 */
282
283 int
synchnet(int peer)284 synchnet(int peer) /* socket to flush */
285 {
286 int i, j = 0;
287 char rbuf[MAXPKTSIZE];
288 struct sockaddr_storage from;
289 socklen_t fromlen;
290
291 while (1) {
292 (void) ioctl(peer, FIONREAD, &i);
293 if (i) {
294 j++;
295 fromlen = sizeof from;
296 (void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
297 (struct sockaddr *)&from, &fromlen);
298 } else {
299 return(j);
300 }
301 }
302 }
303