xref: /freebsd/libexec/tftpd/tftp-file.c (revision a6fe717c2a876105123214c05176cd74106fb94b)
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