1 1.37 christos /* $NetBSD: tftp.c,v 1.37 2023/01/06 17:18:56 christos Exp $ */ 2 1.4 jtc 3 1.1 cgd /* 4 1.4 jtc * Copyright (c) 1983, 1993 5 1.4 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.18 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.6 mrg #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.4 jtc #if 0 35 1.4 jtc static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 36 1.6 mrg #else 37 1.37 christos __RCSID("$NetBSD: tftp.c,v 1.37 2023/01/06 17:18:56 christos Exp $"); 38 1.4 jtc #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 42 1.1 cgd 43 1.1 cgd /* 44 1.1 cgd * TFTP User Program -- Protocol Machines 45 1.1 cgd */ 46 1.1 cgd #include <sys/types.h> 47 1.17 briggs #include <sys/param.h> 48 1.1 cgd #include <sys/socket.h> 49 1.17 briggs #include <sys/stat.h> 50 1.1 cgd #include <sys/time.h> 51 1.1 cgd 52 1.1 cgd #include <netinet/in.h> 53 1.1 cgd 54 1.1 cgd #include <arpa/tftp.h> 55 1.26 jmcneill #include <arpa/inet.h> 56 1.1 cgd 57 1.7 lukem #include <err.h> 58 1.4 jtc #include <errno.h> 59 1.4 jtc #include <setjmp.h> 60 1.1 cgd #include <signal.h> 61 1.1 cgd #include <stdio.h> 62 1.17 briggs #include <stdlib.h> 63 1.5 cgd #include <string.h> 64 1.4 jtc #include <unistd.h> 65 1.15 itojun #include <netdb.h> 66 1.4 jtc 67 1.4 jtc #include "extern.h" 68 1.4 jtc #include "tftpsubs.h" 69 1.1 cgd 70 1.34 he extern jmp_buf toplevel; 71 1.34 he 72 1.1 cgd char ackbuf[PKTSIZE]; 73 1.1 cgd int timeout; 74 1.1 cgd jmp_buf timeoutbuf; 75 1.1 cgd 76 1.35 sevan static void nak(int, struct sockaddr *); 77 1.35 sevan static int makerequest(int, const char *, struct tftphdr *, const char *, off_t); 78 1.35 sevan static void printstats(const char *, unsigned long); 79 1.35 sevan static void startclock(void); 80 1.35 sevan static void stopclock(void); 81 1.36 dholland static __dead void timer(int); 82 1.35 sevan static void tpacket(const char *, struct tftphdr *, int); 83 1.35 sevan static int cmpport(struct sockaddr *, struct sockaddr *); 84 1.1 cgd 85 1.17 briggs static void get_options(struct tftphdr *, int); 86 1.26 jmcneill static int tftp_igmp_join(void); 87 1.26 jmcneill static void tftp_igmp_leave(int); 88 1.17 briggs 89 1.17 briggs static void 90 1.17 briggs get_options(struct tftphdr *ap, int size) 91 1.17 briggs { 92 1.17 briggs unsigned long val; 93 1.17 briggs char *opt, *endp, *nextopt, *valp; 94 1.17 briggs int l; 95 1.17 briggs 96 1.17 briggs size -= 2; /* skip over opcode */ 97 1.17 briggs opt = ap->th_stuff; 98 1.17 briggs endp = opt + size - 1; 99 1.17 briggs *endp = '\0'; 100 1.26 jmcneill 101 1.17 briggs while (opt < endp) { 102 1.26 jmcneill int ismulticast; 103 1.17 briggs l = strlen(opt) + 1; 104 1.17 briggs valp = opt + l; 105 1.26 jmcneill ismulticast = !strcasecmp(opt, "multicast"); 106 1.17 briggs if (valp < endp) { 107 1.17 briggs val = strtoul(valp, NULL, 10); 108 1.17 briggs l = strlen(valp) + 1; 109 1.17 briggs nextopt = valp + l; 110 1.26 jmcneill if (!ismulticast) { 111 1.26 jmcneill if (val == ULONG_MAX && errno == ERANGE) { 112 1.26 jmcneill /* Report illegal value */ 113 1.26 jmcneill opt = nextopt; 114 1.26 jmcneill continue; 115 1.26 jmcneill } 116 1.17 briggs } 117 1.17 briggs } else { 118 1.17 briggs /* Badly formed OACK */ 119 1.17 briggs break; 120 1.17 briggs } 121 1.17 briggs if (strcmp(opt, "tsize") == 0) { 122 1.17 briggs /* cool, but we'll ignore it */ 123 1.17 briggs } else if (strcmp(opt, "timeout") == 0) { 124 1.17 briggs if (val >= 1 && val <= 255) { 125 1.17 briggs rexmtval = val; 126 1.17 briggs } else { 127 1.17 briggs /* Report error? */ 128 1.17 briggs } 129 1.17 briggs } else if (strcmp(opt, "blksize") == 0) { 130 1.17 briggs if (val >= 8 && val <= MAXSEGSIZE) { 131 1.17 briggs blksize = val; 132 1.17 briggs } else { 133 1.17 briggs /* Report error? */ 134 1.17 briggs } 135 1.26 jmcneill } else if (ismulticast) { 136 1.26 jmcneill char multicast[24]; 137 1.26 jmcneill char *pmulticast; 138 1.26 jmcneill char *addr; 139 1.26 jmcneill 140 1.26 jmcneill strlcpy(multicast, valp, sizeof(multicast)); 141 1.26 jmcneill pmulticast = multicast; 142 1.26 jmcneill addr = strsep(&pmulticast, ","); 143 1.29 seanb if (pmulticast == NULL) 144 1.26 jmcneill continue; /* Report error? */ 145 1.26 jmcneill mcport = atoi(strsep(&pmulticast, ",")); 146 1.29 seanb if (pmulticast == NULL) 147 1.26 jmcneill continue; /* Report error? */ 148 1.29 seanb mcmasterslave = atoi(pmulticast); 149 1.26 jmcneill mcaddr = inet_addr(addr); 150 1.26 jmcneill if (mcaddr == INADDR_NONE) 151 1.26 jmcneill continue; /* Report error? */ 152 1.17 briggs } else { 153 1.17 briggs /* unknown option */ 154 1.17 briggs } 155 1.17 briggs opt = nextopt; 156 1.17 briggs } 157 1.17 briggs } 158 1.17 briggs 159 1.26 jmcneill static int 160 1.26 jmcneill tftp_igmp_join(void) 161 1.26 jmcneill { 162 1.26 jmcneill struct ip_mreq req; 163 1.26 jmcneill struct sockaddr_in s; 164 1.26 jmcneill int fd, rv; 165 1.26 jmcneill 166 1.26 jmcneill memset(&req, 0, sizeof(struct ip_mreq)); 167 1.26 jmcneill req.imr_multiaddr.s_addr = mcaddr; 168 1.26 jmcneill req.imr_interface.s_addr = INADDR_ANY; 169 1.26 jmcneill 170 1.26 jmcneill fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 171 1.26 jmcneill if (fd < 0) { 172 1.26 jmcneill perror("socket"); 173 1.26 jmcneill return fd; 174 1.26 jmcneill } 175 1.26 jmcneill 176 1.26 jmcneill memset(&s, 0, sizeof(struct sockaddr_in)); 177 1.26 jmcneill s.sin_family = AF_INET; 178 1.26 jmcneill s.sin_port = htons(mcport); 179 1.26 jmcneill s.sin_len = sizeof(struct sockaddr_in); 180 1.26 jmcneill rv = bind(fd, (struct sockaddr *)&s, sizeof(struct sockaddr_in)); 181 1.26 jmcneill if (rv < 0) { 182 1.26 jmcneill perror("bind"); 183 1.26 jmcneill close(fd); 184 1.26 jmcneill return rv; 185 1.26 jmcneill } 186 1.26 jmcneill 187 1.26 jmcneill rv = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, 188 1.26 jmcneill sizeof(struct ip_mreq)); 189 1.26 jmcneill if (rv < 0) { 190 1.26 jmcneill perror("setsockopt"); 191 1.26 jmcneill close(fd); 192 1.26 jmcneill return rv; 193 1.26 jmcneill } 194 1.26 jmcneill 195 1.26 jmcneill return fd; 196 1.26 jmcneill } 197 1.26 jmcneill 198 1.26 jmcneill static void 199 1.26 jmcneill tftp_igmp_leave(int fd) 200 1.26 jmcneill { 201 1.26 jmcneill struct ip_mreq req; 202 1.26 jmcneill int rv; 203 1.26 jmcneill 204 1.26 jmcneill memset(&req, 0, sizeof(struct ip_mreq)); 205 1.26 jmcneill req.imr_multiaddr.s_addr = mcaddr; 206 1.26 jmcneill req.imr_interface.s_addr = INADDR_ANY; 207 1.26 jmcneill 208 1.26 jmcneill rv = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &req, 209 1.26 jmcneill sizeof(struct ip_mreq)); 210 1.26 jmcneill if (rv < 0) 211 1.26 jmcneill perror("setsockopt"); 212 1.26 jmcneill 213 1.26 jmcneill close(fd); 214 1.26 jmcneill 215 1.26 jmcneill return; 216 1.26 jmcneill } 217 1.26 jmcneill 218 1.1 cgd /* 219 1.1 cgd * Send the requested file. 220 1.1 cgd */ 221 1.4 jtc void 222 1.32 christos sendfile(int fd, const char *name, const char *mode) 223 1.1 cgd { 224 1.7 lukem struct tftphdr *ap; /* data and ack packets */ 225 1.6 mrg struct tftphdr *dp; 226 1.20 he int j, n; 227 1.13 dogcow volatile unsigned int block; 228 1.13 dogcow volatile int size, convert; 229 1.4 jtc volatile unsigned long amount; 230 1.10 itojun struct sockaddr_storage from; 231 1.17 briggs struct stat sbuf; 232 1.28 christos volatile off_t filesize = 0; 233 1.23 christos socklen_t fromlen; 234 1.1 cgd FILE *file; 235 1.10 itojun struct sockaddr_storage peer; 236 1.15 itojun struct sockaddr_storage serv; /* valid server port number */ 237 1.1 cgd 238 1.4 jtc startclock(); /* start stat's clock */ 239 1.4 jtc dp = r_init(); /* reset fillbuf/read-ahead code */ 240 1.23 christos ap = (struct tftphdr *)(void *)ackbuf; 241 1.17 briggs if (tsize) { 242 1.17 briggs if (fstat(fd, &sbuf) == 0) { 243 1.17 briggs filesize = sbuf.st_size; 244 1.17 briggs } else { 245 1.17 briggs filesize = -1ULL; 246 1.17 briggs } 247 1.17 briggs } 248 1.1 cgd file = fdopen(fd, "r"); 249 1.1 cgd convert = !strcmp(mode, "netascii"); 250 1.4 jtc block = 0; 251 1.4 jtc amount = 0; 252 1.25 christos (void)memcpy(&peer, &peeraddr, (size_t)peeraddr.ss_len); 253 1.25 christos (void)memset(&serv, 0, sizeof(serv)); 254 1.1 cgd 255 1.25 christos (void)signal(SIGALRM, timer); 256 1.1 cgd do { 257 1.1 cgd if (block == 0) 258 1.17 briggs size = makerequest(WRQ, name, dp, mode, filesize) - 4; 259 1.1 cgd else { 260 1.4 jtc /* size = read(fd, dp->th_data, SEGSIZE); */ 261 1.17 briggs size = readit(file, &dp, blksize, convert); 262 1.1 cgd if (size < 0) { 263 1.37 christos nak(errno + 100, 264 1.37 christos (struct sockaddr *)(void *)&peer); 265 1.1 cgd break; 266 1.1 cgd } 267 1.1 cgd dp->th_opcode = htons((u_short)DATA); 268 1.1 cgd dp->th_block = htons((u_short)block); 269 1.1 cgd } 270 1.1 cgd timeout = 0; 271 1.1 cgd (void) setjmp(timeoutbuf); 272 1.1 cgd send_data: 273 1.1 cgd if (trace) 274 1.1 cgd tpacket("sent", dp, size + 4); 275 1.23 christos n = sendto(f, dp, (socklen_t)(size + 4), 0, 276 1.23 christos (struct sockaddr *)(void *)&peer, (socklen_t)peer.ss_len); 277 1.1 cgd if (n != size + 4) { 278 1.7 lukem warn("sendto"); 279 1.1 cgd goto abort; 280 1.1 cgd } 281 1.17 briggs if (block) 282 1.17 briggs read_ahead(file, blksize, convert); 283 1.1 cgd for ( ; ; ) { 284 1.25 christos (void)alarm(rexmtval); 285 1.1 cgd do { 286 1.26 jmcneill int curf; 287 1.4 jtc fromlen = sizeof(from); 288 1.26 jmcneill if (mcaddr != INADDR_NONE) 289 1.26 jmcneill curf = mf; 290 1.26 jmcneill else 291 1.26 jmcneill curf = f; 292 1.26 jmcneill n = recvfrom(curf, ackbuf, sizeof(ackbuf), 0, 293 1.23 christos (struct sockaddr *)(void *)&from, &fromlen); 294 1.1 cgd } while (n <= 0); 295 1.25 christos (void)alarm(0); 296 1.1 cgd if (n < 0) { 297 1.7 lukem warn("recvfrom"); 298 1.1 cgd goto abort; 299 1.1 cgd } 300 1.15 itojun if (!serv.ss_family) 301 1.15 itojun serv = from; 302 1.23 christos else if (!cmpport((struct sockaddr *)(void *)&serv, 303 1.23 christos (struct sockaddr *)(void *)&from)) { 304 1.15 itojun warn("server port mismatch"); 305 1.15 itojun goto abort; 306 1.10 itojun } 307 1.15 itojun peer = from; 308 1.1 cgd if (trace) 309 1.1 cgd tpacket("received", ap, n); 310 1.1 cgd /* should verify packet came from server */ 311 1.1 cgd ap->th_opcode = ntohs(ap->th_opcode); 312 1.1 cgd if (ap->th_opcode == ERROR) { 313 1.37 christos (void)printf("Error code %d: %s\n", 314 1.37 christos ntohs(ap->th_code), ap->th_msg); 315 1.1 cgd goto abort; 316 1.1 cgd } 317 1.1 cgd if (ap->th_opcode == ACK) { 318 1.19 erh ap->th_block = ntohs(ap->th_block); 319 1.1 cgd 320 1.17 briggs if (ap->th_block == 0) { 321 1.17 briggs /* 322 1.17 briggs * If the extended options are enabled, 323 1.17 briggs * the server just refused 'em all. 324 1.17 briggs * The only one that _really_ 325 1.17 briggs * matters is blksize, but we'll 326 1.26 jmcneill * clear timeout and mcaddr, too. 327 1.17 briggs */ 328 1.17 briggs blksize = def_blksize; 329 1.17 briggs rexmtval = def_rexmtval; 330 1.26 jmcneill mcaddr = INADDR_NONE; 331 1.17 briggs } 332 1.1 cgd if (ap->th_block == block) { 333 1.1 cgd break; 334 1.1 cgd } 335 1.1 cgd /* On an error, try to synchronize 336 1.1 cgd * both sides. 337 1.1 cgd */ 338 1.17 briggs j = synchnet(f, blksize+4); 339 1.1 cgd if (j && trace) { 340 1.25 christos (void)printf("discarded %d packets\n", 341 1.1 cgd j); 342 1.1 cgd } 343 1.1 cgd if (ap->th_block == (block-1)) { 344 1.1 cgd goto send_data; 345 1.1 cgd } 346 1.1 cgd } 347 1.17 briggs if (ap->th_opcode == OACK) { 348 1.17 briggs if (block == 0) { 349 1.17 briggs blksize = def_blksize; 350 1.17 briggs rexmtval = def_rexmtval; 351 1.26 jmcneill mcaddr = INADDR_NONE; 352 1.17 briggs get_options(ap, n); 353 1.17 briggs break; 354 1.17 briggs } 355 1.17 briggs } 356 1.1 cgd } 357 1.1 cgd if (block > 0) 358 1.1 cgd amount += size; 359 1.1 cgd block++; 360 1.30 lukem } while ((size_t)size == blksize || block == 1); 361 1.1 cgd abort: 362 1.25 christos (void)fclose(file); 363 1.1 cgd stopclock(); 364 1.1 cgd if (amount > 0) 365 1.1 cgd printstats("Sent", amount); 366 1.1 cgd } 367 1.1 cgd 368 1.1 cgd /* 369 1.1 cgd * Receive a file. 370 1.1 cgd */ 371 1.4 jtc void 372 1.32 christos recvfile(int fd, const char *name, const char *mode) 373 1.1 cgd { 374 1.7 lukem struct tftphdr *ap; 375 1.6 mrg struct tftphdr *dp; 376 1.28 christos int j, n; 377 1.28 christos volatile int oack = 0; 378 1.13 dogcow volatile unsigned int block; 379 1.13 dogcow volatile int size, firsttrip; 380 1.4 jtc volatile unsigned long amount; 381 1.10 itojun struct sockaddr_storage from; 382 1.23 christos socklen_t fromlen; 383 1.28 christos volatile size_t readlen; 384 1.1 cgd FILE *file; 385 1.4 jtc volatile int convert; /* true if converting crlf -> lf */ 386 1.10 itojun struct sockaddr_storage peer; 387 1.15 itojun struct sockaddr_storage serv; /* valid server port number */ 388 1.1 cgd 389 1.1 cgd startclock(); 390 1.1 cgd dp = w_init(); 391 1.23 christos ap = (struct tftphdr *)(void *)ackbuf; 392 1.1 cgd file = fdopen(fd, "w"); 393 1.1 cgd convert = !strcmp(mode, "netascii"); 394 1.4 jtc block = 1; 395 1.4 jtc firsttrip = 1; 396 1.4 jtc amount = 0; 397 1.25 christos (void)memcpy(&peer, &peeraddr, (size_t)peeraddr.ss_len); 398 1.25 christos (void)memset(&serv, 0, sizeof(serv)); 399 1.1 cgd 400 1.25 christos (void)signal(SIGALRM, timer); 401 1.1 cgd do { 402 1.1 cgd if (firsttrip) { 403 1.23 christos size = makerequest(RRQ, name, ap, mode, (off_t)0); 404 1.17 briggs readlen = PKTSIZE; 405 1.1 cgd firsttrip = 0; 406 1.1 cgd } else { 407 1.1 cgd ap->th_opcode = htons((u_short)ACK); 408 1.1 cgd ap->th_block = htons((u_short)(block)); 409 1.17 briggs readlen = blksize+4; 410 1.1 cgd size = 4; 411 1.1 cgd block++; 412 1.1 cgd } 413 1.1 cgd timeout = 0; 414 1.1 cgd (void) setjmp(timeoutbuf); 415 1.1 cgd send_ack: 416 1.1 cgd if (trace) 417 1.1 cgd tpacket("sent", ap, size); 418 1.23 christos if (sendto(f, ackbuf, (socklen_t)size, 0, 419 1.23 christos (struct sockaddr *)(void *)&peer, 420 1.23 christos (socklen_t)peer.ss_len) != size) { 421 1.25 christos (void)alarm(0); 422 1.7 lukem warn("sendto"); 423 1.1 cgd goto abort; 424 1.1 cgd } 425 1.26 jmcneill skip_ack: 426 1.23 christos if (write_behind(file, convert) == -1) 427 1.23 christos goto abort; 428 1.1 cgd for ( ; ; ) { 429 1.25 christos (void)alarm(rexmtval); 430 1.1 cgd do { 431 1.26 jmcneill int readfd; 432 1.26 jmcneill if (mf > 0) 433 1.26 jmcneill readfd = mf; 434 1.26 jmcneill else 435 1.26 jmcneill readfd = f; 436 1.4 jtc fromlen = sizeof(from); 437 1.26 jmcneill n = recvfrom(readfd, dp, readlen, 0, 438 1.23 christos (struct sockaddr *)(void *)&from, &fromlen); 439 1.1 cgd } while (n <= 0); 440 1.25 christos (void)alarm(0); 441 1.1 cgd if (n < 0) { 442 1.7 lukem warn("recvfrom"); 443 1.1 cgd goto abort; 444 1.1 cgd } 445 1.15 itojun if (!serv.ss_family) 446 1.15 itojun serv = from; 447 1.23 christos else if (!cmpport((struct sockaddr *)(void *)&serv, 448 1.23 christos (struct sockaddr *)(void *)&from)) { 449 1.15 itojun warn("server port mismatch"); 450 1.15 itojun goto abort; 451 1.10 itojun } 452 1.15 itojun peer = from; 453 1.1 cgd if (trace) 454 1.1 cgd tpacket("received", dp, n); 455 1.1 cgd /* should verify client address */ 456 1.1 cgd dp->th_opcode = ntohs(dp->th_opcode); 457 1.1 cgd if (dp->th_opcode == ERROR) { 458 1.37 christos (void)printf("Error code %d: %s\n", 459 1.37 christos ntohs(dp->th_code), dp->th_msg); 460 1.1 cgd goto abort; 461 1.1 cgd } 462 1.1 cgd if (dp->th_opcode == DATA) { 463 1.19 erh dp->th_block = ntohs(dp->th_block); 464 1.1 cgd 465 1.17 briggs if (dp->th_block == 1 && !oack) { 466 1.17 briggs /* no OACK, revert to defaults */ 467 1.17 briggs blksize = def_blksize; 468 1.17 briggs rexmtval = def_rexmtval; 469 1.17 briggs } 470 1.1 cgd if (dp->th_block == block) { 471 1.4 jtc break; /* have next packet */ 472 1.1 cgd } 473 1.1 cgd /* On an error, try to synchronize 474 1.1 cgd * both sides. 475 1.1 cgd */ 476 1.17 briggs j = synchnet(f, blksize); 477 1.1 cgd if (j && trace) { 478 1.37 christos (void)printf("discarded %d packets\n", 479 1.37 christos j); 480 1.1 cgd } 481 1.1 cgd if (dp->th_block == (block-1)) { 482 1.4 jtc goto send_ack; /* resend ack */ 483 1.1 cgd } 484 1.1 cgd } 485 1.17 briggs if (dp->th_opcode == OACK) { 486 1.17 briggs if (block == 1) { 487 1.17 briggs oack = 1; 488 1.17 briggs blksize = def_blksize; 489 1.17 briggs rexmtval = def_rexmtval; 490 1.17 briggs get_options(dp, n); 491 1.17 briggs ap->th_opcode = htons(ACK); 492 1.17 briggs ap->th_block = 0; 493 1.17 briggs readlen = blksize+4; 494 1.17 briggs size = 4; 495 1.26 jmcneill if (mcaddr != INADDR_NONE) { 496 1.26 jmcneill mf = tftp_igmp_join(); 497 1.27 christos if (mf < 0) 498 1.27 christos goto abort; 499 1.26 jmcneill if (mcmasterslave == 0) 500 1.26 jmcneill goto skip_ack; 501 1.26 jmcneill } 502 1.17 briggs goto send_ack; 503 1.17 briggs } 504 1.17 briggs } 505 1.1 cgd } 506 1.4 jtc /* size = write(fd, dp->th_data, n - 4); */ 507 1.1 cgd size = writeit(file, &dp, n - 4, convert); 508 1.1 cgd if (size < 0) { 509 1.23 christos nak(errno + 100, (struct sockaddr *)(void *)&peer); 510 1.1 cgd break; 511 1.1 cgd } 512 1.1 cgd amount += size; 513 1.30 lukem } while ((size_t)size == blksize); 514 1.4 jtc abort: /* ok to ack, since user */ 515 1.4 jtc ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 516 1.1 cgd ap->th_block = htons((u_short)block); 517 1.27 christos if (mcaddr != INADDR_NONE && mf >= 0) { 518 1.26 jmcneill tftp_igmp_leave(mf); 519 1.26 jmcneill mf = -1; 520 1.26 jmcneill } 521 1.23 christos (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)(void *)&peer, 522 1.23 christos (socklen_t)peer.ss_len); 523 1.23 christos /* 524 1.23 christos * flush last buffer 525 1.23 christos * We do not check for failure because last buffer 526 1.23 christos * can be empty, thus returning an error. 527 1.23 christos * XXX maybe we should fix 'write_behind' instead. 528 1.23 christos */ 529 1.23 christos (void)write_behind(file, convert); 530 1.25 christos (void)fclose(file); 531 1.1 cgd stopclock(); 532 1.1 cgd if (amount > 0) 533 1.1 cgd printstats("Received", amount); 534 1.1 cgd } 535 1.1 cgd 536 1.4 jtc static int 537 1.33 matt makerequest(int request, const char *name, struct tftphdr *tp, const char *mode, 538 1.33 matt off_t filesize) 539 1.1 cgd { 540 1.7 lukem char *cp; 541 1.1 cgd 542 1.1 cgd tp->th_opcode = htons((u_short)request); 543 1.9 christos #ifndef __SVR4 544 1.1 cgd cp = tp->th_stuff; 545 1.9 christos #else 546 1.9 christos cp = (void *)&tp->th_stuff; 547 1.9 christos #endif 548 1.25 christos (void)strcpy(cp, name); 549 1.1 cgd cp += strlen(name); 550 1.1 cgd *cp++ = '\0'; 551 1.25 christos (void)strcpy(cp, mode); 552 1.1 cgd cp += strlen(mode); 553 1.1 cgd *cp++ = '\0'; 554 1.17 briggs if (tsize) { 555 1.25 christos (void)strcpy(cp, "tsize"); 556 1.17 briggs cp += strlen(cp); 557 1.17 briggs *cp++ = '\0'; 558 1.25 christos (void)sprintf(cp, "%lu", (unsigned long) filesize); 559 1.17 briggs cp += strlen(cp); 560 1.17 briggs *cp++ = '\0'; 561 1.17 briggs } 562 1.17 briggs if (tout) { 563 1.25 christos (void)strcpy(cp, "timeout"); 564 1.17 briggs cp += strlen(cp); 565 1.17 briggs *cp++ = '\0'; 566 1.25 christos (void)sprintf(cp, "%d", rexmtval); 567 1.17 briggs cp += strlen(cp); 568 1.17 briggs *cp++ = '\0'; 569 1.17 briggs } 570 1.17 briggs if (blksize != SEGSIZE) { 571 1.25 christos (void)strcpy(cp, "blksize"); 572 1.17 briggs cp += strlen(cp); 573 1.17 briggs *cp++ = '\0'; 574 1.25 christos (void)sprintf(cp, "%zd", blksize); 575 1.17 briggs cp += strlen(cp); 576 1.17 briggs *cp++ = '\0'; 577 1.17 briggs } 578 1.23 christos return (cp - (char *)(void *)tp); 579 1.1 cgd } 580 1.1 cgd 581 1.8 mycroft const struct errmsg { 582 1.1 cgd int e_code; 583 1.8 mycroft const char *e_msg; 584 1.1 cgd } errmsgs[] = { 585 1.1 cgd { EUNDEF, "Undefined error code" }, 586 1.1 cgd { ENOTFOUND, "File not found" }, 587 1.1 cgd { EACCESS, "Access violation" }, 588 1.1 cgd { ENOSPACE, "Disk full or allocation exceeded" }, 589 1.1 cgd { EBADOP, "Illegal TFTP operation" }, 590 1.1 cgd { EBADID, "Unknown transfer ID" }, 591 1.1 cgd { EEXISTS, "File already exists" }, 592 1.1 cgd { ENOUSER, "No such user" }, 593 1.17 briggs { EOPTNEG, "Option negotiation failed" }, 594 1.1 cgd { -1, 0 } 595 1.1 cgd }; 596 1.1 cgd 597 1.1 cgd /* 598 1.1 cgd * Send a nak packet (error message). 599 1.1 cgd * Error code passed in is one of the 600 1.1 cgd * standard TFTP codes, or a UNIX errno 601 1.1 cgd * offset by 100. 602 1.1 cgd */ 603 1.4 jtc static void 604 1.33 matt nak(int error, struct sockaddr *peer) 605 1.1 cgd { 606 1.8 mycroft const struct errmsg *pe; 607 1.7 lukem struct tftphdr *tp; 608 1.1 cgd int length; 609 1.14 itojun size_t msglen; 610 1.1 cgd 611 1.23 christos tp = (struct tftphdr *)(void *)ackbuf; 612 1.1 cgd tp->th_opcode = htons((u_short)ERROR); 613 1.14 itojun msglen = sizeof(ackbuf) - (&tp->th_msg[0] - ackbuf); 614 1.1 cgd for (pe = errmsgs; pe->e_code >= 0; pe++) 615 1.1 cgd if (pe->e_code == error) 616 1.1 cgd break; 617 1.1 cgd if (pe->e_code < 0) { 618 1.1 cgd tp->th_code = EUNDEF; 619 1.25 christos (void)strlcpy(tp->th_msg, strerror(error - 100), msglen); 620 1.8 mycroft } else { 621 1.8 mycroft tp->th_code = htons((u_short)error); 622 1.25 christos (void)strlcpy(tp->th_msg, pe->e_msg, msglen); 623 1.1 cgd } 624 1.14 itojun length = strlen(tp->th_msg); 625 1.14 itojun msglen = &tp->th_msg[length + 1] - ackbuf; 626 1.1 cgd if (trace) 627 1.14 itojun tpacket("sent", tp, (int)msglen); 628 1.30 lukem if ((size_t)sendto(f, ackbuf, msglen, 0, peer, (socklen_t)peer->sa_len) != msglen) 629 1.7 lukem warn("nak"); 630 1.1 cgd } 631 1.1 cgd 632 1.4 jtc static void 633 1.33 matt tpacket(const char *s, struct tftphdr *tp, int n) 634 1.1 cgd { 635 1.22 ross static const char *opcodes[] = 636 1.17 briggs { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; 637 1.22 ross char *cp, *file, *endp, *opt = NULL; 638 1.22 ross const char *spc; 639 1.1 cgd u_short op = ntohs(tp->th_opcode); 640 1.17 briggs int i, o; 641 1.1 cgd 642 1.17 briggs if (op < RRQ || op > OACK) 643 1.25 christos (void)printf("%s opcode=%x ", s, op); 644 1.1 cgd else 645 1.25 christos (void)printf("%s %s ", s, opcodes[op]); 646 1.1 cgd switch (op) { 647 1.1 cgd 648 1.1 cgd case RRQ: 649 1.1 cgd case WRQ: 650 1.1 cgd n -= 2; 651 1.9 christos #ifndef __SVR4 652 1.9 christos cp = tp->th_stuff; 653 1.9 christos #else 654 1.9 christos cp = (void *) &tp->th_stuff; 655 1.9 christos #endif 656 1.17 briggs endp = cp + n - 1; 657 1.17 briggs if (*endp != '\0') { /* Shouldn't happen, but... */ 658 1.17 briggs *endp = '\0'; 659 1.17 briggs } 660 1.9 christos file = cp; 661 1.17 briggs cp = strchr(cp, '\0') + 1; 662 1.25 christos (void)printf("<file=%s, mode=%s", file, cp); 663 1.17 briggs cp = strchr(cp, '\0') + 1; 664 1.17 briggs o = 0; 665 1.17 briggs while (cp < endp) { 666 1.17 briggs i = strlen(cp) + 1; 667 1.17 briggs if (o) { 668 1.25 christos (void)printf(", %s=%s", opt, cp); 669 1.17 briggs } else { 670 1.17 briggs opt = cp; 671 1.17 briggs } 672 1.17 briggs o = (o+1) % 2; 673 1.17 briggs cp += i; 674 1.17 briggs } 675 1.25 christos (void)printf(">\n"); 676 1.1 cgd break; 677 1.1 cgd 678 1.1 cgd case DATA: 679 1.37 christos (void)printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), 680 1.37 christos n - 4); 681 1.1 cgd break; 682 1.1 cgd 683 1.1 cgd case ACK: 684 1.25 christos (void)printf("<block=%d>\n", ntohs(tp->th_block)); 685 1.1 cgd break; 686 1.1 cgd 687 1.1 cgd case ERROR: 688 1.37 christos (void)printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), 689 1.37 christos tp->th_msg); 690 1.17 briggs break; 691 1.17 briggs 692 1.17 briggs case OACK: 693 1.17 briggs o = 0; 694 1.17 briggs n -= 2; 695 1.17 briggs cp = tp->th_stuff; 696 1.17 briggs endp = cp + n - 1; 697 1.17 briggs if (*endp != '\0') { /* Shouldn't happen, but... */ 698 1.17 briggs *endp = '\0'; 699 1.17 briggs } 700 1.25 christos (void)printf("<"); 701 1.17 briggs spc = ""; 702 1.17 briggs while (cp < endp) { 703 1.17 briggs i = strlen(cp) + 1; 704 1.17 briggs if (o) { 705 1.25 christos (void)printf("%s%s=%s", spc, opt, cp); 706 1.17 briggs spc = ", "; 707 1.17 briggs } else { 708 1.17 briggs opt = cp; 709 1.17 briggs } 710 1.17 briggs o = (o+1) % 2; 711 1.17 briggs cp += i; 712 1.17 briggs } 713 1.25 christos (void)printf(">\n"); 714 1.1 cgd break; 715 1.1 cgd } 716 1.1 cgd } 717 1.1 cgd 718 1.1 cgd struct timeval tstart; 719 1.1 cgd struct timeval tstop; 720 1.1 cgd 721 1.4 jtc static void 722 1.33 matt startclock(void) 723 1.4 jtc { 724 1.4 jtc 725 1.4 jtc (void)gettimeofday(&tstart, NULL); 726 1.1 cgd } 727 1.1 cgd 728 1.4 jtc static void 729 1.33 matt stopclock(void) 730 1.4 jtc { 731 1.4 jtc 732 1.4 jtc (void)gettimeofday(&tstop, NULL); 733 1.1 cgd } 734 1.1 cgd 735 1.4 jtc static void 736 1.33 matt printstats(const char *direction, unsigned long amount) 737 1.1 cgd { 738 1.1 cgd double delta; 739 1.6 mrg 740 1.6 mrg /* compute delta in 1/10's second units */ 741 1.1 cgd delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 742 1.1 cgd ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 743 1.1 cgd delta = delta/10.; /* back to seconds */ 744 1.25 christos (void)printf("%s %ld bytes in %.1f seconds", direction, amount, delta); 745 1.1 cgd if (verbose) 746 1.25 christos (void)printf(" [%.0f bits/sec]", (amount*8.)/delta); 747 1.25 christos (void)putchar('\n'); 748 1.1 cgd } 749 1.1 cgd 750 1.4 jtc static void 751 1.23 christos /*ARGSUSED*/ 752 1.33 matt timer(int sig) 753 1.4 jtc { 754 1.4 jtc 755 1.4 jtc timeout += rexmtval; 756 1.4 jtc if (timeout >= maxtimeout) { 757 1.34 he (void)printf("Transfer timed out."); 758 1.4 jtc longjmp(toplevel, -1); 759 1.4 jtc } 760 1.4 jtc longjmp(timeoutbuf, 1); 761 1.15 itojun } 762 1.15 itojun 763 1.15 itojun static int 764 1.33 matt cmpport(struct sockaddr *sa, struct sockaddr *sb) 765 1.15 itojun { 766 1.15 itojun char a[NI_MAXSERV], b[NI_MAXSERV]; 767 1.15 itojun 768 1.37 christos if (getnameinfo(sa, (socklen_t)sa->sa_len, NULL, 0, a, sizeof(a), 769 1.37 christos NI_NUMERICSERV)) 770 1.15 itojun return 0; 771 1.37 christos if (getnameinfo(sb, (socklen_t)sb->sa_len, NULL, 0, b, sizeof(b), 772 1.37 christos NI_NUMERICSERV)) 773 1.15 itojun return 0; 774 1.15 itojun if (strcmp(a, b) != 0) 775 1.15 itojun return 0; 776 1.15 itojun 777 1.15 itojun return 1; 778 1.4 jtc } 779