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