File: | src/usr.sbin/tftpd/tftpd.c |
Warning: | line 474, column 26 Access to field 's' results in a dereference of a null pointer (loaded from variable 'rwmap') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: tftpd.c,v 1.50 2022/10/09 23:04:57 kn 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(*__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 TIMEOUT5 5 /* packet rexmt timeout */ | |||
90 | #define TIMEOUT_MIN1 1 /* minimal packet rexmt timeout */ | |||
91 | #define TIMEOUT_MAX255 255 /* maximal packet rexmt timeout */ | |||
92 | ||||
93 | #define RETRIES5 5 | |||
94 | ||||
95 | #define SEEDPATH"/etc/random.seed" "/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)struct { struct tftp_server *tqe_next; struct tftp_server **tqe_prev ; } entry; | |||
121 | int s; | |||
122 | }; | |||
123 | ||||
124 | TAILQ_HEAD(, tftp_server)struct { struct tftp_server *tqh_first; struct tftp_server ** tqh_last; } tftp_servers; | |||
125 | ||||
126 | struct tftp_client { | |||
127 | char buf[SEGSIZE_MAX65464 + 4]; | |||
128 | struct event sev; | |||
129 | struct sockaddr_storage ss; | |||
130 | ||||
131 | struct timeval tv; | |||
132 | ||||
133 | TAILQ_ENTRY(tftp_client)struct { struct tftp_client *tqe_next; struct tftp_client **tqe_prev ; } 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__attribute__((__noreturn__)) 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 | void tftp_send(struct tftp_client *); | |||
185 | int tftp_wrq_ack_packet(struct tftp_client *); | |||
186 | void tftp_rrq_ack(int, short, void *); | |||
187 | void tftp_wrq_ack(struct tftp_client *client); | |||
188 | void tftp_wrq(int, short, void *); | |||
189 | void tftp_wrq_end(int, short, void *); | |||
190 | ||||
191 | int parse_options(struct tftp_client *, char *, size_t, | |||
192 | struct opt_client *); | |||
193 | int validate_access(struct tftp_client *, const char *); | |||
194 | ||||
195 | struct tftp_client * | |||
196 | client_alloc(void); | |||
197 | void client_free(struct tftp_client *client); | |||
198 | ||||
199 | struct formats { | |||
200 | const char *f_mode; | |||
201 | int (*f_getc)(struct tftp_client *); | |||
202 | int (*f_putc)(struct tftp_client *, int); | |||
203 | } formats[] = { | |||
204 | { "octet", fget_octet, fput_octet }, | |||
205 | { "netascii", fget_netascii, fput_netascii }, | |||
206 | { NULL((void *)0), NULL((void *)0) } | |||
207 | }; | |||
208 | ||||
209 | struct errmsg { | |||
210 | int e_code; | |||
211 | const char *e_msg; | |||
212 | } errmsgs[] = { | |||
213 | { EUNDEF0, "Undefined error code" }, | |||
214 | { ENOTFOUND1, "File not found" }, | |||
215 | { EACCESS2, "Access violation" }, | |||
216 | { ENOSPACE3, "Disk full or allocation exceeded" }, | |||
217 | { EBADOP4, "Illegal TFTP operation" }, | |||
218 | { EBADID5, "Unknown transfer ID" }, | |||
219 | { EEXISTS6, "File already exists" }, | |||
220 | { ENOUSER7, "No such user" }, | |||
221 | { EOPTNEG8, "Option negotiation failed" }, | |||
222 | { -1, NULL((void *)0) } | |||
223 | }; | |||
224 | ||||
225 | struct loggers { | |||
226 | __dead__attribute__((__noreturn__)) void (*err)(int, const char *, ...) | |||
227 | __attribute__((__format__ (printf, 2, 3))); | |||
228 | __dead__attribute__((__noreturn__)) void (*errx)(int, const char *, ...) | |||
229 | __attribute__((__format__ (printf, 2, 3))); | |||
230 | void (*warn)(const char *, ...) | |||
231 | __attribute__((__format__ (printf, 1, 2))); | |||
232 | void (*warnx)(const char *, ...) | |||
233 | __attribute__((__format__ (printf, 1, 2))); | |||
234 | void (*info)(const char *, ...) | |||
235 | __attribute__((__format__ (printf, 1, 2))); | |||
236 | void (*debug)(const char *, ...) | |||
237 | __attribute__((__format__ (printf, 1, 2))); | |||
238 | }; | |||
239 | ||||
240 | const struct loggers conslogger = { | |||
241 | err, | |||
242 | errx, | |||
243 | warn, | |||
244 | warnx, | |||
245 | warnx, /* info */ | |||
246 | warnx /* debug */ | |||
247 | }; | |||
248 | ||||
249 | __dead__attribute__((__noreturn__)) void syslog_err(int, const char *, ...) | |||
250 | __attribute__((__format__ (printf, 2, 3))); | |||
251 | __dead__attribute__((__noreturn__)) void syslog_errx(int, const char *, ...) | |||
252 | __attribute__((__format__ (printf, 2, 3))); | |||
253 | void syslog_warn(const char *, ...) | |||
254 | __attribute__((__format__ (printf, 1, 2))); | |||
255 | void syslog_warnx(const char *, ...) | |||
256 | __attribute__((__format__ (printf, 1, 2))); | |||
257 | void syslog_info(const char *, ...) | |||
258 | __attribute__((__format__ (printf, 1, 2))); | |||
259 | void syslog_debug(const char *, ...) | |||
260 | __attribute__((__format__ (printf, 1, 2))); | |||
261 | void syslog_vstrerror(int, int, const char *, va_list) | |||
262 | __attribute__((__format__ (printf, 3, 0))); | |||
263 | ||||
264 | const struct loggers syslogger = { | |||
265 | syslog_err, | |||
266 | syslog_errx, | |||
267 | syslog_warn, | |||
268 | syslog_warnx, | |||
269 | syslog_info, | |||
270 | syslog_debug | |||
271 | }; | |||
272 | ||||
273 | const struct loggers *logger = &conslogger; | |||
274 | ||||
275 | #define lerr(_e, _f...)logger->err((_e), _f...) logger->err((_e), _f) | |||
276 | #define lerrx(_e, _f...)logger->errx((_e), _f...) logger->errx((_e), _f) | |||
277 | #define lwarn(_f...)logger->warn(_f...) logger->warn(_f) | |||
278 | #define lwarnx(_f...)logger->warnx(_f...) logger->warnx(_f) | |||
279 | #define linfo(_f...)logger->info(_f...) logger->info(_f) | |||
280 | #define ldebug(_f...)logger->debug(_f...) logger->debug(_f) | |||
281 | ||||
282 | __dead__attribute__((__noreturn__)) void | |||
283 | usage(void) | |||
284 | { | |||
285 | extern char *__progname; | |||
286 | fprintf(stderr(&__sF[2]), "usage: %s [-46cdivw] [-l address] [-p port] [-r socket]" | |||
287 | " directory\n", __progname); | |||
288 | exit(1); | |||
289 | } | |||
290 | ||||
291 | int cancreate = 0; | |||
292 | int canwrite = 0; | |||
293 | int verbose = 0; | |||
294 | int debug = 0; | |||
295 | int iflag = 0; | |||
296 | ||||
297 | int | |||
298 | main(int argc, char *argv[]) | |||
299 | { | |||
300 | extern char *__progname; | |||
301 | ||||
302 | int c; | |||
303 | struct passwd *pw; | |||
304 | ||||
305 | char *dir = NULL((void *)0); | |||
306 | char *rewrite = NULL((void *)0); | |||
307 | ||||
308 | char *addr = NULL((void *)0); | |||
309 | char *port = "tftp"; | |||
310 | int family = AF_UNSPEC0; | |||
311 | int devnull = -1; | |||
312 | ||||
313 | while ((c = getopt(argc, argv, "46cdil:p:r:vw")) != -1) { | |||
| ||||
314 | switch (c) { | |||
315 | case '4': | |||
316 | family = AF_INET2; | |||
317 | break; | |||
318 | case '6': | |||
319 | family = AF_INET624; | |||
320 | break; | |||
321 | case 'c': | |||
322 | canwrite = cancreate = 1; | |||
323 | break; | |||
324 | case 'd': | |||
325 | verbose = debug = 1; | |||
326 | break; | |||
327 | case 'i': | |||
328 | if (rewrite != NULL((void *)0)) | |||
329 | errx(1, "options -i and -r are incompatible"); | |||
330 | iflag = 1; | |||
331 | break; | |||
332 | case 'l': | |||
333 | addr = optarg; | |||
334 | break; | |||
335 | case 'p': | |||
336 | port = optarg; | |||
337 | break; | |||
338 | case 'r': | |||
339 | if (iflag
| |||
340 | errx(1, "options -i and -r are incompatible"); | |||
341 | rewrite = optarg; | |||
342 | break; | |||
343 | case 'v': | |||
344 | verbose = 1; | |||
345 | break; | |||
346 | case 'w': | |||
347 | canwrite = 1; | |||
348 | break; | |||
349 | default: | |||
350 | usage(); | |||
351 | /* NOTREACHED */ | |||
352 | } | |||
353 | } | |||
354 | ||||
355 | argc -= optind; | |||
356 | argv += optind; | |||
357 | ||||
358 | if (argc != 1) | |||
359 | usage(); | |||
360 | ||||
361 | dir = argv[0]; | |||
362 | ||||
363 | if (geteuid() != 0) | |||
364 | errx(1, "need root privileges"); | |||
365 | ||||
366 | pw = getpwnam("_tftpd"); | |||
367 | if (pw == NULL((void *)0)) | |||
368 | errx(1, "no _tftpd user"); | |||
369 | ||||
370 | if (!debug
| |||
371 | openlog(__progname, LOG_PID0x01|LOG_NDELAY0x08, LOG_DAEMON(3<<3)); | |||
372 | tzset(); | |||
373 | logger = &syslogger; | |||
374 | devnull = open(_PATH_DEVNULL"/dev/null", O_RDWR0x0002); | |||
375 | if (devnull == -1) | |||
376 | err(1, "open %s", _PATH_DEVNULL"/dev/null"); | |||
377 | } | |||
378 | ||||
379 | if (rewrite != NULL((void *)0)) | |||
380 | rewrite_connect(rewrite); | |||
381 | ||||
382 | tftpd_listen(addr, port, family); | |||
383 | ||||
384 | if (chroot(dir)) | |||
385 | err(1, "chroot %s", dir); | |||
386 | if (chdir("/")) | |||
387 | err(1, "chdir %s", dir); | |||
388 | ||||
389 | /* drop privs */ | |||
390 | if (setgroups(1, &pw->pw_gid) || | |||
391 | setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || | |||
392 | setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) | |||
393 | errx(1, "can't drop privileges"); | |||
394 | ||||
395 | if (!debug
| |||
396 | err(1, "unable to daemonize"); | |||
397 | ||||
398 | if (cancreate
| |||
399 | if (pledge("stdio rpath wpath cpath fattr dns inet", NULL((void *)0)) == -1) | |||
400 | lerr(1, "pledge")logger->err((1), "pledge"); | |||
401 | } else if (canwrite
| |||
402 | if (pledge("stdio rpath wpath dns inet", NULL((void *)0)) == -1) | |||
403 | lerr(1, "pledge")logger->err((1), "pledge"); | |||
404 | } else { | |||
405 | if (pledge("stdio rpath dns inet", NULL((void *)0)) == -1) | |||
406 | lerr(1, "pledge")logger->err((1), "pledge"); | |||
407 | } | |||
408 | ||||
409 | event_init(); | |||
410 | ||||
411 | if (rewrite
| |||
412 | rewrite_events(); | |||
413 | ||||
414 | tftpd_events(); | |||
415 | ||||
416 | event_dispatch(); | |||
417 | ||||
418 | exit(0); | |||
419 | } | |||
420 | ||||
421 | struct rewritemap { | |||
422 | struct event wrev; | |||
423 | struct event rdev; | |||
424 | struct evbuffer *wrbuf; | |||
425 | struct evbuffer *rdbuf; | |||
426 | ||||
427 | TAILQ_HEAD(, tftp_client)struct { struct tftp_client *tqh_first; struct tftp_client ** tqh_last; } clients; | |||
428 | ||||
429 | int s; | |||
430 | }; | |||
431 | ||||
432 | struct rewritemap *rwmap = NULL((void *)0); | |||
433 | ||||
434 | void | |||
435 | rewrite_connect(const char *path) | |||
436 | { | |||
437 | int s; | |||
438 | struct sockaddr_un remote; | |||
439 | size_t len; | |||
440 | ||||
441 | rwmap = malloc(sizeof(*rwmap)); | |||
442 | if (rwmap == NULL((void *)0)) | |||
443 | err(1, "rewrite event malloc"); | |||
444 | ||||
445 | rwmap->wrbuf = evbuffer_new(); | |||
446 | if (rwmap->wrbuf == NULL((void *)0)) | |||
447 | err(1, "rewrite wrbuf"); | |||
448 | ||||
449 | rwmap->rdbuf = evbuffer_new(); | |||
450 | if (rwmap->rdbuf == NULL((void *)0)) | |||
451 | err(1, "rewrite rdbuf"); | |||
452 | ||||
453 | TAILQ_INIT(&rwmap->clients)do { (&rwmap->clients)->tqh_first = ((void *)0); (& rwmap->clients)->tqh_last = &(&rwmap->clients )->tqh_first; } while (0); | |||
454 | ||||
455 | s = socket(AF_UNIX1, SOCK_STREAM1 | SOCK_NONBLOCK0x4000, 0); | |||
456 | if (s == -1) | |||
457 | err(1, "rewrite socket"); | |||
458 | ||||
459 | remote.sun_family = AF_UNIX1; | |||
460 | len = strlcpy(remote.sun_path, path, sizeof(remote.sun_path)); | |||
461 | if (len >= sizeof(remote.sun_path)) | |||
462 | errx(1, "rewrite socket path is too long"); | |||
463 | ||||
464 | len += sizeof(remote.sun_family) + 1; | |||
465 | if (connect(s, (struct sockaddr *)&remote, len) == -1) | |||
466 | err(1, "%s", path); | |||
467 | ||||
468 | rwmap->s = s; | |||
469 | } | |||
470 | ||||
471 | void | |||
472 | rewrite_events(void) | |||
473 | { | |||
474 | event_set(&rwmap->wrev, rwmap->s, EV_WRITE0x04, rewrite_req, NULL((void *)0)); | |||
| ||||
475 | event_set(&rwmap->rdev, rwmap->s, EV_READ0x02 | EV_PERSIST0x10, rewrite_res, NULL((void *)0)); | |||
476 | event_add(&rwmap->rdev, NULL((void *)0)); | |||
477 | } | |||
478 | ||||
479 | void | |||
480 | rewrite_map(struct tftp_client *client, const char *filename) | |||
481 | { | |||
482 | char *nicebuf; | |||
483 | ||||
484 | if (stravis(&nicebuf, filename, VIS_SAFE0x20|VIS_OCTAL0x01) == -1) | |||
485 | lerr(1, "rwmap stravis")logger->err((1), "rwmap stravis"); | |||
486 | ||||
487 | if (evbuffer_add_printf(rwmap->wrbuf, "%s %s %s\n", getip(&client->ss), | |||
488 | client->opcode == WRQ02 ? "write" : "read", nicebuf) == -1) | |||
489 | lerr(1, "rwmap printf")logger->err((1), "rwmap printf"); | |||
490 | ||||
491 | free(nicebuf); | |||
492 | ||||
493 | TAILQ_INSERT_TAIL(&rwmap->clients, client, entry)do { (client)->entry.tqe_next = ((void *)0); (client)-> entry.tqe_prev = (&rwmap->clients)->tqh_last; *(& rwmap->clients)->tqh_last = (client); (&rwmap->clients )->tqh_last = &(client)->entry.tqe_next; } while (0 ); | |||
494 | ||||
495 | event_add(&rwmap->wrev, NULL((void *)0)); | |||
496 | } | |||
497 | ||||
498 | void | |||
499 | rewrite_req(int fd, short events, void *arg) | |||
500 | { | |||
501 | if (evbuffer_write(rwmap->wrbuf, fd) == -1) { | |||
502 | switch (errno(*__errno())) { | |||
503 | case EINTR4: | |||
504 | case EAGAIN35: | |||
505 | event_add(&rwmap->wrev, NULL((void *)0)); | |||
506 | return; | |||
507 | } | |||
508 | ||||
509 | lerr(1, "rewrite socket write")logger->err((1), "rewrite socket write"); | |||
510 | } | |||
511 | ||||
512 | if (EVBUFFER_LENGTH(rwmap->wrbuf)(rwmap->wrbuf)->off) | |||
513 | event_add(&rwmap->wrev, NULL((void *)0)); | |||
514 | } | |||
515 | ||||
516 | void | |||
517 | rewrite_res(int fd, short events, void *arg) | |||
518 | { | |||
519 | struct tftp_client *client; | |||
520 | char *filename; | |||
521 | size_t len; | |||
522 | ||||
523 | switch (evbuffer_read(rwmap->rdbuf, fd, PATH_MAX1024)) { | |||
524 | case -1: | |||
525 | switch (errno(*__errno())) { | |||
526 | case EINTR4: | |||
527 | case EAGAIN35: | |||
528 | return; | |||
529 | } | |||
530 | lerr(1, "rewrite socket read")logger->err((1), "rewrite socket read"); | |||
531 | case 0: | |||
532 | lerrx(1, "rewrite socket closed")logger->errx((1), "rewrite socket closed"); | |||
533 | default: | |||
534 | break; | |||
535 | } | |||
536 | ||||
537 | while ((filename = evbuffer_readln(rwmap->rdbuf, &len, | |||
538 | EVBUFFER_EOL_LF)) != NULL((void *)0)) { | |||
539 | client = TAILQ_FIRST(&rwmap->clients)((&rwmap->clients)->tqh_first); | |||
540 | if (client == NULL((void *)0)) | |||
541 | lerrx(1, "unexpected rwmap reply")logger->errx((1), "unexpected rwmap reply"); | |||
542 | ||||
543 | TAILQ_REMOVE(&rwmap->clients, client, entry)do { if (((client)->entry.tqe_next) != ((void *)0)) (client )->entry.tqe_next->entry.tqe_prev = (client)->entry. tqe_prev; else (&rwmap->clients)->tqh_last = (client )->entry.tqe_prev; *(client)->entry.tqe_prev = (client) ->entry.tqe_next; ; ; } while (0); | |||
544 | ||||
545 | tftp_open(client, filename); | |||
546 | ||||
547 | free(filename); | |||
548 | }; | |||
549 | } | |||
550 | ||||
551 | int | |||
552 | tftpd_listen(const char *addr, const char *port, int family) | |||
553 | { | |||
554 | struct tftp_server *server; | |||
555 | ||||
556 | struct addrinfo hints, *res, *res0; | |||
557 | int error; | |||
558 | int s; | |||
559 | ||||
560 | int cerrno = EADDRNOTAVAIL49; | |||
561 | const char *cause = "getaddrinfo"; | |||
562 | ||||
563 | int on = 1; | |||
564 | ||||
565 | memset(&hints, 0, sizeof(hints)); | |||
566 | hints.ai_family = family; | |||
567 | hints.ai_socktype = SOCK_DGRAM2; | |||
568 | hints.ai_flags = AI_PASSIVE1; | |||
569 | ||||
570 | TAILQ_INIT(&tftp_servers)do { (&tftp_servers)->tqh_first = ((void *)0); (&tftp_servers )->tqh_last = &(&tftp_servers)->tqh_first; } while (0); | |||
571 | ||||
572 | error = getaddrinfo(addr, port, &hints, &res0); | |||
573 | if (error) { | |||
574 | errx(1, "%s:%s: %s", addr ? addr : "*", port, | |||
575 | gai_strerror(error)); | |||
576 | } | |||
577 | ||||
578 | for (res = res0; res != NULL((void *)0); res = res->ai_next) { | |||
579 | s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK0x4000, | |||
580 | res->ai_protocol); | |||
581 | if (s == -1) { | |||
582 | cause = "socket"; | |||
583 | cerrno = errno(*__errno()); | |||
584 | continue; | |||
585 | } | |||
586 | ||||
587 | if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { | |||
588 | cause = "bind"; | |||
589 | cerrno = errno(*__errno()); | |||
590 | close(s); | |||
591 | continue; | |||
592 | } | |||
593 | ||||
594 | switch (res->ai_family) { | |||
595 | case AF_INET2: | |||
596 | if (setsockopt(s, IPPROTO_IP0, IP_RECVDSTADDR7, | |||
597 | &on, sizeof(on)) == -1) | |||
598 | err(1, "setsockopt(IP_RECVDSTADDR)"); | |||
599 | break; | |||
600 | case AF_INET624: | |||
601 | if (setsockopt(s, IPPROTO_IPV641, IPV6_RECVPKTINFO36, | |||
602 | &on, sizeof(on)) == -1) | |||
603 | err(1, "setsockopt(IPV6_RECVPKTINFO)"); | |||
604 | break; | |||
605 | } | |||
606 | ||||
607 | server = malloc(sizeof(*server)); | |||
608 | if (server == NULL((void *)0)) | |||
609 | err(1, "malloc"); | |||
610 | ||||
611 | server->s = s; | |||
612 | TAILQ_INSERT_TAIL(&tftp_servers, server, entry)do { (server)->entry.tqe_next = ((void *)0); (server)-> entry.tqe_prev = (&tftp_servers)->tqh_last; *(&tftp_servers )->tqh_last = (server); (&tftp_servers)->tqh_last = &(server)->entry.tqe_next; } while (0); | |||
613 | } | |||
614 | ||||
615 | if (TAILQ_EMPTY(&tftp_servers)(((&tftp_servers)->tqh_first) == ((void *)0))) | |||
616 | errc(1, cerrno, "%s", cause); | |||
617 | ||||
618 | freeaddrinfo(res0); | |||
619 | return (0); | |||
620 | } | |||
621 | ||||
622 | void | |||
623 | tftpd_events(void) | |||
624 | { | |||
625 | struct tftp_server *server; | |||
626 | TAILQ_FOREACH(server, &tftp_servers, entry)for((server) = ((&tftp_servers)->tqh_first); (server) != ((void *)0); (server) = ((server)->entry.tqe_next)) { | |||
627 | event_set(&server->ev, server->s, EV_READ0x02 | EV_PERSIST0x10, | |||
628 | tftpd_recv, server); | |||
629 | event_add(&server->ev, NULL((void *)0)); | |||
630 | } | |||
631 | } | |||
632 | ||||
633 | struct tftp_client * | |||
634 | client_alloc(void) | |||
635 | { | |||
636 | struct tftp_client *client; | |||
637 | ||||
638 | client = calloc(1, sizeof(*client)); | |||
639 | if (client == NULL((void *)0)) | |||
640 | return (NULL((void *)0)); | |||
641 | ||||
642 | client->segment_size = SEGSIZE512; | |||
643 | client->packet_size = SEGSIZE512 + 4; | |||
644 | ||||
645 | client->tv.tv_sec = TIMEOUT5; | |||
646 | client->tv.tv_usec = 0; | |||
647 | ||||
648 | client->sock = -1; | |||
649 | client->file = NULL((void *)0); | |||
650 | client->newline = 0; | |||
651 | ||||
652 | return (client); | |||
653 | } | |||
654 | ||||
655 | void | |||
656 | client_free(struct tftp_client *client) | |||
657 | { | |||
658 | free(client->options); | |||
659 | ||||
660 | if (client->file != NULL((void *)0)) | |||
661 | fclose(client->file); | |||
662 | ||||
663 | close(client->sock); | |||
664 | ||||
665 | free(client); | |||
666 | } | |||
667 | ||||
668 | void | |||
669 | tftpd_recv(int fd, short events, void *arg) | |||
670 | { | |||
671 | union { | |||
672 | struct cmsghdr hdr; | |||
673 | char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))((((unsigned long)(sizeof(struct cmsghdr)) + (sizeof(long) - 1 )) &~(sizeof(long) - 1)) + (((unsigned long)(sizeof(struct sockaddr_storage)) + (sizeof(long) - 1)) &~(sizeof(long) - 1)))]; | |||
674 | } cmsgbuf; | |||
675 | struct cmsghdr *cmsg; | |||
676 | struct msghdr msg; | |||
677 | struct iovec iov; | |||
678 | ||||
679 | ssize_t n; | |||
680 | struct sockaddr_storage s_in; | |||
681 | int dobind = 1; | |||
682 | int on = 1; | |||
683 | ||||
684 | struct tftphdr *tp; | |||
685 | ||||
686 | struct tftp_client *client; | |||
687 | ||||
688 | client = client_alloc(); | |||
689 | if (client == NULL((void *)0)) { | |||
690 | char buf[SEGSIZE_MAX65464 + 4]; | |||
691 | /* no memory! flush this request... */ | |||
692 | recv(fd, buf, SEGSIZE_MAX65464 + 4, 0); | |||
693 | /* dont care if it fails */ | |||
694 | return; | |||
695 | } | |||
696 | ||||
697 | bzero(&msg, sizeof(msg)); | |||
698 | iov.iov_base = client->buf; | |||
699 | iov.iov_len = client->packet_size; | |||
700 | msg.msg_name = &client->ss; | |||
701 | msg.msg_namelen = sizeof(client->ss); | |||
702 | msg.msg_iov = &iov; | |||
703 | msg.msg_iovlen = 1; | |||
704 | msg.msg_control = &cmsgbuf.buf; | |||
705 | msg.msg_controllen = sizeof(cmsgbuf.buf); | |||
706 | ||||
707 | n = recvmsg(fd, &msg, 0); | |||
708 | if (n == -1) { | |||
709 | lwarn("recvmsg")logger->warn("recvmsg"); | |||
710 | goto err; | |||
711 | } | |||
712 | if (n < 4) | |||
713 | goto err; | |||
714 | ||||
715 | client->sock = socket(client->ss.ss_family, | |||
716 | SOCK_DGRAM2 | SOCK_NONBLOCK0x4000, 0); | |||
717 | if (client->sock == -1) { | |||
718 | lwarn("socket")logger->warn("socket"); | |||
719 | goto err; | |||
720 | } | |||
721 | memset(&s_in, 0, sizeof(s_in)); | |||
722 | s_in.ss_family = client->ss.ss_family; | |||
723 | s_in.ss_len = client->ss.ss_len; | |||
724 | ||||
725 | /* get local address if possible */ | |||
726 | for (cmsg = CMSG_FIRSTHDR(&msg)((&msg)->msg_controllen >= sizeof(struct cmsghdr) ? (struct cmsghdr *)(&msg)->msg_control : (struct cmsghdr *)((void *)0)); cmsg != NULL((void *)0); | |||
727 | cmsg = CMSG_NXTHDR(&msg, cmsg)(((char *)(cmsg) + (((unsigned long)((cmsg)->cmsg_len) + ( sizeof(long) - 1)) &~(sizeof(long) - 1)) + (((unsigned long )(sizeof(struct cmsghdr)) + (sizeof(long) - 1)) &~(sizeof (long) - 1)) > ((char *)(&msg)->msg_control) + (& msg)->msg_controllen) ? (struct cmsghdr *)((void *)0) : (struct cmsghdr *)((char *)(cmsg) + (((unsigned long)((cmsg)->cmsg_len ) + (sizeof(long) - 1)) &~(sizeof(long) - 1))))) { | |||
728 | if (cmsg->cmsg_level == IPPROTO_IP0 && | |||
729 | cmsg->cmsg_type == IP_RECVDSTADDR7) { | |||
730 | memcpy(&((struct sockaddr_in *)&s_in)->sin_addr, | |||
731 | CMSG_DATA(cmsg)((unsigned char *)(cmsg) + (((unsigned long)(sizeof(struct cmsghdr )) + (sizeof(long) - 1)) &~(sizeof(long) - 1))), sizeof(struct in_addr)); | |||
732 | if (((struct sockaddr_in *)&s_in)->sin_addr.s_addr == | |||
733 | INADDR_BROADCAST((u_int32_t)(0xffffffff))) | |||
734 | dobind = 0; | |||
735 | break; | |||
736 | } | |||
737 | if (cmsg->cmsg_level == IPPROTO_IPV641 && | |||
738 | cmsg->cmsg_type == IPV6_PKTINFO46) { | |||
739 | struct in6_pktinfo *ipi; | |||
740 | ||||
741 | ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg)((unsigned char *)(cmsg) + (((unsigned long)(sizeof(struct cmsghdr )) + (sizeof(long) - 1)) &~(sizeof(long) - 1))); | |||
742 | memcpy(&((struct sockaddr_in6 *)&s_in)->sin6_addr, | |||
743 | &ipi->ipi6_addr, sizeof(struct in6_addr)); | |||
744 | if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr)(((&ipi->ipi6_addr)->__u6_addr.__u6_addr8[0] == 0xfe ) && (((&ipi->ipi6_addr)->__u6_addr.__u6_addr8 [1] & 0xc0) == 0x80))) | |||
745 | ((struct sockaddr_in6 *)&s_in)->sin6_scope_id = | |||
746 | ipi->ipi6_ifindex; | |||
747 | break; | |||
748 | } | |||
749 | } | |||
750 | ||||
751 | if (dobind) { | |||
752 | setsockopt(client->sock, SOL_SOCKET0xffff, SO_REUSEADDR0x0004, | |||
753 | &on, sizeof(on)); | |||
754 | setsockopt(client->sock, SOL_SOCKET0xffff, SO_REUSEPORT0x0200, | |||
755 | &on, sizeof(on)); | |||
756 | ||||
757 | if (bind(client->sock, (struct sockaddr *)&s_in, | |||
758 | s_in.ss_len) == -1) { | |||
759 | lwarn("bind to %s", getip(&s_in))logger->warn("bind to %s", getip(&s_in)); | |||
760 | goto err; | |||
761 | } | |||
762 | } | |||
763 | if (connect(client->sock, (struct sockaddr *)&client->ss, | |||
764 | client->ss.ss_len) == -1) { | |||
765 | lwarn("connect to %s", getip(&client->ss))logger->warn("connect to %s", getip(&client->ss)); | |||
766 | goto err; | |||
767 | } | |||
768 | ||||
769 | tp = (struct tftphdr *)client->buf; | |||
770 | client->opcode = ntohs(tp->th_opcode)(__uint16_t)(__builtin_constant_p(tp->th_opcode) ? (__uint16_t )(((__uint16_t)(tp->th_opcode) & 0xffU) << 8 | ( (__uint16_t)(tp->th_opcode) & 0xff00U) >> 8) : __swap16md (tp->th_opcode)); | |||
771 | if (client->opcode != RRQ01 && client->opcode != WRQ02) { | |||
772 | /* bad request */ | |||
773 | goto err; | |||
774 | } | |||
775 | ||||
776 | tftp(client, tp, n); | |||
777 | ||||
778 | return; | |||
779 | ||||
780 | err: | |||
781 | client_free(client); | |||
782 | } | |||
783 | ||||
784 | int | |||
785 | parse_options(struct tftp_client *client, char *cp, size_t size, | |||
786 | struct opt_client *options) | |||
787 | { | |||
788 | char *option; | |||
789 | char *ccp; | |||
790 | int has_options = 0; | |||
791 | int i; | |||
792 | ||||
793 | while (++cp < client->buf + size) { | |||
794 | for (i = 2, ccp = cp; i > 0; ccp++) { | |||
795 | if (ccp >= client->buf + size) { | |||
796 | /* | |||
797 | * Don't reject the request, just stop trying | |||
798 | * to parse the option and get on with it. | |||
799 | * Some Apple OpenFirmware versions have | |||
800 | * trailing garbage on the end of otherwise | |||
801 | * valid requests. | |||
802 | */ | |||
803 | return (has_options); | |||
804 | } else if (*ccp == '\0') | |||
805 | i--; | |||
806 | } | |||
807 | ||||
808 | for (option = cp; *cp; cp++) | |||
809 | *cp = tolower((unsigned char)*cp); | |||
810 | ||||
811 | for (i = 0; i < NOPT; i++) { | |||
812 | if (strcmp(option, opt_names[i]) == 0) { | |||
813 | options[i].o_request = ++cp; | |||
814 | has_options = 1; | |||
815 | } | |||
816 | } | |||
817 | cp = ccp - 1; | |||
818 | } | |||
819 | ||||
820 | return (has_options); | |||
821 | } | |||
822 | ||||
823 | /* | |||
824 | * Handle initial connection protocol. | |||
825 | */ | |||
826 | void | |||
827 | tftp(struct tftp_client *client, struct tftphdr *tp, size_t size) | |||
828 | { | |||
829 | struct opt_client *options; | |||
830 | ||||
831 | char *cp; | |||
832 | int i, first = 1, ecode, to; | |||
833 | struct formats *pf; | |||
834 | char *mode = NULL((void *)0); | |||
835 | char filename[PATH_MAX1024]; | |||
836 | const char *errstr; | |||
837 | ||||
838 | if (size < 5) { | |||
839 | ecode = EBADOP4; | |||
840 | goto error; | |||
841 | } | |||
842 | ||||
843 | cp = tp->th_stuffth_u.tu_stuff; | |||
844 | again: | |||
845 | while (cp < client->buf + size) { | |||
846 | if (*cp == '\0') | |||
847 | break; | |||
848 | cp++; | |||
849 | } | |||
850 | if (*cp != '\0') { | |||
851 | ecode = EBADOP4; | |||
852 | goto error; | |||
853 | } | |||
854 | i = cp - tp->th_stuffth_u.tu_stuff; | |||
855 | if (i >= sizeof(filename)) { | |||
856 | ecode = EBADOP4; | |||
857 | goto error; | |||
858 | } | |||
859 | memcpy(filename, tp->th_stuffth_u.tu_stuff, i); | |||
860 | filename[i] = '\0'; | |||
861 | if (first) { | |||
862 | mode = ++cp; | |||
863 | first = 0; | |||
864 | goto again; | |||
865 | } | |||
866 | for (cp = mode; *cp; cp++) | |||
867 | *cp = tolower((unsigned char)*cp); | |||
868 | ||||
869 | for (pf = formats; pf->f_mode; pf++) { | |||
870 | if (strcmp(pf->f_mode, mode) == 0) | |||
871 | break; | |||
872 | } | |||
873 | if (pf->f_mode == 0) { | |||
874 | ecode = EBADOP4; | |||
875 | goto error; | |||
876 | } | |||
877 | client->fgetc = pf->f_getc; | |||
878 | client->fputc = pf->f_putc; | |||
879 | ||||
880 | client->options = options = calloc(NOPT, sizeof(*client->options)); | |||
881 | if (options == NULL((void *)0)) { | |||
882 | ecode = 100 + ENOMEM12; | |||
883 | goto error; | |||
884 | } | |||
885 | ||||
886 | if (parse_options(client, cp, size, options)) { | |||
887 | if (options[OPT_TIMEOUT].o_request != NULL((void *)0)) { | |||
888 | to = strtonum(options[OPT_TIMEOUT].o_request, | |||
889 | TIMEOUT_MIN1, TIMEOUT_MAX255, &errstr); | |||
890 | if (errstr) { | |||
891 | ecode = EBADOP4; | |||
892 | goto error; | |||
893 | } | |||
894 | options[OPT_TIMEOUT].o_reply = client->tv.tv_sec = to; | |||
895 | } | |||
896 | ||||
897 | if (options[OPT_BLKSIZE].o_request) { | |||
898 | client->segment_size = strtonum( | |||
899 | options[OPT_BLKSIZE].o_request, | |||
900 | SEGSIZE_MIN8, SEGSIZE_MAX65464, &errstr); | |||
901 | if (errstr) { | |||
902 | ecode = EBADOP4; | |||
903 | goto error; | |||
904 | } | |||
905 | client->packet_size = client->segment_size + 4; | |||
906 | options[OPT_BLKSIZE].o_reply = client->segment_size; | |||
907 | } | |||
908 | } else { | |||
909 | free(options); | |||
910 | client->options = NULL((void *)0); | |||
911 | } | |||
912 | ||||
913 | if (verbose) { | |||
914 | char nicebuf[PATH_MAX1024]; | |||
915 | ||||
916 | (void)strnvis(nicebuf, filename, PATH_MAX1024, | |||
917 | VIS_SAFE0x20|VIS_OCTAL0x01); | |||
918 | ||||
919 | linfo("%s: %s request for '%s'", getip(&client->ss),logger->info("%s: %s request for '%s'", getip(&client-> ss), client->opcode == 02 ? "write" : "read", nicebuf) | |||
920 | client->opcode == WRQ ? "write" : "read", nicebuf)logger->info("%s: %s request for '%s'", getip(&client-> ss), client->opcode == 02 ? "write" : "read", nicebuf); | |||
921 | } | |||
922 | ||||
923 | if (rwmap != NULL((void *)0)) | |||
924 | rewrite_map(client, filename); | |||
925 | else | |||
926 | tftp_open(client, filename); | |||
927 | ||||
928 | return; | |||
929 | ||||
930 | error: | |||
931 | nak(client, ecode); | |||
932 | } | |||
933 | ||||
934 | void | |||
935 | tftp_open(struct tftp_client *client, const char *filename) | |||
936 | { | |||
937 | int ecode; | |||
938 | ||||
939 | ecode = validate_access(client, filename); | |||
940 | if (ecode) | |||
941 | goto error; | |||
942 | ||||
943 | if (client->options) { | |||
944 | if (oack(client) == -1) | |||
945 | goto error; | |||
946 | ||||
947 | free(client->options); | |||
948 | client->options = NULL((void *)0); | |||
949 | } else if (client->opcode == WRQ02) { | |||
950 | recvfile(client); | |||
951 | } else | |||
952 | sendfile(client); | |||
953 | ||||
954 | return; | |||
955 | error: | |||
956 | nak(client, ecode); | |||
957 | } | |||
958 | ||||
959 | /* | |||
960 | * Validate file access. Since we | |||
961 | * have no uid or gid, for now require | |||
962 | * file to exist and be publicly | |||
963 | * readable/writable. | |||
964 | * If we were invoked with arguments | |||
965 | * from inetd then the file must also be | |||
966 | * in one of the given directory prefixes. | |||
967 | * Note also, full path name must be | |||
968 | * given as we have no login directory. | |||
969 | */ | |||
970 | int | |||
971 | validate_access(struct tftp_client *client, const char *requested) | |||
972 | { | |||
973 | int mode = client->opcode; | |||
974 | struct opt_client *options = client->options; | |||
975 | struct stat stbuf; | |||
976 | int fd, wmode; | |||
977 | const char *errstr, *filename; | |||
978 | char rewritten[PATH_MAX1024]; | |||
979 | ||||
980 | if (!canwrite && mode != RRQ01) | |||
981 | return (EACCESS2); | |||
982 | ||||
983 | if (strcmp(requested, SEEDPATH"/etc/random.seed") == 0) { | |||
984 | char *buf; | |||
985 | if (mode != RRQ01) | |||
986 | return (EACCESS2); | |||
987 | ||||
988 | buf = client->buf + sizeof(client->buf) - 512; | |||
989 | arc4random_buf(buf, 512); | |||
990 | if (options != NULL((void *)0) && options[OPT_TSIZE].o_request) | |||
991 | options[OPT_TSIZE].o_reply = 512; | |||
992 | client->file = fmemopen(buf, 512, "r"); | |||
993 | if (client->file == NULL((void *)0)) | |||
994 | return (errno(*__errno()) + 100); | |||
995 | ||||
996 | return (0); | |||
997 | } | |||
998 | ||||
999 | if (iflag) { | |||
1000 | int ret; | |||
1001 | ||||
1002 | /* | |||
1003 | * In -i mode, look in the directory named after the | |||
1004 | * client address. | |||
1005 | */ | |||
1006 | ret = snprintf(rewritten, sizeof(rewritten), "%s/%s", | |||
1007 | getip(&client->ss), requested); | |||
1008 | if (ret < 0 || ret >= sizeof(rewritten)) | |||
1009 | return (ENAMETOOLONG63 + 100); | |||
1010 | filename = rewritten; | |||
1011 | } else { | |||
1012 | retryread: | |||
1013 | filename = requested; | |||
1014 | } | |||
1015 | ||||
1016 | /* | |||
1017 | * We use a different permissions scheme if `cancreate' is | |||
1018 | * set. | |||
1019 | */ | |||
1020 | wmode = O_TRUNC0x0400; | |||
1021 | if (stat(filename, &stbuf) == -1) { | |||
1022 | if (!cancreate) { | |||
1023 | /* | |||
1024 | * In -i mode, retry failed read requests from | |||
1025 | * the root directory. | |||
1026 | */ | |||
1027 | if (mode == RRQ01 && errno(*__errno()) == ENOENT2 && | |||
1028 | filename == rewritten) | |||
1029 | goto retryread; | |||
1030 | return (errno(*__errno()) == ENOENT2 ? ENOTFOUND1 : EACCESS2); | |||
1031 | } else { | |||
1032 | if ((errno(*__errno()) == ENOENT2) && (mode != RRQ01)) | |||
1033 | wmode |= O_CREAT0x0200; | |||
1034 | else | |||
1035 | return (EACCESS2); | |||
1036 | } | |||
1037 | } else { | |||
1038 | if (mode == RRQ01) { | |||
1039 | if ((stbuf.st_mode & (S_IRUSR0000400 >> 6)) == 0) | |||
1040 | return (EACCESS2); | |||
1041 | } else { | |||
1042 | if ((stbuf.st_mode & (S_IWUSR0000200 >> 6)) == 0) | |||
1043 | return (EACCESS2); | |||
1044 | } | |||
1045 | } | |||
1046 | ||||
1047 | if (options != NULL((void *)0) && options[OPT_TSIZE].o_request) { | |||
1048 | if (mode == RRQ01) | |||
1049 | options[OPT_TSIZE].o_reply = stbuf.st_size; | |||
1050 | else { | |||
1051 | /* allows writes of 65535 blocks * SEGSIZE_MAX bytes */ | |||
1052 | options[OPT_TSIZE].o_reply = | |||
1053 | strtonum(options[OPT_TSIZE].o_request, | |||
1054 | 1, 65535LL * SEGSIZE_MAX65464, &errstr); | |||
1055 | if (errstr) | |||
1056 | return (EOPTNEG8); | |||
1057 | } | |||
1058 | } | |||
1059 | fd = open(filename, mode == RRQ01 ? O_RDONLY0x0000 : (O_WRONLY0x0001|wmode), 0666); | |||
1060 | if (fd == -1) | |||
1061 | return (errno(*__errno()) + 100); | |||
1062 | /* | |||
1063 | * If the file was created, set default permissions. | |||
1064 | */ | |||
1065 | if ((wmode & O_CREAT0x0200) && fchmod(fd, 0666) == -1) { | |||
1066 | int serrno = errno(*__errno()); | |||
1067 | ||||
1068 | close(fd); | |||
1069 | unlink(filename); | |||
1070 | ||||
1071 | return (serrno + 100); | |||
1072 | } | |||
1073 | client->file = fdopen(fd, mode == RRQ01 ? "r" : "w"); | |||
1074 | if (client->file == NULL((void *)0)) { | |||
1075 | close(fd); | |||
1076 | return (errno(*__errno()) + 100); | |||
1077 | } | |||
1078 | ||||
1079 | return (0); | |||
1080 | } | |||
1081 | ||||
1082 | int | |||
1083 | fget_octet(struct tftp_client *client) | |||
1084 | { | |||
1085 | return (getc(client->file)(!__isthreaded ? (--(client->file)->_r < 0 ? __srget (client->file) : (int)(*(client->file)->_p++)) : (getc )(client->file))); | |||
1086 | } | |||
1087 | ||||
1088 | int | |||
1089 | fput_octet(struct tftp_client *client, int c) | |||
1090 | { | |||
1091 | return (putc(c, client->file)(!__isthreaded ? __sputc(c, client->file) : (putc)(c, client ->file))); | |||
1092 | } | |||
1093 | ||||
1094 | int | |||
1095 | fget_netascii(struct tftp_client *client) | |||
1096 | { | |||
1097 | int c = -1; | |||
1098 | ||||
1099 | switch (client->newline) { | |||
1100 | case 0: | |||
1101 | c = getc(client->file)(!__isthreaded ? (--(client->file)->_r < 0 ? __srget (client->file) : (int)(*(client->file)->_p++)) : (getc )(client->file)); | |||
1102 | if (c == EOF(-1)) | |||
1103 | break; | |||
1104 | ||||
1105 | if (c == '\n' || c == '\r') { | |||
1106 | client->newline = c; | |||
1107 | c = '\r'; | |||
1108 | } | |||
1109 | break; | |||
1110 | case '\n': | |||
1111 | client->newline = 0; | |||
1112 | c = '\n'; | |||
1113 | break; | |||
1114 | case '\r': | |||
1115 | client->newline = 0; | |||
1116 | c = '\0'; | |||
1117 | break; | |||
1118 | } | |||
1119 | ||||
1120 | return (c); | |||
1121 | } | |||
1122 | ||||
1123 | int | |||
1124 | fput_netascii(struct tftp_client *client, int c) | |||
1125 | { | |||
1126 | if (client->newline == '\r') { | |||
1127 | client->newline = 0; | |||
1128 | ||||
1129 | if (c == '\0') | |||
1130 | c = '\r'; | |||
1131 | ||||
1132 | } else if (c == '\r') { | |||
1133 | client->newline = c; | |||
1134 | return (c); | |||
1135 | } | |||
1136 | ||||
1137 | return (putc(c, client->file)(!__isthreaded ? __sputc(c, client->file) : (putc)(c, client ->file))); | |||
1138 | } | |||
1139 | ||||
1140 | void | |||
1141 | sendfile(struct tftp_client *client) | |||
1142 | { | |||
1143 | event_set(&client->sev, client->sock, EV_READ0x02, tftp_rrq_ack, client); | |||
1144 | client->block = 1; | |||
1145 | ||||
1146 | file_read(client); | |||
1147 | } | |||
1148 | ||||
1149 | void | |||
1150 | file_read(struct tftp_client *client) | |||
1151 | { | |||
1152 | u_int8_t *buf; | |||
1153 | struct tftphdr *dp; | |||
1154 | int i; | |||
1155 | int c; | |||
1156 | ||||
1157 | dp = (struct tftphdr *)client->buf; | |||
1158 | dp->th_opcode = htons((u_short)DATA)(__uint16_t)(__builtin_constant_p((u_short)03) ? (__uint16_t) (((__uint16_t)((u_short)03) & 0xffU) << 8 | ((__uint16_t )((u_short)03) & 0xff00U) >> 8) : __swap16md((u_short )03)); | |||
1159 | dp->th_blockth_u.tu_block = htons(client->block)(__uint16_t)(__builtin_constant_p(client->block) ? (__uint16_t )(((__uint16_t)(client->block) & 0xffU) << 8 | ( (__uint16_t)(client->block) & 0xff00U) >> 8) : __swap16md (client->block)); | |||
1160 | buf = (u_int8_t *)dp->th_data; | |||
1161 | ||||
1162 | for (i = 0; i < client->segment_size; i++) { | |||
1163 | c = client->fgetc(client); | |||
1164 | if (c == EOF(-1)) { | |||
1165 | if (ferror(client->file)(!__isthreaded ? (((client->file)->_flags & 0x0040) != 0) : (ferror)(client->file))) { | |||
1166 | nak(client, 100 + EIO5); | |||
1167 | return; | |||
1168 | } | |||
1169 | ||||
1170 | break; | |||
1171 | } | |||
1172 | buf[i] = c; | |||
1173 | } | |||
1174 | ||||
1175 | client->buflen = i + 4; | |||
1176 | client->retries = RETRIES5; | |||
1177 | ||||
1178 | tftp_send(client); | |||
1179 | } | |||
1180 | ||||
1181 | void | |||
1182 | tftp_send(struct tftp_client *client) | |||
1183 | { | |||
1184 | if (send(client->sock, client->buf, client->buflen, 0) == -1) { | |||
1185 | lwarn("send(block)")logger->warn("send(block)"); | |||
1186 | client_free(client); | |||
1187 | return; | |||
1188 | } | |||
1189 | ||||
1190 | event_add(&client->sev, &client->tv); | |||
1191 | } | |||
1192 | ||||
1193 | void | |||
1194 | tftp_rrq_ack(int fd, short events, void *arg) | |||
1195 | { | |||
1196 | struct tftp_client *client = arg; | |||
1197 | struct tftphdr *ap; /* ack packet */ | |||
1198 | char rbuf[SEGSIZE_MIN8]; | |||
1199 | ssize_t n; | |||
1200 | ||||
1201 | if (events & EV_TIMEOUT0x01) { | |||
1202 | if (retry(client) == -1) { | |||
1203 | lwarn("%s: retry", getip(&client->ss))logger->warn("%s: retry", getip(&client->ss)); | |||
1204 | goto done; | |||
1205 | } | |||
1206 | ||||
1207 | return; | |||
1208 | } | |||
1209 | ||||
1210 | n = recv(fd, rbuf, sizeof(rbuf), 0); | |||
1211 | if (n == -1) { | |||
1212 | switch (errno(*__errno())) { | |||
1213 | case EINTR4: | |||
1214 | case EAGAIN35: | |||
1215 | event_add(&client->sev, &client->tv); | |||
1216 | return; | |||
1217 | ||||
1218 | default: | |||
1219 | lwarn("%s: recv", getip(&client->ss))logger->warn("%s: recv", getip(&client->ss)); | |||
1220 | goto done; | |||
1221 | } | |||
1222 | } | |||
1223 | ||||
1224 | ap = (struct tftphdr *)rbuf; | |||
1225 | ap->th_opcode = ntohs((u_short)ap->th_opcode)(__uint16_t)(__builtin_constant_p((u_short)ap->th_opcode) ? (__uint16_t)(((__uint16_t)((u_short)ap->th_opcode) & 0xffU ) << 8 | ((__uint16_t)((u_short)ap->th_opcode) & 0xff00U) >> 8) : __swap16md((u_short)ap->th_opcode) ); | |||
1226 | ap->th_blockth_u.tu_block = ntohs((u_short)ap->th_block)(__uint16_t)(__builtin_constant_p((u_short)ap->th_u.tu_block ) ? (__uint16_t)(((__uint16_t)((u_short)ap->th_u.tu_block) & 0xffU) << 8 | ((__uint16_t)((u_short)ap->th_u .tu_block) & 0xff00U) >> 8) : __swap16md((u_short)ap ->th_u.tu_block)); | |||
1227 | ||||
1228 | switch (ap->th_opcode) { | |||
1229 | case ACK04: | |||
1230 | break; | |||
1231 | case ERROR05: | |||
1232 | default: /* assume the worst */ | |||
1233 | goto done; | |||
1234 | } | |||
1235 | ||||
1236 | if (ap->th_blockth_u.tu_block != client->block) { | |||
1237 | if (tftp_flush(client) == -1) { | |||
1238 | lwarnx("%s: flush", getip(&client->ss))logger->warnx("%s: flush", getip(&client->ss)); | |||
1239 | goto done; | |||
1240 | } | |||
1241 | ||||
1242 | if (ap->th_blockth_u.tu_block != (client->block - 1)) | |||
1243 | goto done; | |||
1244 | ||||
1245 | tftp_send(client); | |||
1246 | return; | |||
1247 | } | |||
1248 | ||||
1249 | if (client->buflen != client->packet_size) { | |||
1250 | /* this was the last packet in the stream */ | |||
1251 | goto done; | |||
1252 | } | |||
1253 | ||||
1254 | client->block++; | |||
1255 | file_read(client); | |||
1256 | return; | |||
1257 | ||||
1258 | done: | |||
1259 | client_free(client); | |||
1260 | } | |||
1261 | ||||
1262 | int | |||
1263 | tftp_flush(struct tftp_client *client) | |||
1264 | { | |||
1265 | char rbuf[SEGSIZE_MIN8]; | |||
1266 | ssize_t n; | |||
1267 | ||||
1268 | for (;;) { | |||
1269 | n = recv(client->sock, rbuf, sizeof(rbuf), 0); | |||
1270 | if (n == -1) { | |||
1271 | switch (errno(*__errno())) { | |||
1272 | case EAGAIN35: | |||
1273 | return (0); | |||
1274 | ||||
1275 | case EINTR4: | |||
1276 | break; | |||
1277 | ||||
1278 | default: | |||
1279 | return (-1); | |||
1280 | } | |||
1281 | } | |||
1282 | } | |||
1283 | } | |||
1284 | ||||
1285 | void | |||
1286 | recvfile(struct tftp_client *client) | |||
1287 | { | |||
1288 | event_set(&client->sev, client->sock, EV_READ0x02, tftp_wrq, client); | |||
1289 | tftp_wrq_ack(client); | |||
1290 | } | |||
1291 | ||||
1292 | int | |||
1293 | tftp_wrq_ack_packet(struct tftp_client *client) | |||
1294 | { | |||
1295 | struct tftphdr *ap; /* ack packet */ | |||
1296 | ||||
1297 | ap = (struct tftphdr *)client->buf; | |||
1298 | ap->th_opcode = htons((u_short)ACK)(__uint16_t)(__builtin_constant_p((u_short)04) ? (__uint16_t) (((__uint16_t)((u_short)04) & 0xffU) << 8 | ((__uint16_t )((u_short)04) & 0xff00U) >> 8) : __swap16md((u_short )04)); | |||
1299 | ap->th_blockth_u.tu_block = htons(client->block)(__uint16_t)(__builtin_constant_p(client->block) ? (__uint16_t )(((__uint16_t)(client->block) & 0xffU) << 8 | ( (__uint16_t)(client->block) & 0xff00U) >> 8) : __swap16md (client->block)); | |||
1300 | ||||
1301 | client->buflen = 4; | |||
1302 | client->retries = RETRIES5; | |||
1303 | ||||
1304 | return (send(client->sock, client->buf, client->buflen, 0) != 4); | |||
1305 | } | |||
1306 | ||||
1307 | void | |||
1308 | tftp_wrq_ack(struct tftp_client *client) | |||
1309 | { | |||
1310 | if (tftp_wrq_ack_packet(client) != 0) { | |||
1311 | lwarn("tftp wrq ack")logger->warn("tftp wrq ack"); | |||
1312 | client_free(client); | |||
1313 | return; | |||
1314 | } | |||
1315 | ||||
1316 | client->block++; | |||
1317 | event_add(&client->sev, &client->tv); | |||
1318 | } | |||
1319 | ||||
1320 | void | |||
1321 | tftp_wrq(int fd, short events, void *arg) | |||
1322 | { | |||
1323 | char wbuf[SEGSIZE_MAX65464 + 4]; | |||
1324 | struct tftp_client *client = arg; | |||
1325 | struct tftphdr *dp; | |||
1326 | ssize_t n; | |||
1327 | int i; | |||
1328 | ||||
1329 | if (events & EV_TIMEOUT0x01) { | |||
1330 | if (retry(client) == -1) { | |||
1331 | lwarn("%s", getip(&client->ss))logger->warn("%s", getip(&client->ss)); | |||
1332 | goto done; | |||
1333 | } | |||
1334 | ||||
1335 | return; | |||
1336 | } | |||
1337 | ||||
1338 | n = recv(fd, wbuf, client->packet_size, 0); | |||
1339 | if (n == -1) { | |||
1340 | switch (errno(*__errno())) { | |||
1341 | case EINTR4: | |||
1342 | case EAGAIN35: | |||
1343 | goto retry; | |||
1344 | ||||
1345 | default: | |||
1346 | lwarn("tftp_wrq recv")logger->warn("tftp_wrq recv"); | |||
1347 | goto done; | |||
1348 | } | |||
1349 | } | |||
1350 | ||||
1351 | if (n < 4) | |||
1352 | goto done; | |||
1353 | ||||
1354 | dp = (struct tftphdr *)wbuf; | |||
1355 | dp->th_opcode = ntohs((u_short)dp->th_opcode)(__uint16_t)(__builtin_constant_p((u_short)dp->th_opcode) ? (__uint16_t)(((__uint16_t)((u_short)dp->th_opcode) & 0xffU ) << 8 | ((__uint16_t)((u_short)dp->th_opcode) & 0xff00U) >> 8) : __swap16md((u_short)dp->th_opcode) ); | |||
1356 | dp->th_blockth_u.tu_block = ntohs((u_short)dp->th_block)(__uint16_t)(__builtin_constant_p((u_short)dp->th_u.tu_block ) ? (__uint16_t)(((__uint16_t)((u_short)dp->th_u.tu_block) & 0xffU) << 8 | ((__uint16_t)((u_short)dp->th_u .tu_block) & 0xff00U) >> 8) : __swap16md((u_short)dp ->th_u.tu_block)); | |||
1357 | ||||
1358 | switch (dp->th_opcode) { | |||
1359 | case ERROR05: | |||
1360 | goto done; | |||
1361 | case DATA03: | |||
1362 | break; | |||
1363 | default: | |||
1364 | goto retry; | |||
1365 | } | |||
1366 | ||||
1367 | if (dp->th_blockth_u.tu_block != client->block) { | |||
1368 | if (tftp_flush(client) == -1) { | |||
1369 | lwarnx("%s: flush", getip(&client->ss))logger->warnx("%s: flush", getip(&client->ss)); | |||
1370 | goto done; | |||
1371 | } | |||
1372 | ||||
1373 | if (dp->th_blockth_u.tu_block != (client->block - 1)) | |||
1374 | goto done; | |||
1375 | ||||
1376 | goto retry; | |||
1377 | } | |||
1378 | ||||
1379 | for (i = 4; i < n; i++) { | |||
1380 | if (client->fputc(client, wbuf[i]) == EOF(-1)) { | |||
1381 | lwarn("tftp wrq")logger->warn("tftp wrq"); | |||
1382 | goto done; | |||
1383 | } | |||
1384 | } | |||
1385 | ||||
1386 | if (n < client->packet_size) { | |||
1387 | tftp_wrq_ack_packet(client); | |||
1388 | fclose(client->file); | |||
1389 | client->file = NULL((void *)0); | |||
1390 | event_set(&client->sev, client->sock, EV_READ0x02, | |||
1391 | tftp_wrq_end, client); | |||
1392 | event_add(&client->sev, &client->tv); | |||
1393 | return; | |||
1394 | } | |||
1395 | ||||
1396 | tftp_wrq_ack(client); | |||
1397 | return; | |||
1398 | ||||
1399 | retry: | |||
1400 | event_add(&client->sev, &client->tv); | |||
1401 | return; | |||
1402 | done: | |||
1403 | client_free(client); | |||
1404 | } | |||
1405 | ||||
1406 | void | |||
1407 | tftp_wrq_end(int fd, short events, void *arg) | |||
1408 | { | |||
1409 | char wbuf[SEGSIZE_MAX65464 + 4]; | |||
1410 | struct tftp_client *client = arg; | |||
1411 | struct tftphdr *dp; | |||
1412 | ssize_t n; | |||
1413 | ||||
1414 | if (events & EV_TIMEOUT0x01) { | |||
1415 | /* this was the last packet, we can clean up */ | |||
1416 | goto done; | |||
1417 | } | |||
1418 | ||||
1419 | n = recv(fd, wbuf, client->packet_size, 0); | |||
1420 | if (n == -1) { | |||
1421 | switch (errno(*__errno())) { | |||
1422 | case EINTR4: | |||
1423 | case EAGAIN35: | |||
1424 | goto retry; | |||
1425 | ||||
1426 | default: | |||
1427 | lwarn("tftp_wrq_end recv")logger->warn("tftp_wrq_end recv"); | |||
1428 | goto done; | |||
1429 | } | |||
1430 | } | |||
1431 | ||||
1432 | if (n < 4) | |||
1433 | goto done; | |||
1434 | ||||
1435 | dp = (struct tftphdr *)wbuf; | |||
1436 | dp->th_opcode = ntohs((u_short)dp->th_opcode)(__uint16_t)(__builtin_constant_p((u_short)dp->th_opcode) ? (__uint16_t)(((__uint16_t)((u_short)dp->th_opcode) & 0xffU ) << 8 | ((__uint16_t)((u_short)dp->th_opcode) & 0xff00U) >> 8) : __swap16md((u_short)dp->th_opcode) ); | |||
1437 | dp->th_blockth_u.tu_block = ntohs((u_short)dp->th_block)(__uint16_t)(__builtin_constant_p((u_short)dp->th_u.tu_block ) ? (__uint16_t)(((__uint16_t)((u_short)dp->th_u.tu_block) & 0xffU) << 8 | ((__uint16_t)((u_short)dp->th_u .tu_block) & 0xff00U) >> 8) : __swap16md((u_short)dp ->th_u.tu_block)); | |||
1438 | ||||
1439 | switch (dp->th_opcode) { | |||
1440 | case ERROR05: | |||
1441 | goto done; | |||
1442 | case DATA03: | |||
1443 | break; | |||
1444 | default: | |||
1445 | goto retry; | |||
1446 | } | |||
1447 | ||||
1448 | if (dp->th_blockth_u.tu_block != client->block) | |||
1449 | goto done; | |||
1450 | ||||
1451 | retry: | |||
1452 | if (retry(client) == -1) { | |||
1453 | lwarn("%s", getip(&client->ss))logger->warn("%s", getip(&client->ss)); | |||
1454 | goto done; | |||
1455 | } | |||
1456 | return; | |||
1457 | done: | |||
1458 | client_free(client); | |||
1459 | return; | |||
1460 | } | |||
1461 | ||||
1462 | ||||
1463 | /* | |||
1464 | * Send a nak packet (error message). | |||
1465 | * Error code passed in is one of the | |||
1466 | * standard TFTP codes, or a UNIX errno | |||
1467 | * offset by 100. | |||
1468 | */ | |||
1469 | void | |||
1470 | nak(struct tftp_client *client, int error) | |||
1471 | { | |||
1472 | struct tftphdr *tp; | |||
1473 | struct errmsg *pe; | |||
1474 | size_t length; | |||
1475 | ssize_t rslt; | |||
1476 | ||||
1477 | tp = (struct tftphdr *)client->buf; | |||
1478 | tp->th_opcode = htons((u_short)ERROR)(__uint16_t)(__builtin_constant_p((u_short)05) ? (__uint16_t) (((__uint16_t)((u_short)05) & 0xffU) << 8 | ((__uint16_t )((u_short)05) & 0xff00U) >> 8) : __swap16md((u_short )05)); | |||
1479 | tp->th_codeth_u.tu_code = htons((u_short)error)(__uint16_t)(__builtin_constant_p((u_short)error) ? (__uint16_t )(((__uint16_t)((u_short)error) & 0xffU) << 8 | ((__uint16_t )((u_short)error) & 0xff00U) >> 8) : __swap16md((u_short )error)); | |||
1480 | ||||
1481 | for (pe = errmsgs; pe->e_code >= 0; pe++) { | |||
1482 | if (pe->e_code == error) | |||
1483 | break; | |||
1484 | } | |||
1485 | if (pe->e_code < 0) { | |||
1486 | pe->e_msg = strerror(error - 100); | |||
1487 | tp->th_codeth_u.tu_code = htons(EUNDEF)(__uint16_t)(__builtin_constant_p(0) ? (__uint16_t)(((__uint16_t )(0) & 0xffU) << 8 | ((__uint16_t)(0) & 0xff00U ) >> 8) : __swap16md(0)); /* set 'undef' errorcode */ | |||
1488 | } | |||
1489 | ||||
1490 | length = strlcpy(tp->th_msgth_data, pe->e_msg, client->packet_size - 5) + 5; | |||
1491 | if (length > client->packet_size) | |||
1492 | length = client->packet_size; | |||
1493 | ||||
1494 | linfo("%s: nak: %s", getip(&client->ss), tp->th_msg)logger->info("%s: nak: %s", getip(&client->ss), tp-> th_data); | |||
1495 | ||||
1496 | rslt = send(client->sock, client->buf, length, 0); | |||
1497 | if (rslt == -1) | |||
1498 | lwarn("%s: nak", getip(&client->ss))logger->warn("%s: nak", getip(&client->ss)); | |||
1499 | else if ((size_t)rslt != length) | |||
1500 | lwarnx("%s: nak: sent %zd of %zu bytes", getip(&client->ss),logger->warnx("%s: nak: sent %zd of %zu bytes", getip(& client->ss), rslt, length) | |||
1501 | rslt, length)logger->warnx("%s: nak: sent %zd of %zu bytes", getip(& client->ss), rslt, length); | |||
1502 | ||||
1503 | client_free(client); | |||
1504 | } | |||
1505 | ||||
1506 | /* | |||
1507 | * Send an oack packet (option acknowledgement). | |||
1508 | */ | |||
1509 | int | |||
1510 | oack(struct tftp_client *client) | |||
1511 | { | |||
1512 | struct opt_client *options = client->options; | |||
1513 | struct tftphdr *tp; | |||
1514 | char *bp; | |||
1515 | int i, n, size; | |||
1516 | ||||
1517 | tp = (struct tftphdr *)client->buf; | |||
1518 | bp = (char *)tp->th_stuffth_u.tu_stuff; | |||
1519 | size = sizeof(client->buf) - 2; | |||
1520 | ||||
1521 | tp->th_opcode = htons((u_short)OACK)(__uint16_t)(__builtin_constant_p((u_short)06) ? (__uint16_t) (((__uint16_t)((u_short)06) & 0xffU) << 8 | ((__uint16_t )((u_short)06) & 0xff00U) >> 8) : __swap16md((u_short )06)); | |||
1522 | for (i = 0; i < NOPT; i++) { | |||
1523 | if (options[i].o_request == NULL((void *)0)) | |||
1524 | continue; | |||
1525 | ||||
1526 | n = snprintf(bp, size, "%s%c%lld", opt_names[i], '\0', | |||
1527 | options[i].o_reply); | |||
1528 | if (n < 0 || n >= size) { | |||
1529 | lwarnx("oack: no buffer space")logger->warnx("oack: no buffer space"); | |||
1530 | goto error; | |||
1531 | } | |||
1532 | ||||
1533 | bp += n + 1; | |||
1534 | size -= n + 1; | |||
1535 | if (size < 0) { | |||
1536 | lwarnx("oack: no buffer space")logger->warnx("oack: no buffer space"); | |||
1537 | goto error; | |||
1538 | } | |||
1539 | } | |||
1540 | ||||
1541 | client->buflen = bp - client->buf; | |||
1542 | client->retries = RETRIES5; | |||
1543 | ||||
1544 | if (send(client->sock, client->buf, client->buflen, 0) == -1) { | |||
1545 | lwarn("oack")logger->warn("oack"); | |||
1546 | goto error; | |||
1547 | } | |||
1548 | ||||
1549 | /* no client ACK for write requests with options */ | |||
1550 | if (client->opcode == WRQ02) { | |||
1551 | client->block = 1; | |||
1552 | event_set(&client->sev, client->sock, EV_READ0x02, | |||
1553 | tftp_wrq, client); | |||
1554 | } else | |||
1555 | event_set(&client->sev, client->sock, EV_READ0x02, | |||
1556 | oack_done, client); | |||
1557 | ||||
1558 | event_add(&client->sev, &client->tv); | |||
1559 | return (0); | |||
1560 | ||||
1561 | error: | |||
1562 | return (-1); | |||
1563 | } | |||
1564 | ||||
1565 | int | |||
1566 | retry(struct tftp_client *client) | |||
1567 | { | |||
1568 | if (--client->retries == 0) { | |||
1569 | errno(*__errno()) = ETIMEDOUT60; | |||
1570 | return (-1); | |||
1571 | } | |||
1572 | ||||
1573 | tftp_send(client); | |||
1574 | ||||
1575 | return (0); | |||
1576 | } | |||
1577 | ||||
1578 | void | |||
1579 | oack_done(int fd, short events, void *arg) | |||
1580 | { | |||
1581 | struct tftp_client *client = arg; | |||
1582 | struct tftphdr *ap; | |||
1583 | ssize_t n; | |||
1584 | ||||
1585 | if (events & EV_TIMEOUT0x01) { | |||
1586 | if (retry(client) == -1) { | |||
1587 | lwarn("%s", getip(&client->ss))logger->warn("%s", getip(&client->ss)); | |||
1588 | goto done; | |||
1589 | } | |||
1590 | ||||
1591 | return; | |||
1592 | } | |||
1593 | ||||
1594 | n = recv(client->sock, client->buf, client->packet_size, 0); | |||
1595 | if (n == -1) { | |||
1596 | switch (errno(*__errno())) { | |||
1597 | case EINTR4: | |||
1598 | case EAGAIN35: | |||
1599 | event_add(&client->sev, &client->tv); | |||
1600 | return; | |||
1601 | ||||
1602 | default: | |||
1603 | lwarn("%s: recv", getip(&client->ss))logger->warn("%s: recv", getip(&client->ss)); | |||
1604 | goto done; | |||
1605 | } | |||
1606 | } | |||
1607 | ||||
1608 | if (n < 4) | |||
1609 | goto done; | |||
1610 | ||||
1611 | ap = (struct tftphdr *)client->buf; | |||
1612 | ap->th_opcode = ntohs((u_short)ap->th_opcode)(__uint16_t)(__builtin_constant_p((u_short)ap->th_opcode) ? (__uint16_t)(((__uint16_t)((u_short)ap->th_opcode) & 0xffU ) << 8 | ((__uint16_t)((u_short)ap->th_opcode) & 0xff00U) >> 8) : __swap16md((u_short)ap->th_opcode) ); | |||
1613 | ap->th_blockth_u.tu_block = ntohs((u_short)ap->th_block)(__uint16_t)(__builtin_constant_p((u_short)ap->th_u.tu_block ) ? (__uint16_t)(((__uint16_t)((u_short)ap->th_u.tu_block) & 0xffU) << 8 | ((__uint16_t)((u_short)ap->th_u .tu_block) & 0xff00U) >> 8) : __swap16md((u_short)ap ->th_u.tu_block)); | |||
1614 | ||||
1615 | if (ap->th_opcode != ACK04 || ap->th_blockth_u.tu_block != 0) | |||
1616 | goto done; | |||
1617 | ||||
1618 | sendfile(client); | |||
1619 | return; | |||
1620 | ||||
1621 | done: | |||
1622 | client_free(client); | |||
1623 | } | |||
1624 | ||||
1625 | const char * | |||
1626 | getip(void *s) | |||
1627 | { | |||
1628 | struct sockaddr *sa = s; | |||
1629 | static char hbuf[NI_MAXHOST256]; | |||
1630 | ||||
1631 | if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), | |||
1632 | NULL((void *)0), 0, NI_NUMERICHOST1)) | |||
1633 | strlcpy(hbuf, "0.0.0.0", sizeof(hbuf)); | |||
1634 | ||||
1635 | return(hbuf); | |||
1636 | } | |||
1637 | ||||
1638 | /* daemon(3) clone, intended to be used in a "r"estricted environment */ | |||
1639 | int | |||
1640 | rdaemon(int devnull) | |||
1641 | { | |||
1642 | if (devnull == -1) { | |||
1643 | errno(*__errno()) = EBADF9; | |||
1644 | return (-1); | |||
1645 | } | |||
1646 | if (fcntl(devnull, F_GETFL3) == -1) | |||
1647 | return (-1); | |||
1648 | ||||
1649 | switch (fork()) { | |||
1650 | case -1: | |||
1651 | return (-1); | |||
1652 | case 0: | |||
1653 | break; | |||
1654 | default: | |||
1655 | _exit(0); | |||
1656 | } | |||
1657 | ||||
1658 | if (setsid() == -1) | |||
1659 | return (-1); | |||
1660 | ||||
1661 | (void)dup2(devnull, STDIN_FILENO0); | |||
1662 | (void)dup2(devnull, STDOUT_FILENO1); | |||
1663 | (void)dup2(devnull, STDERR_FILENO2); | |||
1664 | if (devnull > 2) | |||
1665 | (void)close(devnull); | |||
1666 | ||||
1667 | return (0); | |||
1668 | } | |||
1669 | ||||
1670 | void | |||
1671 | syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) | |||
1672 | { | |||
1673 | char *s; | |||
1674 | ||||
1675 | if (vasprintf(&s, fmt, ap) == -1) { | |||
1676 | syslog(LOG_EMERG0, "unable to alloc in syslog_vstrerror"); | |||
1677 | exit(1); | |||
1678 | } | |||
1679 | ||||
1680 | syslog(priority, "%s: %s", s, strerror(e)); | |||
1681 | ||||
1682 | free(s); | |||
1683 | } | |||
1684 | ||||
1685 | void | |||
1686 | syslog_err(int ecode, const char *fmt, ...) | |||
1687 | { | |||
1688 | va_list ap; | |||
1689 | ||||
1690 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | |||
1691 | syslog_vstrerror(errno(*__errno()), LOG_CRIT2, fmt, ap); | |||
1692 | va_end(ap)__builtin_va_end((ap)); | |||
1693 | ||||
1694 | exit(ecode); | |||
1695 | } | |||
1696 | ||||
1697 | void | |||
1698 | syslog_errx(int ecode, const char *fmt, ...) | |||
1699 | { | |||
1700 | va_list ap; | |||
1701 | ||||
1702 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | |||
1703 | vsyslog(LOG_CRIT2, fmt, ap); | |||
1704 | va_end(ap)__builtin_va_end((ap)); | |||
1705 | ||||
1706 | exit(ecode); | |||
1707 | } | |||
1708 | ||||
1709 | void | |||
1710 | syslog_warn(const char *fmt, ...) | |||
1711 | { | |||
1712 | va_list ap; | |||
1713 | ||||
1714 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | |||
1715 | syslog_vstrerror(errno(*__errno()), LOG_ERR3, fmt, ap); | |||
1716 | va_end(ap)__builtin_va_end((ap)); | |||
1717 | } | |||
1718 | ||||
1719 | void | |||
1720 | syslog_warnx(const char *fmt, ...) | |||
1721 | { | |||
1722 | va_list ap; | |||
1723 | ||||
1724 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | |||
1725 | vsyslog(LOG_ERR3, fmt, ap); | |||
1726 | va_end(ap)__builtin_va_end((ap)); | |||
1727 | } | |||
1728 | ||||
1729 | void | |||
1730 | syslog_info(const char *fmt, ...) | |||
1731 | { | |||
1732 | va_list ap; | |||
1733 | ||||
1734 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | |||
1735 | vsyslog(LOG_INFO6, fmt, ap); | |||
1736 | va_end(ap)__builtin_va_end((ap)); | |||
1737 | } | |||
1738 | ||||
1739 | void | |||
1740 | syslog_debug(const char *fmt, ...) | |||
1741 | { | |||
1742 | va_list ap; | |||
1743 | ||||
1744 | if (!debug) | |||
1745 | return; | |||
1746 | ||||
1747 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | |||
1748 | vsyslog(LOG_DEBUG7, fmt, ap); | |||
1749 | va_end(ap)__builtin_va_end((ap)); | |||
1750 | } |