Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/tftpd/tftp-file.c
34821 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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/types.h>
29
#include <sys/ioctl.h>
30
#include <sys/socket.h>
31
#include <sys/stat.h>
32
33
#include <netinet/in.h>
34
#include <arpa/tftp.h>
35
36
#include <assert.h>
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
int r = fseek(file, -1, SEEK_END);
82
assert(r == 0);
83
convbuffer[n++] = '\n';
84
} else {
85
/* This shouldn't happen */
86
tftp_log(LOG_ERR,
87
"Received LF as first character");
88
abort();
89
}
90
} else
91
convbuffer[n-1] = '\n';
92
gotcr = 0;
93
continue;
94
}
95
96
/* Everything else just accept as is */
97
convbuffer[n++] = buffer[i];
98
gotcr = (buffer[i] == '\r');
99
continue;
100
}
101
102
return fwrite(convbuffer, 1, n, file);
103
}
104
105
static size_t
106
convert_to_net(char *buffer, size_t count, int init)
107
{
108
size_t i;
109
static size_t n = 0, in = 0;
110
static int newline = -1;
111
112
if (init) {
113
newline = -1;
114
n = 0;
115
in = 0;
116
return 0 ;
117
}
118
119
/*
120
* Convert all LF to CR,LF and all CR to CR,NUL
121
*/
122
i = 0;
123
124
if (newline != -1) {
125
buffer[i++] = newline;
126
newline = -1;
127
}
128
129
while (i < count) {
130
if (n == in) {
131
/* When done we're done */
132
if (feof(file)) break;
133
134
/* Otherwise read another bunch */
135
in = fread(convbuffer, 1, count, file);
136
if (in == 0) break;
137
n = 0;
138
}
139
140
/* CR -> CR,NULL */
141
if (convbuffer[n] == '\r') {
142
buffer[i++] = '\r';
143
buffer[i++] = '\0';
144
n++;
145
continue;
146
}
147
148
/* LF -> CR,LF */
149
if (convbuffer[n] == '\n') {
150
buffer[i++] = '\r';
151
buffer[i++] = '\n';
152
n++;
153
continue;
154
}
155
156
buffer[i++] = convbuffer[n++];
157
}
158
159
if (i > count) {
160
/*
161
* Whoops... that isn't allowed (but it will happen
162
* when there is a CR or LF at the end of the buffer)
163
*/
164
newline = buffer[i-1];
165
}
166
167
if (i < count) {
168
/* We are done! */
169
return i;
170
} else
171
return count;
172
173
}
174
175
int
176
write_init(int fd, FILE *f, const char *mode)
177
{
178
179
if (f == NULL) {
180
file = fdopen(fd, "w");
181
if (file == NULL) {
182
int en = errno;
183
tftp_log(LOG_ERR, "fdopen() failed: %s",
184
strerror(errno));
185
return en;
186
}
187
} else
188
file = f;
189
convert = !strcmp(mode, "netascii");
190
return 0;
191
}
192
193
size_t
194
write_file(char *buffer, int count)
195
{
196
197
if (convert == 0)
198
return fwrite(buffer, 1, count, file);
199
200
return convert_from_net(buffer, count);
201
}
202
203
int
204
write_close(void)
205
{
206
207
if (fclose(file) != 0) {
208
tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
209
return 1;
210
}
211
return 0;
212
}
213
214
off_t
215
tell_file(void)
216
{
217
218
return ftello(file);
219
}
220
221
int
222
seek_file(off_t offset)
223
{
224
225
return fseeko(file, offset, SEEK_SET);
226
}
227
228
int
229
read_init(int fd, FILE *f, const char *mode)
230
{
231
232
convert_to_net(NULL, 0, 1);
233
if (f == NULL) {
234
file = fdopen(fd, "r");
235
if (file == NULL) {
236
int en = errno;
237
tftp_log(LOG_ERR, "fdopen() failed: %s",
238
strerror(errno));
239
return en;
240
}
241
} else
242
file = f;
243
convert = !strcmp(mode, "netascii");
244
return 0;
245
}
246
247
size_t
248
read_file(char *buffer, int count)
249
{
250
251
if (convert == 0)
252
return fread(buffer, 1, count, file);
253
254
return convert_to_net(buffer, count, 0);
255
}
256
257
int
258
read_close(void)
259
{
260
261
if (fclose(file) != 0) {
262
tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
263
return 1;
264
}
265
return 0;
266
}
267
268
269
/* When an error has occurred, it is possible that the two sides
270
* are out of synch. Ie: that what I think is the other side's
271
* response to packet N is really their response to packet N-1.
272
*
273
* So, to try to prevent that, we flush all the input queued up
274
* for us on the network connection on our host.
275
*
276
* We return the number of packets we flushed (mostly for reporting
277
* when trace is active).
278
*/
279
280
int
281
synchnet(int peer) /* socket to flush */
282
{
283
int i, j = 0;
284
char rbuf[MAXPKTSIZE];
285
struct sockaddr_storage from;
286
socklen_t fromlen;
287
288
while (1) {
289
(void) ioctl(peer, FIONREAD, &i);
290
if (i) {
291
j++;
292
fromlen = sizeof from;
293
(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
294
(struct sockaddr *)&from, &fromlen);
295
} else {
296
return(j);
297
}
298
}
299
}
300
301