]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/tftpd/tftp-file.c
Followup to r347996
[FreeBSD/FreeBSD.git] / libexec / tftpd / tftp-file.c
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 <assert.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
46
47 #include "tftp-file.h"
48 #include "tftp-utils.h"
49
50 static FILE     *file;
51 static int      convert;
52
53 static char     convbuffer[66000];
54 static int      gotcr = 0;
55
56 static size_t
57 convert_from_net(char *buffer, size_t count)
58 {
59         size_t i, n;
60
61         /*
62          * Convert all CR/LF to LF and all CR,NUL to CR
63          */
64
65         n = 0;
66         for (i = 0; i < count; i++) {
67
68                 if (gotcr == 0) {
69                         convbuffer[n++] = buffer[i];
70                         gotcr = (buffer[i] == '\r');
71                         continue;
72                 }
73
74                 /* CR, NULL -> CR */
75                 if (buffer[i] == '\0') {
76                         gotcr = 0;
77                         continue;
78                 }
79
80                 /* CR, LF -> LF */
81                 if (buffer[i] == '\n') {
82                         if (n == 0) {
83                                 if (ftell(file) != 0) {
84                                         int r = fseek(file, -1, SEEK_END);
85                                         assert(r == 0);
86                                         convbuffer[n++] = '\n';
87                                 } else {
88                                         /* This shouldn't happen */
89                                         tftp_log(LOG_ERR,
90                                             "Received LF as first character");
91                                         abort();
92                                 }
93                         } else
94                                 convbuffer[n-1] = '\n';
95                         gotcr = 0;
96                         continue;
97                 }
98
99                 /* Everything else just accept as is */
100                 convbuffer[n++] = buffer[i];
101                 gotcr = (buffer[i] == '\r');
102                 continue;
103         }
104
105         return fwrite(convbuffer, 1, n, file);
106 }
107
108 static size_t
109 convert_to_net(char *buffer, size_t count, int init)
110 {
111         size_t i;
112         static size_t n = 0, in = 0;
113         static int newline = -1;
114
115         if (init) {
116                 newline = -1;
117                 n = 0;
118                 in = 0;
119                 return 0 ;
120         }
121
122         /*
123          * Convert all LF to CR,LF and all CR to CR,NUL
124          */
125         i = 0;
126
127         if (newline != -1) {
128                 buffer[i++] = newline;
129                 newline = -1;
130         }
131
132         while (i < count) {
133                 if (n == in) {
134                         /* When done we're done */
135                         if (feof(file)) break;
136
137                         /* Otherwise read another bunch */
138                         in = fread(convbuffer, 1, count, file);
139                         if (in == 0) break;
140                         n = 0;
141                 }
142
143                 /* CR -> CR,NULL */
144                 if (convbuffer[n] == '\r') {
145                         buffer[i++] = '\r';
146                         buffer[i++] = '\0';
147                         n++;
148                         continue;
149                 }
150
151                 /* LF -> CR,LF */
152                 if (convbuffer[n] == '\n') {
153                         buffer[i++] = '\r';
154                         buffer[i++] = '\n';
155                         n++;
156                         continue;
157                 }
158
159                 buffer[i++] = convbuffer[n++];
160         }
161
162         if (i > count) {
163                 /*
164                  * Whoops... that isn't allowed (but it will happen
165                  * when there is a CR or LF at the end of the buffer)
166                  */
167                 newline = buffer[i-1];
168         }
169
170         if (i < count) {
171                 /* We are done! */
172                 return i;
173         } else
174                 return count;
175
176 }
177
178 int
179 write_init(int fd, FILE *f, const char *mode)
180 {
181
182         if (f == NULL) {
183                 file = fdopen(fd, "w");
184                 if (file == NULL) {
185                         int en = errno;
186                         tftp_log(LOG_ERR, "fdopen() failed: %s",
187                             strerror(errno));
188                         return en;
189                 }
190         } else
191                 file = f;
192         convert = !strcmp(mode, "netascii");
193         return 0;
194 }
195
196 size_t
197 write_file(char *buffer, int count)
198 {
199
200         if (convert == 0)
201                 return fwrite(buffer, 1, count, file);
202
203         return convert_from_net(buffer, count);
204 }
205
206 int
207 write_close(void)
208 {
209
210         if (fclose(file) != 0) {
211                 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
212                 return 1;
213         }
214         return 0;
215 }
216
217 int
218 read_init(int fd, FILE *f, const char *mode)
219 {
220
221         convert_to_net(NULL, 0, 1);
222         if (f == NULL) {
223                 file = fdopen(fd, "r");
224                 if (file == NULL) {
225                         int en = errno;
226                         tftp_log(LOG_ERR, "fdopen() failed: %s",
227                             strerror(errno));
228                         return en;
229                 }
230         } else
231                 file = f;
232         convert = !strcmp(mode, "netascii");
233         return 0;
234 }
235
236 size_t
237 read_file(char *buffer, int count)
238 {
239
240         if (convert == 0)
241                 return fread(buffer, 1, count, file);
242
243         return convert_to_net(buffer, count, 0);
244 }
245
246 int
247 read_close(void)
248 {
249
250         if (fclose(file) != 0) {
251                 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
252                 return 1;
253         }
254         return 0;
255 }
256
257
258 /* When an error has occurred, it is possible that the two sides
259  * are out of synch.  Ie: that what I think is the other side's
260  * response to packet N is really their response to packet N-1.
261  *
262  * So, to try to prevent that, we flush all the input queued up
263  * for us on the network connection on our host.
264  *
265  * We return the number of packets we flushed (mostly for reporting
266  * when trace is active).
267  */
268
269 int
270 synchnet(int peer)                      /* socket to flush */
271 {
272         int i, j = 0;
273         char rbuf[MAXPKTSIZE];
274         struct sockaddr_storage from;
275         socklen_t fromlen;
276
277         while (1) {
278                 (void) ioctl(peer, FIONREAD, &i);
279                 if (i) {
280                         j++;
281                         fromlen = sizeof from;
282                         (void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
283                                 (struct sockaddr *)&from, &fromlen);
284                 } else {
285                         return(j);
286                 }
287         }
288 }