1 |
|
|
/* $OpenBSD: tftpd.c,v 1.40 2017/11/07 14:15:38 jca Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2012 David Gwynne <dlg@uq.edu.au> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
/* |
20 |
|
|
* Copyright (c) 1983 Regents of the University of California. |
21 |
|
|
* All rights reserved. |
22 |
|
|
* |
23 |
|
|
* Redistribution and use in source and binary forms, with or without |
24 |
|
|
* modification, are permitted provided that the following conditions |
25 |
|
|
* are met: |
26 |
|
|
* 1. Redistributions of source code must retain the above copyright |
27 |
|
|
* notice, this list of conditions and the following disclaimer. |
28 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
29 |
|
|
* notice, this list of conditions and the following disclaimer in the |
30 |
|
|
* documentation and/or other materials provided with the distribution. |
31 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
32 |
|
|
* may be used to endorse or promote products derived from this software |
33 |
|
|
* without specific prior written permission. |
34 |
|
|
* |
35 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
36 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
37 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
38 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
39 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
40 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
41 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
42 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
43 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
44 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
45 |
|
|
* SUCH DAMAGE. |
46 |
|
|
*/ |
47 |
|
|
|
48 |
|
|
/* |
49 |
|
|
* Trivial file transfer protocol server. |
50 |
|
|
* |
51 |
|
|
* This version is based on src/libexec/tftpd which includes many |
52 |
|
|
* modifications by Jim Guyton <guyton@rand-unix>. |
53 |
|
|
* |
54 |
|
|
* It was restructured to be a persistent event driven daemon |
55 |
|
|
* supporting concurrent connections by dlg for use at the University |
56 |
|
|
* of Queensland in the Faculty of Engineering Architecture and |
57 |
|
|
* Information Technology. |
58 |
|
|
*/ |
59 |
|
|
|
60 |
|
|
#include <sys/types.h> |
61 |
|
|
#include <sys/queue.h> |
62 |
|
|
#include <sys/socket.h> |
63 |
|
|
#include <sys/stat.h> |
64 |
|
|
#include <sys/uio.h> |
65 |
|
|
#include <sys/un.h> |
66 |
|
|
|
67 |
|
|
#include <netinet/in.h> |
68 |
|
|
#include <arpa/inet.h> |
69 |
|
|
#include <arpa/tftp.h> |
70 |
|
|
#include <netdb.h> |
71 |
|
|
|
72 |
|
|
#include <err.h> |
73 |
|
|
#include <ctype.h> |
74 |
|
|
#include <errno.h> |
75 |
|
|
#include <event.h> |
76 |
|
|
#include <fcntl.h> |
77 |
|
|
#include <paths.h> |
78 |
|
|
#include <poll.h> |
79 |
|
|
#include <pwd.h> |
80 |
|
|
#include <stdio.h> |
81 |
|
|
#include <stdlib.h> |
82 |
|
|
#include <string.h> |
83 |
|
|
#include <stdarg.h> |
84 |
|
|
#include <syslog.h> |
85 |
|
|
#include <unistd.h> |
86 |
|
|
#include <limits.h> |
87 |
|
|
#include <vis.h> |
88 |
|
|
|
89 |
|
|
#define TIMEOUT 5 /* packet rexmt timeout */ |
90 |
|
|
#define TIMEOUT_MIN 1 /* minimal packet rexmt timeout */ |
91 |
|
|
#define TIMEOUT_MAX 255 /* maximal packet rexmt timeout */ |
92 |
|
|
|
93 |
|
|
#define RETRIES 5 |
94 |
|
|
|
95 |
|
|
#define SEEDPATH "/etc/random.seed" |
96 |
|
|
|
97 |
|
|
struct formats; |
98 |
|
|
|
99 |
|
|
enum opt_enum { |
100 |
|
|
OPT_TSIZE = 0, |
101 |
|
|
OPT_TIMEOUT, |
102 |
|
|
OPT_BLKSIZE, |
103 |
|
|
NOPT |
104 |
|
|
}; |
105 |
|
|
|
106 |
|
|
static char *opt_names[] = { |
107 |
|
|
"tsize", |
108 |
|
|
"timeout", |
109 |
|
|
"blksize" |
110 |
|
|
}; |
111 |
|
|
|
112 |
|
|
struct opt_client { |
113 |
|
|
char *o_request; |
114 |
|
|
long long o_reply; |
115 |
|
|
}; |
116 |
|
|
|
117 |
|
|
|
118 |
|
|
struct tftp_server { |
119 |
|
|
struct event ev; |
120 |
|
|
TAILQ_ENTRY(tftp_server) entry; |
121 |
|
|
int s; |
122 |
|
|
}; |
123 |
|
|
|
124 |
|
|
TAILQ_HEAD(, tftp_server) tftp_servers; |
125 |
|
|
|
126 |
|
|
struct tftp_client { |
127 |
|
|
char buf[SEGSIZE_MAX + 4]; |
128 |
|
|
struct event sev; |
129 |
|
|
struct sockaddr_storage ss; |
130 |
|
|
|
131 |
|
|
struct timeval tv; |
132 |
|
|
|
133 |
|
|
TAILQ_ENTRY(tftp_client) entry; |
134 |
|
|
|
135 |
|
|
struct opt_client *options; |
136 |
|
|
|
137 |
|
|
size_t segment_size; |
138 |
|
|
size_t packet_size; |
139 |
|
|
size_t buflen; |
140 |
|
|
|
141 |
|
|
FILE *file; |
142 |
|
|
int (*fgetc)(struct tftp_client *); |
143 |
|
|
int (*fputc)(struct tftp_client *, int); |
144 |
|
|
|
145 |
|
|
u_int retries; |
146 |
|
|
u_int16_t block; |
147 |
|
|
|
148 |
|
|
int opcode; |
149 |
|
|
int newline; |
150 |
|
|
|
151 |
|
|
int sock; |
152 |
|
|
}; |
153 |
|
|
|
154 |
|
|
__dead void usage(void); |
155 |
|
|
const char *getip(void *); |
156 |
|
|
int rdaemon(int); |
157 |
|
|
|
158 |
|
|
void rewrite_connect(const char *); |
159 |
|
|
void rewrite_events(void); |
160 |
|
|
void rewrite_map(struct tftp_client *, const char *); |
161 |
|
|
void rewrite_req(int, short, void *); |
162 |
|
|
void rewrite_res(int, short, void *); |
163 |
|
|
|
164 |
|
|
int tftpd_listen(const char *, const char *, int); |
165 |
|
|
void tftpd_events(void); |
166 |
|
|
void tftpd_recv(int, short, void *); |
167 |
|
|
int retry(struct tftp_client *); |
168 |
|
|
int tftp_flush(struct tftp_client *); |
169 |
|
|
void tftp_end(struct tftp_client *); |
170 |
|
|
|
171 |
|
|
void tftp(struct tftp_client *, struct tftphdr *, size_t); |
172 |
|
|
void tftp_open(struct tftp_client *, const char *); |
173 |
|
|
void nak(struct tftp_client *, int); |
174 |
|
|
int oack(struct tftp_client *); |
175 |
|
|
void oack_done(int, short, void *); |
176 |
|
|
|
177 |
|
|
void sendfile(struct tftp_client *); |
178 |
|
|
void recvfile(struct tftp_client *); |
179 |
|
|
int fget_octet(struct tftp_client *); |
180 |
|
|
int fput_octet(struct tftp_client *, int); |
181 |
|
|
int fget_netascii(struct tftp_client *); |
182 |
|
|
int fput_netascii(struct tftp_client *, int); |
183 |
|
|
void file_read(struct tftp_client *); |
184 |
|
|
int tftp_wrq_ack_packet(struct tftp_client *); |
185 |
|
|
void tftp_rrq_ack(int, short, void *); |
186 |
|
|
void tftp_wrq_ack(struct tftp_client *client); |
187 |
|
|
void tftp_wrq(int, short, void *); |
188 |
|
|
void tftp_wrq_end(int, short, void *); |
189 |
|
|
|
190 |
|
|
int parse_options(struct tftp_client *, char *, size_t, |
191 |
|
|
struct opt_client *); |
192 |
|
|
int validate_access(struct tftp_client *, const char *); |
193 |
|
|
|
194 |
|
|
struct tftp_client * |
195 |
|
|
client_alloc(void); |
196 |
|
|
void client_free(struct tftp_client *client); |
197 |
|
|
|
198 |
|
|
struct formats { |
199 |
|
|
const char *f_mode; |
200 |
|
|
int (*f_getc)(struct tftp_client *); |
201 |
|
|
int (*f_putc)(struct tftp_client *, int); |
202 |
|
|
} formats[] = { |
203 |
|
|
{ "octet", fget_octet, fput_octet }, |
204 |
|
|
{ "netascii", fget_netascii, fput_netascii }, |
205 |
|
|
{ NULL, NULL } |
206 |
|
|
}; |
207 |
|
|
|
208 |
|
|
struct errmsg { |
209 |
|
|
int e_code; |
210 |
|
|
const char *e_msg; |
211 |
|
|
} errmsgs[] = { |
212 |
|
|
{ EUNDEF, "Undefined error code" }, |
213 |
|
|
{ ENOTFOUND, "File not found" }, |
214 |
|
|
{ EACCESS, "Access violation" }, |
215 |
|
|
{ ENOSPACE, "Disk full or allocation exceeded" }, |
216 |
|
|
{ EBADOP, "Illegal TFTP operation" }, |
217 |
|
|
{ EBADID, "Unknown transfer ID" }, |
218 |
|
|
{ EEXISTS, "File already exists" }, |
219 |
|
|
{ ENOUSER, "No such user" }, |
220 |
|
|
{ EOPTNEG, "Option negotiation failed" }, |
221 |
|
|
{ -1, NULL } |
222 |
|
|
}; |
223 |
|
|
|
224 |
|
|
struct loggers { |
225 |
|
|
__dead void (*err)(int, const char *, ...) |
226 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
227 |
|
|
__dead void (*errx)(int, const char *, ...) |
228 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
229 |
|
|
void (*warn)(const char *, ...) |
230 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
231 |
|
|
void (*warnx)(const char *, ...) |
232 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
233 |
|
|
void (*info)(const char *, ...) |
234 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
235 |
|
|
void (*debug)(const char *, ...) |
236 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
237 |
|
|
}; |
238 |
|
|
|
239 |
|
|
const struct loggers conslogger = { |
240 |
|
|
err, |
241 |
|
|
errx, |
242 |
|
|
warn, |
243 |
|
|
warnx, |
244 |
|
|
warnx, /* info */ |
245 |
|
|
warnx /* debug */ |
246 |
|
|
}; |
247 |
|
|
|
248 |
|
|
__dead void syslog_err(int, const char *, ...) |
249 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
250 |
|
|
__dead void syslog_errx(int, const char *, ...) |
251 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
252 |
|
|
void syslog_warn(const char *, ...) |
253 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
254 |
|
|
void syslog_warnx(const char *, ...) |
255 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
256 |
|
|
void syslog_info(const char *, ...) |
257 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
258 |
|
|
void syslog_debug(const char *, ...) |
259 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
260 |
|
|
void syslog_vstrerror(int, int, const char *, va_list) |
261 |
|
|
__attribute__((__format__ (printf, 3, 0))); |
262 |
|
|
|
263 |
|
|
const struct loggers syslogger = { |
264 |
|
|
syslog_err, |
265 |
|
|
syslog_errx, |
266 |
|
|
syslog_warn, |
267 |
|
|
syslog_warnx, |
268 |
|
|
syslog_info, |
269 |
|
|
syslog_debug |
270 |
|
|
}; |
271 |
|
|
|
272 |
|
|
const struct loggers *logger = &conslogger; |
273 |
|
|
|
274 |
|
|
#define lerr(_e, _f...) logger->err((_e), _f) |
275 |
|
|
#define lerrx(_e, _f...) logger->errx((_e), _f) |
276 |
|
|
#define lwarn(_f...) logger->warn(_f) |
277 |
|
|
#define lwarnx(_f...) logger->warnx(_f) |
278 |
|
|
#define linfo(_f...) logger->info(_f) |
279 |
|
|
#define ldebug(_f...) logger->debug(_f) |
280 |
|
|
|
281 |
|
|
__dead void |
282 |
|
|
usage(void) |
283 |
|
|
{ |
284 |
|
|
extern char *__progname; |
285 |
|
|
fprintf(stderr, "usage: %s [-46cdiv] [-l address] [-p port] [-r socket]" |
286 |
|
|
" directory\n", __progname); |
287 |
|
|
exit(1); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
int cancreate = 0; |
291 |
|
|
int verbose = 0; |
292 |
|
|
int debug = 0; |
293 |
|
|
int iflag = 0; |
294 |
|
|
|
295 |
|
|
int |
296 |
|
|
main(int argc, char *argv[]) |
297 |
|
|
{ |
298 |
|
|
extern char *__progname; |
299 |
|
|
|
300 |
|
|
int c; |
301 |
|
|
struct passwd *pw; |
302 |
|
|
|
303 |
|
|
char *dir = NULL; |
304 |
|
|
char *rewrite = NULL; |
305 |
|
|
|
306 |
|
|
char *addr = NULL; |
307 |
|
|
char *port = "tftp"; |
308 |
|
|
int family = AF_UNSPEC; |
309 |
|
|
int devnull = -1; |
310 |
|
|
|
311 |
|
|
while ((c = getopt(argc, argv, "46cdil:p:r:v")) != -1) { |
312 |
|
|
switch (c) { |
313 |
|
|
case '4': |
314 |
|
|
family = AF_INET; |
315 |
|
|
break; |
316 |
|
|
case '6': |
317 |
|
|
family = AF_INET6; |
318 |
|
|
break; |
319 |
|
|
case 'c': |
320 |
|
|
cancreate = 1; |
321 |
|
|
break; |
322 |
|
|
case 'd': |
323 |
|
|
verbose = debug = 1; |
324 |
|
|
break; |
325 |
|
|
case 'i': |
326 |
|
|
if (rewrite != NULL) |
327 |
|
|
errx(1, "options -i and -r are incompatible"); |
328 |
|
|
iflag = 1; |
329 |
|
|
break; |
330 |
|
|
case 'l': |
331 |
|
|
addr = optarg; |
332 |
|
|
break; |
333 |
|
|
case 'p': |
334 |
|
|
port = optarg; |
335 |
|
|
break; |
336 |
|
|
case 'r': |
337 |
|
|
if (iflag == 1) |
338 |
|
|
errx(1, "options -i and -r are incompatible"); |
339 |
|
|
rewrite = optarg; |
340 |
|
|
break; |
341 |
|
|
case 'v': |
342 |
|
|
verbose = 1; |
343 |
|
|
break; |
344 |
|
|
default: |
345 |
|
|
usage(); |
346 |
|
|
/* NOTREACHED */ |
347 |
|
|
} |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
argc -= optind; |
351 |
|
|
argv += optind; |
352 |
|
|
|
353 |
|
|
if (argc != 1) |
354 |
|
|
usage(); |
355 |
|
|
|
356 |
|
|
dir = argv[0]; |
357 |
|
|
|
358 |
|
|
if (geteuid() != 0) |
359 |
|
|
errx(1, "need root privileges"); |
360 |
|
|
|
361 |
|
|
pw = getpwnam("_tftpd"); |
362 |
|
|
if (pw == NULL) |
363 |
|
|
errx(1, "no _tftpd user"); |
364 |
|
|
|
365 |
|
|
if (!debug) { |
366 |
|
|
openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); |
367 |
|
|
tzset(); |
368 |
|
|
logger = &syslogger; |
369 |
|
|
devnull = open(_PATH_DEVNULL, O_RDWR, 0); |
370 |
|
|
if (devnull == -1) |
371 |
|
|
err(1, "open %s", _PATH_DEVNULL); |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
if (rewrite != NULL) |
375 |
|
|
rewrite_connect(rewrite); |
376 |
|
|
|
377 |
|
|
tftpd_listen(addr, port, family); |
378 |
|
|
|
379 |
|
|
if (chroot(dir)) |
380 |
|
|
err(1, "chroot %s", dir); |
381 |
|
|
if (chdir("/")) |
382 |
|
|
err(1, "chdir %s", dir); |
383 |
|
|
|
384 |
|
|
/* drop privs */ |
385 |
|
|
if (setgroups(1, &pw->pw_gid) || |
386 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
387 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
388 |
|
|
errx(1, "can't drop privileges"); |
389 |
|
|
|
390 |
|
|
if (!debug && rdaemon(devnull) == -1) |
391 |
|
|
err(1, "unable to daemonize"); |
392 |
|
|
|
393 |
|
|
if (pledge("stdio rpath wpath cpath fattr dns inet flock", NULL) == -1) |
394 |
|
|
lerr(1, "pledge"); |
395 |
|
|
|
396 |
|
|
event_init(); |
397 |
|
|
|
398 |
|
|
if (rewrite != NULL) |
399 |
|
|
rewrite_events(); |
400 |
|
|
|
401 |
|
|
tftpd_events(); |
402 |
|
|
|
403 |
|
|
event_dispatch(); |
404 |
|
|
|
405 |
|
|
exit(0); |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
struct rewritemap { |
409 |
|
|
struct event wrev; |
410 |
|
|
struct event rdev; |
411 |
|
|
struct evbuffer *wrbuf; |
412 |
|
|
struct evbuffer *rdbuf; |
413 |
|
|
|
414 |
|
|
TAILQ_HEAD(, tftp_client) clients; |
415 |
|
|
|
416 |
|
|
int s; |
417 |
|
|
}; |
418 |
|
|
|
419 |
|
|
struct rewritemap *rwmap = NULL; |
420 |
|
|
|
421 |
|
|
void |
422 |
|
|
rewrite_connect(const char *path) |
423 |
|
|
{ |
424 |
|
|
int s; |
425 |
|
|
struct sockaddr_un remote; |
426 |
|
|
size_t len; |
427 |
|
|
|
428 |
|
|
rwmap = malloc(sizeof(*rwmap)); |
429 |
|
|
if (rwmap == NULL) |
430 |
|
|
err(1, "rewrite event malloc"); |
431 |
|
|
|
432 |
|
|
rwmap->wrbuf = evbuffer_new(); |
433 |
|
|
if (rwmap->wrbuf == NULL) |
434 |
|
|
err(1, "rewrite wrbuf"); |
435 |
|
|
|
436 |
|
|
rwmap->rdbuf = evbuffer_new(); |
437 |
|
|
if (rwmap->rdbuf == NULL) |
438 |
|
|
err(1, "rewrite rdbuf"); |
439 |
|
|
|
440 |
|
|
TAILQ_INIT(&rwmap->clients); |
441 |
|
|
|
442 |
|
|
s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); |
443 |
|
|
if (s == -1) |
444 |
|
|
err(1, "rewrite socket"); |
445 |
|
|
|
446 |
|
|
remote.sun_family = AF_UNIX; |
447 |
|
|
len = strlcpy(remote.sun_path, path, sizeof(remote.sun_path)); |
448 |
|
|
if (len >= sizeof(remote.sun_path)) |
449 |
|
|
errx(1, "rewrite socket path is too long"); |
450 |
|
|
|
451 |
|
|
len += sizeof(remote.sun_family) + 1; |
452 |
|
|
if (connect(s, (struct sockaddr *)&remote, len) == -1) |
453 |
|
|
err(1, "%s", path); |
454 |
|
|
|
455 |
|
|
rwmap->s = s; |
456 |
|
|
} |
457 |
|
|
|
458 |
|
|
void |
459 |
|
|
rewrite_events(void) |
460 |
|
|
{ |
461 |
|
|
event_set(&rwmap->wrev, rwmap->s, EV_WRITE, rewrite_req, NULL); |
462 |
|
|
event_set(&rwmap->rdev, rwmap->s, EV_READ | EV_PERSIST, rewrite_res, NULL); |
463 |
|
|
event_add(&rwmap->rdev, NULL); |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
void |
467 |
|
|
rewrite_map(struct tftp_client *client, const char *filename) |
468 |
|
|
{ |
469 |
|
|
char *nicebuf; |
470 |
|
|
|
471 |
|
|
if (stravis(&nicebuf, filename, VIS_SAFE|VIS_OCTAL) == -1) |
472 |
|
|
lerr(1, "rwmap stravis"); |
473 |
|
|
|
474 |
|
|
if (evbuffer_add_printf(rwmap->wrbuf, "%s %s %s\n", getip(&client->ss), |
475 |
|
|
client->opcode == WRQ ? "write" : "read", nicebuf) == -1) |
476 |
|
|
lerr(1, "rwmap printf"); |
477 |
|
|
|
478 |
|
|
free(nicebuf); |
479 |
|
|
|
480 |
|
|
TAILQ_INSERT_TAIL(&rwmap->clients, client, entry); |
481 |
|
|
|
482 |
|
|
event_add(&rwmap->wrev, NULL); |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
void |
486 |
|
|
rewrite_req(int fd, short events, void *arg) |
487 |
|
|
{ |
488 |
|
|
if (evbuffer_write(rwmap->wrbuf, fd) == -1) { |
489 |
|
|
switch (errno) { |
490 |
|
|
case EINTR: |
491 |
|
|
case EAGAIN: |
492 |
|
|
event_add(&rwmap->wrev, NULL); |
493 |
|
|
return; |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
lerr(1, "rewrite socket write"); |
497 |
|
|
} |
498 |
|
|
|
499 |
|
|
if (EVBUFFER_LENGTH(rwmap->wrbuf)) |
500 |
|
|
event_add(&rwmap->wrev, NULL); |
501 |
|
|
} |
502 |
|
|
|
503 |
|
|
void |
504 |
|
|
rewrite_res(int fd, short events, void *arg) |
505 |
|
|
{ |
506 |
|
|
struct tftp_client *client; |
507 |
|
|
char *filename; |
508 |
|
|
size_t len; |
509 |
|
|
|
510 |
|
|
switch (evbuffer_read(rwmap->rdbuf, fd, PATH_MAX)) { |
511 |
|
|
case -1: |
512 |
|
|
switch (errno) { |
513 |
|
|
case EINTR: |
514 |
|
|
case EAGAIN: |
515 |
|
|
return; |
516 |
|
|
} |
517 |
|
|
lerr(1, "rewrite socket read"); |
518 |
|
|
case 0: |
519 |
|
|
lerrx(1, "rewrite socket closed"); |
520 |
|
|
default: |
521 |
|
|
break; |
522 |
|
|
} |
523 |
|
|
|
524 |
|
|
while ((filename = evbuffer_readln(rwmap->rdbuf, &len, |
525 |
|
|
EVBUFFER_EOL_LF)) != NULL) { |
526 |
|
|
client = TAILQ_FIRST(&rwmap->clients); |
527 |
|
|
if (client == NULL) |
528 |
|
|
lerrx(1, "unexpected rwmap reply"); |
529 |
|
|
|
530 |
|
|
TAILQ_REMOVE(&rwmap->clients, client, entry); |
531 |
|
|
|
532 |
|
|
tftp_open(client, filename); |
533 |
|
|
|
534 |
|
|
free(filename); |
535 |
|
|
}; |
536 |
|
|
} |
537 |
|
|
|
538 |
|
|
int |
539 |
|
|
tftpd_listen(const char *addr, const char *port, int family) |
540 |
|
|
{ |
541 |
|
|
struct tftp_server *server; |
542 |
|
|
|
543 |
|
|
struct addrinfo hints, *res, *res0; |
544 |
|
|
int error; |
545 |
|
|
int s; |
546 |
|
|
|
547 |
|
|
int cerrno = EADDRNOTAVAIL; |
548 |
|
|
const char *cause = "getaddrinfo"; |
549 |
|
|
|
550 |
|
|
int on = 1; |
551 |
|
|
|
552 |
|
|
memset(&hints, 0, sizeof(hints)); |
553 |
|
|
hints.ai_family = family; |
554 |
|
|
hints.ai_socktype = SOCK_DGRAM; |
555 |
|
|
hints.ai_flags = AI_PASSIVE; |
556 |
|
|
|
557 |
|
|
TAILQ_INIT(&tftp_servers); |
558 |
|
|
|
559 |
|
|
error = getaddrinfo(addr, port, &hints, &res0); |
560 |
|
|
if (error) { |
561 |
|
|
errx(1, "%s:%s: %s", addr ? addr : "*", port, |
562 |
|
|
gai_strerror(error)); |
563 |
|
|
} |
564 |
|
|
|
565 |
|
|
for (res = res0; res != NULL; res = res->ai_next) { |
566 |
|
|
s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, |
567 |
|
|
res->ai_protocol); |
568 |
|
|
if (s == -1) { |
569 |
|
|
cause = "socket"; |
570 |
|
|
cerrno = errno; |
571 |
|
|
continue; |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { |
575 |
|
|
cause = "bind"; |
576 |
|
|
cerrno = errno; |
577 |
|
|
close(s); |
578 |
|
|
continue; |
579 |
|
|
} |
580 |
|
|
|
581 |
|
|
switch (res->ai_family) { |
582 |
|
|
case AF_INET: |
583 |
|
|
if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, |
584 |
|
|
&on, sizeof(on)) == -1) |
585 |
|
|
err(1, "setsockopt(IP_RECVDSTADDR)"); |
586 |
|
|
break; |
587 |
|
|
case AF_INET6: |
588 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, |
589 |
|
|
&on, sizeof(on)) == -1) |
590 |
|
|
err(1, "setsockopt(IPV6_RECVPKTINFO)"); |
591 |
|
|
break; |
592 |
|
|
} |
593 |
|
|
|
594 |
|
|
server = malloc(sizeof(*server)); |
595 |
|
|
if (server == NULL) |
596 |
|
|
err(1, "malloc"); |
597 |
|
|
|
598 |
|
|
server->s = s; |
599 |
|
|
TAILQ_INSERT_TAIL(&tftp_servers, server, entry); |
600 |
|
|
} |
601 |
|
|
|
602 |
|
|
if (TAILQ_EMPTY(&tftp_servers)) |
603 |
|
|
errc(1, cerrno, "%s", cause); |
604 |
|
|
|
605 |
|
|
freeaddrinfo(res0); |
606 |
|
|
return (0); |
607 |
|
|
} |
608 |
|
|
|
609 |
|
|
void |
610 |
|
|
tftpd_events(void) |
611 |
|
|
{ |
612 |
|
|
struct tftp_server *server; |
613 |
|
|
TAILQ_FOREACH(server, &tftp_servers, entry) { |
614 |
|
|
event_set(&server->ev, server->s, EV_READ | EV_PERSIST, |
615 |
|
|
tftpd_recv, server); |
616 |
|
|
event_add(&server->ev, NULL); |
617 |
|
|
} |
618 |
|
|
} |
619 |
|
|
|
620 |
|
|
struct tftp_client * |
621 |
|
|
client_alloc(void) |
622 |
|
|
{ |
623 |
|
|
struct tftp_client *client; |
624 |
|
|
|
625 |
|
|
client = calloc(1, sizeof(*client)); |
626 |
|
|
if (client == NULL) |
627 |
|
|
return (NULL); |
628 |
|
|
|
629 |
|
|
client->segment_size = SEGSIZE; |
630 |
|
|
client->packet_size = SEGSIZE + 4; |
631 |
|
|
|
632 |
|
|
client->tv.tv_sec = TIMEOUT; |
633 |
|
|
client->tv.tv_usec = 0; |
634 |
|
|
|
635 |
|
|
client->sock = -1; |
636 |
|
|
client->file = NULL; |
637 |
|
|
client->newline = 0; |
638 |
|
|
|
639 |
|
|
return (client); |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
void |
643 |
|
|
client_free(struct tftp_client *client) |
644 |
|
|
{ |
645 |
|
|
free(client->options); |
646 |
|
|
|
647 |
|
|
if (client->file != NULL) |
648 |
|
|
fclose(client->file); |
649 |
|
|
|
650 |
|
|
close(client->sock); |
651 |
|
|
|
652 |
|
|
free(client); |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
void |
656 |
|
|
tftpd_recv(int fd, short events, void *arg) |
657 |
|
|
{ |
658 |
|
|
union { |
659 |
|
|
struct cmsghdr hdr; |
660 |
|
|
char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))]; |
661 |
|
|
} cmsgbuf; |
662 |
|
|
struct cmsghdr *cmsg; |
663 |
|
|
struct msghdr msg; |
664 |
|
|
struct iovec iov; |
665 |
|
|
|
666 |
|
|
ssize_t n; |
667 |
|
|
struct sockaddr_storage s_in; |
668 |
|
|
int dobind = 1; |
669 |
|
|
int on = 1; |
670 |
|
|
|
671 |
|
|
struct tftphdr *tp; |
672 |
|
|
|
673 |
|
|
struct tftp_client *client; |
674 |
|
|
|
675 |
|
|
client = client_alloc(); |
676 |
|
|
if (client == NULL) { |
677 |
|
|
char buf[SEGSIZE_MAX + 4]; |
678 |
|
|
/* no memory! flush this request... */ |
679 |
|
|
recv(fd, buf, SEGSIZE_MAX + 4, 0); |
680 |
|
|
/* dont care if it fails */ |
681 |
|
|
return; |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
bzero(&msg, sizeof(msg)); |
685 |
|
|
iov.iov_base = client->buf; |
686 |
|
|
iov.iov_len = client->packet_size; |
687 |
|
|
msg.msg_name = &client->ss; |
688 |
|
|
msg.msg_namelen = sizeof(client->ss); |
689 |
|
|
msg.msg_iov = &iov; |
690 |
|
|
msg.msg_iovlen = 1; |
691 |
|
|
msg.msg_control = &cmsgbuf.buf; |
692 |
|
|
msg.msg_controllen = sizeof(cmsgbuf.buf); |
693 |
|
|
|
694 |
|
|
n = recvmsg(fd, &msg, 0); |
695 |
|
|
if (n == -1) { |
696 |
|
|
lwarn("recvmsg"); |
697 |
|
|
goto err; |
698 |
|
|
} |
699 |
|
|
if (n < 4) |
700 |
|
|
goto err; |
701 |
|
|
|
702 |
|
|
client->sock = socket(client->ss.ss_family, |
703 |
|
|
SOCK_DGRAM | SOCK_NONBLOCK, 0); |
704 |
|
|
if (client->sock == -1) { |
705 |
|
|
lwarn("socket"); |
706 |
|
|
goto err; |
707 |
|
|
} |
708 |
|
|
memset(&s_in, 0, sizeof(s_in)); |
709 |
|
|
s_in.ss_family = client->ss.ss_family; |
710 |
|
|
s_in.ss_len = client->ss.ss_len; |
711 |
|
|
|
712 |
|
|
/* get local address if possible */ |
713 |
|
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; |
714 |
|
|
cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
715 |
|
|
if (cmsg->cmsg_level == IPPROTO_IP && |
716 |
|
|
cmsg->cmsg_type == IP_RECVDSTADDR) { |
717 |
|
|
memcpy(&((struct sockaddr_in *)&s_in)->sin_addr, |
718 |
|
|
CMSG_DATA(cmsg), sizeof(struct in_addr)); |
719 |
|
|
if (((struct sockaddr_in *)&s_in)->sin_addr.s_addr == |
720 |
|
|
INADDR_BROADCAST) |
721 |
|
|
dobind = 0; |
722 |
|
|
break; |
723 |
|
|
} |
724 |
|
|
if (cmsg->cmsg_level == IPPROTO_IPV6 && |
725 |
|
|
cmsg->cmsg_type == IPV6_PKTINFO) { |
726 |
|
|
struct in6_pktinfo *ipi; |
727 |
|
|
|
728 |
|
|
ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); |
729 |
|
|
memcpy(&((struct sockaddr_in6 *)&s_in)->sin6_addr, |
730 |
|
|
&ipi->ipi6_addr, sizeof(struct in6_addr)); |
731 |
|
|
#ifdef __KAME__ |
732 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr)) |
733 |
|
|
((struct sockaddr_in6 *)&s_in)->sin6_scope_id = |
734 |
|
|
ipi->ipi6_ifindex; |
735 |
|
|
#endif |
736 |
|
|
break; |
737 |
|
|
} |
738 |
|
|
} |
739 |
|
|
|
740 |
|
|
if (dobind) { |
741 |
|
|
setsockopt(client->sock, SOL_SOCKET, SO_REUSEADDR, |
742 |
|
|
&on, sizeof(on)); |
743 |
|
|
setsockopt(client->sock, SOL_SOCKET, SO_REUSEPORT, |
744 |
|
|
&on, sizeof(on)); |
745 |
|
|
|
746 |
|
|
if (bind(client->sock, (struct sockaddr *)&s_in, |
747 |
|
|
s_in.ss_len) < 0) { |
748 |
|
|
lwarn("bind to %s", getip(&s_in)); |
749 |
|
|
goto err; |
750 |
|
|
} |
751 |
|
|
} |
752 |
|
|
if (connect(client->sock, (struct sockaddr *)&client->ss, |
753 |
|
|
client->ss.ss_len) == -1) { |
754 |
|
|
lwarn("connect to %s", getip(&client->ss)); |
755 |
|
|
goto err; |
756 |
|
|
} |
757 |
|
|
|
758 |
|
|
tp = (struct tftphdr *)client->buf; |
759 |
|
|
client->opcode = ntohs(tp->th_opcode); |
760 |
|
|
if (client->opcode != RRQ && client->opcode != WRQ) { |
761 |
|
|
/* bad request */ |
762 |
|
|
goto err; |
763 |
|
|
} |
764 |
|
|
|
765 |
|
|
tftp(client, tp, n); |
766 |
|
|
|
767 |
|
|
return; |
768 |
|
|
|
769 |
|
|
err: |
770 |
|
|
client_free(client); |
771 |
|
|
} |
772 |
|
|
|
773 |
|
|
int |
774 |
|
|
parse_options(struct tftp_client *client, char *cp, size_t size, |
775 |
|
|
struct opt_client *options) |
776 |
|
|
{ |
777 |
|
|
char *option; |
778 |
|
|
char *ccp; |
779 |
|
|
int has_options = 0; |
780 |
|
|
int i; |
781 |
|
|
|
782 |
|
|
while (++cp < client->buf + size) { |
783 |
|
|
for (i = 2, ccp = cp; i > 0; ccp++) { |
784 |
|
|
if (ccp >= client->buf + size) { |
785 |
|
|
/* |
786 |
|
|
* Don't reject the request, just stop trying |
787 |
|
|
* to parse the option and get on with it. |
788 |
|
|
* Some Apple OpenFirmware versions have |
789 |
|
|
* trailing garbage on the end of otherwise |
790 |
|
|
* valid requests. |
791 |
|
|
*/ |
792 |
|
|
return (has_options); |
793 |
|
|
} else if (*ccp == '\0') |
794 |
|
|
i--; |
795 |
|
|
} |
796 |
|
|
|
797 |
|
|
for (option = cp; *cp; cp++) |
798 |
|
|
*cp = tolower((unsigned char)*cp); |
799 |
|
|
|
800 |
|
|
for (i = 0; i < NOPT; i++) { |
801 |
|
|
if (strcmp(option, opt_names[i]) == 0) { |
802 |
|
|
options[i].o_request = ++cp; |
803 |
|
|
has_options = 1; |
804 |
|
|
} |
805 |
|
|
} |
806 |
|
|
cp = ccp - 1; |
807 |
|
|
} |
808 |
|
|
|
809 |
|
|
return (has_options); |
810 |
|
|
} |
811 |
|
|
|
812 |
|
|
/* |
813 |
|
|
* Handle initial connection protocol. |
814 |
|
|
*/ |
815 |
|
|
void |
816 |
|
|
tftp(struct tftp_client *client, struct tftphdr *tp, size_t size) |
817 |
|
|
{ |
818 |
|
|
struct opt_client *options; |
819 |
|
|
|
820 |
|
|
char *cp; |
821 |
|
|
int i, first = 1, ecode, to; |
822 |
|
|
struct formats *pf; |
823 |
|
|
char *mode = NULL; |
824 |
|
|
char filename[PATH_MAX]; |
825 |
|
|
const char *errstr; |
826 |
|
|
|
827 |
|
|
if (size < 5) { |
828 |
|
|
ecode = EBADOP; |
829 |
|
|
goto error; |
830 |
|
|
} |
831 |
|
|
|
832 |
|
|
cp = tp->th_stuff; |
833 |
|
|
again: |
834 |
|
|
while (cp < client->buf + size) { |
835 |
|
|
if (*cp == '\0') |
836 |
|
|
break; |
837 |
|
|
cp++; |
838 |
|
|
} |
839 |
|
|
if (*cp != '\0') { |
840 |
|
|
ecode = EBADOP; |
841 |
|
|
goto error; |
842 |
|
|
} |
843 |
|
|
i = cp - tp->th_stuff; |
844 |
|
|
if (i >= sizeof(filename)) { |
845 |
|
|
ecode = EBADOP; |
846 |
|
|
goto error; |
847 |
|
|
} |
848 |
|
|
memcpy(filename, tp->th_stuff, i); |
849 |
|
|
filename[i] = '\0'; |
850 |
|
|
if (first) { |
851 |
|
|
mode = ++cp; |
852 |
|
|
first = 0; |
853 |
|
|
goto again; |
854 |
|
|
} |
855 |
|
|
for (cp = mode; *cp; cp++) |
856 |
|
|
*cp = tolower((unsigned char)*cp); |
857 |
|
|
|
858 |
|
|
for (pf = formats; pf->f_mode; pf++) { |
859 |
|
|
if (strcmp(pf->f_mode, mode) == 0) |
860 |
|
|
break; |
861 |
|
|
} |
862 |
|
|
if (pf->f_mode == 0) { |
863 |
|
|
ecode = EBADOP; |
864 |
|
|
goto error; |
865 |
|
|
} |
866 |
|
|
client->fgetc = pf->f_getc; |
867 |
|
|
client->fputc = pf->f_putc; |
868 |
|
|
|
869 |
|
|
client->options = options = calloc(NOPT, sizeof(*client->options)); |
870 |
|
|
if (options == NULL) { |
871 |
|
|
ecode = 100 + ENOMEM; |
872 |
|
|
goto error; |
873 |
|
|
} |
874 |
|
|
|
875 |
|
|
if (parse_options(client, cp, size, options)) { |
876 |
|
|
if (options[OPT_TIMEOUT].o_request != NULL) { |
877 |
|
|
to = strtonum(options[OPT_TIMEOUT].o_request, |
878 |
|
|
TIMEOUT_MIN, TIMEOUT_MAX, &errstr); |
879 |
|
|
if (errstr) { |
880 |
|
|
ecode = EBADOP; |
881 |
|
|
goto error; |
882 |
|
|
} |
883 |
|
|
options[OPT_TIMEOUT].o_reply = client->tv.tv_sec = to; |
884 |
|
|
} |
885 |
|
|
|
886 |
|
|
if (options[OPT_BLKSIZE].o_request) { |
887 |
|
|
client->segment_size = strtonum( |
888 |
|
|
options[OPT_BLKSIZE].o_request, |
889 |
|
|
SEGSIZE_MIN, SEGSIZE_MAX, &errstr); |
890 |
|
|
if (errstr) { |
891 |
|
|
ecode = EBADOP; |
892 |
|
|
goto error; |
893 |
|
|
} |
894 |
|
|
client->packet_size = client->segment_size + 4; |
895 |
|
|
options[OPT_BLKSIZE].o_reply = client->segment_size; |
896 |
|
|
} |
897 |
|
|
} else { |
898 |
|
|
free(options); |
899 |
|
|
client->options = NULL; |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
if (verbose) { |
903 |
|
|
char nicebuf[PATH_MAX]; |
904 |
|
|
|
905 |
|
|
(void)strnvis(nicebuf, filename, PATH_MAX, |
906 |
|
|
VIS_SAFE|VIS_OCTAL); |
907 |
|
|
|
908 |
|
|
linfo("%s: %s request for '%s'", getip(&client->ss), |
909 |
|
|
client->opcode == WRQ ? "write" : "read", nicebuf); |
910 |
|
|
} |
911 |
|
|
|
912 |
|
|
if (rwmap != NULL) |
913 |
|
|
rewrite_map(client, filename); |
914 |
|
|
else |
915 |
|
|
tftp_open(client, filename); |
916 |
|
|
|
917 |
|
|
return; |
918 |
|
|
|
919 |
|
|
error: |
920 |
|
|
nak(client, ecode); |
921 |
|
|
} |
922 |
|
|
|
923 |
|
|
void |
924 |
|
|
tftp_open(struct tftp_client *client, const char *filename) |
925 |
|
|
{ |
926 |
|
|
int ecode; |
927 |
|
|
|
928 |
|
|
ecode = validate_access(client, filename); |
929 |
|
|
if (ecode) |
930 |
|
|
goto error; |
931 |
|
|
|
932 |
|
|
if (client->options) { |
933 |
|
|
if (oack(client) == -1) |
934 |
|
|
goto error; |
935 |
|
|
|
936 |
|
|
free(client->options); |
937 |
|
|
client->options = NULL; |
938 |
|
|
} else if (client->opcode == WRQ) { |
939 |
|
|
recvfile(client); |
940 |
|
|
} else |
941 |
|
|
sendfile(client); |
942 |
|
|
|
943 |
|
|
return; |
944 |
|
|
error: |
945 |
|
|
nak(client, ecode); |
946 |
|
|
} |
947 |
|
|
|
948 |
|
|
/* |
949 |
|
|
* Validate file access. Since we |
950 |
|
|
* have no uid or gid, for now require |
951 |
|
|
* file to exist and be publicly |
952 |
|
|
* readable/writable. |
953 |
|
|
* If we were invoked with arguments |
954 |
|
|
* from inetd then the file must also be |
955 |
|
|
* in one of the given directory prefixes. |
956 |
|
|
* Note also, full path name must be |
957 |
|
|
* given as we have no login directory. |
958 |
|
|
*/ |
959 |
|
|
int |
960 |
|
|
validate_access(struct tftp_client *client, const char *requested) |
961 |
|
|
{ |
962 |
|
|
int mode = client->opcode; |
963 |
|
|
struct opt_client *options = client->options; |
964 |
|
|
struct stat stbuf; |
965 |
|
|
int fd, wmode; |
966 |
|
|
const char *errstr, *filename; |
967 |
|
|
char rewritten[PATH_MAX]; |
968 |
|
|
|
969 |
|
|
if (strcmp(requested, SEEDPATH) == 0) { |
970 |
|
|
char *buf; |
971 |
|
|
if (mode != RRQ) |
972 |
|
|
return (EACCESS); |
973 |
|
|
|
974 |
|
|
buf = client->buf + sizeof(client->buf) - 512; |
975 |
|
|
arc4random_buf(buf, 512); |
976 |
|
|
client->file = fmemopen(buf, 512, "r"); |
977 |
|
|
if (client->file == NULL) |
978 |
|
|
return (errno + 100); |
979 |
|
|
|
980 |
|
|
return (0); |
981 |
|
|
} |
982 |
|
|
|
983 |
|
|
if (iflag) { |
984 |
|
|
int ret; |
985 |
|
|
|
986 |
|
|
/* |
987 |
|
|
* In -i mode, look in the directory named after the |
988 |
|
|
* client address. |
989 |
|
|
*/ |
990 |
|
|
ret = snprintf(rewritten, sizeof(rewritten), "%s/%s", |
991 |
|
|
getip(&client->ss), requested); |
992 |
|
|
if (ret == -1 || ret >= sizeof(rewritten)) |
993 |
|
|
return (ENAMETOOLONG + 100); |
994 |
|
|
filename = rewritten; |
995 |
|
|
} else { |
996 |
|
|
retryread: |
997 |
|
|
filename = requested; |
998 |
|
|
} |
999 |
|
|
|
1000 |
|
|
/* |
1001 |
|
|
* We use a different permissions scheme if `cancreate' is |
1002 |
|
|
* set. |
1003 |
|
|
*/ |
1004 |
|
|
wmode = O_TRUNC; |
1005 |
|
|
if (stat(filename, &stbuf) < 0) { |
1006 |
|
|
if (!cancreate) { |
1007 |
|
|
/* |
1008 |
|
|
* In -i mode, retry failed read requests from |
1009 |
|
|
* the root directory. |
1010 |
|
|
*/ |
1011 |
|
|
if (mode == RRQ && errno == ENOENT && |
1012 |
|
|
filename == rewritten) |
1013 |
|
|
goto retryread; |
1014 |
|
|
return (errno == ENOENT ? ENOTFOUND : EACCESS); |
1015 |
|
|
} else { |
1016 |
|
|
if ((errno == ENOENT) && (mode != RRQ)) |
1017 |
|
|
wmode |= O_CREAT; |
1018 |
|
|
else |
1019 |
|
|
return (EACCESS); |
1020 |
|
|
} |
1021 |
|
|
} else { |
1022 |
|
|
if (mode == RRQ) { |
1023 |
|
|
if ((stbuf.st_mode & (S_IRUSR >> 6)) == 0) |
1024 |
|
|
return (EACCESS); |
1025 |
|
|
} else { |
1026 |
|
|
if ((stbuf.st_mode & (S_IWUSR >> 6)) == 0) |
1027 |
|
|
return (EACCESS); |
1028 |
|
|
} |
1029 |
|
|
} |
1030 |
|
|
|
1031 |
|
|
if (options != NULL && options[OPT_TSIZE].o_request) { |
1032 |
|
|
if (mode == RRQ) |
1033 |
|
|
options[OPT_TSIZE].o_reply = stbuf.st_size; |
1034 |
|
|
else { |
1035 |
|
|
/* allows writes of 65535 blocks * SEGSIZE_MAX bytes */ |
1036 |
|
|
options[OPT_TSIZE].o_reply = |
1037 |
|
|
strtonum(options[OPT_TSIZE].o_request, |
1038 |
|
|
1, 65535LL * SEGSIZE_MAX, &errstr); |
1039 |
|
|
if (errstr) |
1040 |
|
|
return (EOPTNEG); |
1041 |
|
|
} |
1042 |
|
|
} |
1043 |
|
|
fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666); |
1044 |
|
|
if (fd < 0) |
1045 |
|
|
return (errno + 100); |
1046 |
|
|
/* |
1047 |
|
|
* If the file was created, set default permissions. |
1048 |
|
|
*/ |
1049 |
|
|
if ((wmode & O_CREAT) && fchmod(fd, 0666) < 0) { |
1050 |
|
|
int serrno = errno; |
1051 |
|
|
|
1052 |
|
|
close(fd); |
1053 |
|
|
unlink(filename); |
1054 |
|
|
|
1055 |
|
|
return (serrno + 100); |
1056 |
|
|
} |
1057 |
|
|
client->file = fdopen(fd, mode == RRQ ? "r" : "w"); |
1058 |
|
|
if (client->file == NULL) { |
1059 |
|
|
close(fd); |
1060 |
|
|
return (errno + 100); |
1061 |
|
|
} |
1062 |
|
|
|
1063 |
|
|
return (0); |
1064 |
|
|
} |
1065 |
|
|
|
1066 |
|
|
int |
1067 |
|
|
fget_octet(struct tftp_client *client) |
1068 |
|
|
{ |
1069 |
|
|
return (getc(client->file)); |
1070 |
|
|
} |
1071 |
|
|
|
1072 |
|
|
int |
1073 |
|
|
fput_octet(struct tftp_client *client, int c) |
1074 |
|
|
{ |
1075 |
|
|
return (putc(c, client->file)); |
1076 |
|
|
} |
1077 |
|
|
|
1078 |
|
|
int |
1079 |
|
|
fget_netascii(struct tftp_client *client) |
1080 |
|
|
{ |
1081 |
|
|
int c = -1; |
1082 |
|
|
|
1083 |
|
|
switch (client->newline) { |
1084 |
|
|
case 0: |
1085 |
|
|
c = getc(client->file); |
1086 |
|
|
if (c == EOF) |
1087 |
|
|
break; |
1088 |
|
|
|
1089 |
|
|
if (c == '\n' || c == '\r') { |
1090 |
|
|
client->newline = c; |
1091 |
|
|
c = '\r'; |
1092 |
|
|
} |
1093 |
|
|
break; |
1094 |
|
|
case '\n': |
1095 |
|
|
client->newline = 0; |
1096 |
|
|
c = '\n'; |
1097 |
|
|
break; |
1098 |
|
|
case '\r': |
1099 |
|
|
client->newline = 0; |
1100 |
|
|
c = '\0'; |
1101 |
|
|
break; |
1102 |
|
|
} |
1103 |
|
|
|
1104 |
|
|
return (c); |
1105 |
|
|
} |
1106 |
|
|
|
1107 |
|
|
int |
1108 |
|
|
fput_netascii(struct tftp_client *client, int c) |
1109 |
|
|
{ |
1110 |
|
|
if (client->newline == '\r') { |
1111 |
|
|
client->newline = 0; |
1112 |
|
|
|
1113 |
|
|
if (c == '\0') |
1114 |
|
|
c = '\r'; |
1115 |
|
|
|
1116 |
|
|
} else if (c == '\r') { |
1117 |
|
|
client->newline = c; |
1118 |
|
|
return (c); |
1119 |
|
|
} |
1120 |
|
|
|
1121 |
|
|
return (putc(c, client->file)); |
1122 |
|
|
} |
1123 |
|
|
|
1124 |
|
|
void |
1125 |
|
|
sendfile(struct tftp_client *client) |
1126 |
|
|
{ |
1127 |
|
|
event_set(&client->sev, client->sock, EV_READ, tftp_rrq_ack, client); |
1128 |
|
|
client->block = 1; |
1129 |
|
|
|
1130 |
|
|
file_read(client); |
1131 |
|
|
} |
1132 |
|
|
|
1133 |
|
|
void |
1134 |
|
|
file_read(struct tftp_client *client) |
1135 |
|
|
{ |
1136 |
|
|
u_int8_t *buf; |
1137 |
|
|
struct tftphdr *dp; |
1138 |
|
|
int i; |
1139 |
|
|
int c; |
1140 |
|
|
|
1141 |
|
|
dp = (struct tftphdr *)client->buf; |
1142 |
|
|
dp->th_opcode = htons((u_short)DATA); |
1143 |
|
|
dp->th_block = htons(client->block); |
1144 |
|
|
buf = (u_int8_t *)dp->th_data; |
1145 |
|
|
|
1146 |
|
|
for (i = 0; i < client->segment_size; i++) { |
1147 |
|
|
c = client->fgetc(client); |
1148 |
|
|
if (c == EOF) { |
1149 |
|
|
if (ferror(client->file)) { |
1150 |
|
|
nak(client, 100 + EIO); |
1151 |
|
|
return; |
1152 |
|
|
} |
1153 |
|
|
|
1154 |
|
|
break; |
1155 |
|
|
} |
1156 |
|
|
buf[i] = c; |
1157 |
|
|
} |
1158 |
|
|
|
1159 |
|
|
client->buflen = i + 4; |
1160 |
|
|
client->retries = RETRIES; |
1161 |
|
|
|
1162 |
|
|
if (send(client->sock, client->buf, client->buflen, 0) == -1) { |
1163 |
|
|
lwarn("send(block)"); |
1164 |
|
|
client_free(client); |
1165 |
|
|
return; |
1166 |
|
|
} |
1167 |
|
|
|
1168 |
|
|
event_add(&client->sev, &client->tv); |
1169 |
|
|
} |
1170 |
|
|
|
1171 |
|
|
void |
1172 |
|
|
tftp_rrq_ack(int fd, short events, void *arg) |
1173 |
|
|
{ |
1174 |
|
|
struct tftp_client *client = arg; |
1175 |
|
|
struct tftphdr *ap; /* ack packet */ |
1176 |
|
|
char rbuf[SEGSIZE_MIN]; |
1177 |
|
|
ssize_t n; |
1178 |
|
|
|
1179 |
|
|
if (events & EV_TIMEOUT) { |
1180 |
|
|
if (retry(client) == -1) { |
1181 |
|
|
lwarn("%s: retry", getip(&client->ss)); |
1182 |
|
|
goto done; |
1183 |
|
|
} |
1184 |
|
|
|
1185 |
|
|
return; |
1186 |
|
|
} |
1187 |
|
|
|
1188 |
|
|
n = recv(fd, rbuf, sizeof(rbuf), 0); |
1189 |
|
|
if (n == -1) { |
1190 |
|
|
switch (errno) { |
1191 |
|
|
case EINTR: |
1192 |
|
|
case EAGAIN: |
1193 |
|
|
event_add(&client->sev, &client->tv); |
1194 |
|
|
return; |
1195 |
|
|
|
1196 |
|
|
default: |
1197 |
|
|
lwarn("%s: recv", getip(&client->ss)); |
1198 |
|
|
goto done; |
1199 |
|
|
} |
1200 |
|
|
} |
1201 |
|
|
|
1202 |
|
|
ap = (struct tftphdr *)rbuf; |
1203 |
|
|
ap->th_opcode = ntohs((u_short)ap->th_opcode); |
1204 |
|
|
ap->th_block = ntohs((u_short)ap->th_block); |
1205 |
|
|
|
1206 |
|
|
switch (ap->th_opcode) { |
1207 |
|
|
case ERROR: |
1208 |
|
|
goto done; |
1209 |
|
|
case ACK: |
1210 |
|
|
break; |
1211 |
|
|
default: |
1212 |
|
|
goto retry; |
1213 |
|
|
} |
1214 |
|
|
|
1215 |
|
|
if (ap->th_block != client->block) { |
1216 |
|
|
if (tftp_flush(client) == -1) { |
1217 |
|
|
lwarnx("%s: flush", getip(&client->ss)); |
1218 |
|
|
goto done; |
1219 |
|
|
} |
1220 |
|
|
|
1221 |
|
|
if (ap->th_block != (client->block - 1)) |
1222 |
|
|
goto done; |
1223 |
|
|
|
1224 |
|
|
goto retry; |
1225 |
|
|
} |
1226 |
|
|
|
1227 |
|
|
if (client->buflen != client->packet_size) { |
1228 |
|
|
/* this was the last packet in the stream */ |
1229 |
|
|
goto done; |
1230 |
|
|
} |
1231 |
|
|
|
1232 |
|
|
client->block++; |
1233 |
|
|
file_read(client); |
1234 |
|
|
return; |
1235 |
|
|
|
1236 |
|
|
retry: |
1237 |
|
|
event_add(&client->sev, &client->tv); |
1238 |
|
|
return; |
1239 |
|
|
|
1240 |
|
|
done: |
1241 |
|
|
client_free(client); |
1242 |
|
|
} |
1243 |
|
|
|
1244 |
|
|
int |
1245 |
|
|
tftp_flush(struct tftp_client *client) |
1246 |
|
|
{ |
1247 |
|
|
char rbuf[SEGSIZE_MIN]; |
1248 |
|
|
ssize_t n; |
1249 |
|
|
|
1250 |
|
|
for (;;) { |
1251 |
|
|
n = recv(client->sock, rbuf, sizeof(rbuf), 0); |
1252 |
|
|
if (n == -1) { |
1253 |
|
|
switch (errno) { |
1254 |
|
|
case EAGAIN: |
1255 |
|
|
return (0); |
1256 |
|
|
|
1257 |
|
|
case EINTR: |
1258 |
|
|
break; |
1259 |
|
|
|
1260 |
|
|
default: |
1261 |
|
|
return (-1); |
1262 |
|
|
} |
1263 |
|
|
} |
1264 |
|
|
} |
1265 |
|
|
} |
1266 |
|
|
|
1267 |
|
|
void |
1268 |
|
|
recvfile(struct tftp_client *client) |
1269 |
|
|
{ |
1270 |
|
|
event_set(&client->sev, client->sock, EV_READ, tftp_wrq, client); |
1271 |
|
|
tftp_wrq_ack(client); |
1272 |
|
|
} |
1273 |
|
|
|
1274 |
|
|
int |
1275 |
|
|
tftp_wrq_ack_packet(struct tftp_client *client) |
1276 |
|
|
{ |
1277 |
|
|
struct tftphdr *ap; /* ack packet */ |
1278 |
|
|
|
1279 |
|
|
ap = (struct tftphdr *)client->buf; |
1280 |
|
|
ap->th_opcode = htons((u_short)ACK); |
1281 |
|
|
ap->th_block = htons(client->block); |
1282 |
|
|
|
1283 |
|
|
client->buflen = 4; |
1284 |
|
|
client->retries = RETRIES; |
1285 |
|
|
|
1286 |
|
|
return (send(client->sock, client->buf, client->buflen, 0) != 4); |
1287 |
|
|
} |
1288 |
|
|
|
1289 |
|
|
void |
1290 |
|
|
tftp_wrq_ack(struct tftp_client *client) |
1291 |
|
|
{ |
1292 |
|
|
if (tftp_wrq_ack_packet(client) != 0) { |
1293 |
|
|
lwarn("tftp wrq ack"); |
1294 |
|
|
client_free(client); |
1295 |
|
|
return; |
1296 |
|
|
} |
1297 |
|
|
|
1298 |
|
|
client->block++; |
1299 |
|
|
event_add(&client->sev, &client->tv); |
1300 |
|
|
} |
1301 |
|
|
|
1302 |
|
|
void |
1303 |
|
|
tftp_wrq(int fd, short events, void *arg) |
1304 |
|
|
{ |
1305 |
|
|
char wbuf[SEGSIZE_MAX + 4]; |
1306 |
|
|
struct tftp_client *client = arg; |
1307 |
|
|
struct tftphdr *dp; |
1308 |
|
|
ssize_t n; |
1309 |
|
|
int i; |
1310 |
|
|
|
1311 |
|
|
if (events & EV_TIMEOUT) { |
1312 |
|
|
if (retry(client) == -1) { |
1313 |
|
|
lwarn("%s", getip(&client->ss)); |
1314 |
|
|
goto done; |
1315 |
|
|
} |
1316 |
|
|
|
1317 |
|
|
return; |
1318 |
|
|
} |
1319 |
|
|
|
1320 |
|
|
n = recv(fd, wbuf, client->packet_size, 0); |
1321 |
|
|
if (n == -1) { |
1322 |
|
|
switch (errno) { |
1323 |
|
|
case EINTR: |
1324 |
|
|
case EAGAIN: |
1325 |
|
|
goto retry; |
1326 |
|
|
|
1327 |
|
|
default: |
1328 |
|
|
lwarn("tftp_wrq recv"); |
1329 |
|
|
goto done; |
1330 |
|
|
} |
1331 |
|
|
} |
1332 |
|
|
|
1333 |
|
|
if (n < 4) |
1334 |
|
|
goto done; |
1335 |
|
|
|
1336 |
|
|
dp = (struct tftphdr *)wbuf; |
1337 |
|
|
dp->th_opcode = ntohs((u_short)dp->th_opcode); |
1338 |
|
|
dp->th_block = ntohs((u_short)dp->th_block); |
1339 |
|
|
|
1340 |
|
|
switch (dp->th_opcode) { |
1341 |
|
|
case ERROR: |
1342 |
|
|
goto done; |
1343 |
|
|
case DATA: |
1344 |
|
|
break; |
1345 |
|
|
default: |
1346 |
|
|
goto retry; |
1347 |
|
|
} |
1348 |
|
|
|
1349 |
|
|
if (dp->th_block != client->block) { |
1350 |
|
|
if (tftp_flush(client) == -1) { |
1351 |
|
|
lwarnx("%s: flush", getip(&client->ss)); |
1352 |
|
|
goto done; |
1353 |
|
|
} |
1354 |
|
|
|
1355 |
|
|
if (dp->th_block != (client->block - 1)) |
1356 |
|
|
goto done; |
1357 |
|
|
|
1358 |
|
|
goto retry; |
1359 |
|
|
} |
1360 |
|
|
|
1361 |
|
|
for (i = 4; i < n; i++) { |
1362 |
|
|
if (client->fputc(client, wbuf[i]) == EOF) { |
1363 |
|
|
lwarn("tftp wrq"); |
1364 |
|
|
goto done; |
1365 |
|
|
} |
1366 |
|
|
} |
1367 |
|
|
|
1368 |
|
|
if (n < client->packet_size) { |
1369 |
|
|
tftp_wrq_ack_packet(client); |
1370 |
|
|
fclose(client->file); |
1371 |
|
|
client->file = NULL; |
1372 |
|
|
event_set(&client->sev, client->sock, EV_READ, |
1373 |
|
|
tftp_wrq_end, client); |
1374 |
|
|
event_add(&client->sev, &client->tv); |
1375 |
|
|
return; |
1376 |
|
|
} |
1377 |
|
|
|
1378 |
|
|
tftp_wrq_ack(client); |
1379 |
|
|
return; |
1380 |
|
|
|
1381 |
|
|
retry: |
1382 |
|
|
event_add(&client->sev, &client->tv); |
1383 |
|
|
return; |
1384 |
|
|
done: |
1385 |
|
|
client_free(client); |
1386 |
|
|
} |
1387 |
|
|
|
1388 |
|
|
void |
1389 |
|
|
tftp_wrq_end(int fd, short events, void *arg) |
1390 |
|
|
{ |
1391 |
|
|
char wbuf[SEGSIZE_MAX + 4]; |
1392 |
|
|
struct tftp_client *client = arg; |
1393 |
|
|
struct tftphdr *dp; |
1394 |
|
|
ssize_t n; |
1395 |
|
|
|
1396 |
|
|
if (events & EV_TIMEOUT) { |
1397 |
|
|
/* this was the last packet, we can clean up */ |
1398 |
|
|
goto done; |
1399 |
|
|
} |
1400 |
|
|
|
1401 |
|
|
n = recv(fd, wbuf, client->packet_size, 0); |
1402 |
|
|
if (n == -1) { |
1403 |
|
|
switch (errno) { |
1404 |
|
|
case EINTR: |
1405 |
|
|
case EAGAIN: |
1406 |
|
|
goto retry; |
1407 |
|
|
|
1408 |
|
|
default: |
1409 |
|
|
lwarn("tftp_wrq_end recv"); |
1410 |
|
|
goto done; |
1411 |
|
|
} |
1412 |
|
|
} |
1413 |
|
|
|
1414 |
|
|
if (n < 4) |
1415 |
|
|
goto done; |
1416 |
|
|
|
1417 |
|
|
dp = (struct tftphdr *)wbuf; |
1418 |
|
|
dp->th_opcode = ntohs((u_short)dp->th_opcode); |
1419 |
|
|
dp->th_block = ntohs((u_short)dp->th_block); |
1420 |
|
|
|
1421 |
|
|
switch (dp->th_opcode) { |
1422 |
|
|
case ERROR: |
1423 |
|
|
goto done; |
1424 |
|
|
case DATA: |
1425 |
|
|
break; |
1426 |
|
|
default: |
1427 |
|
|
goto retry; |
1428 |
|
|
} |
1429 |
|
|
|
1430 |
|
|
if (dp->th_block != client->block) |
1431 |
|
|
goto done; |
1432 |
|
|
|
1433 |
|
|
retry: |
1434 |
|
|
if (retry(client) == -1) { |
1435 |
|
|
lwarn("%s", getip(&client->ss)); |
1436 |
|
|
goto done; |
1437 |
|
|
} |
1438 |
|
|
return; |
1439 |
|
|
done: |
1440 |
|
|
client_free(client); |
1441 |
|
|
return; |
1442 |
|
|
} |
1443 |
|
|
|
1444 |
|
|
|
1445 |
|
|
/* |
1446 |
|
|
* Send a nak packet (error message). |
1447 |
|
|
* Error code passed in is one of the |
1448 |
|
|
* standard TFTP codes, or a UNIX errno |
1449 |
|
|
* offset by 100. |
1450 |
|
|
*/ |
1451 |
|
|
void |
1452 |
|
|
nak(struct tftp_client *client, int error) |
1453 |
|
|
{ |
1454 |
|
|
struct tftphdr *tp; |
1455 |
|
|
struct errmsg *pe; |
1456 |
|
|
size_t length; |
1457 |
|
|
|
1458 |
|
|
tp = (struct tftphdr *)client->buf; |
1459 |
|
|
tp->th_opcode = htons((u_short)ERROR); |
1460 |
|
|
tp->th_code = htons((u_short)error); |
1461 |
|
|
|
1462 |
|
|
for (pe = errmsgs; pe->e_code >= 0; pe++) { |
1463 |
|
|
if (pe->e_code == error) |
1464 |
|
|
break; |
1465 |
|
|
} |
1466 |
|
|
if (pe->e_code < 0) { |
1467 |
|
|
pe->e_msg = strerror(error - 100); |
1468 |
|
|
tp->th_code = htons(EUNDEF); /* set 'undef' errorcode */ |
1469 |
|
|
} |
1470 |
|
|
|
1471 |
|
|
length = strlcpy(tp->th_msg, pe->e_msg, client->packet_size - 5) + 5; |
1472 |
|
|
if (length > client->packet_size) |
1473 |
|
|
length = client->packet_size; |
1474 |
|
|
|
1475 |
|
|
if (send(client->sock, client->buf, length, 0) != length) |
1476 |
|
|
lwarn("nak"); |
1477 |
|
|
|
1478 |
|
|
client_free(client); |
1479 |
|
|
} |
1480 |
|
|
|
1481 |
|
|
/* |
1482 |
|
|
* Send an oack packet (option acknowledgement). |
1483 |
|
|
*/ |
1484 |
|
|
int |
1485 |
|
|
oack(struct tftp_client *client) |
1486 |
|
|
{ |
1487 |
|
|
struct opt_client *options = client->options; |
1488 |
|
|
struct tftphdr *tp; |
1489 |
|
|
char *bp; |
1490 |
|
|
int i, n, size; |
1491 |
|
|
|
1492 |
|
|
tp = (struct tftphdr *)client->buf; |
1493 |
|
|
bp = (char *)tp->th_stuff; |
1494 |
|
|
size = sizeof(client->buf) - 2; |
1495 |
|
|
|
1496 |
|
|
tp->th_opcode = htons((u_short)OACK); |
1497 |
|
|
for (i = 0; i < NOPT; i++) { |
1498 |
|
|
if (options[i].o_request == NULL) |
1499 |
|
|
continue; |
1500 |
|
|
|
1501 |
|
|
n = snprintf(bp, size, "%s%c%lld", opt_names[i], '\0', |
1502 |
|
|
options[i].o_reply); |
1503 |
|
|
if (n == -1 || n >= size) { |
1504 |
|
|
lwarnx("oack: no buffer space"); |
1505 |
|
|
goto error; |
1506 |
|
|
} |
1507 |
|
|
|
1508 |
|
|
bp += n + 1; |
1509 |
|
|
size -= n + 1; |
1510 |
|
|
if (size < 0) { |
1511 |
|
|
lwarnx("oack: no buffer space"); |
1512 |
|
|
goto error; |
1513 |
|
|
} |
1514 |
|
|
} |
1515 |
|
|
|
1516 |
|
|
client->buflen = bp - client->buf; |
1517 |
|
|
client->retries = RETRIES; |
1518 |
|
|
|
1519 |
|
|
if (send(client->sock, client->buf, client->buflen, 0) == -1) { |
1520 |
|
|
lwarn("oack"); |
1521 |
|
|
goto error; |
1522 |
|
|
} |
1523 |
|
|
|
1524 |
|
|
/* no client ACK for write requests with options */ |
1525 |
|
|
if (client->opcode == WRQ) { |
1526 |
|
|
client->block = 1; |
1527 |
|
|
event_set(&client->sev, client->sock, EV_READ, |
1528 |
|
|
tftp_wrq, client); |
1529 |
|
|
} else |
1530 |
|
|
event_set(&client->sev, client->sock, EV_READ, |
1531 |
|
|
oack_done, client); |
1532 |
|
|
|
1533 |
|
|
event_add(&client->sev, &client->tv); |
1534 |
|
|
return (0); |
1535 |
|
|
|
1536 |
|
|
error: |
1537 |
|
|
return (-1); |
1538 |
|
|
} |
1539 |
|
|
|
1540 |
|
|
int |
1541 |
|
|
retry(struct tftp_client *client) |
1542 |
|
|
{ |
1543 |
|
|
if (--client->retries == 0) { |
1544 |
|
|
errno = ETIMEDOUT; |
1545 |
|
|
return (-1); |
1546 |
|
|
} |
1547 |
|
|
|
1548 |
|
|
if (send(client->sock, client->buf, client->buflen, 0) == -1) |
1549 |
|
|
return (-1); |
1550 |
|
|
|
1551 |
|
|
event_add(&client->sev, &client->tv); |
1552 |
|
|
|
1553 |
|
|
return (0); |
1554 |
|
|
} |
1555 |
|
|
|
1556 |
|
|
void |
1557 |
|
|
oack_done(int fd, short events, void *arg) |
1558 |
|
|
{ |
1559 |
|
|
struct tftp_client *client = arg; |
1560 |
|
|
struct tftphdr *ap; |
1561 |
|
|
ssize_t n; |
1562 |
|
|
|
1563 |
|
|
if (events & EV_TIMEOUT) { |
1564 |
|
|
if (retry(client) == -1) { |
1565 |
|
|
lwarn("%s", getip(&client->ss)); |
1566 |
|
|
goto done; |
1567 |
|
|
} |
1568 |
|
|
|
1569 |
|
|
return; |
1570 |
|
|
} |
1571 |
|
|
|
1572 |
|
|
n = recv(client->sock, client->buf, client->packet_size, 0); |
1573 |
|
|
if (n == -1) { |
1574 |
|
|
switch (errno) { |
1575 |
|
|
case EINTR: |
1576 |
|
|
case EAGAIN: |
1577 |
|
|
event_add(&client->sev, &client->tv); |
1578 |
|
|
return; |
1579 |
|
|
|
1580 |
|
|
default: |
1581 |
|
|
lwarn("%s: recv", getip(&client->ss)); |
1582 |
|
|
goto done; |
1583 |
|
|
} |
1584 |
|
|
} |
1585 |
|
|
|
1586 |
|
|
if (n < 4) |
1587 |
|
|
goto done; |
1588 |
|
|
|
1589 |
|
|
ap = (struct tftphdr *)client->buf; |
1590 |
|
|
ap->th_opcode = ntohs((u_short)ap->th_opcode); |
1591 |
|
|
ap->th_block = ntohs((u_short)ap->th_block); |
1592 |
|
|
|
1593 |
|
|
if (ap->th_opcode != ACK || ap->th_block != 0) |
1594 |
|
|
goto done; |
1595 |
|
|
|
1596 |
|
|
sendfile(client); |
1597 |
|
|
return; |
1598 |
|
|
|
1599 |
|
|
done: |
1600 |
|
|
client_free(client); |
1601 |
|
|
} |
1602 |
|
|
|
1603 |
|
|
const char * |
1604 |
|
|
getip(void *s) |
1605 |
|
|
{ |
1606 |
|
|
struct sockaddr *sa = s; |
1607 |
|
|
static char hbuf[NI_MAXHOST]; |
1608 |
|
|
|
1609 |
|
|
if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), |
1610 |
|
|
NULL, 0, NI_NUMERICHOST)) |
1611 |
|
|
strlcpy(hbuf, "0.0.0.0", sizeof(hbuf)); |
1612 |
|
|
|
1613 |
|
|
return(hbuf); |
1614 |
|
|
} |
1615 |
|
|
|
1616 |
|
|
/* daemon(3) clone, intended to be used in a "r"estricted environment */ |
1617 |
|
|
int |
1618 |
|
|
rdaemon(int devnull) |
1619 |
|
|
{ |
1620 |
|
|
if (devnull == -1) { |
1621 |
|
|
errno = EBADF; |
1622 |
|
|
return (-1); |
1623 |
|
|
} |
1624 |
|
|
if (fcntl(devnull, F_GETFL) == -1) |
1625 |
|
|
return (-1); |
1626 |
|
|
|
1627 |
|
|
switch (fork()) { |
1628 |
|
|
case -1: |
1629 |
|
|
return (-1); |
1630 |
|
|
case 0: |
1631 |
|
|
break; |
1632 |
|
|
default: |
1633 |
|
|
_exit(0); |
1634 |
|
|
} |
1635 |
|
|
|
1636 |
|
|
if (setsid() == -1) |
1637 |
|
|
return (-1); |
1638 |
|
|
|
1639 |
|
|
(void)dup2(devnull, STDIN_FILENO); |
1640 |
|
|
(void)dup2(devnull, STDOUT_FILENO); |
1641 |
|
|
(void)dup2(devnull, STDERR_FILENO); |
1642 |
|
|
if (devnull > 2) |
1643 |
|
|
(void)close(devnull); |
1644 |
|
|
|
1645 |
|
|
return (0); |
1646 |
|
|
} |
1647 |
|
|
|
1648 |
|
|
void |
1649 |
|
|
syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) |
1650 |
|
|
{ |
1651 |
|
|
char *s; |
1652 |
|
|
|
1653 |
|
|
if (vasprintf(&s, fmt, ap) == -1) { |
1654 |
|
|
syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); |
1655 |
|
|
exit(1); |
1656 |
|
|
} |
1657 |
|
|
|
1658 |
|
|
syslog(priority, "%s: %s", s, strerror(e)); |
1659 |
|
|
|
1660 |
|
|
free(s); |
1661 |
|
|
} |
1662 |
|
|
|
1663 |
|
|
void |
1664 |
|
|
syslog_err(int ecode, const char *fmt, ...) |
1665 |
|
|
{ |
1666 |
|
|
va_list ap; |
1667 |
|
|
|
1668 |
|
|
va_start(ap, fmt); |
1669 |
|
|
syslog_vstrerror(errno, LOG_CRIT, fmt, ap); |
1670 |
|
|
va_end(ap); |
1671 |
|
|
|
1672 |
|
|
exit(ecode); |
1673 |
|
|
} |
1674 |
|
|
|
1675 |
|
|
void |
1676 |
|
|
syslog_errx(int ecode, const char *fmt, ...) |
1677 |
|
|
{ |
1678 |
|
|
va_list ap; |
1679 |
|
|
|
1680 |
|
|
va_start(ap, fmt); |
1681 |
|
|
vsyslog(LOG_CRIT, fmt, ap); |
1682 |
|
|
va_end(ap); |
1683 |
|
|
|
1684 |
|
|
exit(ecode); |
1685 |
|
|
} |
1686 |
|
|
|
1687 |
|
|
void |
1688 |
|
|
syslog_warn(const char *fmt, ...) |
1689 |
|
|
{ |
1690 |
|
|
va_list ap; |
1691 |
|
|
|
1692 |
|
|
va_start(ap, fmt); |
1693 |
|
|
syslog_vstrerror(errno, LOG_ERR, fmt, ap); |
1694 |
|
|
va_end(ap); |
1695 |
|
|
} |
1696 |
|
|
|
1697 |
|
|
void |
1698 |
|
|
syslog_warnx(const char *fmt, ...) |
1699 |
|
|
{ |
1700 |
|
|
va_list ap; |
1701 |
|
|
|
1702 |
|
|
va_start(ap, fmt); |
1703 |
|
|
vsyslog(LOG_ERR, fmt, ap); |
1704 |
|
|
va_end(ap); |
1705 |
|
|
} |
1706 |
|
|
|
1707 |
|
|
void |
1708 |
|
|
syslog_info(const char *fmt, ...) |
1709 |
|
|
{ |
1710 |
|
|
va_list ap; |
1711 |
|
|
|
1712 |
|
|
va_start(ap, fmt); |
1713 |
|
|
vsyslog(LOG_INFO, fmt, ap); |
1714 |
|
|
va_end(ap); |
1715 |
|
|
} |
1716 |
|
|
|
1717 |
|
|
void |
1718 |
|
|
syslog_debug(const char *fmt, ...) |
1719 |
|
|
{ |
1720 |
|
|
va_list ap; |
1721 |
|
|
|
1722 |
|
|
if (!debug) |
1723 |
|
|
return; |
1724 |
|
|
|
1725 |
|
|
va_start(ap, fmt); |
1726 |
|
|
vsyslog(LOG_DEBUG, fmt, ap); |
1727 |
|
|
va_end(ap); |
1728 |
|
|
} |