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