1*479c151dSjsg /* $OpenBSD: tftpd.c,v 1.52 2024/09/20 02:00:46 jsg Exp $ */ 28871c557Sdlg 38871c557Sdlg /* 48871c557Sdlg * Copyright (c) 2012 David Gwynne <dlg@uq.edu.au> 58871c557Sdlg * 68871c557Sdlg * Permission to use, copy, modify, and distribute this software for any 78871c557Sdlg * purpose with or without fee is hereby granted, provided that the above 88871c557Sdlg * copyright notice and this permission notice appear in all copies. 98871c557Sdlg * 108871c557Sdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 118871c557Sdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 128871c557Sdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 138871c557Sdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 148871c557Sdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 158871c557Sdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 168871c557Sdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 178871c557Sdlg */ 188871c557Sdlg 198871c557Sdlg /* 208871c557Sdlg * Copyright (c) 1983 Regents of the University of California. 218871c557Sdlg * All rights reserved. 228871c557Sdlg * 238871c557Sdlg * Redistribution and use in source and binary forms, with or without 248871c557Sdlg * modification, are permitted provided that the following conditions 258871c557Sdlg * are met: 268871c557Sdlg * 1. Redistributions of source code must retain the above copyright 278871c557Sdlg * notice, this list of conditions and the following disclaimer. 288871c557Sdlg * 2. Redistributions in binary form must reproduce the above copyright 298871c557Sdlg * notice, this list of conditions and the following disclaimer in the 308871c557Sdlg * documentation and/or other materials provided with the distribution. 318871c557Sdlg * 3. Neither the name of the University nor the names of its contributors 328871c557Sdlg * may be used to endorse or promote products derived from this software 338871c557Sdlg * without specific prior written permission. 348871c557Sdlg * 358871c557Sdlg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 368871c557Sdlg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 378871c557Sdlg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 388871c557Sdlg * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 398871c557Sdlg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 408871c557Sdlg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 418871c557Sdlg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 428871c557Sdlg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 438871c557Sdlg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 448871c557Sdlg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 458871c557Sdlg * SUCH DAMAGE. 468871c557Sdlg */ 478871c557Sdlg 488871c557Sdlg /* 498871c557Sdlg * Trivial file transfer protocol server. 508871c557Sdlg * 518871c557Sdlg * This version is based on src/libexec/tftpd which includes many 528871c557Sdlg * modifications by Jim Guyton <guyton@rand-unix>. 538871c557Sdlg * 548871c557Sdlg * It was restructured to be a persistent event driven daemon 558871c557Sdlg * supporting concurrent connections by dlg for use at the University 568871c557Sdlg * of Queensland in the Faculty of Engineering Architecture and 578871c557Sdlg * Information Technology. 588871c557Sdlg */ 598871c557Sdlg 6015a98d3dSderaadt #include <sys/types.h> 611faad7bfSnicm #include <sys/queue.h> 628871c557Sdlg #include <sys/socket.h> 638871c557Sdlg #include <sys/stat.h> 648871c557Sdlg #include <sys/uio.h> 658871c557Sdlg #include <sys/un.h> 668871c557Sdlg 678871c557Sdlg #include <netinet/in.h> 688871c557Sdlg #include <arpa/inet.h> 698871c557Sdlg #include <arpa/tftp.h> 708871c557Sdlg #include <netdb.h> 718871c557Sdlg 728871c557Sdlg #include <err.h> 738871c557Sdlg #include <ctype.h> 748871c557Sdlg #include <errno.h> 758871c557Sdlg #include <event.h> 768871c557Sdlg #include <fcntl.h> 77eaf6a821Sjca #include <paths.h> 788871c557Sdlg #include <poll.h> 798871c557Sdlg #include <pwd.h> 808871c557Sdlg #include <stdio.h> 818871c557Sdlg #include <stdlib.h> 828871c557Sdlg #include <string.h> 838871c557Sdlg #include <stdarg.h> 848871c557Sdlg #include <syslog.h> 858871c557Sdlg #include <unistd.h> 86b9fc9a72Sderaadt #include <limits.h> 878871c557Sdlg #include <vis.h> 888871c557Sdlg 898871c557Sdlg #define TIMEOUT 5 /* packet rexmt timeout */ 908871c557Sdlg #define TIMEOUT_MIN 1 /* minimal packet rexmt timeout */ 918871c557Sdlg #define TIMEOUT_MAX 255 /* maximal packet rexmt timeout */ 928871c557Sdlg 938871c557Sdlg #define RETRIES 5 948871c557Sdlg 954a5fd927Smcbride #define SEEDPATH "/etc/random.seed" 964a5fd927Smcbride 978871c557Sdlg struct formats; 988871c557Sdlg 998871c557Sdlg enum opt_enum { 1008871c557Sdlg OPT_TSIZE = 0, 1018871c557Sdlg OPT_TIMEOUT, 1028871c557Sdlg OPT_BLKSIZE, 1038871c557Sdlg NOPT 1048871c557Sdlg }; 1058871c557Sdlg 1068871c557Sdlg static char *opt_names[] = { 1078871c557Sdlg "tsize", 1088871c557Sdlg "timeout", 1098871c557Sdlg "blksize" 1108871c557Sdlg }; 1118871c557Sdlg 1128871c557Sdlg struct opt_client { 1138871c557Sdlg char *o_request; 1148871c557Sdlg long long o_reply; 1158871c557Sdlg }; 1168871c557Sdlg 1178871c557Sdlg 1188871c557Sdlg struct tftp_server { 1198871c557Sdlg struct event ev; 1208871c557Sdlg TAILQ_ENTRY(tftp_server) entry; 1218871c557Sdlg int s; 1228871c557Sdlg }; 1238871c557Sdlg 1248871c557Sdlg TAILQ_HEAD(, tftp_server) tftp_servers; 1258871c557Sdlg 1268871c557Sdlg struct tftp_client { 1278871c557Sdlg char buf[SEGSIZE_MAX + 4]; 1288871c557Sdlg struct event sev; 1298871c557Sdlg struct sockaddr_storage ss; 1308871c557Sdlg 1318871c557Sdlg struct timeval tv; 1328871c557Sdlg 1338871c557Sdlg TAILQ_ENTRY(tftp_client) entry; 1348871c557Sdlg 1358871c557Sdlg struct opt_client *options; 1368871c557Sdlg 1378871c557Sdlg size_t segment_size; 1388871c557Sdlg size_t packet_size; 1398871c557Sdlg size_t buflen; 1408871c557Sdlg 1418871c557Sdlg FILE *file; 1428871c557Sdlg int (*fgetc)(struct tftp_client *); 1438871c557Sdlg int (*fputc)(struct tftp_client *, int); 1448871c557Sdlg 1458871c557Sdlg u_int retries; 1468871c557Sdlg u_int16_t block; 1478871c557Sdlg 1488871c557Sdlg int opcode; 1498871c557Sdlg int newline; 1508871c557Sdlg 1518871c557Sdlg int sock; 1528871c557Sdlg }; 1538871c557Sdlg 1548871c557Sdlg __dead void usage(void); 1558871c557Sdlg const char *getip(void *); 1568972ba64Sjca int rdaemon(int); 1578871c557Sdlg 1588871c557Sdlg void rewrite_connect(const char *); 1598871c557Sdlg void rewrite_events(void); 1608871c557Sdlg void rewrite_map(struct tftp_client *, const char *); 1618871c557Sdlg void rewrite_req(int, short, void *); 1628871c557Sdlg void rewrite_res(int, short, void *); 1638871c557Sdlg 1648871c557Sdlg int tftpd_listen(const char *, const char *, int); 1658871c557Sdlg void tftpd_events(void); 1668871c557Sdlg void tftpd_recv(int, short, void *); 1678871c557Sdlg int retry(struct tftp_client *); 1681e449da8Sdlg int tftp_flush(struct tftp_client *); 1698871c557Sdlg 1708871c557Sdlg void tftp(struct tftp_client *, struct tftphdr *, size_t); 1718871c557Sdlg void tftp_open(struct tftp_client *, const char *); 1728871c557Sdlg void nak(struct tftp_client *, int); 173eca1b59cSdlg int oack(struct tftp_client *); 1748871c557Sdlg void oack_done(int, short, void *); 1758871c557Sdlg 1768871c557Sdlg void sendfile(struct tftp_client *); 1778871c557Sdlg void recvfile(struct tftp_client *); 1788871c557Sdlg int fget_octet(struct tftp_client *); 1798871c557Sdlg int fput_octet(struct tftp_client *, int); 1808871c557Sdlg int fget_netascii(struct tftp_client *); 1818871c557Sdlg int fput_netascii(struct tftp_client *, int); 1828871c557Sdlg void file_read(struct tftp_client *); 18380a17660Sdlg void tftp_send(struct tftp_client *); 1848871c557Sdlg int tftp_wrq_ack_packet(struct tftp_client *); 1858871c557Sdlg void tftp_rrq_ack(int, short, void *); 1868871c557Sdlg void tftp_wrq_ack(struct tftp_client *client); 1878871c557Sdlg void tftp_wrq(int, short, void *); 1886ac42c1aSdlg void tftp_wrq_end(int, short, void *); 1898871c557Sdlg 1908871c557Sdlg int parse_options(struct tftp_client *, char *, size_t, 1918871c557Sdlg struct opt_client *); 1928871c557Sdlg int validate_access(struct tftp_client *, const char *); 1938871c557Sdlg 194d2c62849Sderaadt struct tftp_client * 195d2c62849Sderaadt client_alloc(void); 196d2c62849Sderaadt void client_free(struct tftp_client *client); 197d2c62849Sderaadt 1988871c557Sdlg struct formats { 1998871c557Sdlg const char *f_mode; 2008871c557Sdlg int (*f_getc)(struct tftp_client *); 2018871c557Sdlg int (*f_putc)(struct tftp_client *, int); 2028871c557Sdlg } formats[] = { 2038871c557Sdlg { "octet", fget_octet, fput_octet }, 2048871c557Sdlg { "netascii", fget_netascii, fput_netascii }, 2058871c557Sdlg { NULL, NULL } 2068871c557Sdlg }; 2078871c557Sdlg 2088871c557Sdlg struct errmsg { 2098871c557Sdlg int e_code; 2108871c557Sdlg const char *e_msg; 2118871c557Sdlg } errmsgs[] = { 2128871c557Sdlg { EUNDEF, "Undefined error code" }, 2138871c557Sdlg { ENOTFOUND, "File not found" }, 2148871c557Sdlg { EACCESS, "Access violation" }, 2158871c557Sdlg { ENOSPACE, "Disk full or allocation exceeded" }, 2168871c557Sdlg { EBADOP, "Illegal TFTP operation" }, 2178871c557Sdlg { EBADID, "Unknown transfer ID" }, 2188871c557Sdlg { EEXISTS, "File already exists" }, 2198871c557Sdlg { ENOUSER, "No such user" }, 2208871c557Sdlg { EOPTNEG, "Option negotiation failed" }, 2218871c557Sdlg { -1, NULL } 2228871c557Sdlg }; 2238871c557Sdlg 2248871c557Sdlg struct loggers { 2250e7810bdSflorian __dead void (*err)(int, const char *, ...) 2260e7810bdSflorian __attribute__((__format__ (printf, 2, 3))); 2270e7810bdSflorian __dead void (*errx)(int, const char *, ...) 2280e7810bdSflorian __attribute__((__format__ (printf, 2, 3))); 2290e7810bdSflorian void (*warn)(const char *, ...) 2300e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2310e7810bdSflorian void (*warnx)(const char *, ...) 2320e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2330e7810bdSflorian void (*info)(const char *, ...) 2340e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2350e7810bdSflorian void (*debug)(const char *, ...) 2360e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2378871c557Sdlg }; 2388871c557Sdlg 2398871c557Sdlg const struct loggers conslogger = { 2408871c557Sdlg err, 2418871c557Sdlg errx, 2428871c557Sdlg warn, 2438871c557Sdlg warnx, 2440e7810bdSflorian warnx, /* info */ 2450e7810bdSflorian warnx /* debug */ 2468871c557Sdlg }; 2478871c557Sdlg 2480e7810bdSflorian __dead void syslog_err(int, const char *, ...) 2490e7810bdSflorian __attribute__((__format__ (printf, 2, 3))); 2500e7810bdSflorian __dead void syslog_errx(int, const char *, ...) 2510e7810bdSflorian __attribute__((__format__ (printf, 2, 3))); 2520e7810bdSflorian void syslog_warn(const char *, ...) 2530e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2540e7810bdSflorian void syslog_warnx(const char *, ...) 2550e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2560e7810bdSflorian void syslog_info(const char *, ...) 2570e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2580e7810bdSflorian void syslog_debug(const char *, ...) 2590e7810bdSflorian __attribute__((__format__ (printf, 1, 2))); 2600e7810bdSflorian void syslog_vstrerror(int, int, const char *, va_list) 2610e7810bdSflorian __attribute__((__format__ (printf, 3, 0))); 2628871c557Sdlg 2638871c557Sdlg const struct loggers syslogger = { 2648871c557Sdlg syslog_err, 2658871c557Sdlg syslog_errx, 2668871c557Sdlg syslog_warn, 2678871c557Sdlg syslog_warnx, 2688871c557Sdlg syslog_info, 2690e7810bdSflorian syslog_debug 2708871c557Sdlg }; 2718871c557Sdlg 2728871c557Sdlg const struct loggers *logger = &conslogger; 2738871c557Sdlg 2748871c557Sdlg #define lerr(_e, _f...) logger->err((_e), _f) 2758871c557Sdlg #define lerrx(_e, _f...) logger->errx((_e), _f) 2768871c557Sdlg #define lwarn(_f...) logger->warn(_f) 2778871c557Sdlg #define lwarnx(_f...) logger->warnx(_f) 2788871c557Sdlg #define linfo(_f...) logger->info(_f) 2790e7810bdSflorian #define ldebug(_f...) logger->debug(_f) 2808871c557Sdlg 2818871c557Sdlg __dead void 2828871c557Sdlg usage(void) 2838871c557Sdlg { 2848871c557Sdlg extern char *__progname; 285a5c6d06aSkn fprintf(stderr, "usage: %s [-46cdivw] [-l address] [-p port] [-r socket]" 2868871c557Sdlg " directory\n", __progname); 2878871c557Sdlg exit(1); 2888871c557Sdlg } 2898871c557Sdlg 2908871c557Sdlg int cancreate = 0; 291a5c6d06aSkn int canwrite = 0; 2928871c557Sdlg int verbose = 0; 2930e7810bdSflorian int debug = 0; 294f923cd8eSjca int iflag = 0; 2958871c557Sdlg 2968871c557Sdlg int 2978871c557Sdlg main(int argc, char *argv[]) 2988871c557Sdlg { 2998871c557Sdlg extern char *__progname; 3008871c557Sdlg 301769bdeccSdlg int c; 3028871c557Sdlg struct passwd *pw; 3038871c557Sdlg 3048871c557Sdlg char *dir = NULL; 3058871c557Sdlg char *rewrite = NULL; 3068871c557Sdlg 3078871c557Sdlg char *addr = NULL; 3088871c557Sdlg char *port = "tftp"; 3098871c557Sdlg int family = AF_UNSPEC; 310eaf6a821Sjca int devnull = -1; 3118871c557Sdlg 312a5c6d06aSkn while ((c = getopt(argc, argv, "46cdil:p:r:vw")) != -1) { 3138871c557Sdlg switch (c) { 3148871c557Sdlg case '4': 3158871c557Sdlg family = AF_INET; 3168871c557Sdlg break; 3178871c557Sdlg case '6': 3188871c557Sdlg family = AF_INET6; 3198871c557Sdlg break; 3208871c557Sdlg case 'c': 321a5c6d06aSkn canwrite = cancreate = 1; 3228871c557Sdlg break; 3238871c557Sdlg case 'd': 3248871c557Sdlg verbose = debug = 1; 3258871c557Sdlg break; 326f923cd8eSjca case 'i': 327f923cd8eSjca if (rewrite != NULL) 328f923cd8eSjca errx(1, "options -i and -r are incompatible"); 329f923cd8eSjca iflag = 1; 330f923cd8eSjca break; 3318871c557Sdlg case 'l': 3328871c557Sdlg addr = optarg; 3338871c557Sdlg break; 3348871c557Sdlg case 'p': 3358871c557Sdlg port = optarg; 3368871c557Sdlg break; 3378871c557Sdlg case 'r': 338f923cd8eSjca if (iflag == 1) 339f923cd8eSjca errx(1, "options -i and -r are incompatible"); 3408871c557Sdlg rewrite = optarg; 3418871c557Sdlg break; 3428871c557Sdlg case 'v': 3438871c557Sdlg verbose = 1; 3448871c557Sdlg break; 345a5c6d06aSkn case 'w': 346a5c6d06aSkn canwrite = 1; 347a5c6d06aSkn break; 3488871c557Sdlg default: 3498871c557Sdlg usage(); 3508871c557Sdlg /* NOTREACHED */ 3518871c557Sdlg } 3528871c557Sdlg } 3538871c557Sdlg 3548871c557Sdlg argc -= optind; 3558871c557Sdlg argv += optind; 3568871c557Sdlg 3578871c557Sdlg if (argc != 1) 3588871c557Sdlg usage(); 3598871c557Sdlg 3608871c557Sdlg dir = argv[0]; 3618871c557Sdlg 3628871c557Sdlg if (geteuid() != 0) 3638871c557Sdlg errx(1, "need root privileges"); 3648871c557Sdlg 3658871c557Sdlg pw = getpwnam("_tftpd"); 3668871c557Sdlg if (pw == NULL) 3670146b59bSdlg errx(1, "no _tftpd user"); 3688871c557Sdlg 3698871c557Sdlg if (!debug) { 3708871c557Sdlg openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); 3718871c557Sdlg tzset(); 3728871c557Sdlg logger = &syslogger; 373b7041c07Sderaadt devnull = open(_PATH_DEVNULL, O_RDWR); 374eaf6a821Sjca if (devnull == -1) 375eaf6a821Sjca err(1, "open %s", _PATH_DEVNULL); 3768871c557Sdlg } 3778871c557Sdlg 3788871c557Sdlg if (rewrite != NULL) 3798871c557Sdlg rewrite_connect(rewrite); 3808871c557Sdlg 3818871c557Sdlg tftpd_listen(addr, port, family); 3828871c557Sdlg 3838871c557Sdlg if (chroot(dir)) 3848871c557Sdlg err(1, "chroot %s", dir); 3858871c557Sdlg if (chdir("/")) 3868871c557Sdlg err(1, "chdir %s", dir); 3878871c557Sdlg 3888871c557Sdlg /* drop privs */ 3898871c557Sdlg if (setgroups(1, &pw->pw_gid) || 3908871c557Sdlg setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 3918871c557Sdlg setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 3928871c557Sdlg errx(1, "can't drop privileges"); 3938871c557Sdlg 394eaf6a821Sjca if (!debug && rdaemon(devnull) == -1) 395eaf6a821Sjca err(1, "unable to daemonize"); 396eaf6a821Sjca 397704f9ac0Skn if (cancreate) { 3983b6a0738Sderaadt if (pledge("stdio rpath wpath cpath fattr dns inet", NULL) == -1) 399eaf6a821Sjca lerr(1, "pledge"); 400a5c6d06aSkn } else if (canwrite) { 401ad0375c2Skn if (pledge("stdio rpath wpath dns inet", NULL) == -1) 402704f9ac0Skn lerr(1, "pledge"); 403a5c6d06aSkn } else { 404a5c6d06aSkn if (pledge("stdio rpath dns inet", NULL) == -1) 405a5c6d06aSkn lerr(1, "pledge"); 406704f9ac0Skn } 407a1840542Sderaadt 4088871c557Sdlg event_init(); 4098871c557Sdlg 4108871c557Sdlg if (rewrite != NULL) 4118871c557Sdlg rewrite_events(); 4128871c557Sdlg 4138871c557Sdlg tftpd_events(); 4148871c557Sdlg 4158871c557Sdlg event_dispatch(); 4168871c557Sdlg 4178871c557Sdlg exit(0); 4188871c557Sdlg } 4198871c557Sdlg 4208871c557Sdlg struct rewritemap { 4218871c557Sdlg struct event wrev; 4228871c557Sdlg struct event rdev; 4238871c557Sdlg struct evbuffer *wrbuf; 4248871c557Sdlg struct evbuffer *rdbuf; 4258871c557Sdlg 4268871c557Sdlg TAILQ_HEAD(, tftp_client) clients; 4278871c557Sdlg 4288871c557Sdlg int s; 4298871c557Sdlg }; 4308871c557Sdlg 4318871c557Sdlg struct rewritemap *rwmap = NULL; 4328871c557Sdlg 4338871c557Sdlg void 4348871c557Sdlg rewrite_connect(const char *path) 4358871c557Sdlg { 4368871c557Sdlg int s; 4378871c557Sdlg struct sockaddr_un remote; 4388871c557Sdlg size_t len; 4398871c557Sdlg 4408871c557Sdlg rwmap = malloc(sizeof(*rwmap)); 4418871c557Sdlg if (rwmap == NULL) 4428871c557Sdlg err(1, "rewrite event malloc"); 4438871c557Sdlg 4448871c557Sdlg rwmap->wrbuf = evbuffer_new(); 4458871c557Sdlg if (rwmap->wrbuf == NULL) 4468871c557Sdlg err(1, "rewrite wrbuf"); 4478871c557Sdlg 4488871c557Sdlg rwmap->rdbuf = evbuffer_new(); 4498871c557Sdlg if (rwmap->rdbuf == NULL) 4508871c557Sdlg err(1, "rewrite rdbuf"); 4518871c557Sdlg 4528871c557Sdlg TAILQ_INIT(&rwmap->clients); 4538871c557Sdlg 454e22aa732Sdlg s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); 4558871c557Sdlg if (s == -1) 4568871c557Sdlg err(1, "rewrite socket"); 4578871c557Sdlg 4588871c557Sdlg remote.sun_family = AF_UNIX; 4598871c557Sdlg len = strlcpy(remote.sun_path, path, sizeof(remote.sun_path)); 4608871c557Sdlg if (len >= sizeof(remote.sun_path)) 4618871c557Sdlg errx(1, "rewrite socket path is too long"); 4628871c557Sdlg 4638871c557Sdlg len += sizeof(remote.sun_family) + 1; 4648871c557Sdlg if (connect(s, (struct sockaddr *)&remote, len) == -1) 4658871c557Sdlg err(1, "%s", path); 4668871c557Sdlg 4678871c557Sdlg rwmap->s = s; 4688871c557Sdlg } 4698871c557Sdlg 4708871c557Sdlg void 4718871c557Sdlg rewrite_events(void) 4728871c557Sdlg { 4738871c557Sdlg event_set(&rwmap->wrev, rwmap->s, EV_WRITE, rewrite_req, NULL); 4748871c557Sdlg event_set(&rwmap->rdev, rwmap->s, EV_READ | EV_PERSIST, rewrite_res, NULL); 4758871c557Sdlg event_add(&rwmap->rdev, NULL); 4768871c557Sdlg } 4778871c557Sdlg 4788871c557Sdlg void 4798871c557Sdlg rewrite_map(struct tftp_client *client, const char *filename) 4808871c557Sdlg { 4810d71831bSdlg char *nicebuf; 4828871c557Sdlg 4830d71831bSdlg if (stravis(&nicebuf, filename, VIS_SAFE|VIS_OCTAL) == -1) 4840d71831bSdlg lerr(1, "rwmap stravis"); 4858871c557Sdlg 4868871c557Sdlg if (evbuffer_add_printf(rwmap->wrbuf, "%s %s %s\n", getip(&client->ss), 4878871c557Sdlg client->opcode == WRQ ? "write" : "read", nicebuf) == -1) 4888871c557Sdlg lerr(1, "rwmap printf"); 4898871c557Sdlg 4900d71831bSdlg free(nicebuf); 4910d71831bSdlg 4928871c557Sdlg TAILQ_INSERT_TAIL(&rwmap->clients, client, entry); 4938871c557Sdlg 4948871c557Sdlg event_add(&rwmap->wrev, NULL); 4958871c557Sdlg } 4968871c557Sdlg 4978871c557Sdlg void 4988871c557Sdlg rewrite_req(int fd, short events, void *arg) 4998871c557Sdlg { 5004f801cb6Sdlg if (evbuffer_write(rwmap->wrbuf, fd) == -1) { 5014f801cb6Sdlg switch (errno) { 5024f801cb6Sdlg case EINTR: 5034f801cb6Sdlg case EAGAIN: 5044f801cb6Sdlg event_add(&rwmap->wrev, NULL); 5054f801cb6Sdlg return; 5064f801cb6Sdlg } 5074f801cb6Sdlg 5084f801cb6Sdlg lerr(1, "rewrite socket write"); 5094f801cb6Sdlg } 5108871c557Sdlg 5118871c557Sdlg if (EVBUFFER_LENGTH(rwmap->wrbuf)) 5128871c557Sdlg event_add(&rwmap->wrev, NULL); 5138871c557Sdlg } 5148871c557Sdlg 5158871c557Sdlg void 5168871c557Sdlg rewrite_res(int fd, short events, void *arg) 5178871c557Sdlg { 5188871c557Sdlg struct tftp_client *client; 5198871c557Sdlg char *filename; 5208871c557Sdlg size_t len; 5218871c557Sdlg 522b9fc9a72Sderaadt switch (evbuffer_read(rwmap->rdbuf, fd, PATH_MAX)) { 5234f801cb6Sdlg case -1: 5244f801cb6Sdlg switch (errno) { 5254f801cb6Sdlg case EINTR: 5264f801cb6Sdlg case EAGAIN: 5274f801cb6Sdlg return; 5284f801cb6Sdlg } 5294f801cb6Sdlg lerr(1, "rewrite socket read"); 5304f801cb6Sdlg case 0: 5314f801cb6Sdlg lerrx(1, "rewrite socket closed"); 5324f801cb6Sdlg default: 5334f801cb6Sdlg break; 5344f801cb6Sdlg } 5358871c557Sdlg 536769bdeccSdlg while ((filename = evbuffer_readln(rwmap->rdbuf, &len, 537769bdeccSdlg EVBUFFER_EOL_LF)) != NULL) { 5388871c557Sdlg client = TAILQ_FIRST(&rwmap->clients); 5398871c557Sdlg if (client == NULL) 5408871c557Sdlg lerrx(1, "unexpected rwmap reply"); 5418871c557Sdlg 5428871c557Sdlg TAILQ_REMOVE(&rwmap->clients, client, entry); 5438871c557Sdlg 5448871c557Sdlg tftp_open(client, filename); 5458871c557Sdlg 5468871c557Sdlg free(filename); 547*479c151dSjsg } 5488871c557Sdlg } 5498871c557Sdlg 5508871c557Sdlg int 5518871c557Sdlg tftpd_listen(const char *addr, const char *port, int family) 5528871c557Sdlg { 5538871c557Sdlg struct tftp_server *server; 5548871c557Sdlg 5558871c557Sdlg struct addrinfo hints, *res, *res0; 5568871c557Sdlg int error; 5578871c557Sdlg int s; 5588871c557Sdlg 559aee3563dSdlg int cerrno = EADDRNOTAVAIL; 560aee3563dSdlg const char *cause = "getaddrinfo"; 5618871c557Sdlg 562ced7784eSdlg int on = 1; 563ced7784eSdlg 5648871c557Sdlg memset(&hints, 0, sizeof(hints)); 5658871c557Sdlg hints.ai_family = family; 5668871c557Sdlg hints.ai_socktype = SOCK_DGRAM; 5678871c557Sdlg hints.ai_flags = AI_PASSIVE; 5688871c557Sdlg 5698871c557Sdlg TAILQ_INIT(&tftp_servers); 5708871c557Sdlg 5718871c557Sdlg error = getaddrinfo(addr, port, &hints, &res0); 5728871c557Sdlg if (error) { 5738871c557Sdlg errx(1, "%s:%s: %s", addr ? addr : "*", port, 5748871c557Sdlg gai_strerror(error)); 5758871c557Sdlg } 5768871c557Sdlg 5778871c557Sdlg for (res = res0; res != NULL; res = res->ai_next) { 578e22aa732Sdlg s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, 579e22aa732Sdlg res->ai_protocol); 5808871c557Sdlg if (s == -1) { 5818871c557Sdlg cause = "socket"; 582aee3563dSdlg cerrno = errno; 5838871c557Sdlg continue; 5848871c557Sdlg } 5858871c557Sdlg 5868871c557Sdlg if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { 5878871c557Sdlg cause = "bind"; 588aee3563dSdlg cerrno = errno; 5898871c557Sdlg close(s); 5908871c557Sdlg continue; 5918871c557Sdlg } 5928871c557Sdlg 5938871c557Sdlg switch (res->ai_family) { 5948871c557Sdlg case AF_INET: 5958871c557Sdlg if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, 5968871c557Sdlg &on, sizeof(on)) == -1) 597bca13417Sdlg err(1, "setsockopt(IP_RECVDSTADDR)"); 5988871c557Sdlg break; 5998871c557Sdlg case AF_INET6: 6008871c557Sdlg if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, 6018871c557Sdlg &on, sizeof(on)) == -1) 602bca13417Sdlg err(1, "setsockopt(IPV6_RECVPKTINFO)"); 6038871c557Sdlg break; 6048871c557Sdlg } 6058871c557Sdlg 6068871c557Sdlg server = malloc(sizeof(*server)); 6078871c557Sdlg if (server == NULL) 6088871c557Sdlg err(1, "malloc"); 6098871c557Sdlg 6108871c557Sdlg server->s = s; 6118871c557Sdlg TAILQ_INSERT_TAIL(&tftp_servers, server, entry); 6128871c557Sdlg } 6138871c557Sdlg 6148871c557Sdlg if (TAILQ_EMPTY(&tftp_servers)) 615aee3563dSdlg errc(1, cerrno, "%s", cause); 6168871c557Sdlg 61700d70eefStobias freeaddrinfo(res0); 6188871c557Sdlg return (0); 6198871c557Sdlg } 6208871c557Sdlg 6218871c557Sdlg void 6228871c557Sdlg tftpd_events(void) 6238871c557Sdlg { 6248871c557Sdlg struct tftp_server *server; 6258871c557Sdlg TAILQ_FOREACH(server, &tftp_servers, entry) { 6268871c557Sdlg event_set(&server->ev, server->s, EV_READ | EV_PERSIST, 6278871c557Sdlg tftpd_recv, server); 6288871c557Sdlg event_add(&server->ev, NULL); 6298871c557Sdlg } 6308871c557Sdlg } 6318871c557Sdlg 6328871c557Sdlg struct tftp_client * 633d2c62849Sderaadt client_alloc(void) 6348871c557Sdlg { 6358871c557Sdlg struct tftp_client *client; 6368871c557Sdlg 637f4e49ee5Sdlg client = calloc(1, sizeof(*client)); 6388871c557Sdlg if (client == NULL) 6398871c557Sdlg return (NULL); 6408871c557Sdlg 6418871c557Sdlg client->segment_size = SEGSIZE; 6428871c557Sdlg client->packet_size = SEGSIZE + 4; 6438871c557Sdlg 6448871c557Sdlg client->tv.tv_sec = TIMEOUT; 6458871c557Sdlg client->tv.tv_usec = 0; 6468871c557Sdlg 6478871c557Sdlg client->sock = -1; 6488871c557Sdlg client->file = NULL; 6498871c557Sdlg client->newline = 0; 6508871c557Sdlg 6518871c557Sdlg return (client); 6528871c557Sdlg } 6538871c557Sdlg 6548871c557Sdlg void 6558871c557Sdlg client_free(struct tftp_client *client) 6568871c557Sdlg { 6578871c557Sdlg free(client->options); 6588871c557Sdlg 6598871c557Sdlg if (client->file != NULL) 6608871c557Sdlg fclose(client->file); 6618871c557Sdlg 6628871c557Sdlg close(client->sock); 6638871c557Sdlg 6648871c557Sdlg free(client); 6658871c557Sdlg } 6668871c557Sdlg 6678871c557Sdlg void 6688871c557Sdlg tftpd_recv(int fd, short events, void *arg) 6698871c557Sdlg { 6708871c557Sdlg union { 6718871c557Sdlg struct cmsghdr hdr; 6728871c557Sdlg char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))]; 6738871c557Sdlg } cmsgbuf; 6748871c557Sdlg struct cmsghdr *cmsg; 6758871c557Sdlg struct msghdr msg; 6768871c557Sdlg struct iovec iov; 6778871c557Sdlg 6788871c557Sdlg ssize_t n; 6798871c557Sdlg struct sockaddr_storage s_in; 6808871c557Sdlg int dobind = 1; 6818871c557Sdlg int on = 1; 6828871c557Sdlg 6838871c557Sdlg struct tftphdr *tp; 6848871c557Sdlg 6858871c557Sdlg struct tftp_client *client; 6868871c557Sdlg 6878871c557Sdlg client = client_alloc(); 6888871c557Sdlg if (client == NULL) { 6890b75d96cStedu char buf[SEGSIZE_MAX + 4]; 6908871c557Sdlg /* no memory! flush this request... */ 6918871c557Sdlg recv(fd, buf, SEGSIZE_MAX + 4, 0); 6928871c557Sdlg /* dont care if it fails */ 6938871c557Sdlg return; 6948871c557Sdlg } 6958871c557Sdlg 6968871c557Sdlg bzero(&msg, sizeof(msg)); 6978871c557Sdlg iov.iov_base = client->buf; 6988871c557Sdlg iov.iov_len = client->packet_size; 6998871c557Sdlg msg.msg_name = &client->ss; 7008871c557Sdlg msg.msg_namelen = sizeof(client->ss); 7018871c557Sdlg msg.msg_iov = &iov; 7028871c557Sdlg msg.msg_iovlen = 1; 7038871c557Sdlg msg.msg_control = &cmsgbuf.buf; 7048871c557Sdlg msg.msg_controllen = sizeof(cmsgbuf.buf); 7058871c557Sdlg 7068871c557Sdlg n = recvmsg(fd, &msg, 0); 7078871c557Sdlg if (n == -1) { 7088871c557Sdlg lwarn("recvmsg"); 7098871c557Sdlg goto err; 7108871c557Sdlg } 7118871c557Sdlg if (n < 4) 7128871c557Sdlg goto err; 7138871c557Sdlg 714e22aa732Sdlg client->sock = socket(client->ss.ss_family, 715e22aa732Sdlg SOCK_DGRAM | SOCK_NONBLOCK, 0); 7168871c557Sdlg if (client->sock == -1) { 7178871c557Sdlg lwarn("socket"); 7188871c557Sdlg goto err; 7198871c557Sdlg } 7208871c557Sdlg memset(&s_in, 0, sizeof(s_in)); 7218871c557Sdlg s_in.ss_family = client->ss.ss_family; 7228871c557Sdlg s_in.ss_len = client->ss.ss_len; 7238871c557Sdlg 7248871c557Sdlg /* get local address if possible */ 7258871c557Sdlg for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 7268871c557Sdlg cmsg = CMSG_NXTHDR(&msg, cmsg)) { 7278871c557Sdlg if (cmsg->cmsg_level == IPPROTO_IP && 7288871c557Sdlg cmsg->cmsg_type == IP_RECVDSTADDR) { 7298871c557Sdlg memcpy(&((struct sockaddr_in *)&s_in)->sin_addr, 7308871c557Sdlg CMSG_DATA(cmsg), sizeof(struct in_addr)); 7318871c557Sdlg if (((struct sockaddr_in *)&s_in)->sin_addr.s_addr == 7328871c557Sdlg INADDR_BROADCAST) 7338871c557Sdlg dobind = 0; 7348871c557Sdlg break; 7358871c557Sdlg } 7368871c557Sdlg if (cmsg->cmsg_level == IPPROTO_IPV6 && 7378871c557Sdlg cmsg->cmsg_type == IPV6_PKTINFO) { 7388871c557Sdlg struct in6_pktinfo *ipi; 7398871c557Sdlg 7408871c557Sdlg ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); 7418871c557Sdlg memcpy(&((struct sockaddr_in6 *)&s_in)->sin6_addr, 7428871c557Sdlg &ipi->ipi6_addr, sizeof(struct in6_addr)); 7438871c557Sdlg if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr)) 7448871c557Sdlg ((struct sockaddr_in6 *)&s_in)->sin6_scope_id = 7458871c557Sdlg ipi->ipi6_ifindex; 7468871c557Sdlg break; 7478871c557Sdlg } 7488871c557Sdlg } 7498871c557Sdlg 7508871c557Sdlg if (dobind) { 7518871c557Sdlg setsockopt(client->sock, SOL_SOCKET, SO_REUSEADDR, 7528871c557Sdlg &on, sizeof(on)); 7538871c557Sdlg setsockopt(client->sock, SOL_SOCKET, SO_REUSEPORT, 7548871c557Sdlg &on, sizeof(on)); 7558871c557Sdlg 7568871c557Sdlg if (bind(client->sock, (struct sockaddr *)&s_in, 757df69c215Sderaadt s_in.ss_len) == -1) { 7588871c557Sdlg lwarn("bind to %s", getip(&s_in)); 7598871c557Sdlg goto err; 7608871c557Sdlg } 7618871c557Sdlg } 7628871c557Sdlg if (connect(client->sock, (struct sockaddr *)&client->ss, 7638871c557Sdlg client->ss.ss_len) == -1) { 7648871c557Sdlg lwarn("connect to %s", getip(&client->ss)); 7658871c557Sdlg goto err; 7668871c557Sdlg } 7678871c557Sdlg 7688871c557Sdlg tp = (struct tftphdr *)client->buf; 7698871c557Sdlg client->opcode = ntohs(tp->th_opcode); 7708871c557Sdlg if (client->opcode != RRQ && client->opcode != WRQ) { 7718871c557Sdlg /* bad request */ 7728871c557Sdlg goto err; 7738871c557Sdlg } 7748871c557Sdlg 7758871c557Sdlg tftp(client, tp, n); 7768871c557Sdlg 7778871c557Sdlg return; 7788871c557Sdlg 7798871c557Sdlg err: 7808871c557Sdlg client_free(client); 7818871c557Sdlg } 7828871c557Sdlg 7838871c557Sdlg int 7848871c557Sdlg parse_options(struct tftp_client *client, char *cp, size_t size, 7858871c557Sdlg struct opt_client *options) 7868871c557Sdlg { 7878871c557Sdlg char *option; 7888871c557Sdlg char *ccp; 7898871c557Sdlg int has_options = 0; 7908871c557Sdlg int i; 7918871c557Sdlg 7928871c557Sdlg while (++cp < client->buf + size) { 7938871c557Sdlg for (i = 2, ccp = cp; i > 0; ccp++) { 7948871c557Sdlg if (ccp >= client->buf + size) { 7958871c557Sdlg /* 7968871c557Sdlg * Don't reject the request, just stop trying 7978871c557Sdlg * to parse the option and get on with it. 7988871c557Sdlg * Some Apple OpenFirmware versions have 7998871c557Sdlg * trailing garbage on the end of otherwise 8008871c557Sdlg * valid requests. 8018871c557Sdlg */ 8028871c557Sdlg return (has_options); 8038871c557Sdlg } else if (*ccp == '\0') 8048871c557Sdlg i--; 8058871c557Sdlg } 8068871c557Sdlg 8078871c557Sdlg for (option = cp; *cp; cp++) 8088d9633d1Sderaadt *cp = tolower((unsigned char)*cp); 8098871c557Sdlg 8108871c557Sdlg for (i = 0; i < NOPT; i++) { 8118871c557Sdlg if (strcmp(option, opt_names[i]) == 0) { 8128871c557Sdlg options[i].o_request = ++cp; 8138871c557Sdlg has_options = 1; 8148871c557Sdlg } 8158871c557Sdlg } 8168871c557Sdlg cp = ccp - 1; 8178871c557Sdlg } 8188871c557Sdlg 8198871c557Sdlg return (has_options); 8208871c557Sdlg } 8218871c557Sdlg 8228871c557Sdlg /* 8238871c557Sdlg * Handle initial connection protocol. 8248871c557Sdlg */ 8258871c557Sdlg void 8268871c557Sdlg tftp(struct tftp_client *client, struct tftphdr *tp, size_t size) 8278871c557Sdlg { 8288871c557Sdlg struct opt_client *options; 8298871c557Sdlg 8308871c557Sdlg char *cp; 8318871c557Sdlg int i, first = 1, ecode, to; 8328871c557Sdlg struct formats *pf; 833769bdeccSdlg char *mode = NULL; 834b9fc9a72Sderaadt char filename[PATH_MAX]; 8358871c557Sdlg const char *errstr; 8368871c557Sdlg 8378871c557Sdlg if (size < 5) { 8388871c557Sdlg ecode = EBADOP; 8398871c557Sdlg goto error; 8408871c557Sdlg } 8418871c557Sdlg 8428871c557Sdlg cp = tp->th_stuff; 8438871c557Sdlg again: 8448871c557Sdlg while (cp < client->buf + size) { 8458871c557Sdlg if (*cp == '\0') 8468871c557Sdlg break; 8478871c557Sdlg cp++; 8488871c557Sdlg } 8498871c557Sdlg if (*cp != '\0') { 8508871c557Sdlg ecode = EBADOP; 8518871c557Sdlg goto error; 8528871c557Sdlg } 8538871c557Sdlg i = cp - tp->th_stuff; 8548871c557Sdlg if (i >= sizeof(filename)) { 8558871c557Sdlg ecode = EBADOP; 8568871c557Sdlg goto error; 8578871c557Sdlg } 8588871c557Sdlg memcpy(filename, tp->th_stuff, i); 8598871c557Sdlg filename[i] = '\0'; 8608871c557Sdlg if (first) { 8618871c557Sdlg mode = ++cp; 8628871c557Sdlg first = 0; 8638871c557Sdlg goto again; 8648871c557Sdlg } 8658871c557Sdlg for (cp = mode; *cp; cp++) 8668d9633d1Sderaadt *cp = tolower((unsigned char)*cp); 8678871c557Sdlg 8688871c557Sdlg for (pf = formats; pf->f_mode; pf++) { 8698871c557Sdlg if (strcmp(pf->f_mode, mode) == 0) 8708871c557Sdlg break; 8718871c557Sdlg } 8728871c557Sdlg if (pf->f_mode == 0) { 8738871c557Sdlg ecode = EBADOP; 8748871c557Sdlg goto error; 8758871c557Sdlg } 8768871c557Sdlg client->fgetc = pf->f_getc; 8778871c557Sdlg client->fputc = pf->f_putc; 8788871c557Sdlg 879f4e49ee5Sdlg client->options = options = calloc(NOPT, sizeof(*client->options)); 8808871c557Sdlg if (options == NULL) { 8818871c557Sdlg ecode = 100 + ENOMEM; 8828871c557Sdlg goto error; 8838871c557Sdlg } 8848871c557Sdlg 8858871c557Sdlg if (parse_options(client, cp, size, options)) { 8868871c557Sdlg if (options[OPT_TIMEOUT].o_request != NULL) { 8878871c557Sdlg to = strtonum(options[OPT_TIMEOUT].o_request, 8888871c557Sdlg TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 8898871c557Sdlg if (errstr) { 8908871c557Sdlg ecode = EBADOP; 8918871c557Sdlg goto error; 8928871c557Sdlg } 8938871c557Sdlg options[OPT_TIMEOUT].o_reply = client->tv.tv_sec = to; 8948871c557Sdlg } 8958871c557Sdlg 8968871c557Sdlg if (options[OPT_BLKSIZE].o_request) { 8978871c557Sdlg client->segment_size = strtonum( 8988871c557Sdlg options[OPT_BLKSIZE].o_request, 8998871c557Sdlg SEGSIZE_MIN, SEGSIZE_MAX, &errstr); 9008871c557Sdlg if (errstr) { 9018871c557Sdlg ecode = EBADOP; 9028871c557Sdlg goto error; 9038871c557Sdlg } 9048871c557Sdlg client->packet_size = client->segment_size + 4; 9058871c557Sdlg options[OPT_BLKSIZE].o_reply = client->segment_size; 9068871c557Sdlg } 9078871c557Sdlg } else { 9088871c557Sdlg free(options); 9098871c557Sdlg client->options = NULL; 9108871c557Sdlg } 9118871c557Sdlg 9128871c557Sdlg if (verbose) { 913b9fc9a72Sderaadt char nicebuf[PATH_MAX]; 9148871c557Sdlg 915b9fc9a72Sderaadt (void)strnvis(nicebuf, filename, PATH_MAX, 9168871c557Sdlg VIS_SAFE|VIS_OCTAL); 9178871c557Sdlg 9188871c557Sdlg linfo("%s: %s request for '%s'", getip(&client->ss), 9198871c557Sdlg client->opcode == WRQ ? "write" : "read", nicebuf); 9208871c557Sdlg } 9218871c557Sdlg 9228871c557Sdlg if (rwmap != NULL) 9238871c557Sdlg rewrite_map(client, filename); 9248871c557Sdlg else 9258871c557Sdlg tftp_open(client, filename); 9268871c557Sdlg 9278871c557Sdlg return; 9288871c557Sdlg 9298871c557Sdlg error: 9308871c557Sdlg nak(client, ecode); 9318871c557Sdlg } 9328871c557Sdlg 9338871c557Sdlg void 9348871c557Sdlg tftp_open(struct tftp_client *client, const char *filename) 9358871c557Sdlg { 9368871c557Sdlg int ecode; 9378871c557Sdlg 9388871c557Sdlg ecode = validate_access(client, filename); 939ba119d57Sdlg if (ecode) 940ba119d57Sdlg goto error; 9418871c557Sdlg 9428871c557Sdlg if (client->options) { 943eca1b59cSdlg if (oack(client) == -1) 944ba119d57Sdlg goto error; 9458871c557Sdlg 9468871c557Sdlg free(client->options); 9478871c557Sdlg client->options = NULL; 9488871c557Sdlg } else if (client->opcode == WRQ) { 9498871c557Sdlg recvfile(client); 9508871c557Sdlg } else 9518871c557Sdlg sendfile(client); 9528871c557Sdlg 9538871c557Sdlg return; 954ba119d57Sdlg error: 955ba119d57Sdlg nak(client, ecode); 9568871c557Sdlg } 9578871c557Sdlg 9588871c557Sdlg /* 9598871c557Sdlg * Validate file access. Since we 9608871c557Sdlg * have no uid or gid, for now require 9618871c557Sdlg * file to exist and be publicly 9628871c557Sdlg * readable/writable. 9638871c557Sdlg * If we were invoked with arguments 9648871c557Sdlg * from inetd then the file must also be 9658871c557Sdlg * in one of the given directory prefixes. 9668871c557Sdlg * Note also, full path name must be 9678871c557Sdlg * given as we have no login directory. 9688871c557Sdlg */ 9698871c557Sdlg int 970f923cd8eSjca validate_access(struct tftp_client *client, const char *requested) 9718871c557Sdlg { 9728871c557Sdlg int mode = client->opcode; 9738871c557Sdlg struct opt_client *options = client->options; 9748871c557Sdlg struct stat stbuf; 975769bdeccSdlg int fd, wmode; 976f923cd8eSjca const char *errstr, *filename; 977f923cd8eSjca char rewritten[PATH_MAX]; 9788871c557Sdlg 979a5c6d06aSkn if (!canwrite && mode != RRQ) 980a5c6d06aSkn return (EACCESS); 981a5c6d06aSkn 982f923cd8eSjca if (strcmp(requested, SEEDPATH) == 0) { 9836fbffbdaSdlg char *buf; 9844a5fd927Smcbride if (mode != RRQ) 9854a5fd927Smcbride return (EACCESS); 9866fbffbdaSdlg 9876fbffbdaSdlg buf = client->buf + sizeof(client->buf) - 512; 9886fbffbdaSdlg arc4random_buf(buf, 512); 9890c1f8293Snaddy if (options != NULL && options[OPT_TSIZE].o_request) 9900c1f8293Snaddy options[OPT_TSIZE].o_reply = 512; 9916fbffbdaSdlg client->file = fmemopen(buf, 512, "r"); 9926fbffbdaSdlg if (client->file == NULL) 9936fbffbdaSdlg return (errno + 100); 9946fbffbdaSdlg 9954a5fd927Smcbride return (0); 9964a5fd927Smcbride } 9974a5fd927Smcbride 998f923cd8eSjca if (iflag) { 999f923cd8eSjca int ret; 1000f923cd8eSjca 1001f923cd8eSjca /* 1002f923cd8eSjca * In -i mode, look in the directory named after the 1003f923cd8eSjca * client address. 1004f923cd8eSjca */ 1005f923cd8eSjca ret = snprintf(rewritten, sizeof(rewritten), "%s/%s", 1006f923cd8eSjca getip(&client->ss), requested); 1007515e489cSderaadt if (ret < 0 || ret >= sizeof(rewritten)) 1008f923cd8eSjca return (ENAMETOOLONG + 100); 1009f923cd8eSjca filename = rewritten; 1010f923cd8eSjca } else { 1011f923cd8eSjca retryread: 1012f923cd8eSjca filename = requested; 1013f923cd8eSjca } 1014f923cd8eSjca 10158871c557Sdlg /* 10168871c557Sdlg * We use a different permissions scheme if `cancreate' is 10178871c557Sdlg * set. 10188871c557Sdlg */ 10198871c557Sdlg wmode = O_TRUNC; 1020df69c215Sderaadt if (stat(filename, &stbuf) == -1) { 1021f923cd8eSjca if (!cancreate) { 1022f923cd8eSjca /* 1023f923cd8eSjca * In -i mode, retry failed read requests from 1024f923cd8eSjca * the root directory. 1025f923cd8eSjca */ 1026f923cd8eSjca if (mode == RRQ && errno == ENOENT && 1027f923cd8eSjca filename == rewritten) 1028f923cd8eSjca goto retryread; 10298871c557Sdlg return (errno == ENOENT ? ENOTFOUND : EACCESS); 1030f923cd8eSjca } else { 10318871c557Sdlg if ((errno == ENOENT) && (mode != RRQ)) 10328871c557Sdlg wmode |= O_CREAT; 10338871c557Sdlg else 10348871c557Sdlg return (EACCESS); 10358871c557Sdlg } 10368871c557Sdlg } else { 10378871c557Sdlg if (mode == RRQ) { 1038d17353c4Sderaadt if ((stbuf.st_mode & (S_IRUSR >> 6)) == 0) 10398871c557Sdlg return (EACCESS); 10408871c557Sdlg } else { 1041d17353c4Sderaadt if ((stbuf.st_mode & (S_IWUSR >> 6)) == 0) 10428871c557Sdlg return (EACCESS); 10438871c557Sdlg } 10448871c557Sdlg } 10458871c557Sdlg 10468871c557Sdlg if (options != NULL && options[OPT_TSIZE].o_request) { 10478871c557Sdlg if (mode == RRQ) 10488871c557Sdlg options[OPT_TSIZE].o_reply = stbuf.st_size; 10498871c557Sdlg else { 10508871c557Sdlg /* allows writes of 65535 blocks * SEGSIZE_MAX bytes */ 10518871c557Sdlg options[OPT_TSIZE].o_reply = 10528871c557Sdlg strtonum(options[OPT_TSIZE].o_request, 10538871c557Sdlg 1, 65535LL * SEGSIZE_MAX, &errstr); 10548871c557Sdlg if (errstr) 10558871c557Sdlg return (EOPTNEG); 10568871c557Sdlg } 10578871c557Sdlg } 10588871c557Sdlg fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666); 1059df69c215Sderaadt if (fd == -1) 10608871c557Sdlg return (errno + 100); 10618871c557Sdlg /* 10628871c557Sdlg * If the file was created, set default permissions. 10638871c557Sdlg */ 1064df69c215Sderaadt if ((wmode & O_CREAT) && fchmod(fd, 0666) == -1) { 10658871c557Sdlg int serrno = errno; 10668871c557Sdlg 10678871c557Sdlg close(fd); 10688871c557Sdlg unlink(filename); 10698871c557Sdlg 10708871c557Sdlg return (serrno + 100); 10718871c557Sdlg } 10728871c557Sdlg client->file = fdopen(fd, mode == RRQ ? "r" : "w"); 10738871c557Sdlg if (client->file == NULL) { 10748871c557Sdlg close(fd); 10758871c557Sdlg return (errno + 100); 10768871c557Sdlg } 10778871c557Sdlg 10788871c557Sdlg return (0); 10798871c557Sdlg } 10808871c557Sdlg 10818871c557Sdlg int 10828871c557Sdlg fget_octet(struct tftp_client *client) 10838871c557Sdlg { 10848871c557Sdlg return (getc(client->file)); 10858871c557Sdlg } 10868871c557Sdlg 10878871c557Sdlg int 10888871c557Sdlg fput_octet(struct tftp_client *client, int c) 10898871c557Sdlg { 10908871c557Sdlg return (putc(c, client->file)); 10918871c557Sdlg } 10928871c557Sdlg 10938871c557Sdlg int 10948871c557Sdlg fget_netascii(struct tftp_client *client) 10958871c557Sdlg { 1096769bdeccSdlg int c = -1; 10978871c557Sdlg 10988871c557Sdlg switch (client->newline) { 10998871c557Sdlg case 0: 11008871c557Sdlg c = getc(client->file); 11018871c557Sdlg if (c == EOF) 11028871c557Sdlg break; 11038871c557Sdlg 11048871c557Sdlg if (c == '\n' || c == '\r') { 11058871c557Sdlg client->newline = c; 11068871c557Sdlg c = '\r'; 11078871c557Sdlg } 11088871c557Sdlg break; 11098871c557Sdlg case '\n': 11108871c557Sdlg client->newline = 0; 11118871c557Sdlg c = '\n'; 11128871c557Sdlg break; 11138871c557Sdlg case '\r': 11148871c557Sdlg client->newline = 0; 11158871c557Sdlg c = '\0'; 11168871c557Sdlg break; 11178871c557Sdlg } 11188871c557Sdlg 11198871c557Sdlg return (c); 11208871c557Sdlg } 11218871c557Sdlg 11228871c557Sdlg int 11238871c557Sdlg fput_netascii(struct tftp_client *client, int c) 11248871c557Sdlg { 11258871c557Sdlg if (client->newline == '\r') { 11268871c557Sdlg client->newline = 0; 11278871c557Sdlg 11288871c557Sdlg if (c == '\0') 11298871c557Sdlg c = '\r'; 11308871c557Sdlg 11318871c557Sdlg } else if (c == '\r') { 11328871c557Sdlg client->newline = c; 11338871c557Sdlg return (c); 11348871c557Sdlg } 11358871c557Sdlg 11368871c557Sdlg return (putc(c, client->file)); 11378871c557Sdlg } 11388871c557Sdlg 11398871c557Sdlg void 11408871c557Sdlg sendfile(struct tftp_client *client) 11418871c557Sdlg { 11428871c557Sdlg event_set(&client->sev, client->sock, EV_READ, tftp_rrq_ack, client); 11438871c557Sdlg client->block = 1; 11448871c557Sdlg 11458871c557Sdlg file_read(client); 11468871c557Sdlg } 11478871c557Sdlg 11488871c557Sdlg void 11498871c557Sdlg file_read(struct tftp_client *client) 11508871c557Sdlg { 11518871c557Sdlg u_int8_t *buf; 11528871c557Sdlg struct tftphdr *dp; 11538871c557Sdlg int i; 11548871c557Sdlg int c; 11558871c557Sdlg 11568871c557Sdlg dp = (struct tftphdr *)client->buf; 11578871c557Sdlg dp->th_opcode = htons((u_short)DATA); 11588871c557Sdlg dp->th_block = htons(client->block); 11598871c557Sdlg buf = (u_int8_t *)dp->th_data; 11608871c557Sdlg 11618871c557Sdlg for (i = 0; i < client->segment_size; i++) { 11628871c557Sdlg c = client->fgetc(client); 11638871c557Sdlg if (c == EOF) { 11648871c557Sdlg if (ferror(client->file)) { 11658871c557Sdlg nak(client, 100 + EIO); 11668871c557Sdlg return; 11678871c557Sdlg } 11688871c557Sdlg 11698871c557Sdlg break; 11708871c557Sdlg } 11718871c557Sdlg buf[i] = c; 11728871c557Sdlg } 11738871c557Sdlg 11748871c557Sdlg client->buflen = i + 4; 11758871c557Sdlg client->retries = RETRIES; 11768871c557Sdlg 117780a17660Sdlg tftp_send(client); 117880a17660Sdlg } 117980a17660Sdlg 118080a17660Sdlg void 118180a17660Sdlg tftp_send(struct tftp_client *client) 118280a17660Sdlg { 11838871c557Sdlg if (send(client->sock, client->buf, client->buflen, 0) == -1) { 11848871c557Sdlg lwarn("send(block)"); 11858871c557Sdlg client_free(client); 11868871c557Sdlg return; 11878871c557Sdlg } 11888871c557Sdlg 11898871c557Sdlg event_add(&client->sev, &client->tv); 11908871c557Sdlg } 11918871c557Sdlg 11928871c557Sdlg void 11938871c557Sdlg tftp_rrq_ack(int fd, short events, void *arg) 11948871c557Sdlg { 11958871c557Sdlg struct tftp_client *client = arg; 11968871c557Sdlg struct tftphdr *ap; /* ack packet */ 11971e449da8Sdlg char rbuf[SEGSIZE_MIN]; 11988871c557Sdlg ssize_t n; 11998871c557Sdlg 12008871c557Sdlg if (events & EV_TIMEOUT) { 12011e449da8Sdlg if (retry(client) == -1) { 12021e449da8Sdlg lwarn("%s: retry", getip(&client->ss)); 12038871c557Sdlg goto done; 12041e449da8Sdlg } 12058871c557Sdlg 12068871c557Sdlg return; 12078871c557Sdlg } 12088871c557Sdlg 12091e449da8Sdlg n = recv(fd, rbuf, sizeof(rbuf), 0); 12108871c557Sdlg if (n == -1) { 12118871c557Sdlg switch (errno) { 12128871c557Sdlg case EINTR: 12138871c557Sdlg case EAGAIN: 12148871c557Sdlg event_add(&client->sev, &client->tv); 12158871c557Sdlg return; 12168871c557Sdlg 12178871c557Sdlg default: 12181e449da8Sdlg lwarn("%s: recv", getip(&client->ss)); 12198871c557Sdlg goto done; 12208871c557Sdlg } 12218871c557Sdlg } 12228871c557Sdlg 12231e449da8Sdlg ap = (struct tftphdr *)rbuf; 12248871c557Sdlg ap->th_opcode = ntohs((u_short)ap->th_opcode); 12258871c557Sdlg ap->th_block = ntohs((u_short)ap->th_block); 12268871c557Sdlg 12271e449da8Sdlg switch (ap->th_opcode) { 12281e449da8Sdlg case ACK: 12291e449da8Sdlg break; 123080a17660Sdlg case ERROR: 123180a17660Sdlg default: /* assume the worst */ 123280a17660Sdlg goto done; 12331e449da8Sdlg } 12341e449da8Sdlg 12351e449da8Sdlg if (ap->th_block != client->block) { 12361e449da8Sdlg if (tftp_flush(client) == -1) { 12371e449da8Sdlg lwarnx("%s: flush", getip(&client->ss)); 12381e449da8Sdlg goto done; 12391e449da8Sdlg } 12401e449da8Sdlg 12411e449da8Sdlg if (ap->th_block != (client->block - 1)) 12421e449da8Sdlg goto done; 12431e449da8Sdlg 124480a17660Sdlg tftp_send(client); 124580a17660Sdlg return; 12461e449da8Sdlg } 12478871c557Sdlg 12488871c557Sdlg if (client->buflen != client->packet_size) { 12498871c557Sdlg /* this was the last packet in the stream */ 12508871c557Sdlg goto done; 12518871c557Sdlg } 12528871c557Sdlg 12538871c557Sdlg client->block++; 12548871c557Sdlg file_read(client); 12558871c557Sdlg return; 12568871c557Sdlg 12578871c557Sdlg done: 12588871c557Sdlg client_free(client); 12598871c557Sdlg } 12608871c557Sdlg 12611e449da8Sdlg int 12621e449da8Sdlg tftp_flush(struct tftp_client *client) 12631e449da8Sdlg { 12641e449da8Sdlg char rbuf[SEGSIZE_MIN]; 12651e449da8Sdlg ssize_t n; 12661e449da8Sdlg 12671e449da8Sdlg for (;;) { 12681e449da8Sdlg n = recv(client->sock, rbuf, sizeof(rbuf), 0); 12691e449da8Sdlg if (n == -1) { 12701e449da8Sdlg switch (errno) { 12711e449da8Sdlg case EAGAIN: 12721e449da8Sdlg return (0); 12731e449da8Sdlg 12741e449da8Sdlg case EINTR: 12751e449da8Sdlg break; 12761e449da8Sdlg 12771e449da8Sdlg default: 12781e449da8Sdlg return (-1); 12791e449da8Sdlg } 12801e449da8Sdlg } 12811e449da8Sdlg } 12821e449da8Sdlg } 12831e449da8Sdlg 12848871c557Sdlg void 12858871c557Sdlg recvfile(struct tftp_client *client) 12868871c557Sdlg { 12878871c557Sdlg event_set(&client->sev, client->sock, EV_READ, tftp_wrq, client); 12888871c557Sdlg tftp_wrq_ack(client); 12898871c557Sdlg } 12908871c557Sdlg 12918871c557Sdlg int 12928871c557Sdlg tftp_wrq_ack_packet(struct tftp_client *client) 12938871c557Sdlg { 12948871c557Sdlg struct tftphdr *ap; /* ack packet */ 12958871c557Sdlg 12968871c557Sdlg ap = (struct tftphdr *)client->buf; 12978871c557Sdlg ap->th_opcode = htons((u_short)ACK); 12988871c557Sdlg ap->th_block = htons(client->block); 12998871c557Sdlg 13008871c557Sdlg client->buflen = 4; 13011e449da8Sdlg client->retries = RETRIES; 13028871c557Sdlg 13038871c557Sdlg return (send(client->sock, client->buf, client->buflen, 0) != 4); 13048871c557Sdlg } 13058871c557Sdlg 13068871c557Sdlg void 13078871c557Sdlg tftp_wrq_ack(struct tftp_client *client) 13088871c557Sdlg { 13098871c557Sdlg if (tftp_wrq_ack_packet(client) != 0) { 13108871c557Sdlg lwarn("tftp wrq ack"); 13118871c557Sdlg client_free(client); 13128871c557Sdlg return; 13138871c557Sdlg } 13148871c557Sdlg 13158871c557Sdlg client->block++; 13168871c557Sdlg event_add(&client->sev, &client->tv); 13178871c557Sdlg } 13188871c557Sdlg 13198871c557Sdlg void 13208871c557Sdlg tftp_wrq(int fd, short events, void *arg) 13218871c557Sdlg { 13221e449da8Sdlg char wbuf[SEGSIZE_MAX + 4]; 13238871c557Sdlg struct tftp_client *client = arg; 13248871c557Sdlg struct tftphdr *dp; 13258871c557Sdlg ssize_t n; 13268871c557Sdlg int i; 13278871c557Sdlg 13288871c557Sdlg if (events & EV_TIMEOUT) { 13291e449da8Sdlg if (retry(client) == -1) { 13301e449da8Sdlg lwarn("%s", getip(&client->ss)); 13318871c557Sdlg goto done; 13321e449da8Sdlg } 13338871c557Sdlg 13348871c557Sdlg return; 13358871c557Sdlg } 13368871c557Sdlg 13371e449da8Sdlg n = recv(fd, wbuf, client->packet_size, 0); 13388871c557Sdlg if (n == -1) { 13398871c557Sdlg switch (errno) { 13408871c557Sdlg case EINTR: 13418871c557Sdlg case EAGAIN: 13428871c557Sdlg goto retry; 13438871c557Sdlg 13448871c557Sdlg default: 13458871c557Sdlg lwarn("tftp_wrq recv"); 13468871c557Sdlg goto done; 13478871c557Sdlg } 13488871c557Sdlg } 13498871c557Sdlg 13508871c557Sdlg if (n < 4) 13518871c557Sdlg goto done; 13528871c557Sdlg 13531e449da8Sdlg dp = (struct tftphdr *)wbuf; 13548871c557Sdlg dp->th_opcode = ntohs((u_short)dp->th_opcode); 13558871c557Sdlg dp->th_block = ntohs((u_short)dp->th_block); 13568871c557Sdlg 13571e449da8Sdlg switch (dp->th_opcode) { 13581e449da8Sdlg case ERROR: 13591e449da8Sdlg goto done; 13601e449da8Sdlg case DATA: 13611e449da8Sdlg break; 13621e449da8Sdlg default: 13631e449da8Sdlg goto retry; 13641e449da8Sdlg } 13651e449da8Sdlg 13661e449da8Sdlg if (dp->th_block != client->block) { 13671e449da8Sdlg if (tftp_flush(client) == -1) { 13681e449da8Sdlg lwarnx("%s: flush", getip(&client->ss)); 13691e449da8Sdlg goto done; 13701e449da8Sdlg } 13711e449da8Sdlg 13721e449da8Sdlg if (dp->th_block != (client->block - 1)) 13738871c557Sdlg goto done; 13748871c557Sdlg 13751e449da8Sdlg goto retry; 13761e449da8Sdlg } 13778871c557Sdlg 13788871c557Sdlg for (i = 4; i < n; i++) { 13791e449da8Sdlg if (client->fputc(client, wbuf[i]) == EOF) { 13808871c557Sdlg lwarn("tftp wrq"); 13818871c557Sdlg goto done; 13828871c557Sdlg } 13838871c557Sdlg } 13848871c557Sdlg 13858871c557Sdlg if (n < client->packet_size) { 13868871c557Sdlg tftp_wrq_ack_packet(client); 138763ce4e59Sdlg fclose(client->file); 138863ce4e59Sdlg client->file = NULL; 13896ac42c1aSdlg event_set(&client->sev, client->sock, EV_READ, 13906ac42c1aSdlg tftp_wrq_end, client); 13916ac42c1aSdlg event_add(&client->sev, &client->tv); 13926ac42c1aSdlg return; 13938871c557Sdlg } 13948871c557Sdlg 13958871c557Sdlg tftp_wrq_ack(client); 13968871c557Sdlg return; 13978871c557Sdlg 13988871c557Sdlg retry: 13998871c557Sdlg event_add(&client->sev, &client->tv); 14008871c557Sdlg return; 14018871c557Sdlg done: 14028871c557Sdlg client_free(client); 14038871c557Sdlg } 14048871c557Sdlg 14056ac42c1aSdlg void 14066ac42c1aSdlg tftp_wrq_end(int fd, short events, void *arg) 14076ac42c1aSdlg { 14086ac42c1aSdlg char wbuf[SEGSIZE_MAX + 4]; 14096ac42c1aSdlg struct tftp_client *client = arg; 14106ac42c1aSdlg struct tftphdr *dp; 14116ac42c1aSdlg ssize_t n; 14126ac42c1aSdlg 14136ac42c1aSdlg if (events & EV_TIMEOUT) { 14146ac42c1aSdlg /* this was the last packet, we can clean up */ 14156ac42c1aSdlg goto done; 14166ac42c1aSdlg } 14176ac42c1aSdlg 14186ac42c1aSdlg n = recv(fd, wbuf, client->packet_size, 0); 14196ac42c1aSdlg if (n == -1) { 14206ac42c1aSdlg switch (errno) { 14216ac42c1aSdlg case EINTR: 14226ac42c1aSdlg case EAGAIN: 14236ac42c1aSdlg goto retry; 14246ac42c1aSdlg 14256ac42c1aSdlg default: 14266ac42c1aSdlg lwarn("tftp_wrq_end recv"); 14276ac42c1aSdlg goto done; 14286ac42c1aSdlg } 14296ac42c1aSdlg } 14306ac42c1aSdlg 14316ac42c1aSdlg if (n < 4) 14326ac42c1aSdlg goto done; 14336ac42c1aSdlg 14346ac42c1aSdlg dp = (struct tftphdr *)wbuf; 14356ac42c1aSdlg dp->th_opcode = ntohs((u_short)dp->th_opcode); 14366ac42c1aSdlg dp->th_block = ntohs((u_short)dp->th_block); 14376ac42c1aSdlg 14386ac42c1aSdlg switch (dp->th_opcode) { 14396ac42c1aSdlg case ERROR: 14406ac42c1aSdlg goto done; 14416ac42c1aSdlg case DATA: 14426ac42c1aSdlg break; 14436ac42c1aSdlg default: 14446ac42c1aSdlg goto retry; 14456ac42c1aSdlg } 14466ac42c1aSdlg 14476ac42c1aSdlg if (dp->th_block != client->block) 14486ac42c1aSdlg goto done; 14496ac42c1aSdlg 14506ac42c1aSdlg retry: 14516ac42c1aSdlg if (retry(client) == -1) { 14526ac42c1aSdlg lwarn("%s", getip(&client->ss)); 14536ac42c1aSdlg goto done; 14546ac42c1aSdlg } 14556ac42c1aSdlg return; 14566ac42c1aSdlg done: 14576ac42c1aSdlg client_free(client); 14586ac42c1aSdlg return; 14596ac42c1aSdlg } 14606ac42c1aSdlg 14616ac42c1aSdlg 14628871c557Sdlg /* 14638871c557Sdlg * Send a nak packet (error message). 14648871c557Sdlg * Error code passed in is one of the 14658871c557Sdlg * standard TFTP codes, or a UNIX errno 14668871c557Sdlg * offset by 100. 14678871c557Sdlg */ 14688871c557Sdlg void 14698871c557Sdlg nak(struct tftp_client *client, int error) 14708871c557Sdlg { 14718871c557Sdlg struct tftphdr *tp; 14728871c557Sdlg struct errmsg *pe; 14738871c557Sdlg size_t length; 14744a80bfc2Skrw ssize_t rslt; 14758871c557Sdlg 14768871c557Sdlg tp = (struct tftphdr *)client->buf; 14778871c557Sdlg tp->th_opcode = htons((u_short)ERROR); 14788871c557Sdlg tp->th_code = htons((u_short)error); 14798871c557Sdlg 14808871c557Sdlg for (pe = errmsgs; pe->e_code >= 0; pe++) { 14818871c557Sdlg if (pe->e_code == error) 14828871c557Sdlg break; 14838871c557Sdlg } 14848871c557Sdlg if (pe->e_code < 0) { 14858871c557Sdlg pe->e_msg = strerror(error - 100); 148689df2fcbSdlg tp->th_code = htons(EUNDEF); /* set 'undef' errorcode */ 14878871c557Sdlg } 14888871c557Sdlg 14898871c557Sdlg length = strlcpy(tp->th_msg, pe->e_msg, client->packet_size - 5) + 5; 14908871c557Sdlg if (length > client->packet_size) 14918871c557Sdlg length = client->packet_size; 14928871c557Sdlg 14934a80bfc2Skrw linfo("%s: nak: %s", getip(&client->ss), tp->th_msg); 14944a80bfc2Skrw 14954a80bfc2Skrw rslt = send(client->sock, client->buf, length, 0); 14964a80bfc2Skrw if (rslt == -1) 14974a80bfc2Skrw lwarn("%s: nak", getip(&client->ss)); 14984a80bfc2Skrw else if ((size_t)rslt != length) 14994a80bfc2Skrw lwarnx("%s: nak: sent %zd of %zu bytes", getip(&client->ss), 15004a80bfc2Skrw rslt, length); 15018871c557Sdlg 15028871c557Sdlg client_free(client); 15038871c557Sdlg } 15048871c557Sdlg 15058871c557Sdlg /* 15068871c557Sdlg * Send an oack packet (option acknowledgement). 15078871c557Sdlg */ 1508eca1b59cSdlg int 15098871c557Sdlg oack(struct tftp_client *client) 15108871c557Sdlg { 15118871c557Sdlg struct opt_client *options = client->options; 15128871c557Sdlg struct tftphdr *tp; 15138871c557Sdlg char *bp; 15148871c557Sdlg int i, n, size; 15158871c557Sdlg 15168871c557Sdlg tp = (struct tftphdr *)client->buf; 15178871c557Sdlg bp = (char *)tp->th_stuff; 151831d5426eSdlg size = sizeof(client->buf) - 2; 15198871c557Sdlg 15208871c557Sdlg tp->th_opcode = htons((u_short)OACK); 15218871c557Sdlg for (i = 0; i < NOPT; i++) { 15228871c557Sdlg if (options[i].o_request == NULL) 15238871c557Sdlg continue; 15248871c557Sdlg 15258871c557Sdlg n = snprintf(bp, size, "%s%c%lld", opt_names[i], '\0', 15268871c557Sdlg options[i].o_reply); 1527515e489cSderaadt if (n < 0 || n >= size) { 152875b3d082Sdlg lwarnx("oack: no buffer space"); 15298871c557Sdlg goto error; 15308871c557Sdlg } 15318871c557Sdlg 15328871c557Sdlg bp += n + 1; 15338871c557Sdlg size -= n + 1; 15348871c557Sdlg if (size < 0) { 153575b3d082Sdlg lwarnx("oack: no buffer space"); 15368871c557Sdlg goto error; 15378871c557Sdlg } 15388871c557Sdlg } 15398871c557Sdlg 15408871c557Sdlg client->buflen = bp - client->buf; 15418871c557Sdlg client->retries = RETRIES; 15428871c557Sdlg 15438871c557Sdlg if (send(client->sock, client->buf, client->buflen, 0) == -1) { 15448871c557Sdlg lwarn("oack"); 15458871c557Sdlg goto error; 15468871c557Sdlg } 15478871c557Sdlg 15488871c557Sdlg /* no client ACK for write requests with options */ 15498871c557Sdlg if (client->opcode == WRQ) { 15508871c557Sdlg client->block = 1; 15518871c557Sdlg event_set(&client->sev, client->sock, EV_READ, 15528871c557Sdlg tftp_wrq, client); 15538871c557Sdlg } else 15548871c557Sdlg event_set(&client->sev, client->sock, EV_READ, 15558871c557Sdlg oack_done, client); 15568871c557Sdlg 15578871c557Sdlg event_add(&client->sev, &client->tv); 1558eca1b59cSdlg return (0); 15598871c557Sdlg 15608871c557Sdlg error: 1561eca1b59cSdlg return (-1); 15628871c557Sdlg } 15638871c557Sdlg 15648871c557Sdlg int 15658871c557Sdlg retry(struct tftp_client *client) 15668871c557Sdlg { 15671e449da8Sdlg if (--client->retries == 0) { 15681e449da8Sdlg errno = ETIMEDOUT; 15698871c557Sdlg return (-1); 15701e449da8Sdlg } 15718871c557Sdlg 157280a17660Sdlg tftp_send(client); 15738871c557Sdlg 15748871c557Sdlg return (0); 15758871c557Sdlg } 15768871c557Sdlg 15778871c557Sdlg void 15788871c557Sdlg oack_done(int fd, short events, void *arg) 15798871c557Sdlg { 15808871c557Sdlg struct tftp_client *client = arg; 15818871c557Sdlg struct tftphdr *ap; 15828871c557Sdlg ssize_t n; 15838871c557Sdlg 15848871c557Sdlg if (events & EV_TIMEOUT) { 15851e449da8Sdlg if (retry(client) == -1) { 15861e449da8Sdlg lwarn("%s", getip(&client->ss)); 15878871c557Sdlg goto done; 15881e449da8Sdlg } 15898871c557Sdlg 15908871c557Sdlg return; 15918871c557Sdlg } 15928871c557Sdlg 15938871c557Sdlg n = recv(client->sock, client->buf, client->packet_size, 0); 15948871c557Sdlg if (n == -1) { 15958871c557Sdlg switch (errno) { 15968871c557Sdlg case EINTR: 15978871c557Sdlg case EAGAIN: 15988871c557Sdlg event_add(&client->sev, &client->tv); 15998871c557Sdlg return; 16008871c557Sdlg 16018871c557Sdlg default: 16021e449da8Sdlg lwarn("%s: recv", getip(&client->ss)); 16038871c557Sdlg goto done; 16048871c557Sdlg } 16058871c557Sdlg } 16068871c557Sdlg 16078871c557Sdlg if (n < 4) 16088871c557Sdlg goto done; 16098871c557Sdlg 16108871c557Sdlg ap = (struct tftphdr *)client->buf; 16118871c557Sdlg ap->th_opcode = ntohs((u_short)ap->th_opcode); 16128871c557Sdlg ap->th_block = ntohs((u_short)ap->th_block); 16138871c557Sdlg 16148871c557Sdlg if (ap->th_opcode != ACK || ap->th_block != 0) 16158871c557Sdlg goto done; 16168871c557Sdlg 16178871c557Sdlg sendfile(client); 16188871c557Sdlg return; 16198871c557Sdlg 16208871c557Sdlg done: 16218871c557Sdlg client_free(client); 16228871c557Sdlg } 16238871c557Sdlg 16248871c557Sdlg const char * 16258871c557Sdlg getip(void *s) 16268871c557Sdlg { 16278871c557Sdlg struct sockaddr *sa = s; 16288871c557Sdlg static char hbuf[NI_MAXHOST]; 16298871c557Sdlg 16308871c557Sdlg if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), 16318871c557Sdlg NULL, 0, NI_NUMERICHOST)) 16328871c557Sdlg strlcpy(hbuf, "0.0.0.0", sizeof(hbuf)); 16338871c557Sdlg 16348871c557Sdlg return(hbuf); 16358871c557Sdlg } 16368871c557Sdlg 1637eaf6a821Sjca /* daemon(3) clone, intended to be used in a "r"estricted environment */ 1638eaf6a821Sjca int 1639eaf6a821Sjca rdaemon(int devnull) 1640eaf6a821Sjca { 1641ec8c1742Sjca if (devnull == -1) { 1642ec8c1742Sjca errno = EBADF; 1643ec8c1742Sjca return (-1); 1644ec8c1742Sjca } 1645ec8c1742Sjca if (fcntl(devnull, F_GETFL) == -1) 1646ec8c1742Sjca return (-1); 1647eaf6a821Sjca 1648eaf6a821Sjca switch (fork()) { 1649eaf6a821Sjca case -1: 1650eaf6a821Sjca return (-1); 1651eaf6a821Sjca case 0: 1652eaf6a821Sjca break; 1653eaf6a821Sjca default: 1654eaf6a821Sjca _exit(0); 1655eaf6a821Sjca } 1656eaf6a821Sjca 1657eaf6a821Sjca if (setsid() == -1) 1658eaf6a821Sjca return (-1); 1659eaf6a821Sjca 1660eaf6a821Sjca (void)dup2(devnull, STDIN_FILENO); 1661eaf6a821Sjca (void)dup2(devnull, STDOUT_FILENO); 1662eaf6a821Sjca (void)dup2(devnull, STDERR_FILENO); 1663eaf6a821Sjca if (devnull > 2) 1664eaf6a821Sjca (void)close(devnull); 1665eaf6a821Sjca 1666eaf6a821Sjca return (0); 1667eaf6a821Sjca } 1668eaf6a821Sjca 16698871c557Sdlg void 16708871c557Sdlg syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) 16718871c557Sdlg { 16728871c557Sdlg char *s; 16738871c557Sdlg 16748871c557Sdlg if (vasprintf(&s, fmt, ap) == -1) { 16758871c557Sdlg syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); 16768871c557Sdlg exit(1); 16778871c557Sdlg } 16788871c557Sdlg 16798871c557Sdlg syslog(priority, "%s: %s", s, strerror(e)); 16808871c557Sdlg 16818871c557Sdlg free(s); 16828871c557Sdlg } 16838871c557Sdlg 16848871c557Sdlg void 16858871c557Sdlg syslog_err(int ecode, const char *fmt, ...) 16868871c557Sdlg { 16878871c557Sdlg va_list ap; 16888871c557Sdlg 16898871c557Sdlg va_start(ap, fmt); 1690a52b97d1Sflorian syslog_vstrerror(errno, LOG_CRIT, fmt, ap); 16918871c557Sdlg va_end(ap); 16928871c557Sdlg 16938871c557Sdlg exit(ecode); 16948871c557Sdlg } 16958871c557Sdlg 16968871c557Sdlg void 16978871c557Sdlg syslog_errx(int ecode, const char *fmt, ...) 16988871c557Sdlg { 16998871c557Sdlg va_list ap; 17008871c557Sdlg 17018871c557Sdlg va_start(ap, fmt); 1702a52b97d1Sflorian vsyslog(LOG_CRIT, fmt, ap); 17038871c557Sdlg va_end(ap); 17048871c557Sdlg 17058871c557Sdlg exit(ecode); 17068871c557Sdlg } 17078871c557Sdlg 17088871c557Sdlg void 17098871c557Sdlg syslog_warn(const char *fmt, ...) 17108871c557Sdlg { 17118871c557Sdlg va_list ap; 17128871c557Sdlg 17138871c557Sdlg va_start(ap, fmt); 1714a52b97d1Sflorian syslog_vstrerror(errno, LOG_ERR, fmt, ap); 17158871c557Sdlg va_end(ap); 17168871c557Sdlg } 17178871c557Sdlg 17188871c557Sdlg void 17198871c557Sdlg syslog_warnx(const char *fmt, ...) 17208871c557Sdlg { 17218871c557Sdlg va_list ap; 17228871c557Sdlg 17238871c557Sdlg va_start(ap, fmt); 1724a52b97d1Sflorian vsyslog(LOG_ERR, fmt, ap); 17258871c557Sdlg va_end(ap); 17268871c557Sdlg } 17278871c557Sdlg 17288871c557Sdlg void 17298871c557Sdlg syslog_info(const char *fmt, ...) 17308871c557Sdlg { 17318871c557Sdlg va_list ap; 17328871c557Sdlg 17338871c557Sdlg va_start(ap, fmt); 17348871c557Sdlg vsyslog(LOG_INFO, fmt, ap); 17358871c557Sdlg va_end(ap); 17368871c557Sdlg } 17378871c557Sdlg 17380e7810bdSflorian void 17390e7810bdSflorian syslog_debug(const char *fmt, ...) 17400e7810bdSflorian { 17410e7810bdSflorian va_list ap; 17420e7810bdSflorian 17430e7810bdSflorian if (!debug) 17440e7810bdSflorian return; 17450e7810bdSflorian 17460e7810bdSflorian va_start(ap, fmt); 17470e7810bdSflorian vsyslog(LOG_DEBUG, fmt, ap); 17480e7810bdSflorian va_end(ap); 17490e7810bdSflorian } 1750