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