1114472Sru/* $NetBSD: tftp.c,v 1.37 2023/01/06 17:18:56 christos Exp $ */ 2146515Sru 356160Sru/* 4146515Sru * Copyright (c) 1983, 1993 556160Sru * The Regents of the University of California. All rights reserved. 656160Sru * 756160Sru * Redistribution and use in source and binary forms, with or without 856160Sru * modification, are permitted provided that the following conditions 956160Sru * are met: 1056160Sru * 1. Redistributions of source code must retain the above copyright 1156160Sru * notice, this list of conditions and the following disclaimer. 1256160Sru * 2. Redistributions in binary form must reproduce the above copyright 1356160Sru * notice, this list of conditions and the following disclaimer in the 1456160Sru * documentation and/or other materials provided with the distribution. 1556160Sru * 3. Neither the name of the University nor the names of its contributors 1656160Sru * may be used to endorse or promote products derived from this software 1756160Sru * without specific prior written permission. 1856160Sru * 1956160Sru * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20114472Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2156160Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2256160Sru * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2356160Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2456160Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2556160Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2656160Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2756160Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2856160Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2993139Sru * SUCH DAMAGE. 3056160Sru */ 3156160Sru 3256160Sru#include <sys/cdefs.h> 3356160Sru#ifndef lint 3456160Sru#if 0 3556160Srustatic char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 3656160Sru#else 37146515Sru__RCSID("$NetBSD: tftp.c,v 1.37 2023/01/06 17:18:56 christos Exp $"); 3856160Sru#endif 3956160Sru#endif /* not lint */ 4056160Sru 4156160Sru/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 4256160Sru 4356160Sru/* 4456160Sru * TFTP User Program -- Protocol Machines 4556160Sru */ 4656160Sru#include <sys/types.h> 4756160Sru#include <sys/param.h> 4856160Sru#include <sys/socket.h> 4956160Sru#include <sys/stat.h> 5056160Sru#include <sys/time.h> 5156160Sru 5256160Sru#include <netinet/in.h> 5356160Sru 5456160Sru#include <arpa/tftp.h> 5556160Sru#include <arpa/inet.h> 56116525Sru 5756160Sru#include <err.h> 5856160Sru#include <errno.h> 5956160Sru#include <setjmp.h> 6056160Sru#include <signal.h> 6156160Sru#include <stdio.h> 6256160Sru#include <stdlib.h> 6356160Sru#include <string.h> 6456160Sru#include <unistd.h> 6556160Sru#include <netdb.h> 6656160Sru 6756160Sru#include "extern.h" 6856160Sru#include "tftpsubs.h" 6956160Sru 7056160Sruextern jmp_buf toplevel; 7156160Sru 7256160Sruchar ackbuf[PKTSIZE]; 7356160Sruint timeout; 7456160Srujmp_buf timeoutbuf; 7556160Sru 7656160Srustatic void nak(int, struct sockaddr *); 7756160Srustatic int makerequest(int, const char *, struct tftphdr *, const char *, off_t); 7856160Srustatic void printstats(const char *, unsigned long); 7956160Srustatic void startclock(void); 8056160Srustatic void stopclock(void); 8156160Srustatic __dead void timer(int); 8256160Srustatic void tpacket(const char *, struct tftphdr *, int); 8356160Srustatic int cmpport(struct sockaddr *, struct sockaddr *); 8456160Sru 85146515Srustatic void get_options(struct tftphdr *, int); 86146515Srustatic int tftp_igmp_join(void); 87146515Srustatic void tftp_igmp_leave(int); 88146515Sru 89146515Srustatic void 90146515Sruget_options(struct tftphdr *ap, int size) 9156160Sru{ 9256160Sru unsigned long val; 9356160Sru char *opt, *endp, *nextopt, *valp; 9456160Sru int l; 95146515Sru 96146515Sru size -= 2; /* skip over opcode */ 9756160Sru opt = ap->th_stuff; 9856160Sru endp = opt + size - 1; 9956160Sru *endp = '\0'; 10056160Sru 10156160Sru while (opt < endp) { 10256160Sru int ismulticast; 10356160Sru l = strlen(opt) + 1; 10456160Sru valp = opt + l; 10556160Sru ismulticast = !strcasecmp(opt, "multicast"); 10656160Sru if (valp < endp) { 10756160Sru val = strtoul(valp, NULL, 10); 10856160Sru l = strlen(valp) + 1; 10956160Sru nextopt = valp + l; 11056160Sru if (!ismulticast) { 111114472Sru if (val == ULONG_MAX && errno == ERANGE) { 11256160Sru /* Report illegal value */ 11356160Sru opt = nextopt; 114114472Sru continue; 11556160Sru } 11656160Sru } 11756160Sru } else { 11856160Sru /* Badly formed OACK */ 11956160Sru break; 12056160Sru } 12156160Sru if (strcmp(opt, "tsize") == 0) { 122146515Sru /* cool, but we'll ignore it */ 12356160Sru } else if (strcmp(opt, "timeout") == 0) { 124146515Sru if (val >= 1 && val <= 255) { 12556160Sru rexmtval = val; 126146515Sru } else { 127146515Sru /* Report error? */ 128146515Sru } 129146515Sru } else if (strcmp(opt, "blksize") == 0) { 130146515Sru if (val >= 8 && val <= MAXSEGSIZE) { 13156160Sru blksize = val; 13256160Sru } else { 13356160Sru /* Report error? */ 13456160Sru } 13556160Sru } else if (ismulticast) { 13656160Sru char multicast[24]; 137146515Sru char *pmulticast; 13856160Sru char *addr; 13956160Sru 14056160Sru strlcpy(multicast, valp, sizeof(multicast)); 14156160Sru pmulticast = multicast; 14256160Sru addr = strsep(&pmulticast, ","); 14356160Sru if (pmulticast == NULL) 14456160Sru continue; /* Report error? */ 14556160Sru mcport = atoi(strsep(&pmulticast, ",")); 14656160Sru if (pmulticast == NULL) 14756160Sru continue; /* Report error? */ 14856160Sru mcmasterslave = atoi(pmulticast); 14956160Sru mcaddr = inet_addr(addr); 15056160Sru if (mcaddr == INADDR_NONE) 15156160Sru continue; /* Report error? */ 15256160Sru } else { 153146515Sru /* unknown option */ 154146515Sru } 15556160Sru opt = nextopt; 15656160Sru } 15756160Sru} 15856160Sru 15956160Srustatic int 16056160Srutftp_igmp_join(void) 16156160Sru{ 162116525Sru struct ip_mreq req; 16356160Sru struct sockaddr_in s; 16456160Sru int fd, rv; 16556160Sru 16656160Sru memset(&req, 0, sizeof(struct ip_mreq)); 16756160Sru req.imr_multiaddr.s_addr = mcaddr; 16856160Sru req.imr_interface.s_addr = INADDR_ANY; 16956160Sru 17056160Sru fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 17156160Sru if (fd < 0) { 172116525Sru perror("socket"); 173146515Sru return fd; 174146515Sru } 175146515Sru 176146515Sru memset(&s, 0, sizeof(struct sockaddr_in)); 177146515Sru s.sin_family = AF_INET; 17856160Sru s.sin_port = htons(mcport); 179146515Sru s.sin_len = sizeof(struct sockaddr_in); 18056160Sru rv = bind(fd, (struct sockaddr *)&s, sizeof(struct sockaddr_in)); 18156160Sru if (rv < 0) { 18256160Sru perror("bind"); 18356160Sru close(fd); 18456160Sru return rv; 18556160Sru } 18656160Sru 18756160Sru rv = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, 18856160Sru sizeof(struct ip_mreq)); 18956160Sru if (rv < 0) { 19056160Sru perror("setsockopt"); 19156160Sru close(fd); 19256160Sru return rv; 19356160Sru } 19456160Sru 19556160Sru return fd; 19656160Sru} 19756160Sru 19856160Srustatic void 19956160Srutftp_igmp_leave(int fd) 20056160Sru{ 20156160Sru struct ip_mreq req; 20256160Sru int rv; 20356160Sru 20456160Sru memset(&req, 0, sizeof(struct ip_mreq)); 20556160Sru req.imr_multiaddr.s_addr = mcaddr; 20656160Sru req.imr_interface.s_addr = INADDR_ANY; 20756160Sru 20856160Sru rv = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &req, 20956160Sru sizeof(struct ip_mreq)); 21056160Sru if (rv < 0) 21156160Sru perror("setsockopt"); 21256160Sru 21356160Sru close(fd); 21456160Sru 21556160Sru return; 21656160Sru} 217114472Sru 21856160Sru/* 219146515Sru * Send the requested file. 220146515Sru */ 221146515Sruvoid 222146515Srusendfile(int fd, const char *name, const char *mode) 223146515Sru{ 224146515Sru struct tftphdr *ap; /* data and ack packets */ 225146515Sru struct tftphdr *dp; 226146515Sru int j, n; 227146515Sru volatile unsigned int block; 228146515Sru volatile int size, convert; 229146515Sru volatile unsigned long amount; 230146515Sru struct sockaddr_storage from; 231146515Sru struct stat sbuf; 232146515Sru volatile off_t filesize = 0; 233146515Sru socklen_t fromlen; 234146515Sru FILE *file; 235146515Sru struct sockaddr_storage peer; 236146515Sru struct sockaddr_storage serv; /* valid server port number */ 237146515Sru 238146515Sru startclock(); /* start stat's clock */ 239146515Sru dp = r_init(); /* reset fillbuf/read-ahead code */ 240146515Sru ap = (struct tftphdr *)(void *)ackbuf; 241146515Sru if (tsize) { 242146515Sru if (fstat(fd, &sbuf) == 0) { 24356160Sru filesize = sbuf.st_size; 24456160Sru } else { 24556160Sru filesize = -1ULL; 24656160Sru } 24756160Sru } 248146515Sru file = fdopen(fd, "r"); 249146515Sru convert = !strcmp(mode, "netascii"); 250146515Sru block = 0; 251146515Sru amount = 0; 252146515Sru (void)memcpy(&peer, &peeraddr, (size_t)peeraddr.ss_len); 253146515Sru (void)memset(&serv, 0, sizeof(serv)); 254146515Sru 255146515Sru (void)signal(SIGALRM, timer); 256146515Sru do { 257146515Sru if (block == 0) 258146515Sru size = makerequest(WRQ, name, dp, mode, filesize) - 4; 259221386Sdim else { 260146515Sru /* size = read(fd, dp->th_data, SEGSIZE); */ 261146515Sru size = readit(file, &dp, blksize, convert); 262146515Sru if (size < 0) { 263146515Sru nak(errno + 100, 264146515Sru (struct sockaddr *)(void *)&peer); 265221386Sdim break; 266146515Sru } 267146515Sru dp->th_opcode = htons((u_short)DATA); 268146515Sru dp->th_block = htons((u_short)block); 269146515Sru } 270146515Sru timeout = 0; 271146515Sru (void) setjmp(timeoutbuf); 272146515Srusend_data: 273146515Sru if (trace) 274146515Sru tpacket("sent", dp, size + 4); 275146515Sru n = sendto(f, dp, (socklen_t)(size + 4), 0, 276146515Sru (struct sockaddr *)(void *)&peer, (socklen_t)peer.ss_len); 277146515Sru if (n != size + 4) { 278146515Sru warn("sendto"); 279146515Sru goto abort; 280146515Sru } 281146515Sru if (block) 282146515Sru read_ahead(file, blksize, convert); 283146515Sru for ( ; ; ) { 284146515Sru (void)alarm(rexmtval); 285146515Sru do { 286146515Sru int curf; 287146515Sru fromlen = sizeof(from); 288146515Sru if (mcaddr != INADDR_NONE) 289146515Sru curf = mf; 290146515Sru else 291146515Sru curf = f; 292146515Sru n = recvfrom(curf, ackbuf, sizeof(ackbuf), 0, 293146515Sru (struct sockaddr *)(void *)&from, &fromlen); 294146515Sru } while (n <= 0); 295146515Sru (void)alarm(0); 296146515Sru if (n < 0) { 297146515Sru warn("recvfrom"); 298146515Sru goto abort; 299146515Sru } 300146515Sru if (!serv.ss_family) 301146515Sru serv = from; 302146515Sru else if (!cmpport((struct sockaddr *)(void *)&serv, 303146515Sru (struct sockaddr *)(void *)&from)) { 304146515Sru warn("server port mismatch"); 305146515Sru goto abort; 306146515Sru } 307146515Sru peer = from; 308146515Sru if (trace) 309146515Sru tpacket("received", ap, n); 310146515Sru /* should verify packet came from server */ 311146515Sru ap->th_opcode = ntohs(ap->th_opcode); 312146515Sru if (ap->th_opcode == ERROR) { 313146515Sru (void)printf("Error code %d: %s\n", 314146515Sru ntohs(ap->th_code), ap->th_msg); 315146515Sru goto abort; 316146515Sru } 317146515Sru if (ap->th_opcode == ACK) { 318146515Sru ap->th_block = ntohs(ap->th_block); 31956160Sru 320146515Sru if (ap->th_block == 0) { 32156160Sru /* 322146515Sru * If the extended options are enabled, 323146515Sru * the server just refused 'em all. 324146515Sru * The only one that _really_ 325116525Sru * matters is blksize, but we'll 326116525Sru * clear timeout and mcaddr, too. 327116525Sru */ 328116525Sru blksize = def_blksize; 329116525Sru rexmtval = def_rexmtval; 33056160Sru mcaddr = INADDR_NONE; 33156160Sru } 33256160Sru if (ap->th_block == block) { 333146515Sru break; 33456160Sru } 33556160Sru /* On an error, try to synchronize 33656160Sru * both sides. 337146515Sru */ 33856160Sru j = synchnet(f, blksize+4); 339146515Sru if (j && trace) { 340146515Sru (void)printf("discarded %d packets\n", 341146515Sru j); 34256160Sru } 343146515Sru if (ap->th_block == (block-1)) { 344146515Sru goto send_data; 345146515Sru } 346146515Sru } 347146515Sru if (ap->th_opcode == OACK) { 348146515Sru if (block == 0) { 349146515Sru blksize = def_blksize; 350146515Sru rexmtval = def_rexmtval; 351146515Sru mcaddr = INADDR_NONE; 352146515Sru get_options(ap, n); 353146515Sru break; 35456160Sru } 35556160Sru } 356146515Sru } 35756160Sru if (block > 0) 358146515Sru amount += size; 359146515Sru block++; 360146515Sru } while ((size_t)size == blksize || block == 1); 361146515Sruabort: 362146515Sru (void)fclose(file); 363146515Sru stopclock(); 364146515Sru if (amount > 0) 365146515Sru printstats("Sent", amount); 366146515Sru} 367146515Sru 368116525Sru/* 369146515Sru * Receive a file. 370146515Sru */ 371146515Sruvoid 372146515Srurecvfile(int fd, const char *name, const char *mode) 373146515Sru{ 374146515Sru struct tftphdr *ap; 375146515Sru struct tftphdr *dp; 376146515Sru int j, n; 377146515Sru volatile int oack = 0; 378146515Sru volatile unsigned int block; 379146515Sru volatile int size, firsttrip; 380146515Sru volatile unsigned long amount; 381146515Sru struct sockaddr_storage from; 382146515Sru socklen_t fromlen; 383146515Sru volatile size_t readlen; 384146515Sru FILE *file; 385146515Sru volatile int convert; /* true if converting crlf -> lf */ 386146515Sru struct sockaddr_storage peer; 387146515Sru struct sockaddr_storage serv; /* valid server port number */ 388146515Sru 389146515Sru startclock(); 390146515Sru dp = w_init(); 391146515Sru ap = (struct tftphdr *)(void *)ackbuf; 392146515Sru file = fdopen(fd, "w"); 393146515Sru convert = !strcmp(mode, "netascii"); 394146515Sru block = 1; 395146515Sru firsttrip = 1; 396146515Sru amount = 0; 397146515Sru (void)memcpy(&peer, &peeraddr, (size_t)peeraddr.ss_len); 398146515Sru (void)memset(&serv, 0, sizeof(serv)); 399146515Sru 400146515Sru (void)signal(SIGALRM, timer); 401146515Sru do { 402114472Sru if (firsttrip) { 403146515Sru size = makerequest(RRQ, name, ap, mode, (off_t)0); 404146515Sru readlen = PKTSIZE; 405146515Sru firsttrip = 0; 406146515Sru } else { 407146515Sru ap->th_opcode = htons((u_short)ACK); 408146515Sru ap->th_block = htons((u_short)(block)); 409146515Sru readlen = blksize+4; 410146515Sru size = 4; 411146515Sru block++; 412146515Sru } 413146515Sru timeout = 0; 41456160Sru (void) setjmp(timeoutbuf); 415146515Srusend_ack: 416146515Sru if (trace) 417146515Sru tpacket("sent", ap, size); 418116525Sru if (sendto(f, ackbuf, (socklen_t)size, 0, 419146515Sru (struct sockaddr *)(void *)&peer, 420114472Sru (socklen_t)peer.ss_len) != size) { 421146515Sru (void)alarm(0); 422146515Sru warn("sendto"); 423146515Sru goto abort; 424146515Sru } 425146515Sruskip_ack: 42656160Sru if (write_behind(file, convert) == -1) 42756160Sru goto abort; 42856160Sru for ( ; ; ) { 42956160Sru (void)alarm(rexmtval); 43056160Sru do { 43156160Sru int readfd; 43256160Sru if (mf > 0) 433146515Sru readfd = mf; 43456160Sru else 43556160Sru readfd = f; 43656160Sru fromlen = sizeof(from); 43756160Sru n = recvfrom(readfd, dp, readlen, 0, 43856160Sru (struct sockaddr *)(void *)&from, &fromlen); 43956160Sru } while (n <= 0); 440146515Sru (void)alarm(0); 44156160Sru if (n < 0) { 44256160Sru warn("recvfrom"); 44356160Sru goto abort; 44456160Sru } 44556160Sru if (!serv.ss_family) 44656160Sru serv = from; 44756160Sru else if (!cmpport((struct sockaddr *)(void *)&serv, 44856160Sru (struct sockaddr *)(void *)&from)) { 44956160Sru warn("server port mismatch"); 45056160Sru goto abort; 451146515Sru } 452146515Sru peer = from; 453146515Sru if (trace) 454146515Sru tpacket("received", dp, n); 455146515Sru /* should verify client address */ 45656160Sru dp->th_opcode = ntohs(dp->th_opcode); 45756160Sru if (dp->th_opcode == ERROR) { 458146515Sru (void)printf("Error code %d: %s\n", 459146515Sru ntohs(dp->th_code), dp->th_msg); 460146515Sru goto abort; 461146515Sru } 462146515Sru if (dp->th_opcode == DATA) { 46356160Sru dp->th_block = ntohs(dp->th_block); 46456160Sru 46556160Sru if (dp->th_block == 1 && !oack) { 46656160Sru /* no OACK, revert to defaults */ 467146515Sru blksize = def_blksize; 46856160Sru rexmtval = def_rexmtval; 46956160Sru } 47056160Sru if (dp->th_block == block) { 47156160Sru break; /* have next packet */ 47256160Sru } 47356160Sru /* On an error, try to synchronize 47456160Sru * both sides. 47556160Sru */ 476146515Sru j = synchnet(f, blksize); 47756160Sru if (j && trace) { 47856160Sru (void)printf("discarded %d packets\n", 47956160Sru j); 48056160Sru } 48156160Sru if (dp->th_block == (block-1)) { 48256160Sru goto send_ack; /* resend ack */ 48356160Sru } 48456160Sru } 48556160Sru if (dp->th_opcode == OACK) { 48656160Sru if (block == 1) { 48756160Sru oack = 1; 48856160Sru blksize = def_blksize; 48956160Sru rexmtval = def_rexmtval; 49056160Sru get_options(dp, n); 49156160Sru ap->th_opcode = htons(ACK); 49256160Sru ap->th_block = 0; 49356160Sru readlen = blksize+4; 49456160Sru size = 4; 49556160Sru if (mcaddr != INADDR_NONE) { 49656160Sru mf = tftp_igmp_join(); 49756160Sru if (mf < 0) 49856160Sru goto abort; 49956160Sru if (mcmasterslave == 0) 50056160Sru goto skip_ack; 50156160Sru } 50256160Sru goto send_ack; 50356160Sru } 50456160Sru } 50556160Sru } 50656160Sru /* size = write(fd, dp->th_data, n - 4); */ 50756160Sru size = writeit(file, &dp, n - 4, convert); 50856160Sru if (size < 0) { 50956160Sru nak(errno + 100, (struct sockaddr *)(void *)&peer); 51056160Sru break; 511116525Sru } 51256160Sru amount += size; 513146515Sru } while ((size_t)size == blksize); 51456160Sruabort: /* ok to ack, since user */ 51556160Sru ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 51656160Sru ap->th_block = htons((u_short)block); 51756160Sru if (mcaddr != INADDR_NONE && mf >= 0) { 51856160Sru tftp_igmp_leave(mf); 51956160Sru mf = -1; 52056160Sru } 52156160Sru (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)(void *)&peer, 52256160Sru (socklen_t)peer.ss_len); 52356160Sru /* 52456160Sru * flush last buffer 52556160Sru * We do not check for failure because last buffer 526146515Sru * can be empty, thus returning an error. 527146515Sru * XXX maybe we should fix 'write_behind' instead. 52856160Sru */ 52956160Sru (void)write_behind(file, convert); 53056160Sru (void)fclose(file); 53156160Sru stopclock(); 53256160Sru if (amount > 0) 53393139Sru printstats("Received", amount); 53493139Sru} 53556160Sru 53693139Srustatic int 53793139Srumakerequest(int request, const char *name, struct tftphdr *tp, const char *mode, 53856160Sru off_t filesize) 53993139Sru{ 540114472Sru char *cp; 541114472Sru 542116525Sru tp->th_opcode = htons((u_short)request); 543114472Sru#ifndef __SVR4 544114472Sru cp = tp->th_stuff; 54593139Sru#else 54656160Sru cp = (void *)&tp->th_stuff; 54756160Sru#endif 54856160Sru (void)strcpy(cp, name); 54956160Sru cp += strlen(name); 55056160Sru *cp++ = '\0'; 55156160Sru (void)strcpy(cp, mode); 55256160Sru cp += strlen(mode); 55356160Sru *cp++ = '\0'; 554146515Sru if (tsize) { 555146515Sru (void)strcpy(cp, "tsize"); 556146515Sru cp += strlen(cp); 557146515Sru *cp++ = '\0'; 55856160Sru (void)sprintf(cp, "%lu", (unsigned long) filesize); 55956160Sru cp += strlen(cp); 56056160Sru *cp++ = '\0'; 56156160Sru } 56256160Sru if (tout) { 56356160Sru (void)strcpy(cp, "timeout"); 56456160Sru cp += strlen(cp); 56556160Sru *cp++ = '\0'; 56656160Sru (void)sprintf(cp, "%d", rexmtval); 567146515Sru cp += strlen(cp); 568146515Sru *cp++ = '\0'; 569146515Sru } 570146515Sru if (blksize != SEGSIZE) { 571146515Sru (void)strcpy(cp, "blksize"); 57256160Sru cp += strlen(cp); 57356160Sru *cp++ = '\0'; 57456160Sru (void)sprintf(cp, "%zd", blksize); 57556160Sru cp += strlen(cp); 57656160Sru *cp++ = '\0'; 57756160Sru } 57856160Sru return (cp - (char *)(void *)tp); 57956160Sru} 58056160Sru 58156160Sruconst struct errmsg { 58256160Sru int e_code; 58356160Sru const char *e_msg; 58456160Sru} errmsgs[] = { 58556160Sru { EUNDEF, "Undefined error code" }, 58656160Sru { ENOTFOUND, "File not found" }, 58756160Sru { EACCESS, "Access violation" }, 58856160Sru { ENOSPACE, "Disk full or allocation exceeded" }, 58956160Sru { EBADOP, "Illegal TFTP operation" }, 59093139Sru { EBADID, "Unknown transfer ID" }, 59156160Sru { EEXISTS, "File already exists" }, 59256160Sru { ENOUSER, "No such user" }, 59356160Sru { EOPTNEG, "Option negotiation failed" }, 59456160Sru { -1, 0 } 59556160Sru}; 59693139Sru 59756160Sru/* 59856160Sru * Send a nak packet (error message). 59956160Sru * Error code passed in is one of the 60056160Sru * standard TFTP codes, or a UNIX errno 60156160Sru * offset by 100. 60256160Sru */ 60356160Srustatic void 60456160Srunak(int error, struct sockaddr *peer) 605146515Sru{ 60656160Sru const struct errmsg *pe; 60756160Sru struct tftphdr *tp; 60856160Sru int length; 60956160Sru size_t msglen; 61056160Sru 61156160Sru tp = (struct tftphdr *)(void *)ackbuf; 61256160Sru tp->th_opcode = htons((u_short)ERROR); 613146515Sru msglen = sizeof(ackbuf) - (&tp->th_msg[0] - ackbuf); 61456160Sru for (pe = errmsgs; pe->e_code >= 0; pe++) 61556160Sru if (pe->e_code == error) 61656160Sru break; 61756160Sru if (pe->e_code < 0) { 61856160Sru tp->th_code = EUNDEF; 61956160Sru (void)strlcpy(tp->th_msg, strerror(error - 100), msglen); 62056160Sru } else { 621146515Sru tp->th_code = htons((u_short)error); 62256160Sru (void)strlcpy(tp->th_msg, pe->e_msg, msglen); 62356160Sru } 62456160Sru length = strlen(tp->th_msg); 62556160Sru msglen = &tp->th_msg[length + 1] - ackbuf; 62656160Sru if (trace) 62756160Sru tpacket("sent", tp, (int)msglen); 62856160Sru if ((size_t)sendto(f, ackbuf, msglen, 0, peer, (socklen_t)peer->sa_len) != msglen) 62956160Sru warn("nak"); 63056160Sru} 63156160Sru 632146515Srustatic void 63356160Srutpacket(const char *s, struct tftphdr *tp, int n) 63456160Sru{ 63556160Sru static const char *opcodes[] = 63656160Sru { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; 63756160Sru char *cp, *file, *endp, *opt = NULL; 63856160Sru const char *spc; 63956160Sru u_short op = ntohs(tp->th_opcode); 64056160Sru int i, o; 64156160Sru 64256160Sru if (op < RRQ || op > OACK) 64356160Sru (void)printf("%s opcode=%x ", s, op); 64456160Sru else 64556160Sru (void)printf("%s %s ", s, opcodes[op]); 64693139Sru switch (op) { 647114472Sru 64856160Sru case RRQ: 64956160Sru case WRQ: 65056160Sru n -= 2; 65156160Sru#ifndef __SVR4 65256160Sru cp = tp->th_stuff; 65356160Sru#else 65456160Sru cp = (void *) &tp->th_stuff; 65556160Sru#endif 65656160Sru endp = cp + n - 1; 65793139Sru if (*endp != '\0') { /* Shouldn't happen, but... */ 65856160Sru *endp = '\0'; 65956160Sru } 66056160Sru file = cp; 66156160Sru cp = strchr(cp, '\0') + 1; 66256160Sru (void)printf("<file=%s, mode=%s", file, cp); 66356160Sru cp = strchr(cp, '\0') + 1; 66456160Sru o = 0; 66556160Sru while (cp < endp) { 66656160Sru i = strlen(cp) + 1; 66756160Sru if (o) { 66856160Sru (void)printf(", %s=%s", opt, cp); 66956160Sru } else { 67056160Sru opt = cp; 67156160Sru } 67256160Sru o = (o+1) % 2; 67356160Sru cp += i; 67456160Sru } 67556160Sru (void)printf(">\n"); 67656160Sru break; 67756160Sru 67856160Sru case DATA: 67956160Sru (void)printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), 68056160Sru n - 4); 68156160Sru break; 68256160Sru 68356160Sru case ACK: 68456160Sru (void)printf("<block=%d>\n", ntohs(tp->th_block)); 68556160Sru break; 68656160Sru 68756160Sru case ERROR: 68856160Sru (void)printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), 68956160Sru tp->th_msg); 69056160Sru break; 69156160Sru 69256160Sru case OACK: 69356160Sru o = 0; 69456160Sru n -= 2; 69556160Sru cp = tp->th_stuff; 696146515Sru endp = cp + n - 1; 697146515Sru if (*endp != '\0') { /* Shouldn't happen, but... */ 69856160Sru *endp = '\0'; 69956160Sru } 70056160Sru (void)printf("<"); 70156160Sru spc = ""; 70256160Sru while (cp < endp) { 70356160Sru i = strlen(cp) + 1; 70456160Sru if (o) { 70556160Sru (void)printf("%s%s=%s", spc, opt, cp); 70656160Sru spc = ", "; 70756160Sru } else { 70856160Sru opt = cp; 70956160Sru } 71056160Sru o = (o+1) % 2; 711146515Sru cp += i; 71256160Sru } 713146515Sru (void)printf(">\n"); 71456160Sru break; 71556160Sru } 71656160Sru} 71756160Sru 71856160Srustruct timeval tstart; 719146515Srustruct timeval tstop; 72056160Sru 72156160Srustatic void 72256160Srustartclock(void) 72356160Sru{ 72456160Sru 72556160Sru (void)gettimeofday(&tstart, NULL); 726146515Sru} 72756160Sru 72856160Srustatic void 72956160Srustopclock(void) 73056160Sru{ 73156160Sru 73256160Sru (void)gettimeofday(&tstop, NULL); 733146515Sru} 73456160Sru 73556160Srustatic void 73656160Sruprintstats(const char *direction, unsigned long amount) 73756160Sru{ 73856160Sru double delta; 73956160Sru 740146515Sru /* compute delta in 1/10's second units */ 74156160Sru delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 742146515Sru ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 74356160Sru delta = delta/10.; /* back to seconds */ 74456160Sru (void)printf("%s %ld bytes in %.1f seconds", direction, amount, delta); 74556160Sru if (verbose) 74656160Sru (void)printf(" [%.0f bits/sec]", (amount*8.)/delta); 74756160Sru (void)putchar('\n'); 748146515Sru} 74956160Sru 75056160Srustatic void 75156160Sru/*ARGSUSED*/ 75256160Srutimer(int sig) 75356160Sru{ 75456160Sru 75556160Sru timeout += rexmtval; 756146515Sru if (timeout >= maxtimeout) { 75756160Sru (void)printf("Transfer timed out."); 75856160Sru longjmp(toplevel, -1); 75956160Sru } 76056160Sru longjmp(timeoutbuf, 1); 76156160Sru} 76256160Sru 76356160Srustatic int 764146515Srucmpport(struct sockaddr *sa, struct sockaddr *sb) 76556160Sru{ 76656160Sru char a[NI_MAXSERV], b[NI_MAXSERV]; 76756160Sru 76856160Sru if (getnameinfo(sa, (socklen_t)sa->sa_len, NULL, 0, a, sizeof(a), 76956160Sru NI_NUMERICSERV)) 77056160Sru return 0; 771146515Sru if (getnameinfo(sb, (socklen_t)sb->sa_len, NULL, 0, b, sizeof(b), 77256160Sru NI_NUMERICSERV)) 773146515Sru return 0; 774146515Sru if (strcmp(a, b) != 0) 775146515Sru return 0; 776146515Sru 77756160Sru return 1; 77856160Sru} 77956160Sru