xref: /freebsd/libexec/tftpd/tftp-file.c (revision cd675bb60e39395c92b8d08704bc01cdbdc03fac)
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
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
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
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
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
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
218 tell_file(void)
219 {
220 
221 	return ftello(file);
222 }
223 
224 int
225 seek_file(off_t offset)
226 {
227 
228 	return fseeko(file, offset, SEEK_SET);
229 }
230 
231 int
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
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
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
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