]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - libexec/tftpd/tftp-file.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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 <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 }