]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - libexec/tftpd/tftp-file.c
MFC r338216:
[FreeBSD/stable/10.git] / libexec / tftpd / tftp-file.c
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 <assert.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <unistd.h>
44
45 #include "tftp-file.h"
46 #include "tftp-utils.h"
47
48 static FILE     *file;
49 static int      convert;
50
51 static char     convbuffer[66000];
52 static int      gotcr = 0;
53
54 static size_t
55 convert_from_net(char *buffer, size_t count)
56 {
57         size_t i, n;
58
59         /*
60          * Convert all CR/LF to LF and all CR,NUL to CR
61          */
62
63         n = 0;
64         for (i = 0; i < count; i++) {
65
66                 if (gotcr == 0) {
67                         convbuffer[n++] = buffer[i];
68                         gotcr = (buffer[i] == '\r');
69                         continue;
70                 }
71
72                 /* CR, NULL -> CR */
73                 if (buffer[i] == '\0') {
74                         gotcr = 0;
75                         continue;
76                 }
77
78                 /* CR, LF -> LF */
79                 if (buffer[i] == '\n') {
80                         if (n == 0) {
81                                 if (ftell(file) != 0) {
82                                         int r = fseek(file, -1, SEEK_END);
83                                         assert(r == 0);
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 = -1;
112
113         if (init) {
114                 newline = -1;
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 != -1) {
126                 buffer[i++] = newline;
127                 newline = -1;
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 allowed (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 }