Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/tftpd/tftp-io.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/socket.h>
30
#include <sys/stat.h>
31
32
#include <netinet/in.h>
33
#include <arpa/inet.h>
34
#include <arpa/tftp.h>
35
36
#include <assert.h>
37
#include <errno.h>
38
#include <poll.h>
39
#include <stddef.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
#include <syslog.h>
44
#include <unistd.h>
45
46
#include "tftp-file.h"
47
#include "tftp-io.h"
48
#include "tftp-utils.h"
49
#include "tftp-options.h"
50
51
struct sockaddr_storage peer_sock;
52
struct sockaddr_storage me_sock;
53
54
static int send_packet(int peer, uint16_t block, char *pkt, int size);
55
56
static struct errmsg {
57
int e_code;
58
const char *e_msg;
59
} errmsgs[] = {
60
{ EUNDEF, "Undefined error code" },
61
{ ENOTFOUND, "File not found" },
62
{ EACCESS, "Access violation" },
63
{ ENOSPACE, "Disk full or allocation exceeded" },
64
{ EBADOP, "Illegal TFTP operation" },
65
{ EBADID, "Unknown transfer ID" },
66
{ EEXISTS, "File already exists" },
67
{ ENOUSER, "No such user" },
68
{ EOPTNEG, "Option negotiation" },
69
{ -1, NULL }
70
};
71
72
#define DROPPACKET(s) \
73
if (packetdroppercentage != 0 && \
74
arc4random()%100 < packetdroppercentage) { \
75
tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \
76
return; \
77
}
78
#define DROPPACKETn(s,n) \
79
if (packetdroppercentage != 0 && \
80
arc4random()%100 < packetdroppercentage) { \
81
tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \
82
return (n); \
83
}
84
85
const char *
86
errtomsg(int error)
87
{
88
static char ebuf[40];
89
struct errmsg *pe;
90
91
if (error == 0)
92
return ("success");
93
for (pe = errmsgs; pe->e_code >= 0; pe++)
94
if (pe->e_code == error)
95
return (pe->e_msg);
96
snprintf(ebuf, sizeof(ebuf), "error %d", error);
97
return (ebuf);
98
}
99
100
static int
101
send_packet(int peer, uint16_t block, char *pkt, int size)
102
{
103
int i;
104
int t = 1;
105
106
for (i = 0; i < 12 ; i++) {
107
DROPPACKETn("send_packet", 0);
108
109
if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
110
peer_sock.ss_len) == size) {
111
if (i)
112
tftp_log(LOG_ERR,
113
"%s block %d, attempt %d successful",
114
packettype(ntohs(((struct tftphdr *)
115
(pkt))->th_opcode)), block, i);
116
return (0);
117
}
118
tftp_log(LOG_ERR,
119
"%s block %d, attempt %d failed (Error %d: %s)",
120
packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
121
block, i, errno, strerror(errno));
122
sleep(t);
123
if (t < 32)
124
t <<= 1;
125
}
126
tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
127
return (1);
128
}
129
130
/*
131
* Send an ERROR packet (error message).
132
* Error code passed in is one of the
133
* standard TFTP codes, or a UNIX errno
134
* offset by 100.
135
*/
136
void
137
send_error(int peer, int error)
138
{
139
struct tftphdr *tp;
140
int length;
141
struct errmsg *pe;
142
char buf[MAXPKTSIZE];
143
144
if (debug & DEBUG_PACKETS)
145
tftp_log(LOG_DEBUG, "Sending ERROR %d", error);
146
147
DROPPACKET("send_error");
148
149
tp = (struct tftphdr *)buf;
150
tp->th_opcode = htons((u_short)ERROR);
151
tp->th_code = htons((u_short)error);
152
for (pe = errmsgs; pe->e_code >= 0; pe++)
153
if (pe->e_code == error)
154
break;
155
if (pe->e_code < 0) {
156
pe->e_msg = strerror(error - 100);
157
tp->th_code = EUNDEF; /* set 'undef' errorcode */
158
}
159
snprintf(tp->th_msg, MAXPKTSIZE - 4, "%s%n", pe->e_msg, &length);
160
length += 5; /* header and terminator */
161
162
if (debug & DEBUG_PACKETS)
163
tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
164
165
if (sendto(peer, buf, length, 0,
166
(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
167
tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
168
}
169
170
/*
171
* Send an WRQ packet (write request).
172
*/
173
int
174
send_wrq(int peer, char *filename, char *mode)
175
{
176
int n;
177
struct tftphdr *tp;
178
char *bp;
179
char buf[MAXPKTSIZE];
180
int size;
181
182
if (debug & DEBUG_PACKETS)
183
tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
184
filename, mode
185
);
186
187
DROPPACKETn("send_wrq", 0);
188
189
tp = (struct tftphdr *)buf;
190
tp->th_opcode = htons((u_short)WRQ);
191
size = offsetof(struct tftphdr, th_stuff);
192
193
bp = tp->th_stuff;
194
strlcpy(bp, filename, sizeof(buf) - size);
195
bp += strlen(filename);
196
*bp = 0;
197
bp++;
198
size += strlen(filename) + 1;
199
200
strlcpy(bp, mode, sizeof(buf) - size);
201
bp += strlen(mode);
202
*bp = 0;
203
bp++;
204
size += strlen(mode) + 1;
205
206
if (options_rfc_enabled)
207
size += make_options(peer, bp, sizeof(buf) - size);
208
209
n = sendto(peer, buf, size, 0,
210
(struct sockaddr *)&peer_sock, peer_sock.ss_len);
211
if (n != size) {
212
tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
213
return (1);
214
}
215
return (0);
216
}
217
218
/*
219
* Send an RRQ packet (write request).
220
*/
221
int
222
send_rrq(int peer, char *filename, char *mode)
223
{
224
int n;
225
struct tftphdr *tp;
226
char *bp;
227
char buf[MAXPKTSIZE];
228
int size;
229
230
if (debug & DEBUG_PACKETS)
231
tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
232
filename, mode
233
);
234
235
DROPPACKETn("send_rrq", 0);
236
237
tp = (struct tftphdr *)buf;
238
tp->th_opcode = htons((u_short)RRQ);
239
size = offsetof(struct tftphdr, th_stuff);
240
241
bp = tp->th_stuff;
242
strlcpy(bp, filename, sizeof(buf) - size);
243
bp += strlen(filename);
244
*bp = 0;
245
bp++;
246
size += strlen(filename) + 1;
247
248
strlcpy(bp, mode, sizeof(buf) - size);
249
bp += strlen(mode);
250
*bp = 0;
251
bp++;
252
size += strlen(mode) + 1;
253
254
if (options_rfc_enabled) {
255
options_set_request(OPT_TSIZE, "0");
256
size += make_options(peer, bp, sizeof(buf) - size);
257
}
258
259
n = sendto(peer, buf, size, 0,
260
(struct sockaddr *)&peer_sock, peer_sock.ss_len);
261
if (n != size) {
262
tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
263
return (1);
264
}
265
return (0);
266
}
267
268
/*
269
* Send an OACK packet (option acknowledgement).
270
*/
271
int
272
send_oack(int peer)
273
{
274
struct tftphdr *tp;
275
int size, i, n;
276
char *bp;
277
char buf[MAXPKTSIZE];
278
279
if (debug & DEBUG_PACKETS)
280
tftp_log(LOG_DEBUG, "Sending OACK");
281
282
DROPPACKETn("send_oack", 0);
283
284
/*
285
* Send back an options acknowledgement (only the ones with
286
* a reply for)
287
*/
288
tp = (struct tftphdr *)buf;
289
bp = buf + 2;
290
size = sizeof(buf) - 2;
291
tp->th_opcode = htons((u_short)OACK);
292
for (i = 0; options[i].o_type != NULL; i++) {
293
if (options[i].o_reply != NULL) {
294
n = snprintf(bp, size, "%s%c%s", options[i].o_type,
295
0, options[i].o_reply);
296
bp += n+1;
297
size -= n+1;
298
if (size < 0) {
299
tftp_log(LOG_ERR, "oack: buffer overflow");
300
exit(1);
301
}
302
}
303
}
304
size = bp - buf;
305
306
if (sendto(peer, buf, size, 0,
307
(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
308
tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
309
return (1);
310
}
311
312
return (0);
313
}
314
315
/*
316
* Send an ACK packet (acknowledgement).
317
*/
318
int
319
send_ack(int fp, uint16_t block)
320
{
321
struct tftphdr *tp;
322
int size;
323
char buf[MAXPKTSIZE];
324
325
if (debug & DEBUG_PACKETS)
326
tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
327
328
DROPPACKETn("send_ack", 0);
329
330
tp = (struct tftphdr *)buf;
331
tp->th_opcode = htons((u_short)ACK);
332
tp->th_block = htons((u_short)block);
333
size = 4;
334
335
if (sendto(fp, buf, size, 0,
336
(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
337
tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
338
return (1);
339
}
340
341
return (0);
342
}
343
344
/*
345
* Send a DATA packet
346
*/
347
int
348
send_data(int peer, uint16_t block, char *data, int size)
349
{
350
char buf[MAXPKTSIZE];
351
struct tftphdr *pkt;
352
int n;
353
354
if (debug & DEBUG_PACKETS)
355
tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
356
block, size);
357
358
DROPPACKETn("send_data", 0);
359
360
pkt = (struct tftphdr *)buf;
361
362
pkt->th_opcode = htons((u_short)DATA);
363
pkt->th_block = htons((u_short)block);
364
memcpy(pkt->th_data, data, size);
365
366
n = send_packet(peer, block, (char *)pkt, size + 4);
367
return (n);
368
}
369
370
371
/*
372
* Receive a packet
373
*
374
* If timeout is negative, no error will be logged on timeout.
375
*/
376
int
377
receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
378
int timeout)
379
{
380
struct pollfd pfd;
381
struct tftphdr *pkt;
382
struct sockaddr_storage from_local;
383
struct sockaddr_storage *pfrom;
384
socklen_t fromlen;
385
int n;
386
387
if (debug & DEBUG_PACKETS)
388
tftp_log(LOG_DEBUG,
389
"Waiting %d seconds for packet", timeoutpacket);
390
391
pkt = (struct tftphdr *)data;
392
393
pfd.fd = peer;
394
pfd.events = POLLIN;
395
if (poll(&pfd, 1, 1000 * (timeout < 0 ? -timeout : timeout)) < 1) {
396
if (timeout > 0)
397
tftp_log(LOG_ERR, "receive_packet: timeout");
398
return (RP_TIMEOUT);
399
}
400
401
pfrom = (from == NULL) ? &from_local : from;
402
fromlen = sizeof(*pfrom);
403
n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
404
405
DROPPACKETn("receive_packet", RP_TIMEOUT);
406
407
if (n < 0) {
408
/* No idea what could have happened if it isn't a timeout */
409
tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
410
return (RP_RECVFROM);
411
}
412
if (n < 4) {
413
tftp_log(LOG_ERR,
414
"receive_packet: packet too small (%d bytes)", n);
415
return (RP_TOOSMALL);
416
}
417
418
pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
419
if (pkt->th_opcode == DATA ||
420
pkt->th_opcode == ACK)
421
pkt->th_block = ntohs((u_short)pkt->th_block);
422
423
if (pkt->th_opcode == DATA && n > pktsize) {
424
tftp_log(LOG_ERR, "receive_packet: packet too big");
425
return (RP_TOOBIG);
426
}
427
428
if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
429
((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
430
tftp_log(LOG_ERR,
431
"receive_packet: received packet from wrong source");
432
return (RP_WRONGSOURCE);
433
}
434
435
if (pkt->th_opcode == ERROR) {
436
tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
437
"Got ERROR packet: %s", pkt->th_msg);
438
return (RP_ERROR);
439
}
440
441
if (debug & DEBUG_PACKETS)
442
tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
443
n, packettype(pkt->th_opcode));
444
445
return n - 4;
446
}
447
448