1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate * 22*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 23*0Sstevel@tonic-gate * Use is subject to license terms. 24*0Sstevel@tonic-gate */ 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate /* 27*0Sstevel@tonic-gate * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T 28*0Sstevel@tonic-gate * All Rights Reserved. 29*0Sstevel@tonic-gate */ 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate /* 32*0Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 33*0Sstevel@tonic-gate * The Regents of the University of California. 34*0Sstevel@tonic-gate * All Rights Reserved. 35*0Sstevel@tonic-gate * 36*0Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 37*0Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 38*0Sstevel@tonic-gate * contributors. 39*0Sstevel@tonic-gate */ 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gate /* 44*0Sstevel@tonic-gate * Trivial file transfer protocol server. A top level process runs in 45*0Sstevel@tonic-gate * an infinite loop fielding new TFTP requests. A child process, 46*0Sstevel@tonic-gate * communicating via a pipe with the top level process, sends delayed 47*0Sstevel@tonic-gate * NAKs for those that we can't handle. A new child process is created 48*0Sstevel@tonic-gate * to service each request that we can handle. The top level process 49*0Sstevel@tonic-gate * exits after a period of time during which no new requests are 50*0Sstevel@tonic-gate * received. 51*0Sstevel@tonic-gate */ 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate #include <sys/types.h> 54*0Sstevel@tonic-gate #include <sys/socket.h> 55*0Sstevel@tonic-gate #include <sys/wait.h> 56*0Sstevel@tonic-gate #include <sys/stat.h> 57*0Sstevel@tonic-gate #include <sys/time.h> 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate #include <netinet/in.h> 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate #include <arpa/inet.h> 62*0Sstevel@tonic-gate #include <dirent.h> 63*0Sstevel@tonic-gate #include <signal.h> 64*0Sstevel@tonic-gate #include <stdio.h> 65*0Sstevel@tonic-gate #include <stdlib.h> 66*0Sstevel@tonic-gate #include <unistd.h> 67*0Sstevel@tonic-gate #include <errno.h> 68*0Sstevel@tonic-gate #include <ctype.h> 69*0Sstevel@tonic-gate #include <netdb.h> 70*0Sstevel@tonic-gate #include <setjmp.h> 71*0Sstevel@tonic-gate #include <syslog.h> 72*0Sstevel@tonic-gate #include <sys/param.h> 73*0Sstevel@tonic-gate #include <fcntl.h> 74*0Sstevel@tonic-gate #include <pwd.h> 75*0Sstevel@tonic-gate #include <string.h> 76*0Sstevel@tonic-gate #include <priv_utils.h> 77*0Sstevel@tonic-gate #include "tftpcommon.h" 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate #define TIMEOUT 5 80*0Sstevel@tonic-gate #define DELAY_SECS 3 81*0Sstevel@tonic-gate #define DALLYSECS 60 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate #define SYSLOG_MSG(message) \ 84*0Sstevel@tonic-gate (syslog((((errno == ENETUNREACH) || (errno == EHOSTUNREACH) || \ 85*0Sstevel@tonic-gate (errno == ECONNREFUSED)) ? LOG_WARNING : LOG_ERR), message)) 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate static int rexmtval = TIMEOUT; 88*0Sstevel@tonic-gate static int maxtimeout = 5*TIMEOUT; 89*0Sstevel@tonic-gate static int securetftp; 90*0Sstevel@tonic-gate static int debug; 91*0Sstevel@tonic-gate static int disable_pnp; 92*0Sstevel@tonic-gate static int standalone; 93*0Sstevel@tonic-gate static uid_t uid_nobody = UID_NOBODY; 94*0Sstevel@tonic-gate static uid_t gid_nobody = GID_NOBODY; 95*0Sstevel@tonic-gate static int reqsock = -1; 96*0Sstevel@tonic-gate /* file descriptor of request socket */ 97*0Sstevel@tonic-gate static socklen_t fromlen; 98*0Sstevel@tonic-gate static socklen_t fromplen; 99*0Sstevel@tonic-gate static struct sockaddr_storage client; 100*0Sstevel@tonic-gate static struct sockaddr_in6 *sin6_ptr; 101*0Sstevel@tonic-gate static struct sockaddr_in *sin_ptr; 102*0Sstevel@tonic-gate static struct sockaddr_in6 *from6_ptr; 103*0Sstevel@tonic-gate static struct sockaddr_in *from_ptr; 104*0Sstevel@tonic-gate static int addrfmly; 105*0Sstevel@tonic-gate static int peer; 106*0Sstevel@tonic-gate static off_t tsize; 107*0Sstevel@tonic-gate static tftpbuf ackbuf; 108*0Sstevel@tonic-gate static struct sockaddr_storage from; 109*0Sstevel@tonic-gate static boolean_t tsize_set; 110*0Sstevel@tonic-gate static pid_t child; 111*0Sstevel@tonic-gate /* pid of child handling delayed replys */ 112*0Sstevel@tonic-gate static int delay_fd [2]; 113*0Sstevel@tonic-gate /* pipe for communicating with child */ 114*0Sstevel@tonic-gate static FILE *file; 115*0Sstevel@tonic-gate static char *filename; 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate static union { 118*0Sstevel@tonic-gate struct tftphdr hdr; 119*0Sstevel@tonic-gate char data[SEGSIZE + 4]; 120*0Sstevel@tonic-gate } buf; 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate static union { 123*0Sstevel@tonic-gate struct tftphdr hdr; 124*0Sstevel@tonic-gate char data[SEGSIZE]; 125*0Sstevel@tonic-gate } oackbuf; 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate struct delay_info { 128*0Sstevel@tonic-gate long timestamp; /* time request received */ 129*0Sstevel@tonic-gate int ecode; /* error code to return */ 130*0Sstevel@tonic-gate struct sockaddr_storage from; /* address of client */ 131*0Sstevel@tonic-gate }; 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate int blocksize = SEGSIZE; /* Number of data bytes in a DATA packet */ 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate /* 136*0Sstevel@tonic-gate * Default directory for unqualified names 137*0Sstevel@tonic-gate * Used by TFTP boot procedures 138*0Sstevel@tonic-gate */ 139*0Sstevel@tonic-gate static char *homedir = "/tftpboot"; 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate struct formats { 142*0Sstevel@tonic-gate char *f_mode; 143*0Sstevel@tonic-gate int (*f_validate)(int); 144*0Sstevel@tonic-gate void (*f_send)(struct formats *, int); 145*0Sstevel@tonic-gate void (*f_recv)(struct formats *, int); 146*0Sstevel@tonic-gate int f_convert; 147*0Sstevel@tonic-gate }; 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate static void delayed_responder(void); 150*0Sstevel@tonic-gate static void tftp(struct tftphdr *, int); 151*0Sstevel@tonic-gate static int validate_filename(int); 152*0Sstevel@tonic-gate static void tftpd_sendfile(struct formats *, int); 153*0Sstevel@tonic-gate static void tftpd_recvfile(struct formats *, int); 154*0Sstevel@tonic-gate static void nak(int); 155*0Sstevel@tonic-gate static char *blksize_handler(int, char *, int *); 156*0Sstevel@tonic-gate static char *timeout_handler(int, char *, int *); 157*0Sstevel@tonic-gate static char *tsize_handler(int, char *, int *); 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate static struct formats formats[] = { 160*0Sstevel@tonic-gate { "netascii", validate_filename, tftpd_sendfile, tftpd_recvfile, 1 }, 161*0Sstevel@tonic-gate { "octet", validate_filename, tftpd_sendfile, tftpd_recvfile, 0 }, 162*0Sstevel@tonic-gate { NULL } 163*0Sstevel@tonic-gate }; 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate struct options { 166*0Sstevel@tonic-gate char *opt_name; 167*0Sstevel@tonic-gate char *(*opt_handler)(int, char *, int *); 168*0Sstevel@tonic-gate }; 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate static struct options options[] = { 171*0Sstevel@tonic-gate { "blksize", blksize_handler }, 172*0Sstevel@tonic-gate { "timeout", timeout_handler }, 173*0Sstevel@tonic-gate { "tsize", tsize_handler }, 174*0Sstevel@tonic-gate { NULL } 175*0Sstevel@tonic-gate }; 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate static char optbuf[MAX_OPTVAL_LEN]; 178*0Sstevel@tonic-gate static int timeout; 179*0Sstevel@tonic-gate static sigjmp_buf timeoutbuf; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate int 182*0Sstevel@tonic-gate main(int argc, char **argv) 183*0Sstevel@tonic-gate { 184*0Sstevel@tonic-gate struct tftphdr *tp; 185*0Sstevel@tonic-gate int n; 186*0Sstevel@tonic-gate int c; 187*0Sstevel@tonic-gate struct passwd *pwd; /* for "nobody" entry */ 188*0Sstevel@tonic-gate struct in_addr ipv4addr; 189*0Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 190*0Sstevel@tonic-gate socklen_t addrlen; 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate openlog("tftpd", LOG_PID, LOG_DAEMON); 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate pwd = getpwnam("nobody"); 195*0Sstevel@tonic-gate if (pwd != NULL) { 196*0Sstevel@tonic-gate uid_nobody = pwd->pw_uid; 197*0Sstevel@tonic-gate gid_nobody = pwd->pw_gid; 198*0Sstevel@tonic-gate } 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate (void) __init_daemon_priv( 201*0Sstevel@tonic-gate PU_LIMITPRIVS, 202*0Sstevel@tonic-gate uid_nobody, gid_nobody, 203*0Sstevel@tonic-gate PRIV_PROC_FORK, PRIV_PROC_CHROOT, NULL); 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate /* 206*0Sstevel@tonic-gate * Limit set is still "all." Trim it down to just what we need: 207*0Sstevel@tonic-gate * fork and chroot. 208*0Sstevel@tonic-gate */ 209*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, 210*0Sstevel@tonic-gate PRIV_ALLSETS, PRIV_PROC_FORK, PRIV_PROC_CHROOT, NULL); 211*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL); 212*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_INHERITABLE, NULL); 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "dspS")) != EOF) 215*0Sstevel@tonic-gate switch (c) { 216*0Sstevel@tonic-gate case 'd': /* enable debug */ 217*0Sstevel@tonic-gate debug++; 218*0Sstevel@tonic-gate continue; 219*0Sstevel@tonic-gate case 's': /* secure daemon */ 220*0Sstevel@tonic-gate securetftp = 1; 221*0Sstevel@tonic-gate continue; 222*0Sstevel@tonic-gate case 'p': /* disable name pnp mapping */ 223*0Sstevel@tonic-gate disable_pnp = 1; 224*0Sstevel@tonic-gate continue; 225*0Sstevel@tonic-gate case 'S': 226*0Sstevel@tonic-gate standalone = 1; 227*0Sstevel@tonic-gate continue; 228*0Sstevel@tonic-gate case '?': 229*0Sstevel@tonic-gate default: 230*0Sstevel@tonic-gate usage: 231*0Sstevel@tonic-gate (void) fprintf(stderr, 232*0Sstevel@tonic-gate "usage: %s [-spd] [home-directory]\n", argv[0]); 233*0Sstevel@tonic-gate for (; optind < argc; optind++) 234*0Sstevel@tonic-gate syslog(LOG_ERR, "bad argument %s", 235*0Sstevel@tonic-gate argv[optind]); 236*0Sstevel@tonic-gate exit(1); 237*0Sstevel@tonic-gate } 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate if (optind < argc) 240*0Sstevel@tonic-gate if (optind == argc - 1 && *argv [optind] == '/') 241*0Sstevel@tonic-gate homedir = argv [optind]; 242*0Sstevel@tonic-gate else 243*0Sstevel@tonic-gate goto usage; 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate if (pipe(delay_fd) < 0) { 246*0Sstevel@tonic-gate syslog(LOG_ERR, "pipe (main): %m"); 247*0Sstevel@tonic-gate exit(1); 248*0Sstevel@tonic-gate } 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gate (void) sigset(SIGCHLD, SIG_IGN); /* no zombies please */ 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate if (standalone) { 253*0Sstevel@tonic-gate socklen_t clientlen; 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate sin6_ptr = (struct sockaddr_in6 *)&client; 256*0Sstevel@tonic-gate clientlen = sizeof (struct sockaddr_in6); 257*0Sstevel@tonic-gate reqsock = socket(AF_INET6, SOCK_DGRAM, 0); 258*0Sstevel@tonic-gate if (reqsock == -1) { 259*0Sstevel@tonic-gate perror("socket"); 260*0Sstevel@tonic-gate exit(1); 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate (void) memset(&client, 0, clientlen); 263*0Sstevel@tonic-gate sin6_ptr->sin6_family = AF_INET6; 264*0Sstevel@tonic-gate sin6_ptr->sin6_port = htons(IPPORT_TFTP); 265*0Sstevel@tonic-gate if (bind(reqsock, (struct sockaddr *)&client, 266*0Sstevel@tonic-gate clientlen) == -1) { 267*0Sstevel@tonic-gate perror("bind"); 268*0Sstevel@tonic-gate exit(1); 269*0Sstevel@tonic-gate } 270*0Sstevel@tonic-gate if (debug) 271*0Sstevel@tonic-gate (void) puts("running in standalone mode..."); 272*0Sstevel@tonic-gate } else { 273*0Sstevel@tonic-gate /* request socket passed on fd 0 by inetd */ 274*0Sstevel@tonic-gate reqsock = 0; 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate if (debug) { 277*0Sstevel@tonic-gate int on = 1; 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate (void) setsockopt(reqsock, SOL_SOCKET, SO_DEBUG, 280*0Sstevel@tonic-gate (char *)&on, sizeof (on)); 281*0Sstevel@tonic-gate } 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate (void) chdir(homedir); 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL); 286*0Sstevel@tonic-gate if ((child = fork()) < 0) { 287*0Sstevel@tonic-gate syslog(LOG_ERR, "fork (main): %m"); 288*0Sstevel@tonic-gate exit(1); 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL); 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate if (child == 0) { 293*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_ALLSETS, NULL); 294*0Sstevel@tonic-gate delayed_responder(); 295*0Sstevel@tonic-gate } /* child */ 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate /* close read side of pipe */ 298*0Sstevel@tonic-gate (void) close(delay_fd[0]); 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate /* 302*0Sstevel@tonic-gate * Top level handling of incomming tftp requests. Read a request 303*0Sstevel@tonic-gate * and pass it off to be handled. If request is valid, handling 304*0Sstevel@tonic-gate * forks off and parent returns to this loop. If no new requests 305*0Sstevel@tonic-gate * are received for DALLYSECS, exit and return to inetd. 306*0Sstevel@tonic-gate */ 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gate for (;;) { 309*0Sstevel@tonic-gate fd_set readfds; 310*0Sstevel@tonic-gate struct timeval dally; 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate FD_ZERO(&readfds); 313*0Sstevel@tonic-gate FD_SET(reqsock, &readfds); 314*0Sstevel@tonic-gate dally.tv_sec = DALLYSECS; 315*0Sstevel@tonic-gate dally.tv_usec = 0; 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gate n = select(reqsock + 1, &readfds, NULL, NULL, &dally); 318*0Sstevel@tonic-gate if (n < 0) { 319*0Sstevel@tonic-gate if (errno == EINTR) 320*0Sstevel@tonic-gate continue; 321*0Sstevel@tonic-gate syslog(LOG_ERR, "select: %m"); 322*0Sstevel@tonic-gate (void) kill(child, SIGKILL); 323*0Sstevel@tonic-gate exit(1); 324*0Sstevel@tonic-gate } 325*0Sstevel@tonic-gate if (n == 0) { 326*0Sstevel@tonic-gate /* Select timed out. Its time to die. */ 327*0Sstevel@tonic-gate if (standalone) 328*0Sstevel@tonic-gate continue; 329*0Sstevel@tonic-gate else { 330*0Sstevel@tonic-gate (void) kill(child, SIGKILL); 331*0Sstevel@tonic-gate exit(0); 332*0Sstevel@tonic-gate } 333*0Sstevel@tonic-gate } 334*0Sstevel@tonic-gate addrlen = sizeof (from); 335*0Sstevel@tonic-gate if (getsockname(reqsock, (struct sockaddr *)&from, 336*0Sstevel@tonic-gate &addrlen) < 0) { 337*0Sstevel@tonic-gate syslog(LOG_ERR, "getsockname: %m"); 338*0Sstevel@tonic-gate exit(1); 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate switch (from.ss_family) { 342*0Sstevel@tonic-gate case AF_INET: 343*0Sstevel@tonic-gate fromlen = (socklen_t)sizeof (struct sockaddr_in); 344*0Sstevel@tonic-gate break; 345*0Sstevel@tonic-gate case AF_INET6: 346*0Sstevel@tonic-gate fromlen = (socklen_t)sizeof (struct sockaddr_in6); 347*0Sstevel@tonic-gate break; 348*0Sstevel@tonic-gate default: 349*0Sstevel@tonic-gate syslog(LOG_ERR, 350*0Sstevel@tonic-gate "Unknown address Family on peer connection %d", 351*0Sstevel@tonic-gate from.ss_family); 352*0Sstevel@tonic-gate exit(1); 353*0Sstevel@tonic-gate } 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate n = recvfrom(reqsock, &buf, sizeof (buf), 0, 356*0Sstevel@tonic-gate (struct sockaddr *)&from, &fromlen); 357*0Sstevel@tonic-gate if (n < 0) { 358*0Sstevel@tonic-gate if (errno == EINTR) 359*0Sstevel@tonic-gate continue; 360*0Sstevel@tonic-gate if (standalone) 361*0Sstevel@tonic-gate perror("recvfrom"); 362*0Sstevel@tonic-gate else 363*0Sstevel@tonic-gate syslog(LOG_ERR, "recvfrom: %m"); 364*0Sstevel@tonic-gate (void) kill(child, SIGKILL); 365*0Sstevel@tonic-gate exit(1); 366*0Sstevel@tonic-gate } 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate (void) alarm(0); 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate switch (from.ss_family) { 371*0Sstevel@tonic-gate case AF_INET: 372*0Sstevel@tonic-gate addrfmly = AF_INET; 373*0Sstevel@tonic-gate fromplen = sizeof (struct sockaddr_in); 374*0Sstevel@tonic-gate sin_ptr = (struct sockaddr_in *)&client; 375*0Sstevel@tonic-gate (void) memset(&client, 0, fromplen); 376*0Sstevel@tonic-gate sin_ptr->sin_family = AF_INET; 377*0Sstevel@tonic-gate break; 378*0Sstevel@tonic-gate case AF_INET6: 379*0Sstevel@tonic-gate addrfmly = AF_INET6; 380*0Sstevel@tonic-gate fromplen = sizeof (struct sockaddr_in6); 381*0Sstevel@tonic-gate sin6_ptr = (struct sockaddr_in6 *)&client; 382*0Sstevel@tonic-gate (void) memset(&client, 0, fromplen); 383*0Sstevel@tonic-gate sin6_ptr->sin6_family = AF_INET6; 384*0Sstevel@tonic-gate break; 385*0Sstevel@tonic-gate default: 386*0Sstevel@tonic-gate syslog(LOG_ERR, 387*0Sstevel@tonic-gate "Unknown address Family on peer connection"); 388*0Sstevel@tonic-gate exit(1); 389*0Sstevel@tonic-gate } 390*0Sstevel@tonic-gate peer = socket(addrfmly, SOCK_DGRAM, 0); 391*0Sstevel@tonic-gate if (peer < 0) { 392*0Sstevel@tonic-gate if (standalone) 393*0Sstevel@tonic-gate perror("socket (main)"); 394*0Sstevel@tonic-gate else 395*0Sstevel@tonic-gate syslog(LOG_ERR, "socket (main): %m"); 396*0Sstevel@tonic-gate (void) kill(child, SIGKILL); 397*0Sstevel@tonic-gate exit(1); 398*0Sstevel@tonic-gate } 399*0Sstevel@tonic-gate if (debug) { 400*0Sstevel@tonic-gate int on = 1; 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate (void) setsockopt(peer, SOL_SOCKET, SO_DEBUG, 403*0Sstevel@tonic-gate (char *)&on, sizeof (on)); 404*0Sstevel@tonic-gate } 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) { 407*0Sstevel@tonic-gate if (standalone) 408*0Sstevel@tonic-gate perror("bind (main)"); 409*0Sstevel@tonic-gate else 410*0Sstevel@tonic-gate syslog(LOG_ERR, "bind (main): %m"); 411*0Sstevel@tonic-gate (void) kill(child, SIGKILL); 412*0Sstevel@tonic-gate exit(1); 413*0Sstevel@tonic-gate } 414*0Sstevel@tonic-gate if (standalone && debug) { 415*0Sstevel@tonic-gate sin6_ptr = (struct sockaddr_in6 *)&client; 416*0Sstevel@tonic-gate from6_ptr = (struct sockaddr_in6 *)&from; 417*0Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(&from6_ptr->sin6_addr)) { 418*0Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(&from6_ptr->sin6_addr, 419*0Sstevel@tonic-gate &ipv4addr); 420*0Sstevel@tonic-gate (void) inet_ntop(AF_INET, &ipv4addr, abuf, 421*0Sstevel@tonic-gate sizeof (abuf)); 422*0Sstevel@tonic-gate } else { 423*0Sstevel@tonic-gate (void) inet_ntop(AF_INET6, 424*0Sstevel@tonic-gate &from6_ptr->sin6_addr, abuf, 425*0Sstevel@tonic-gate sizeof (abuf)); 426*0Sstevel@tonic-gate } 427*0Sstevel@tonic-gate /* get local port */ 428*0Sstevel@tonic-gate if (getsockname(peer, (struct sockaddr *)&client, 429*0Sstevel@tonic-gate &fromplen) < 0) 430*0Sstevel@tonic-gate perror("getsockname (main)"); 431*0Sstevel@tonic-gate (void) fprintf(stderr, 432*0Sstevel@tonic-gate "request from %s port %d; local port %d\n", 433*0Sstevel@tonic-gate abuf, from6_ptr->sin6_port, sin6_ptr->sin6_port); 434*0Sstevel@tonic-gate } 435*0Sstevel@tonic-gate tp = &buf.hdr; 436*0Sstevel@tonic-gate tp->th_opcode = ntohs((ushort_t)tp->th_opcode); 437*0Sstevel@tonic-gate if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 438*0Sstevel@tonic-gate tftp(tp, n); 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate (void) close(peer); 441*0Sstevel@tonic-gate (void) fclose(file); 442*0Sstevel@tonic-gate } 443*0Sstevel@tonic-gate 444*0Sstevel@tonic-gate /*NOTREACHED*/ 445*0Sstevel@tonic-gate return (0); 446*0Sstevel@tonic-gate } 447*0Sstevel@tonic-gate 448*0Sstevel@tonic-gate static void 449*0Sstevel@tonic-gate delayed_responder(void) 450*0Sstevel@tonic-gate { 451*0Sstevel@tonic-gate struct delay_info dinfo; 452*0Sstevel@tonic-gate long now; 453*0Sstevel@tonic-gate 454*0Sstevel@tonic-gate /* we don't use the descriptors passed in to the parent */ 455*0Sstevel@tonic-gate (void) close(0); 456*0Sstevel@tonic-gate (void) close(1); 457*0Sstevel@tonic-gate if (standalone) 458*0Sstevel@tonic-gate (void) close(reqsock); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate /* close write side of pipe */ 461*0Sstevel@tonic-gate (void) close(delay_fd[1]); 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate for (;;) { 464*0Sstevel@tonic-gate int n; 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate if ((n = read(delay_fd[0], &dinfo, 467*0Sstevel@tonic-gate sizeof (dinfo))) != sizeof (dinfo)) { 468*0Sstevel@tonic-gate if (n < 0) { 469*0Sstevel@tonic-gate if (errno == EINTR) 470*0Sstevel@tonic-gate continue; 471*0Sstevel@tonic-gate if (standalone) 472*0Sstevel@tonic-gate perror("read from pipe " 473*0Sstevel@tonic-gate "(delayed responder)"); 474*0Sstevel@tonic-gate else 475*0Sstevel@tonic-gate syslog(LOG_ERR, "read from pipe: %m"); 476*0Sstevel@tonic-gate } 477*0Sstevel@tonic-gate exit(1); 478*0Sstevel@tonic-gate } 479*0Sstevel@tonic-gate switch (dinfo.from.ss_family) { 480*0Sstevel@tonic-gate case AF_INET: 481*0Sstevel@tonic-gate addrfmly = AF_INET; 482*0Sstevel@tonic-gate fromplen = sizeof (struct sockaddr_in); 483*0Sstevel@tonic-gate sin_ptr = (struct sockaddr_in *)&client; 484*0Sstevel@tonic-gate (void) memset(&client, 0, fromplen); 485*0Sstevel@tonic-gate sin_ptr->sin_family = AF_INET; 486*0Sstevel@tonic-gate break; 487*0Sstevel@tonic-gate case AF_INET6: 488*0Sstevel@tonic-gate addrfmly = AF_INET6; 489*0Sstevel@tonic-gate fromplen = sizeof (struct sockaddr_in6); 490*0Sstevel@tonic-gate sin6_ptr = (struct sockaddr_in6 *)&client; 491*0Sstevel@tonic-gate (void) memset(&client, 0, fromplen); 492*0Sstevel@tonic-gate sin6_ptr->sin6_family = AF_INET6; 493*0Sstevel@tonic-gate break; 494*0Sstevel@tonic-gate } 495*0Sstevel@tonic-gate peer = socket(addrfmly, SOCK_DGRAM, 0); 496*0Sstevel@tonic-gate if (peer == -1) { 497*0Sstevel@tonic-gate if (standalone) 498*0Sstevel@tonic-gate perror("socket (delayed responder)"); 499*0Sstevel@tonic-gate else 500*0Sstevel@tonic-gate syslog(LOG_ERR, "socket (delay): %m"); 501*0Sstevel@tonic-gate exit(1); 502*0Sstevel@tonic-gate } 503*0Sstevel@tonic-gate if (debug) { 504*0Sstevel@tonic-gate int on = 1; 505*0Sstevel@tonic-gate 506*0Sstevel@tonic-gate (void) setsockopt(peer, SOL_SOCKET, SO_DEBUG, 507*0Sstevel@tonic-gate (char *)&on, sizeof (on)); 508*0Sstevel@tonic-gate } 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) { 511*0Sstevel@tonic-gate if (standalone) 512*0Sstevel@tonic-gate perror("bind (delayed responder)"); 513*0Sstevel@tonic-gate else 514*0Sstevel@tonic-gate syslog(LOG_ERR, "bind (delay): %m"); 515*0Sstevel@tonic-gate exit(1); 516*0Sstevel@tonic-gate } 517*0Sstevel@tonic-gate if (client.ss_family == AF_INET) { 518*0Sstevel@tonic-gate from_ptr = (struct sockaddr_in *)&dinfo.from; 519*0Sstevel@tonic-gate from_ptr->sin_family = AF_INET; 520*0Sstevel@tonic-gate } else { 521*0Sstevel@tonic-gate from6_ptr = (struct sockaddr_in6 *)&dinfo.from; 522*0Sstevel@tonic-gate from6_ptr->sin6_family = AF_INET6; 523*0Sstevel@tonic-gate } 524*0Sstevel@tonic-gate /* 525*0Sstevel@tonic-gate * Since a request hasn't been received from the client 526*0Sstevel@tonic-gate * before the delayed responder process is forked, the 527*0Sstevel@tonic-gate * from variable is uninitialized. So set it to contain 528*0Sstevel@tonic-gate * the client address. 529*0Sstevel@tonic-gate */ 530*0Sstevel@tonic-gate from = dinfo.from; 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate /* 533*0Sstevel@tonic-gate * only sleep if DELAY_SECS has not elapsed since 534*0Sstevel@tonic-gate * original request was received. Ensure that `now' 535*0Sstevel@tonic-gate * is not earlier than `dinfo.timestamp' 536*0Sstevel@tonic-gate */ 537*0Sstevel@tonic-gate now = time(0); 538*0Sstevel@tonic-gate if ((uint_t)(now - dinfo.timestamp) < DELAY_SECS) 539*0Sstevel@tonic-gate (void) sleep(DELAY_SECS - (now - dinfo.timestamp)); 540*0Sstevel@tonic-gate nak(dinfo.ecode); 541*0Sstevel@tonic-gate (void) close(peer); 542*0Sstevel@tonic-gate } /* for */ 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate /* NOTREACHED */ 545*0Sstevel@tonic-gate } 546*0Sstevel@tonic-gate 547*0Sstevel@tonic-gate /* 548*0Sstevel@tonic-gate * Handle the Blocksize option. 549*0Sstevel@tonic-gate * Return the blksize option value string to include in the OACK reply. 550*0Sstevel@tonic-gate */ 551*0Sstevel@tonic-gate /*ARGSUSED*/ 552*0Sstevel@tonic-gate static char * 553*0Sstevel@tonic-gate blksize_handler(int opcode, char *optval, int *errcode) 554*0Sstevel@tonic-gate { 555*0Sstevel@tonic-gate char *endp; 556*0Sstevel@tonic-gate int value; 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate *errcode = -1; 559*0Sstevel@tonic-gate errno = 0; 560*0Sstevel@tonic-gate value = (int)strtol(optval, &endp, 10); 561*0Sstevel@tonic-gate if (errno != 0 || value < MIN_BLKSIZE || *endp != '\0') 562*0Sstevel@tonic-gate return (NULL); 563*0Sstevel@tonic-gate /* 564*0Sstevel@tonic-gate * As the blksize value in the OACK reply can be less than the value 565*0Sstevel@tonic-gate * requested, to support broken clients if the value requested is larger 566*0Sstevel@tonic-gate * than allowed in the RFC, reply with the maximum value permitted. 567*0Sstevel@tonic-gate */ 568*0Sstevel@tonic-gate if (value > MAX_BLKSIZE) 569*0Sstevel@tonic-gate value = MAX_BLKSIZE; 570*0Sstevel@tonic-gate 571*0Sstevel@tonic-gate blocksize = value; 572*0Sstevel@tonic-gate (void) snprintf(optbuf, sizeof (optbuf), "%d", blocksize); 573*0Sstevel@tonic-gate return (optbuf); 574*0Sstevel@tonic-gate } 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate /* 577*0Sstevel@tonic-gate * Handle the Timeout Interval option. 578*0Sstevel@tonic-gate * Return the timeout option value string to include in the OACK reply. 579*0Sstevel@tonic-gate */ 580*0Sstevel@tonic-gate /*ARGSUSED*/ 581*0Sstevel@tonic-gate static char * 582*0Sstevel@tonic-gate timeout_handler(int opcode, char *optval, int *errcode) 583*0Sstevel@tonic-gate { 584*0Sstevel@tonic-gate char *endp; 585*0Sstevel@tonic-gate int value; 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate *errcode = -1; 588*0Sstevel@tonic-gate errno = 0; 589*0Sstevel@tonic-gate value = (int)strtol(optval, &endp, 10); 590*0Sstevel@tonic-gate if (errno != 0 || *endp != '\0') 591*0Sstevel@tonic-gate return (NULL); 592*0Sstevel@tonic-gate /* 593*0Sstevel@tonic-gate * The timeout value in the OACK reply must match the value specified 594*0Sstevel@tonic-gate * by the client, so if an invalid timeout is requested don't include 595*0Sstevel@tonic-gate * the timeout option in the OACK reply. 596*0Sstevel@tonic-gate */ 597*0Sstevel@tonic-gate if (value < MIN_TIMEOUT || value > MAX_TIMEOUT) 598*0Sstevel@tonic-gate return (NULL); 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate rexmtval = value; 601*0Sstevel@tonic-gate maxtimeout = 5 * rexmtval; 602*0Sstevel@tonic-gate (void) snprintf(optbuf, sizeof (optbuf), "%d", rexmtval); 603*0Sstevel@tonic-gate return (optbuf); 604*0Sstevel@tonic-gate } 605*0Sstevel@tonic-gate 606*0Sstevel@tonic-gate /* 607*0Sstevel@tonic-gate * Handle the Transfer Size option. 608*0Sstevel@tonic-gate * Return the tsize option value string to include in the OACK reply. 609*0Sstevel@tonic-gate */ 610*0Sstevel@tonic-gate static char * 611*0Sstevel@tonic-gate tsize_handler(int opcode, char *optval, int *errcode) 612*0Sstevel@tonic-gate { 613*0Sstevel@tonic-gate char *endp; 614*0Sstevel@tonic-gate longlong_t value; 615*0Sstevel@tonic-gate 616*0Sstevel@tonic-gate *errcode = -1; 617*0Sstevel@tonic-gate errno = 0; 618*0Sstevel@tonic-gate value = strtoll(optval, &endp, 10); 619*0Sstevel@tonic-gate if (errno != 0 || value < 0 || *endp != '\0') 620*0Sstevel@tonic-gate return (NULL); 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate if (opcode == RRQ) { 623*0Sstevel@tonic-gate if (tsize_set == B_FALSE) 624*0Sstevel@tonic-gate return (NULL); 625*0Sstevel@tonic-gate /* 626*0Sstevel@tonic-gate * The tsize value should be 0 for a read request, but to 627*0Sstevel@tonic-gate * support broken clients we don't check that it is. 628*0Sstevel@tonic-gate */ 629*0Sstevel@tonic-gate } else { 630*0Sstevel@tonic-gate #if _FILE_OFFSET_BITS == 32 631*0Sstevel@tonic-gate if (value > MAXOFF_T) { 632*0Sstevel@tonic-gate *errcode = ENOSPACE; 633*0Sstevel@tonic-gate return (NULL); 634*0Sstevel@tonic-gate } 635*0Sstevel@tonic-gate #endif 636*0Sstevel@tonic-gate tsize = value; 637*0Sstevel@tonic-gate tsize_set = B_TRUE; 638*0Sstevel@tonic-gate } 639*0Sstevel@tonic-gate (void) snprintf(optbuf, sizeof (optbuf), OFF_T_FMT, tsize); 640*0Sstevel@tonic-gate return (optbuf); 641*0Sstevel@tonic-gate } 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate /* 644*0Sstevel@tonic-gate * Process any options included by the client in the request packet. 645*0Sstevel@tonic-gate * Return the size of the OACK reply packet built or 0 for no OACK reply. 646*0Sstevel@tonic-gate */ 647*0Sstevel@tonic-gate static int 648*0Sstevel@tonic-gate process_options(int opcode, char *opts, char *endopts) 649*0Sstevel@tonic-gate { 650*0Sstevel@tonic-gate char *cp, *optname, *optval, *ostr, *oackend; 651*0Sstevel@tonic-gate struct tftphdr *oackp; 652*0Sstevel@tonic-gate int i, errcode; 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate /* 655*0Sstevel@tonic-gate * To continue to interoperate with broken TFTP clients, ignore 656*0Sstevel@tonic-gate * null padding appended to requests which don't include options. 657*0Sstevel@tonic-gate */ 658*0Sstevel@tonic-gate cp = opts; 659*0Sstevel@tonic-gate while ((cp < endopts) && (*cp == '\0')) 660*0Sstevel@tonic-gate cp++; 661*0Sstevel@tonic-gate if (cp == endopts) 662*0Sstevel@tonic-gate return (0); 663*0Sstevel@tonic-gate 664*0Sstevel@tonic-gate /* 665*0Sstevel@tonic-gate * Construct an Option ACKnowledgement packet if any requested option 666*0Sstevel@tonic-gate * is recognized. 667*0Sstevel@tonic-gate */ 668*0Sstevel@tonic-gate oackp = &oackbuf.hdr; 669*0Sstevel@tonic-gate oackend = oackbuf.data + sizeof (oackbuf.data); 670*0Sstevel@tonic-gate oackp->th_opcode = htons((ushort_t)OACK); 671*0Sstevel@tonic-gate cp = (char *)&oackp->th_stuff; 672*0Sstevel@tonic-gate while (opts < endopts) { 673*0Sstevel@tonic-gate optname = opts; 674*0Sstevel@tonic-gate if ((optval = next_field(optname, endopts)) == NULL) { 675*0Sstevel@tonic-gate nak(EOPTNEG); 676*0Sstevel@tonic-gate exit(1); 677*0Sstevel@tonic-gate } 678*0Sstevel@tonic-gate if ((opts = next_field(optval, endopts)) == NULL) { 679*0Sstevel@tonic-gate nak(EOPTNEG); 680*0Sstevel@tonic-gate exit(1); 681*0Sstevel@tonic-gate } 682*0Sstevel@tonic-gate for (i = 0; options[i].opt_name != NULL; i++) { 683*0Sstevel@tonic-gate if (strcasecmp(optname, options[i].opt_name) == 0) 684*0Sstevel@tonic-gate break; 685*0Sstevel@tonic-gate } 686*0Sstevel@tonic-gate if (options[i].opt_name != NULL) { 687*0Sstevel@tonic-gate ostr = options[i].opt_handler(opcode, optval, &errcode); 688*0Sstevel@tonic-gate if (ostr != NULL) { 689*0Sstevel@tonic-gate cp += strlcpy(cp, options[i].opt_name, 690*0Sstevel@tonic-gate oackend - cp) + 1; 691*0Sstevel@tonic-gate if (cp <= oackend) 692*0Sstevel@tonic-gate cp += strlcpy(cp, ostr, oackend - cp) 693*0Sstevel@tonic-gate + 1; 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate if (cp > oackend) { 696*0Sstevel@tonic-gate nak(EOPTNEG); 697*0Sstevel@tonic-gate exit(1); 698*0Sstevel@tonic-gate } 699*0Sstevel@tonic-gate } else if (errcode >= 0) { 700*0Sstevel@tonic-gate nak(errcode); 701*0Sstevel@tonic-gate exit(1); 702*0Sstevel@tonic-gate } 703*0Sstevel@tonic-gate } 704*0Sstevel@tonic-gate } 705*0Sstevel@tonic-gate if (cp != (char *)&oackp->th_stuff) 706*0Sstevel@tonic-gate return (cp - oackbuf.data); 707*0Sstevel@tonic-gate return (0); 708*0Sstevel@tonic-gate } 709*0Sstevel@tonic-gate 710*0Sstevel@tonic-gate /* 711*0Sstevel@tonic-gate * Handle access errors caused by client requests. 712*0Sstevel@tonic-gate */ 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate static void 715*0Sstevel@tonic-gate delay_exit(int ecode) 716*0Sstevel@tonic-gate { 717*0Sstevel@tonic-gate struct delay_info dinfo; 718*0Sstevel@tonic-gate 719*0Sstevel@tonic-gate /* 720*0Sstevel@tonic-gate * The most likely cause of an error here is that 721*0Sstevel@tonic-gate * someone has broadcast an RRQ packet because s/he's 722*0Sstevel@tonic-gate * trying to boot and doesn't know who the server is. 723*0Sstevel@tonic-gate * Rather then sending an ERROR packet immediately, we 724*0Sstevel@tonic-gate * wait a while so that the real server has a better chance 725*0Sstevel@tonic-gate * of getting through (in case client has lousy Ethernet 726*0Sstevel@tonic-gate * interface). We write to a child that handles delayed 727*0Sstevel@tonic-gate * ERROR packets to avoid delaying service to new 728*0Sstevel@tonic-gate * requests. Of course, we would rather just not answer 729*0Sstevel@tonic-gate * RRQ packets that are broadcasted, but there's no way 730*0Sstevel@tonic-gate * for a user process to determine this. 731*0Sstevel@tonic-gate */ 732*0Sstevel@tonic-gate 733*0Sstevel@tonic-gate dinfo.timestamp = time(0); 734*0Sstevel@tonic-gate 735*0Sstevel@tonic-gate /* 736*0Sstevel@tonic-gate * If running in secure mode, we map all errors to EACCESS 737*0Sstevel@tonic-gate * so that the client gets no information about which files 738*0Sstevel@tonic-gate * or directories exist. 739*0Sstevel@tonic-gate */ 740*0Sstevel@tonic-gate if (securetftp) 741*0Sstevel@tonic-gate dinfo.ecode = EACCESS; 742*0Sstevel@tonic-gate else 743*0Sstevel@tonic-gate dinfo.ecode = ecode; 744*0Sstevel@tonic-gate 745*0Sstevel@tonic-gate dinfo.from = from; 746*0Sstevel@tonic-gate if (write(delay_fd[1], &dinfo, sizeof (dinfo)) != 747*0Sstevel@tonic-gate sizeof (dinfo)) { 748*0Sstevel@tonic-gate syslog(LOG_ERR, "delayed write failed."); 749*0Sstevel@tonic-gate (void) kill(child, SIGKILL); 750*0Sstevel@tonic-gate exit(1); 751*0Sstevel@tonic-gate } 752*0Sstevel@tonic-gate exit(0); 753*0Sstevel@tonic-gate } 754*0Sstevel@tonic-gate 755*0Sstevel@tonic-gate /* 756*0Sstevel@tonic-gate * Handle initial connection protocol. 757*0Sstevel@tonic-gate */ 758*0Sstevel@tonic-gate static void 759*0Sstevel@tonic-gate tftp(struct tftphdr *tp, int size) 760*0Sstevel@tonic-gate { 761*0Sstevel@tonic-gate char *cp; 762*0Sstevel@tonic-gate int readmode, ecode; 763*0Sstevel@tonic-gate struct formats *pf; 764*0Sstevel@tonic-gate char *mode; 765*0Sstevel@tonic-gate int fd; 766*0Sstevel@tonic-gate static boolean_t firsttime = B_TRUE; 767*0Sstevel@tonic-gate int oacklen; 768*0Sstevel@tonic-gate struct stat statb; 769*0Sstevel@tonic-gate 770*0Sstevel@tonic-gate readmode = (tp->th_opcode == RRQ); 771*0Sstevel@tonic-gate filename = (char *)&tp->th_stuff; 772*0Sstevel@tonic-gate mode = next_field(filename, &buf.data[size]); 773*0Sstevel@tonic-gate cp = (mode != NULL) ? next_field(mode, &buf.data[size]) : NULL; 774*0Sstevel@tonic-gate if (cp == NULL) { 775*0Sstevel@tonic-gate nak(EBADOP); 776*0Sstevel@tonic-gate exit(1); 777*0Sstevel@tonic-gate } 778*0Sstevel@tonic-gate if (debug && standalone) { 779*0Sstevel@tonic-gate (void) fprintf(stderr, "%s for %s %s ", 780*0Sstevel@tonic-gate readmode ? "RRQ" : "WRQ", filename, mode); 781*0Sstevel@tonic-gate print_options(stderr, cp, size + buf.data - cp); 782*0Sstevel@tonic-gate (void) putc('\n', stderr); 783*0Sstevel@tonic-gate } 784*0Sstevel@tonic-gate for (pf = formats; pf->f_mode != NULL; pf++) 785*0Sstevel@tonic-gate if (strcasecmp(pf->f_mode, mode) == 0) 786*0Sstevel@tonic-gate break; 787*0Sstevel@tonic-gate if (pf->f_mode == NULL) { 788*0Sstevel@tonic-gate nak(EBADOP); 789*0Sstevel@tonic-gate exit(1); 790*0Sstevel@tonic-gate } 791*0Sstevel@tonic-gate 792*0Sstevel@tonic-gate /* 793*0Sstevel@tonic-gate * XXX fork a new process to handle this request before 794*0Sstevel@tonic-gate * chroot(), otherwise the parent won't be able to create a 795*0Sstevel@tonic-gate * new socket as that requires library access to system files 796*0Sstevel@tonic-gate * and devices. 797*0Sstevel@tonic-gate */ 798*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL); 799*0Sstevel@tonic-gate switch (fork()) { 800*0Sstevel@tonic-gate case -1: 801*0Sstevel@tonic-gate syslog(LOG_ERR, "fork (tftp): %m"); 802*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL); 803*0Sstevel@tonic-gate return; 804*0Sstevel@tonic-gate case 0: 805*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL); 806*0Sstevel@tonic-gate break; 807*0Sstevel@tonic-gate default: 808*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL); 809*0Sstevel@tonic-gate return; 810*0Sstevel@tonic-gate } 811*0Sstevel@tonic-gate 812*0Sstevel@tonic-gate /* 813*0Sstevel@tonic-gate * Try to see if we can access the file. The access can still 814*0Sstevel@tonic-gate * fail later if we are running in secure mode because of 815*0Sstevel@tonic-gate * the chroot() call. We only want to execute the chroot() once. 816*0Sstevel@tonic-gate */ 817*0Sstevel@tonic-gate if (securetftp && firsttime) { 818*0Sstevel@tonic-gate (void) priv_set( 819*0Sstevel@tonic-gate PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_CHROOT, NULL); 820*0Sstevel@tonic-gate if (chroot(homedir) == -1) { 821*0Sstevel@tonic-gate syslog(LOG_ERR, 822*0Sstevel@tonic-gate "tftpd: cannot chroot to directory %s: %m\n", 823*0Sstevel@tonic-gate homedir); 824*0Sstevel@tonic-gate delay_exit(EACCESS); 825*0Sstevel@tonic-gate } 826*0Sstevel@tonic-gate else 827*0Sstevel@tonic-gate { 828*0Sstevel@tonic-gate firsttime = B_FALSE; 829*0Sstevel@tonic-gate } 830*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL); 831*0Sstevel@tonic-gate (void) chdir("/"); /* cd to new root */ 832*0Sstevel@tonic-gate } 833*0Sstevel@tonic-gate (void) priv_set(PRIV_SET, PRIV_ALLSETS, NULL); 834*0Sstevel@tonic-gate 835*0Sstevel@tonic-gate ecode = (*pf->f_validate)(tp->th_opcode); 836*0Sstevel@tonic-gate if (ecode != 0) 837*0Sstevel@tonic-gate delay_exit(ecode); 838*0Sstevel@tonic-gate 839*0Sstevel@tonic-gate /* we don't use the descriptors passed in to the parent */ 840*0Sstevel@tonic-gate (void) close(STDIN_FILENO); 841*0Sstevel@tonic-gate (void) close(STDOUT_FILENO); 842*0Sstevel@tonic-gate 843*0Sstevel@tonic-gate /* 844*0Sstevel@tonic-gate * Try to open file as low-priv setuid/setgid. Note that 845*0Sstevel@tonic-gate * a chroot() has already been done. 846*0Sstevel@tonic-gate */ 847*0Sstevel@tonic-gate fd = open(filename, 848*0Sstevel@tonic-gate (readmode ? O_RDONLY : (O_WRONLY|O_TRUNC)) | O_NONBLOCK); 849*0Sstevel@tonic-gate if ((fd < 0) || (fstat(fd, &statb) < 0)) 850*0Sstevel@tonic-gate delay_exit((errno == ENOENT) ? ENOTFOUND : EACCESS); 851*0Sstevel@tonic-gate 852*0Sstevel@tonic-gate if (((statb.st_mode & ((readmode) ? S_IROTH : S_IWOTH)) == 0) || 853*0Sstevel@tonic-gate ((statb.st_mode & S_IFMT) != S_IFREG)) 854*0Sstevel@tonic-gate delay_exit(EACCESS); 855*0Sstevel@tonic-gate 856*0Sstevel@tonic-gate file = fdopen(fd, readmode ? "r" : "w"); 857*0Sstevel@tonic-gate if (file == NULL) 858*0Sstevel@tonic-gate delay_exit(errno + 100); 859*0Sstevel@tonic-gate 860*0Sstevel@tonic-gate /* Don't know the size of transfers which involve conversion */ 861*0Sstevel@tonic-gate tsize_set = (readmode && (pf->f_convert == 0)); 862*0Sstevel@tonic-gate if (tsize_set) 863*0Sstevel@tonic-gate tsize = statb.st_size; 864*0Sstevel@tonic-gate 865*0Sstevel@tonic-gate /* Deal with any options sent by the client */ 866*0Sstevel@tonic-gate oacklen = process_options(tp->th_opcode, cp, buf.data + size); 867*0Sstevel@tonic-gate 868*0Sstevel@tonic-gate if (tp->th_opcode == WRQ) 869*0Sstevel@tonic-gate (*pf->f_recv)(pf, oacklen); 870*0Sstevel@tonic-gate else 871*0Sstevel@tonic-gate (*pf->f_send)(pf, oacklen); 872*0Sstevel@tonic-gate 873*0Sstevel@tonic-gate exit(0); 874*0Sstevel@tonic-gate } 875*0Sstevel@tonic-gate 876*0Sstevel@tonic-gate /* 877*0Sstevel@tonic-gate * Maybe map filename into another one. 878*0Sstevel@tonic-gate * 879*0Sstevel@tonic-gate * For PNP, we get TFTP boot requests for filenames like 880*0Sstevel@tonic-gate * <Unknown Hex IP Addr>.<Architecture Name>. We must 881*0Sstevel@tonic-gate * map these to 'pnp.<Architecture Name>'. Note that 882*0Sstevel@tonic-gate * uppercase is mapped to lowercase in the architecture names. 883*0Sstevel@tonic-gate * 884*0Sstevel@tonic-gate * For names <Hex IP Addr> there are two cases. First, 885*0Sstevel@tonic-gate * it may be a buggy prom that omits the architecture code. 886*0Sstevel@tonic-gate * So first check if <Hex IP Addr>.<arch> is on the filesystem. 887*0Sstevel@tonic-gate * Second, this is how most Sun3s work; assume <arch> is sun3. 888*0Sstevel@tonic-gate */ 889*0Sstevel@tonic-gate 890*0Sstevel@tonic-gate static char * 891*0Sstevel@tonic-gate pnp_check(char *origname) 892*0Sstevel@tonic-gate { 893*0Sstevel@tonic-gate static char buf [MAXNAMLEN + 1]; 894*0Sstevel@tonic-gate char *arch, *s, *bufend; 895*0Sstevel@tonic-gate in_addr_t ipaddr; 896*0Sstevel@tonic-gate int len = (origname ? strlen(origname) : 0); 897*0Sstevel@tonic-gate DIR *dir; 898*0Sstevel@tonic-gate struct dirent *dp; 899*0Sstevel@tonic-gate 900*0Sstevel@tonic-gate if (securetftp || disable_pnp || len < 8 || len > 14) 901*0Sstevel@tonic-gate return (NULL); 902*0Sstevel@tonic-gate 903*0Sstevel@tonic-gate /* 904*0Sstevel@tonic-gate * XXX see if this cable allows pnp; if not, return NULL 905*0Sstevel@tonic-gate * Requires YP support for determining this! 906*0Sstevel@tonic-gate */ 907*0Sstevel@tonic-gate 908*0Sstevel@tonic-gate ipaddr = htonl(strtol(origname, &arch, 16)); 909*0Sstevel@tonic-gate if ((arch == NULL) || (len > 8 && *arch != '.')) 910*0Sstevel@tonic-gate return (NULL); 911*0Sstevel@tonic-gate if (len == 8) 912*0Sstevel@tonic-gate arch = "SUN3"; 913*0Sstevel@tonic-gate else 914*0Sstevel@tonic-gate arch++; 915*0Sstevel@tonic-gate 916*0Sstevel@tonic-gate /* 917*0Sstevel@tonic-gate * Allow <Hex IP Addr>* filename request to to be 918*0Sstevel@tonic-gate * satisfied by <Hex IP Addr><Any Suffix> rather 919*0Sstevel@tonic-gate * than enforcing this to be Sun3 systems. Also serves 920*0Sstevel@tonic-gate * to make case of suffix a don't-care. 921*0Sstevel@tonic-gate */ 922*0Sstevel@tonic-gate if ((dir = opendir(homedir)) == NULL) 923*0Sstevel@tonic-gate return (NULL); 924*0Sstevel@tonic-gate while ((dp = readdir(dir)) != NULL) { 925*0Sstevel@tonic-gate if (strncmp(origname, dp->d_name, 8) == 0) { 926*0Sstevel@tonic-gate (void) strlcpy(buf, dp->d_name, sizeof (buf)); 927*0Sstevel@tonic-gate (void) closedir(dir); 928*0Sstevel@tonic-gate return (buf); 929*0Sstevel@tonic-gate } 930*0Sstevel@tonic-gate } 931*0Sstevel@tonic-gate (void) closedir(dir); 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate /* 934*0Sstevel@tonic-gate * XXX maybe call YP master for most current data iff 935*0Sstevel@tonic-gate * pnp is enabled. 936*0Sstevel@tonic-gate */ 937*0Sstevel@tonic-gate 938*0Sstevel@tonic-gate /* 939*0Sstevel@tonic-gate * only do mapping PNP boot file name for machines that 940*0Sstevel@tonic-gate * are not in the hosts database. 941*0Sstevel@tonic-gate */ 942*0Sstevel@tonic-gate if (gethostbyaddr((char *)&ipaddr, sizeof (ipaddr), AF_INET) != NULL) 943*0Sstevel@tonic-gate return (NULL); 944*0Sstevel@tonic-gate 945*0Sstevel@tonic-gate s = buf + strlcpy(buf, "pnp.", sizeof (buf)); 946*0Sstevel@tonic-gate bufend = &buf[sizeof (buf) - 1]; 947*0Sstevel@tonic-gate while ((*arch != '\0') && (s < bufend)) 948*0Sstevel@tonic-gate *s++ = tolower (*arch++); 949*0Sstevel@tonic-gate *s = '\0'; 950*0Sstevel@tonic-gate return (buf); 951*0Sstevel@tonic-gate } 952*0Sstevel@tonic-gate 953*0Sstevel@tonic-gate 954*0Sstevel@tonic-gate /* 955*0Sstevel@tonic-gate * Try to validate filename. If the filename doesn't exist try PNP mapping. 956*0Sstevel@tonic-gate */ 957*0Sstevel@tonic-gate static int 958*0Sstevel@tonic-gate validate_filename(int mode) 959*0Sstevel@tonic-gate { 960*0Sstevel@tonic-gate struct stat stbuf; 961*0Sstevel@tonic-gate char *origfile; 962*0Sstevel@tonic-gate 963*0Sstevel@tonic-gate if (stat(filename, &stbuf) < 0) { 964*0Sstevel@tonic-gate if (errno != ENOENT) 965*0Sstevel@tonic-gate return (EACCESS); 966*0Sstevel@tonic-gate if (mode == WRQ) 967*0Sstevel@tonic-gate return (ENOTFOUND); 968*0Sstevel@tonic-gate 969*0Sstevel@tonic-gate /* try to map requested filename into a pnp filename */ 970*0Sstevel@tonic-gate origfile = filename; 971*0Sstevel@tonic-gate filename = pnp_check(origfile); 972*0Sstevel@tonic-gate if (filename == NULL) 973*0Sstevel@tonic-gate return (ENOTFOUND); 974*0Sstevel@tonic-gate 975*0Sstevel@tonic-gate if (stat(filename, &stbuf) < 0) 976*0Sstevel@tonic-gate return (errno == ENOENT ? ENOTFOUND : EACCESS); 977*0Sstevel@tonic-gate syslog(LOG_NOTICE, "%s -> %s\n", origfile, filename); 978*0Sstevel@tonic-gate } 979*0Sstevel@tonic-gate 980*0Sstevel@tonic-gate return (0); 981*0Sstevel@tonic-gate } 982*0Sstevel@tonic-gate 983*0Sstevel@tonic-gate /* ARGSUSED */ 984*0Sstevel@tonic-gate static void 985*0Sstevel@tonic-gate timer(int signum) 986*0Sstevel@tonic-gate { 987*0Sstevel@tonic-gate timeout += rexmtval; 988*0Sstevel@tonic-gate if (timeout >= maxtimeout) 989*0Sstevel@tonic-gate exit(1); 990*0Sstevel@tonic-gate siglongjmp(timeoutbuf, 1); 991*0Sstevel@tonic-gate } 992*0Sstevel@tonic-gate 993*0Sstevel@tonic-gate /* 994*0Sstevel@tonic-gate * Send the requested file. 995*0Sstevel@tonic-gate */ 996*0Sstevel@tonic-gate static void 997*0Sstevel@tonic-gate tftpd_sendfile(struct formats *pf, int oacklen) 998*0Sstevel@tonic-gate { 999*0Sstevel@tonic-gate struct tftphdr *dp; 1000*0Sstevel@tonic-gate volatile int block = 1; 1001*0Sstevel@tonic-gate int size, n, serrno; 1002*0Sstevel@tonic-gate 1003*0Sstevel@tonic-gate if (oacklen != 0) { 1004*0Sstevel@tonic-gate (void) sigset(SIGALRM, timer); 1005*0Sstevel@tonic-gate timeout = 0; 1006*0Sstevel@tonic-gate (void) sigsetjmp(timeoutbuf, 1); 1007*0Sstevel@tonic-gate if (debug && standalone) { 1008*0Sstevel@tonic-gate (void) fputs("Sending OACK ", stderr); 1009*0Sstevel@tonic-gate print_options(stderr, (char *)&oackbuf.hdr.th_stuff, 1010*0Sstevel@tonic-gate oacklen - 2); 1011*0Sstevel@tonic-gate (void) putc('\n', stderr); 1012*0Sstevel@tonic-gate } 1013*0Sstevel@tonic-gate if (sendto(peer, &oackbuf, oacklen, 0, 1014*0Sstevel@tonic-gate (struct sockaddr *)&from, fromplen) != oacklen) { 1015*0Sstevel@tonic-gate if (debug && standalone) { 1016*0Sstevel@tonic-gate serrno = errno; 1017*0Sstevel@tonic-gate perror("sendto (oack)"); 1018*0Sstevel@tonic-gate errno = serrno; 1019*0Sstevel@tonic-gate } 1020*0Sstevel@tonic-gate SYSLOG_MSG("sendto (oack): %m"); 1021*0Sstevel@tonic-gate goto abort; 1022*0Sstevel@tonic-gate } 1023*0Sstevel@tonic-gate (void) alarm(rexmtval); /* read the ack */ 1024*0Sstevel@tonic-gate for (;;) { 1025*0Sstevel@tonic-gate (void) sigrelse(SIGALRM); 1026*0Sstevel@tonic-gate n = recv(peer, &ackbuf, sizeof (ackbuf), 0); 1027*0Sstevel@tonic-gate (void) sighold(SIGALRM); 1028*0Sstevel@tonic-gate if (n < 0) { 1029*0Sstevel@tonic-gate if (errno == EINTR) 1030*0Sstevel@tonic-gate continue; 1031*0Sstevel@tonic-gate serrno = errno; 1032*0Sstevel@tonic-gate SYSLOG_MSG("recv (ack): %m"); 1033*0Sstevel@tonic-gate if (debug && standalone) { 1034*0Sstevel@tonic-gate errno = serrno; 1035*0Sstevel@tonic-gate perror("recv (ack)"); 1036*0Sstevel@tonic-gate } 1037*0Sstevel@tonic-gate goto abort; 1038*0Sstevel@tonic-gate } 1039*0Sstevel@tonic-gate ackbuf.tb_hdr.th_opcode = 1040*0Sstevel@tonic-gate ntohs((ushort_t)ackbuf.tb_hdr.th_opcode); 1041*0Sstevel@tonic-gate ackbuf.tb_hdr.th_block = 1042*0Sstevel@tonic-gate ntohs((ushort_t)ackbuf.tb_hdr.th_block); 1043*0Sstevel@tonic-gate 1044*0Sstevel@tonic-gate if (ackbuf.tb_hdr.th_opcode == ERROR) { 1045*0Sstevel@tonic-gate if (debug && standalone) { 1046*0Sstevel@tonic-gate (void) fprintf(stderr, 1047*0Sstevel@tonic-gate "received ERROR %d", 1048*0Sstevel@tonic-gate ackbuf.tb_hdr.th_code); 1049*0Sstevel@tonic-gate if (n > 4) 1050*0Sstevel@tonic-gate (void) fprintf(stderr, 1051*0Sstevel@tonic-gate " %.*s", n - 4, 1052*0Sstevel@tonic-gate ackbuf.tb_hdr.th_msg); 1053*0Sstevel@tonic-gate (void) putc('\n', stderr); 1054*0Sstevel@tonic-gate } 1055*0Sstevel@tonic-gate goto abort; 1056*0Sstevel@tonic-gate } 1057*0Sstevel@tonic-gate 1058*0Sstevel@tonic-gate if (ackbuf.tb_hdr.th_opcode == ACK) { 1059*0Sstevel@tonic-gate if (debug && standalone) 1060*0Sstevel@tonic-gate (void) fprintf(stderr, 1061*0Sstevel@tonic-gate "received ACK for block %d\n", 1062*0Sstevel@tonic-gate ackbuf.tb_hdr.th_block); 1063*0Sstevel@tonic-gate if (ackbuf.tb_hdr.th_block == 0) 1064*0Sstevel@tonic-gate break; 1065*0Sstevel@tonic-gate /* 1066*0Sstevel@tonic-gate * Don't resend the OACK, avoids getting stuck 1067*0Sstevel@tonic-gate * in an OACK/ACK loop if the client keeps 1068*0Sstevel@tonic-gate * replying with a bad ACK. Client will either 1069*0Sstevel@tonic-gate * send a good ACK or timeout sending bad ones. 1070*0Sstevel@tonic-gate */ 1071*0Sstevel@tonic-gate } 1072*0Sstevel@tonic-gate } 1073*0Sstevel@tonic-gate cancel_alarm(); 1074*0Sstevel@tonic-gate } 1075*0Sstevel@tonic-gate dp = r_init(); 1076*0Sstevel@tonic-gate do { 1077*0Sstevel@tonic-gate (void) sigset(SIGALRM, timer); 1078*0Sstevel@tonic-gate size = readit(file, &dp, pf->f_convert); 1079*0Sstevel@tonic-gate if (size < 0) { 1080*0Sstevel@tonic-gate nak(errno + 100); 1081*0Sstevel@tonic-gate goto abort; 1082*0Sstevel@tonic-gate } 1083*0Sstevel@tonic-gate dp->th_opcode = htons((ushort_t)DATA); 1084*0Sstevel@tonic-gate dp->th_block = htons((ushort_t)block); 1085*0Sstevel@tonic-gate timeout = 0; 1086*0Sstevel@tonic-gate (void) sigsetjmp(timeoutbuf, 1); 1087*0Sstevel@tonic-gate if (debug && standalone) 1088*0Sstevel@tonic-gate (void) fprintf(stderr, "Sending DATA block %d\n", 1089*0Sstevel@tonic-gate block); 1090*0Sstevel@tonic-gate if (sendto(peer, dp, size + 4, 0, 1091*0Sstevel@tonic-gate (struct sockaddr *)&from, fromplen) != size + 4) { 1092*0Sstevel@tonic-gate if (debug && standalone) { 1093*0Sstevel@tonic-gate serrno = errno; 1094*0Sstevel@tonic-gate perror("sendto (data)"); 1095*0Sstevel@tonic-gate errno = serrno; 1096*0Sstevel@tonic-gate } 1097*0Sstevel@tonic-gate SYSLOG_MSG("sendto (data): %m"); 1098*0Sstevel@tonic-gate goto abort; 1099*0Sstevel@tonic-gate } 1100*0Sstevel@tonic-gate read_ahead(file, pf->f_convert); 1101*0Sstevel@tonic-gate (void) alarm(rexmtval); /* read the ack */ 1102*0Sstevel@tonic-gate for (;;) { 1103*0Sstevel@tonic-gate (void) sigrelse(SIGALRM); 1104*0Sstevel@tonic-gate n = recv(peer, &ackbuf, sizeof (ackbuf), 0); 1105*0Sstevel@tonic-gate (void) sighold(SIGALRM); 1106*0Sstevel@tonic-gate if (n < 0) { 1107*0Sstevel@tonic-gate if (errno == EINTR) 1108*0Sstevel@tonic-gate continue; 1109*0Sstevel@tonic-gate serrno = errno; 1110*0Sstevel@tonic-gate SYSLOG_MSG("recv (ack): %m"); 1111*0Sstevel@tonic-gate if (debug && standalone) { 1112*0Sstevel@tonic-gate errno = serrno; 1113*0Sstevel@tonic-gate perror("recv (ack)"); 1114*0Sstevel@tonic-gate } 1115*0Sstevel@tonic-gate goto abort; 1116*0Sstevel@tonic-gate } 1117*0Sstevel@tonic-gate ackbuf.tb_hdr.th_opcode = 1118*0Sstevel@tonic-gate ntohs((ushort_t)ackbuf.tb_hdr.th_opcode); 1119*0Sstevel@tonic-gate ackbuf.tb_hdr.th_block = 1120*0Sstevel@tonic-gate ntohs((ushort_t)ackbuf.tb_hdr.th_block); 1121*0Sstevel@tonic-gate 1122*0Sstevel@tonic-gate if (ackbuf.tb_hdr.th_opcode == ERROR) { 1123*0Sstevel@tonic-gate if (debug && standalone) { 1124*0Sstevel@tonic-gate (void) fprintf(stderr, 1125*0Sstevel@tonic-gate "received ERROR %d", 1126*0Sstevel@tonic-gate ackbuf.tb_hdr.th_code); 1127*0Sstevel@tonic-gate if (n > 4) 1128*0Sstevel@tonic-gate (void) fprintf(stderr, 1129*0Sstevel@tonic-gate " %.*s", n - 4, 1130*0Sstevel@tonic-gate ackbuf.tb_hdr.th_msg); 1131*0Sstevel@tonic-gate (void) putc('\n', stderr); 1132*0Sstevel@tonic-gate } 1133*0Sstevel@tonic-gate goto abort; 1134*0Sstevel@tonic-gate } 1135*0Sstevel@tonic-gate 1136*0Sstevel@tonic-gate if (ackbuf.tb_hdr.th_opcode == ACK) { 1137*0Sstevel@tonic-gate if (debug && standalone) 1138*0Sstevel@tonic-gate (void) fprintf(stderr, 1139*0Sstevel@tonic-gate "received ACK for block %d\n", 1140*0Sstevel@tonic-gate ackbuf.tb_hdr.th_block); 1141*0Sstevel@tonic-gate if (ackbuf.tb_hdr.th_block == block) { 1142*0Sstevel@tonic-gate break; 1143*0Sstevel@tonic-gate } 1144*0Sstevel@tonic-gate /* 1145*0Sstevel@tonic-gate * Never resend the current DATA packet on 1146*0Sstevel@tonic-gate * receipt of a duplicate ACK, doing so would 1147*0Sstevel@tonic-gate * cause the "Sorcerer's Apprentice Syndrome". 1148*0Sstevel@tonic-gate */ 1149*0Sstevel@tonic-gate } 1150*0Sstevel@tonic-gate } 1151*0Sstevel@tonic-gate cancel_alarm(); 1152*0Sstevel@tonic-gate block++; 1153*0Sstevel@tonic-gate } while (size == blocksize); 1154*0Sstevel@tonic-gate 1155*0Sstevel@tonic-gate abort: 1156*0Sstevel@tonic-gate cancel_alarm(); 1157*0Sstevel@tonic-gate (void) fclose(file); 1158*0Sstevel@tonic-gate } 1159*0Sstevel@tonic-gate 1160*0Sstevel@tonic-gate /* ARGSUSED */ 1161*0Sstevel@tonic-gate static void 1162*0Sstevel@tonic-gate justquit(int signum) 1163*0Sstevel@tonic-gate { 1164*0Sstevel@tonic-gate exit(0); 1165*0Sstevel@tonic-gate } 1166*0Sstevel@tonic-gate 1167*0Sstevel@tonic-gate /* 1168*0Sstevel@tonic-gate * Receive a file. 1169*0Sstevel@tonic-gate */ 1170*0Sstevel@tonic-gate static void 1171*0Sstevel@tonic-gate tftpd_recvfile(struct formats *pf, int oacklen) 1172*0Sstevel@tonic-gate { 1173*0Sstevel@tonic-gate struct tftphdr *dp; 1174*0Sstevel@tonic-gate struct tftphdr *ap; /* ack buffer */ 1175*0Sstevel@tonic-gate int block = 0, n, size, acklen, serrno; 1176*0Sstevel@tonic-gate 1177*0Sstevel@tonic-gate dp = w_init(); 1178*0Sstevel@tonic-gate ap = &ackbuf.tb_hdr; 1179*0Sstevel@tonic-gate do { 1180*0Sstevel@tonic-gate (void) sigset(SIGALRM, timer); 1181*0Sstevel@tonic-gate timeout = 0; 1182*0Sstevel@tonic-gate if (oacklen == 0) { 1183*0Sstevel@tonic-gate ap->th_opcode = htons((ushort_t)ACK); 1184*0Sstevel@tonic-gate ap->th_block = htons((ushort_t)block); 1185*0Sstevel@tonic-gate acklen = 4; 1186*0Sstevel@tonic-gate } else { 1187*0Sstevel@tonic-gate /* copy OACK packet to the ack buffer ready to send */ 1188*0Sstevel@tonic-gate (void) memcpy(&ackbuf, &oackbuf, oacklen); 1189*0Sstevel@tonic-gate acklen = oacklen; 1190*0Sstevel@tonic-gate oacklen = 0; 1191*0Sstevel@tonic-gate } 1192*0Sstevel@tonic-gate block++; 1193*0Sstevel@tonic-gate (void) sigsetjmp(timeoutbuf, 1); 1194*0Sstevel@tonic-gate send_ack: 1195*0Sstevel@tonic-gate if (debug && standalone) { 1196*0Sstevel@tonic-gate if (ap->th_opcode == htons((ushort_t)ACK)) { 1197*0Sstevel@tonic-gate (void) fprintf(stderr, 1198*0Sstevel@tonic-gate "Sending ACK for block %d\n", block - 1); 1199*0Sstevel@tonic-gate } else { 1200*0Sstevel@tonic-gate (void) fprintf(stderr, "Sending OACK "); 1201*0Sstevel@tonic-gate print_options(stderr, (char *)&ap->th_stuff, 1202*0Sstevel@tonic-gate acklen - 2); 1203*0Sstevel@tonic-gate (void) putc('\n', stderr); 1204*0Sstevel@tonic-gate } 1205*0Sstevel@tonic-gate } 1206*0Sstevel@tonic-gate if (sendto(peer, &ackbuf, acklen, 0, (struct sockaddr *)&from, 1207*0Sstevel@tonic-gate fromplen) != acklen) { 1208*0Sstevel@tonic-gate if (ap->th_opcode == htons((ushort_t)ACK)) { 1209*0Sstevel@tonic-gate if (debug && standalone) { 1210*0Sstevel@tonic-gate serrno = errno; 1211*0Sstevel@tonic-gate perror("sendto (ack)"); 1212*0Sstevel@tonic-gate errno = serrno; 1213*0Sstevel@tonic-gate } 1214*0Sstevel@tonic-gate syslog(LOG_ERR, "sendto (ack): %m\n"); 1215*0Sstevel@tonic-gate } else { 1216*0Sstevel@tonic-gate if (debug && standalone) { 1217*0Sstevel@tonic-gate serrno = errno; 1218*0Sstevel@tonic-gate perror("sendto (oack)"); 1219*0Sstevel@tonic-gate errno = serrno; 1220*0Sstevel@tonic-gate } 1221*0Sstevel@tonic-gate syslog(LOG_ERR, "sendto (oack): %m\n"); 1222*0Sstevel@tonic-gate } 1223*0Sstevel@tonic-gate goto abort; 1224*0Sstevel@tonic-gate } 1225*0Sstevel@tonic-gate if (write_behind(file, pf->f_convert) < 0) { 1226*0Sstevel@tonic-gate nak(errno + 100); 1227*0Sstevel@tonic-gate goto abort; 1228*0Sstevel@tonic-gate } 1229*0Sstevel@tonic-gate (void) alarm(rexmtval); 1230*0Sstevel@tonic-gate for (;;) { 1231*0Sstevel@tonic-gate (void) sigrelse(SIGALRM); 1232*0Sstevel@tonic-gate n = recv(peer, dp, blocksize + 4, 0); 1233*0Sstevel@tonic-gate (void) sighold(SIGALRM); 1234*0Sstevel@tonic-gate if (n < 0) { /* really? */ 1235*0Sstevel@tonic-gate if (errno == EINTR) 1236*0Sstevel@tonic-gate continue; 1237*0Sstevel@tonic-gate syslog(LOG_ERR, "recv (data): %m"); 1238*0Sstevel@tonic-gate goto abort; 1239*0Sstevel@tonic-gate } 1240*0Sstevel@tonic-gate dp->th_opcode = ntohs((ushort_t)dp->th_opcode); 1241*0Sstevel@tonic-gate dp->th_block = ntohs((ushort_t)dp->th_block); 1242*0Sstevel@tonic-gate if (dp->th_opcode == ERROR) { 1243*0Sstevel@tonic-gate cancel_alarm(); 1244*0Sstevel@tonic-gate if (debug && standalone) { 1245*0Sstevel@tonic-gate (void) fprintf(stderr, 1246*0Sstevel@tonic-gate "received ERROR %d", dp->th_code); 1247*0Sstevel@tonic-gate if (n > 4) 1248*0Sstevel@tonic-gate (void) fprintf(stderr, 1249*0Sstevel@tonic-gate " %.*s", n - 4, dp->th_msg); 1250*0Sstevel@tonic-gate (void) putc('\n', stderr); 1251*0Sstevel@tonic-gate } 1252*0Sstevel@tonic-gate return; 1253*0Sstevel@tonic-gate } 1254*0Sstevel@tonic-gate if (dp->th_opcode == DATA) { 1255*0Sstevel@tonic-gate if (debug && standalone) 1256*0Sstevel@tonic-gate (void) fprintf(stderr, 1257*0Sstevel@tonic-gate "Received DATA block %d\n", 1258*0Sstevel@tonic-gate dp->th_block); 1259*0Sstevel@tonic-gate if (dp->th_block == block) { 1260*0Sstevel@tonic-gate break; /* normal */ 1261*0Sstevel@tonic-gate } 1262*0Sstevel@tonic-gate /* Re-synchronize with the other side */ 1263*0Sstevel@tonic-gate if (synchnet(peer) < 0) { 1264*0Sstevel@tonic-gate nak(errno + 100); 1265*0Sstevel@tonic-gate goto abort; 1266*0Sstevel@tonic-gate } 1267*0Sstevel@tonic-gate if (dp->th_block == (block-1)) 1268*0Sstevel@tonic-gate goto send_ack; /* rexmit */ 1269*0Sstevel@tonic-gate } 1270*0Sstevel@tonic-gate } 1271*0Sstevel@tonic-gate cancel_alarm(); 1272*0Sstevel@tonic-gate /* size = write(file, dp->th_data, n - 4); */ 1273*0Sstevel@tonic-gate size = writeit(file, &dp, n - 4, pf->f_convert); 1274*0Sstevel@tonic-gate if (size != (n - 4)) { 1275*0Sstevel@tonic-gate nak((size < 0) ? (errno + 100) : ENOSPACE); 1276*0Sstevel@tonic-gate goto abort; 1277*0Sstevel@tonic-gate } 1278*0Sstevel@tonic-gate } while (size == blocksize); 1279*0Sstevel@tonic-gate if (write_behind(file, pf->f_convert) < 0) { 1280*0Sstevel@tonic-gate nak(errno + 100); 1281*0Sstevel@tonic-gate goto abort; 1282*0Sstevel@tonic-gate } 1283*0Sstevel@tonic-gate n = fclose(file); /* close data file */ 1284*0Sstevel@tonic-gate file = NULL; 1285*0Sstevel@tonic-gate if (n == EOF) { 1286*0Sstevel@tonic-gate nak(errno + 100); 1287*0Sstevel@tonic-gate goto abort; 1288*0Sstevel@tonic-gate } 1289*0Sstevel@tonic-gate 1290*0Sstevel@tonic-gate ap->th_opcode = htons((ushort_t)ACK); /* send the "final" ack */ 1291*0Sstevel@tonic-gate ap->th_block = htons((ushort_t)(block)); 1292*0Sstevel@tonic-gate if (debug && standalone) 1293*0Sstevel@tonic-gate (void) fprintf(stderr, "Sending ACK for block %d\n", block); 1294*0Sstevel@tonic-gate if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from, 1295*0Sstevel@tonic-gate fromplen) == -1) { 1296*0Sstevel@tonic-gate if (debug && standalone) 1297*0Sstevel@tonic-gate perror("sendto (ack)"); 1298*0Sstevel@tonic-gate } 1299*0Sstevel@tonic-gate (void) sigset(SIGALRM, justquit); /* just quit on timeout */ 1300*0Sstevel@tonic-gate (void) alarm(rexmtval); 1301*0Sstevel@tonic-gate /* normally times out and quits */ 1302*0Sstevel@tonic-gate n = recv(peer, dp, blocksize + 4, 0); 1303*0Sstevel@tonic-gate (void) alarm(0); 1304*0Sstevel@tonic-gate dp->th_opcode = ntohs((ushort_t)dp->th_opcode); 1305*0Sstevel@tonic-gate dp->th_block = ntohs((ushort_t)dp->th_block); 1306*0Sstevel@tonic-gate if (n >= 4 && /* if read some data */ 1307*0Sstevel@tonic-gate dp->th_opcode == DATA && /* and got a data block */ 1308*0Sstevel@tonic-gate block == dp->th_block) { /* then my last ack was lost */ 1309*0Sstevel@tonic-gate if (debug && standalone) { 1310*0Sstevel@tonic-gate (void) fprintf(stderr, "Sending ACK for block %d\n", 1311*0Sstevel@tonic-gate block); 1312*0Sstevel@tonic-gate } 1313*0Sstevel@tonic-gate /* resend final ack */ 1314*0Sstevel@tonic-gate if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from, 1315*0Sstevel@tonic-gate fromplen) == -1) { 1316*0Sstevel@tonic-gate if (debug && standalone) 1317*0Sstevel@tonic-gate perror("sendto (last ack)"); 1318*0Sstevel@tonic-gate } 1319*0Sstevel@tonic-gate } 1320*0Sstevel@tonic-gate 1321*0Sstevel@tonic-gate abort: 1322*0Sstevel@tonic-gate cancel_alarm(); 1323*0Sstevel@tonic-gate if (file != NULL) 1324*0Sstevel@tonic-gate (void) fclose(file); 1325*0Sstevel@tonic-gate } 1326*0Sstevel@tonic-gate 1327*0Sstevel@tonic-gate /* 1328*0Sstevel@tonic-gate * Send a nak packet (error message). 1329*0Sstevel@tonic-gate * Error code passed in is one of the 1330*0Sstevel@tonic-gate * standard TFTP codes, or a UNIX errno 1331*0Sstevel@tonic-gate * offset by 100. 1332*0Sstevel@tonic-gate * Handles connected as well as unconnected peer. 1333*0Sstevel@tonic-gate */ 1334*0Sstevel@tonic-gate static void 1335*0Sstevel@tonic-gate nak(int error) 1336*0Sstevel@tonic-gate { 1337*0Sstevel@tonic-gate struct tftphdr *tp; 1338*0Sstevel@tonic-gate int length; 1339*0Sstevel@tonic-gate struct errmsg *pe; 1340*0Sstevel@tonic-gate int ret; 1341*0Sstevel@tonic-gate 1342*0Sstevel@tonic-gate tp = &buf.hdr; 1343*0Sstevel@tonic-gate tp->th_opcode = htons((ushort_t)ERROR); 1344*0Sstevel@tonic-gate tp->th_code = htons((ushort_t)error); 1345*0Sstevel@tonic-gate for (pe = errmsgs; pe->e_code >= 0; pe++) 1346*0Sstevel@tonic-gate if (pe->e_code == error) 1347*0Sstevel@tonic-gate break; 1348*0Sstevel@tonic-gate if (pe->e_code < 0) { 1349*0Sstevel@tonic-gate pe->e_msg = strerror(error - 100); 1350*0Sstevel@tonic-gate tp->th_code = EUNDEF; /* set 'undef' errorcode */ 1351*0Sstevel@tonic-gate } 1352*0Sstevel@tonic-gate (void) strlcpy(tp->th_msg, (pe->e_msg != NULL) ? pe->e_msg : "UNKNOWN", 1353*0Sstevel@tonic-gate sizeof (buf) - sizeof (struct tftphdr)); 1354*0Sstevel@tonic-gate length = strlen(tp->th_msg); 1355*0Sstevel@tonic-gate length += sizeof (struct tftphdr); 1356*0Sstevel@tonic-gate if (debug && standalone) 1357*0Sstevel@tonic-gate (void) fprintf(stderr, "Sending NAK: %s\n", tp->th_msg); 1358*0Sstevel@tonic-gate 1359*0Sstevel@tonic-gate ret = sendto(peer, &buf, length, 0, (struct sockaddr *)&from, 1360*0Sstevel@tonic-gate fromplen); 1361*0Sstevel@tonic-gate if (ret == -1 && errno == EISCONN) { 1362*0Sstevel@tonic-gate /* Try without an address */ 1363*0Sstevel@tonic-gate ret = send(peer, &buf, length, 0); 1364*0Sstevel@tonic-gate } 1365*0Sstevel@tonic-gate if (ret == -1) { 1366*0Sstevel@tonic-gate if (standalone) 1367*0Sstevel@tonic-gate perror("sendto (nak)"); 1368*0Sstevel@tonic-gate else 1369*0Sstevel@tonic-gate syslog(LOG_ERR, "tftpd: nak: %m\n"); 1370*0Sstevel@tonic-gate } else if (ret != length) { 1371*0Sstevel@tonic-gate if (standalone) 1372*0Sstevel@tonic-gate perror("sendto (nak) lost data"); 1373*0Sstevel@tonic-gate else 1374*0Sstevel@tonic-gate syslog(LOG_ERR, "tftpd: nak: %d lost\n", length - ret); 1375*0Sstevel@tonic-gate } 1376*0Sstevel@tonic-gate } 1377