xref: /freebsd/libexec/tftpd/tftp-file.c (revision 780fb4a2)
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 <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <syslog.h>
44 #include <unistd.h>
45 
46 #include "tftp-file.h"
47 #include "tftp-utils.h"
48 
49 static FILE	*file;
50 static int	convert;
51 
52 static char	convbuffer[66000];
53 static int	gotcr = 0;
54 
55 static size_t
56 convert_from_net(char *buffer, size_t count)
57 {
58 	size_t i, n;
59 
60 	/*
61 	 * Convert all CR/LF to LF and all CR,NUL to CR
62 	 */
63 
64 	n = 0;
65 	for (i = 0; i < count; i++) {
66 
67 		if (gotcr == 0) {
68 			convbuffer[n++] = buffer[i];
69 			gotcr = (buffer[i] == '\r');
70 			continue;
71 		}
72 
73 		/* CR, NULL -> CR */
74 		if (buffer[i] == '\0') {
75 			gotcr = 0;
76 			continue;
77 		}
78 
79 		/* CR, LF -> LF */
80 		if (buffer[i] == '\n') {
81 			if (n == 0) {
82 				if (ftell(file) != 0) {
83 					fseek(file, -1, SEEK_END);
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 = 0;
112 
113 	if (init) {
114 		newline = 0;
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) {
126 		buffer[i++] = newline;
127 		newline = 0;
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 alllowed (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 int
216 read_init(int fd, FILE *f, const char *mode)
217 {
218 
219 	convert_to_net(NULL, 0, 1);
220 	if (f == NULL) {
221 		file = fdopen(fd, "r");
222 		if (file == NULL) {
223 			int en = errno;
224 			tftp_log(LOG_ERR, "fdopen() failed: %s",
225 			    strerror(errno));
226 			return en;
227 		}
228 	} else
229 		file = f;
230 	convert = !strcmp(mode, "netascii");
231 	return 0;
232 }
233 
234 size_t
235 read_file(char *buffer, int count)
236 {
237 
238 	if (convert == 0)
239 		return fread(buffer, 1, count, file);
240 
241 	return convert_to_net(buffer, count, 0);
242 }
243 
244 int
245 read_close(void)
246 {
247 
248 	if (fclose(file) != 0) {
249 		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
250 		return 1;
251 	}
252 	return 0;
253 }
254 
255 
256 /* When an error has occurred, it is possible that the two sides
257  * are out of synch.  Ie: that what I think is the other side's
258  * response to packet N is really their response to packet N-1.
259  *
260  * So, to try to prevent that, we flush all the input queued up
261  * for us on the network connection on our host.
262  *
263  * We return the number of packets we flushed (mostly for reporting
264  * when trace is active).
265  */
266 
267 int
268 synchnet(int peer)			/* socket to flush */
269 {
270 	int i, j = 0;
271 	char rbuf[MAXPKTSIZE];
272 	struct sockaddr_storage from;
273 	socklen_t fromlen;
274 
275 	while (1) {
276 		(void) ioctl(peer, FIONREAD, &i);
277 		if (i) {
278 			j++;
279 			fromlen = sizeof from;
280 			(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
281 				(struct sockaddr *)&from, &fromlen);
282 		} else {
283 			return(j);
284 		}
285 	}
286 }
287