xref: /freebsd/libexec/tftpd/tftp-file.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
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/stat.h>
31 
32 #include <netinet/in.h>
33 #include <arpa/tftp.h>
34 
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <syslog.h>
40 #include <unistd.h>
41 
42 #include "tftp-file.h"
43 #include "tftp-utils.h"
44 
45 static FILE	*file;
46 static int	convert;
47 
48 static char	convbuffer[66000];
49 static int	gotcr = 0;
50 
51 static size_t
52 convert_from_net(char *buffer, size_t count)
53 {
54 	size_t i, n;
55 
56 	/*
57 	 * Convert all CR/LF to LF and all CR,NUL to CR
58 	 */
59 
60 	n = 0;
61 	for (i = 0; i < count; i++) {
62 
63 		if (gotcr == 0) {
64 			convbuffer[n++] = buffer[i];
65 			gotcr = (buffer[i] == '\r');
66 			continue;
67 		}
68 
69 		/* CR, NULL -> CR */
70 		if (buffer[i] == '\0') {
71 			gotcr = 0;
72 			continue;
73 		}
74 
75 		/* CR, LF -> LF */
76 		if (buffer[i] == '\n') {
77 			if (n == 0) {
78 				if (ftell(file) != 0) {
79 					fseek(file, -1, SEEK_END);
80 					convbuffer[n++] = '\n';
81 				} else {
82 					/* This shouldn't happen */
83 					tftp_log(LOG_ERR,
84 					    "Received LF as first character");
85 					abort();
86 				}
87 			} else
88 				convbuffer[n-1] = '\n';
89 			gotcr = 0;
90 			continue;
91 		}
92 
93 		/* Everything else just accept as is */
94 		convbuffer[n++] = buffer[i];
95 		gotcr = (buffer[i] == '\r');
96 		continue;
97 	}
98 
99 	return fwrite(convbuffer, 1, n, file);
100 }
101 
102 static size_t
103 convert_to_net(char *buffer, size_t count, int init)
104 {
105 	size_t i;
106 	static size_t n = 0, in = 0;
107 	static int newline = 0;
108 
109 	if (init) {
110 		newline = 0;
111 		n = 0;
112 		in = 0;
113 		return 0 ;
114 	}
115 
116 	/*
117 	 * Convert all LF to CR,LF and all CR to CR,NUL
118 	 */
119 	i = 0;
120 
121 	if (newline) {
122 		buffer[i++] = newline;
123 		newline = 0;
124 	}
125 
126 	while (i < count) {
127 		if (n == in) {
128 			/* When done we're done */
129 			if (feof(file)) break;
130 
131 			/* Otherwise read another bunch */
132 			in = fread(convbuffer, 1, count, file);
133 			if (in == 0) break;
134 			n = 0;
135 		}
136 
137 		/* CR -> CR,NULL */
138 		if (convbuffer[n] == '\r') {
139 			buffer[i++] = '\r';
140 			buffer[i++] = '\0';
141 			n++;
142 			continue;
143 		}
144 
145 		/* LF -> CR,LF */
146 		if (convbuffer[n] == '\n') {
147 			buffer[i++] = '\r';
148 			buffer[i++] = '\n';
149 			n++;
150 			continue;
151 		}
152 
153 		buffer[i++] = convbuffer[n++];
154 	}
155 
156 	if (i > count) {
157 		/*
158 		 * Whoops... that isn't alllowed (but it will happen
159 		 * when there is a CR or LF at the end of the buffer)
160 		 */
161 		newline = buffer[i-1];
162 	}
163 
164 	if (i < count) {
165 		/* We are done! */
166 		return i;
167 	} else
168 		return count;
169 
170 }
171 
172 int
173 write_init(int fd, FILE *f, const char *mode)
174 {
175 
176 	if (f == NULL) {
177 		file = fdopen(fd, "w");
178 		if (file == NULL) {
179 			int en = errno;
180 			tftp_log(LOG_ERR, "fdopen() failed: %s",
181 			    strerror(errno));
182 			return en;
183 		}
184 	} else
185 		file = f;
186 	convert = !strcmp(mode, "netascii");
187 	return 0;
188 }
189 
190 size_t
191 write_file(char *buffer, int count)
192 {
193 
194 	if (convert == 0)
195 		return fwrite(buffer, 1, count, file);
196 
197 	return convert_from_net(buffer, count);
198 }
199 
200 int
201 write_close(void)
202 {
203 
204 	if (fclose(file) != 0) {
205 		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
206 		return 1;
207 	}
208 	return 0;
209 }
210 
211 int
212 read_init(int fd, FILE *f, const char *mode)
213 {
214 
215 	convert_to_net(NULL, 0, 1);
216 	if (f == NULL) {
217 		file = fdopen(fd, "r");
218 		if (file == NULL) {
219 			int en = errno;
220 			tftp_log(LOG_ERR, "fdopen() failed: %s",
221 			    strerror(errno));
222 			return en;
223 		}
224 	} else
225 		file = f;
226 	convert = !strcmp(mode, "netascii");
227 	return 0;
228 }
229 
230 size_t
231 read_file(char *buffer, int count)
232 {
233 
234 	if (convert == 0)
235 		return fread(buffer, 1, count, file);
236 
237 	return convert_to_net(buffer, count, 0);
238 }
239 
240 int
241 read_close(void)
242 {
243 
244 	if (fclose(file) != 0) {
245 		tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
246 		return 1;
247 	}
248 	return 0;
249 }
250 
251 
252 int
253 synchnet(int peer __unused)
254 {
255 
256 	return 0;
257 }
258