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
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 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
convert_to_net(char * buffer,size_t count,int init)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
write_init(int fd,FILE * f,const char * mode)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
write_file(char * buffer,int count)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
write_close(void)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
read_init(int fd,FILE * f,const char * mode)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
read_file(char * buffer,int count)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
read_close(void)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
synchnet(int peer)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