xref: /freebsd/libexec/tftpd/tftp-file.c (revision f05cddf9)
1 /*
2  * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
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 <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
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 					fseek(file, -1, SEEK_END);
82 					convbuffer[n++] = '\n';
83 				} else {
84 					/* This shouldn't happen */
85 					tftp_log(LOG_ERR,
86 					    "Received LF as first character");
87 					abort();
88 				}
89 			} else
90 				convbuffer[n-1] = '\n';
91 			gotcr = 0;
92 			continue;
93 		}
94 
95 		/* Everything else just accept as is */
96 		convbuffer[n++] = buffer[i];
97 		gotcr = (buffer[i] == '\r');
98 		continue;
99 	}
100 
101 	return fwrite(convbuffer, 1, n, file);
102 }
103 
104 static size_t
105 convert_to_net(char *buffer, size_t count, int init)
106 {
107 	size_t i;
108 	static size_t n = 0, in = 0;
109 	static int newline = 0;
110 
111 	if (init) {
112 		newline = 0;
113 		n = 0;
114 		in = 0;
115 		return 0 ;
116 	}
117 
118 	/*
119 	 * Convert all LF to CR,LF and all CR to CR,NUL
120 	 */
121 	i = 0;
122 
123 	if (newline) {
124 		buffer[i++] = newline;
125 		newline = 0;
126 	}
127 
128 	while (i < count) {
129 		if (n == in) {
130 			/* When done we're done */
131 			if (feof(file)) break;
132 
133 			/* Otherwise read another bunch */
134 			in = fread(convbuffer, 1, count, file);
135 			if (in == 0) break;
136 			n = 0;
137 		}
138 
139 		/* CR -> CR,NULL */
140 		if (convbuffer[n] == '\r') {
141 			buffer[i++] = '\r';
142 			buffer[i++] = '\0';
143 			n++;
144 			continue;
145 		}
146 
147 		/* LF -> CR,LF */
148 		if (convbuffer[n] == '\n') {
149 			buffer[i++] = '\r';
150 			buffer[i++] = '\n';
151 			n++;
152 			continue;
153 		}
154 
155 		buffer[i++] = convbuffer[n++];
156 	}
157 
158 	if (i > count) {
159 		/*
160 		 * Whoops... that isn't alllowed (but it will happen
161 		 * when there is a CR or LF at the end of the buffer)
162 		 */
163 		newline = buffer[i-1];
164 	}
165 
166 	if (i < count) {
167 		/* We are done! */
168 		return i;
169 	} else
170 		return count;
171 
172 }
173 
174 int
175 write_init(int fd, FILE *f, const char *mode)
176 {
177 
178 	if (f == NULL) {
179 		file = fdopen(fd, "w");
180 		if (file == NULL) {
181 			int en = errno;
182 			tftp_log(LOG_ERR, "fdopen() failed: %s",
183 			    strerror(errno));
184 			return en;
185 		}
186 	} else
187 		file = f;
188 	convert = !strcmp(mode, "netascii");
189 	return 0;
190 }
191 
192 size_t
193 write_file(char *buffer, int count)
194 {
195 
196 	if (convert == 0)
197 		return fwrite(buffer, 1, count, file);
198 
199 	return convert_from_net(buffer, count);
200 }
201 
202 int
203 write_close(void)
204 {
205 
206 	if (fclose(file) != 0) {
207 		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
208 		return 1;
209 	}
210 	return 0;
211 }
212 
213 int
214 read_init(int fd, FILE *f, const char *mode)
215 {
216 
217 	convert_to_net(NULL, 0, 1);
218 	if (f == NULL) {
219 		file = fdopen(fd, "r");
220 		if (file == NULL) {
221 			int en = errno;
222 			tftp_log(LOG_ERR, "fdopen() failed: %s",
223 			    strerror(errno));
224 			return en;
225 		}
226 	} else
227 		file = f;
228 	convert = !strcmp(mode, "netascii");
229 	return 0;
230 }
231 
232 size_t
233 read_file(char *buffer, int count)
234 {
235 
236 	if (convert == 0)
237 		return fread(buffer, 1, count, file);
238 
239 	return convert_to_net(buffer, count, 0);
240 }
241 
242 int
243 read_close(void)
244 {
245 
246 	if (fclose(file) != 0) {
247 		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
248 		return 1;
249 	}
250 	return 0;
251 }
252 
253 
254 /* When an error has occurred, it is possible that the two sides
255  * are out of synch.  Ie: that what I think is the other side's
256  * response to packet N is really their response to packet N-1.
257  *
258  * So, to try to prevent that, we flush all the input queued up
259  * for us on the network connection on our host.
260  *
261  * We return the number of packets we flushed (mostly for reporting
262  * when trace is active).
263  */
264 
265 int
266 synchnet(int peer)			/* socket to flush */
267 {
268 	int i, j = 0;
269 	char rbuf[MAXPKTSIZE];
270 	struct sockaddr_storage from;
271 	socklen_t fromlen;
272 
273 	while (1) {
274 		(void) ioctl(peer, FIONREAD, &i);
275 		if (i) {
276 			j++;
277 			fromlen = sizeof from;
278 			(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
279 				(struct sockaddr *)&from, &fromlen);
280 		} else {
281 			return(j);
282 		}
283 	}
284 }
285