1207614Simp/* 2207614Simp * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 3207614Simp * 4207614Simp * Redistribution and use in source and binary forms, with or without 5207614Simp * modification, are permitted provided that the following conditions 6207614Simp * are met: 7207614Simp * 1. Redistributions of source code must retain the above copyright 8207614Simp * notice, this list of conditions and the following disclaimer. 9207614Simp * 2. Redistributions in binary form must reproduce the above copyright 10207614Simp * notice, this list of conditions and the following disclaimer in the 11207614Simp * documentation and/or other materials provided with the distribution. 12207614Simp * 13207614Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14207614Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15207614Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16207614Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17207614Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18207614Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19207614Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20207614Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21207614Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22207614Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23207614Simp * SUCH DAMAGE. 24207614Simp */ 25207614Simp 26207614Simp#include <sys/cdefs.h> 27207614Simp__FBSDID("$FreeBSD: stable/11/libexec/tftpd/tftp-io.c 345389 2019-03-21 21:45:18Z asomers $"); 28207614Simp 29207614Simp#include <sys/stat.h> 30207614Simp#include <sys/types.h> 31207614Simp#include <sys/socket.h> 32207614Simp 33207614Simp#include <netinet/in.h> 34207614Simp#include <arpa/tftp.h> 35207614Simp#include <arpa/inet.h> 36207614Simp 37339051Sasomers#include <assert.h> 38207614Simp#include <errno.h> 39207614Simp#include <setjmp.h> 40207614Simp#include <signal.h> 41345389Sasomers#include <stddef.h> 42207614Simp#include <stdio.h> 43207614Simp#include <stdlib.h> 44207614Simp#include <string.h> 45207614Simp#include <syslog.h> 46207614Simp#include <unistd.h> 47207614Simp 48207614Simp#include "tftp-file.h" 49207614Simp#include "tftp-io.h" 50207614Simp#include "tftp-utils.h" 51207614Simp#include "tftp-options.h" 52207614Simp 53207614Simpstruct sockaddr_storage peer_sock; 54207614Simpstruct sockaddr_storage me_sock; 55207614Simp 56207614Simpstatic int send_packet(int peer, uint16_t block, char *pkt, int size); 57207614Simp 58241720Sedstatic struct errmsg { 59207614Simp int e_code; 60207614Simp const char *e_msg; 61207614Simp} errmsgs[] = { 62207614Simp { EUNDEF, "Undefined error code" }, 63207614Simp { ENOTFOUND, "File not found" }, 64207614Simp { EACCESS, "Access violation" }, 65207614Simp { ENOSPACE, "Disk full or allocation exceeded" }, 66207614Simp { EBADOP, "Illegal TFTP operation" }, 67207614Simp { EBADID, "Unknown transfer ID" }, 68207614Simp { EEXISTS, "File already exists" }, 69207614Simp { ENOUSER, "No such user" }, 70207614Simp { EOPTNEG, "Option negotiation" }, 71207614Simp { -1, NULL } 72207614Simp}; 73207614Simp 74207614Simp#define DROPPACKET(s) \ 75207614Simp if (packetdroppercentage != 0 && \ 76207614Simp random()%100 < packetdroppercentage) { \ 77229780Suqs tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ 78207614Simp return; \ 79207614Simp } 80207614Simp#define DROPPACKETn(s,n) \ 81207614Simp if (packetdroppercentage != 0 && \ 82207614Simp random()%100 < packetdroppercentage) { \ 83229780Suqs tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ 84207614Simp return (n); \ 85207614Simp } 86207614Simp 87207614Simpconst char * 88207614Simperrtomsg(int error) 89207614Simp{ 90207614Simp static char ebuf[40]; 91207614Simp struct errmsg *pe; 92207614Simp 93207614Simp if (error == 0) 94207614Simp return ("success"); 95207614Simp for (pe = errmsgs; pe->e_code >= 0; pe++) 96207614Simp if (pe->e_code == error) 97207614Simp return (pe->e_msg); 98244686Santoine snprintf(ebuf, sizeof(ebuf), "error %d", error); 99207614Simp return (ebuf); 100207614Simp} 101207614Simp 102207614Simpstatic int 103207614Simpsend_packet(int peer, uint16_t block, char *pkt, int size) 104207614Simp{ 105207614Simp int i; 106207614Simp int t = 1; 107207614Simp 108207614Simp for (i = 0; i < 12 ; i++) { 109207614Simp DROPPACKETn("send_packet", 0); 110207614Simp 111246139Smarius if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock, 112246139Smarius peer_sock.ss_len) == size) { 113207614Simp if (i) 114207614Simp tftp_log(LOG_ERR, 115207614Simp "%s block %d, attempt %d successful", 116246139Smarius packettype(ntohs(((struct tftphdr *) 117246139Smarius (pkt))->th_opcode)), block, i); 118207614Simp return (0); 119207614Simp } 120207614Simp tftp_log(LOG_ERR, 121207614Simp "%s block %d, attempt %d failed (Error %d: %s)", 122207614Simp packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)), 123207614Simp block, i, errno, strerror(errno)); 124207614Simp sleep(t); 125207614Simp if (t < 32) 126207614Simp t <<= 1; 127207614Simp } 128207614Simp tftp_log(LOG_ERR, "send_packet: %s", strerror(errno)); 129207614Simp return (1); 130207614Simp} 131207614Simp 132207614Simp/* 133207614Simp * Send an ERROR packet (error message). 134207614Simp * Error code passed in is one of the 135207614Simp * standard TFTP codes, or a UNIX errno 136207614Simp * offset by 100. 137207614Simp */ 138207614Simpvoid 139207614Simpsend_error(int peer, int error) 140207614Simp{ 141207614Simp struct tftphdr *tp; 142207614Simp int length; 143207614Simp struct errmsg *pe; 144207614Simp char buf[MAXPKTSIZE]; 145207614Simp 146207614Simp if (debug&DEBUG_PACKETS) 147246106Ssbruno tftp_log(LOG_DEBUG, "Sending ERROR %d", error); 148207614Simp 149207614Simp DROPPACKET("send_error"); 150207614Simp 151207614Simp tp = (struct tftphdr *)buf; 152207614Simp tp->th_opcode = htons((u_short)ERROR); 153207614Simp tp->th_code = htons((u_short)error); 154207614Simp for (pe = errmsgs; pe->e_code >= 0; pe++) 155207614Simp if (pe->e_code == error) 156207614Simp break; 157207614Simp if (pe->e_code < 0) { 158207614Simp pe->e_msg = strerror(error - 100); 159207614Simp tp->th_code = EUNDEF; /* set 'undef' errorcode */ 160207614Simp } 161207614Simp strcpy(tp->th_msg, pe->e_msg); 162207614Simp length = strlen(pe->e_msg); 163207614Simp tp->th_msg[length] = '\0'; 164207614Simp length += 5; 165207614Simp 166207614Simp if (debug&DEBUG_PACKETS) 167207614Simp tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg); 168207614Simp 169207614Simp if (sendto(peer, buf, length, 0, 170207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length) 171207614Simp tftp_log(LOG_ERR, "send_error: %s", strerror(errno)); 172207614Simp} 173207614Simp 174207614Simp/* 175207614Simp * Send an WRQ packet (write request). 176207614Simp */ 177207614Simpint 178207614Simpsend_wrq(int peer, char *filename, char *mode) 179207614Simp{ 180207614Simp int n; 181207614Simp struct tftphdr *tp; 182207614Simp char *bp; 183207614Simp char buf[MAXPKTSIZE]; 184207614Simp int size; 185207614Simp 186207614Simp if (debug&DEBUG_PACKETS) 187207614Simp tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", 188207614Simp filename, mode 189207614Simp ); 190207614Simp 191207614Simp DROPPACKETn("send_wrq", 1); 192207614Simp 193207614Simp tp = (struct tftphdr *)buf; 194207614Simp tp->th_opcode = htons((u_short)WRQ); 195345389Sasomers size = offsetof(struct tftphdr, th_stuff); 196207614Simp 197207614Simp bp = tp->th_stuff; 198345389Sasomers strlcpy(bp, filename, sizeof(buf) - size); 199207614Simp bp += strlen(filename); 200207614Simp *bp = 0; 201207614Simp bp++; 202207614Simp size += strlen(filename) + 1; 203207614Simp 204345389Sasomers strlcpy(bp, mode, sizeof(buf) - size); 205207614Simp bp += strlen(mode); 206207614Simp *bp = 0; 207207614Simp bp++; 208207614Simp size += strlen(mode) + 1; 209207614Simp 210207614Simp if (options_rfc_enabled) 211207614Simp size += make_options(peer, bp, sizeof(buf) - size); 212207614Simp 213207614Simp n = sendto(peer, buf, size, 0, 214207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len); 215207614Simp if (n != size) { 216207614Simp tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); 217207614Simp return (1); 218207614Simp } 219207614Simp return (0); 220207614Simp} 221207614Simp 222207614Simp/* 223207614Simp * Send an RRQ packet (write request). 224207614Simp */ 225207614Simpint 226207614Simpsend_rrq(int peer, char *filename, char *mode) 227207614Simp{ 228207614Simp int n; 229207614Simp struct tftphdr *tp; 230207614Simp char *bp; 231207614Simp char buf[MAXPKTSIZE]; 232207614Simp int size; 233207614Simp 234207614Simp if (debug&DEBUG_PACKETS) 235207614Simp tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", 236207614Simp filename, mode 237207614Simp ); 238207614Simp 239207614Simp DROPPACKETn("send_rrq", 1); 240207614Simp 241207614Simp tp = (struct tftphdr *)buf; 242207614Simp tp->th_opcode = htons((u_short)RRQ); 243345389Sasomers size = offsetof(struct tftphdr, th_stuff); 244207614Simp 245207614Simp bp = tp->th_stuff; 246345389Sasomers strlcpy(bp, filename, sizeof(buf) - size); 247207614Simp bp += strlen(filename); 248207614Simp *bp = 0; 249207614Simp bp++; 250207614Simp size += strlen(filename) + 1; 251207614Simp 252345389Sasomers strlcpy(bp, mode, sizeof(buf) - size); 253207614Simp bp += strlen(mode); 254207614Simp *bp = 0; 255207614Simp bp++; 256207614Simp size += strlen(mode) + 1; 257207614Simp 258207614Simp if (options_rfc_enabled) { 259207614Simp options[OPT_TSIZE].o_request = strdup("0"); 260207614Simp size += make_options(peer, bp, sizeof(buf) - size); 261207614Simp } 262207614Simp 263207614Simp n = sendto(peer, buf, size, 0, 264207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len); 265207614Simp if (n != size) { 266222326Srodrigc tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno)); 267207614Simp return (1); 268207614Simp } 269207614Simp return (0); 270207614Simp} 271207614Simp 272207614Simp/* 273207614Simp * Send an OACK packet (option acknowledgement). 274207614Simp */ 275207614Simpint 276207614Simpsend_oack(int peer) 277207614Simp{ 278207614Simp struct tftphdr *tp; 279207614Simp int size, i, n; 280207614Simp char *bp; 281207614Simp char buf[MAXPKTSIZE]; 282207614Simp 283207614Simp if (debug&DEBUG_PACKETS) 284207614Simp tftp_log(LOG_DEBUG, "Sending OACK"); 285207614Simp 286207614Simp DROPPACKETn("send_oack", 0); 287207614Simp 288207614Simp /* 289207614Simp * Send back an options acknowledgement (only the ones with 290207614Simp * a reply for) 291207614Simp */ 292207614Simp tp = (struct tftphdr *)buf; 293207614Simp bp = buf + 2; 294207614Simp size = sizeof(buf) - 2; 295207614Simp tp->th_opcode = htons((u_short)OACK); 296207614Simp for (i = 0; options[i].o_type != NULL; i++) { 297207614Simp if (options[i].o_reply != NULL) { 298207614Simp n = snprintf(bp, size, "%s%c%s", options[i].o_type, 299207614Simp 0, options[i].o_reply); 300207614Simp bp += n+1; 301207614Simp size -= n+1; 302207614Simp if (size < 0) { 303207614Simp tftp_log(LOG_ERR, "oack: buffer overflow"); 304207614Simp exit(1); 305207614Simp } 306207614Simp } 307207614Simp } 308207614Simp size = bp - buf; 309207614Simp 310207614Simp if (sendto(peer, buf, size, 0, 311207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { 312207614Simp tftp_log(LOG_INFO, "send_oack: %s", strerror(errno)); 313207614Simp return (1); 314207614Simp } 315207614Simp 316207614Simp return (0); 317207614Simp} 318207614Simp 319207614Simp/* 320207614Simp * Send an ACK packet (acknowledgement). 321207614Simp */ 322207614Simpint 323207614Simpsend_ack(int fp, uint16_t block) 324207614Simp{ 325207614Simp struct tftphdr *tp; 326207614Simp int size; 327207614Simp char buf[MAXPKTSIZE]; 328207614Simp 329207614Simp if (debug&DEBUG_PACKETS) 330207614Simp tftp_log(LOG_DEBUG, "Sending ACK for block %d", block); 331207614Simp 332207614Simp DROPPACKETn("send_ack", 0); 333207614Simp 334207614Simp tp = (struct tftphdr *)buf; 335207614Simp size = sizeof(buf) - 2; 336207614Simp tp->th_opcode = htons((u_short)ACK); 337207614Simp tp->th_block = htons((u_short)block); 338207614Simp size = 4; 339207614Simp 340207614Simp if (sendto(fp, buf, size, 0, 341207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { 342207614Simp tftp_log(LOG_INFO, "send_ack: %s", strerror(errno)); 343207614Simp return (1); 344207614Simp } 345207614Simp 346207614Simp return (0); 347207614Simp} 348207614Simp 349207614Simp/* 350207614Simp * Send a DATA packet 351207614Simp */ 352207614Simpint 353207614Simpsend_data(int peer, uint16_t block, char *data, int size) 354207614Simp{ 355207614Simp char buf[MAXPKTSIZE]; 356207614Simp struct tftphdr *pkt; 357207614Simp int n; 358207614Simp 359207614Simp if (debug&DEBUG_PACKETS) 360207614Simp tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes", 361207614Simp block, size); 362207614Simp 363207614Simp DROPPACKETn("send_data", 0); 364207614Simp 365207614Simp pkt = (struct tftphdr *)buf; 366207614Simp 367207614Simp pkt->th_opcode = htons((u_short)DATA); 368207614Simp pkt->th_block = htons((u_short)block); 369207614Simp memcpy(pkt->th_data, data, size); 370207614Simp 371207614Simp n = send_packet(peer, block, (char *)pkt, size + 4); 372207614Simp return (n); 373207614Simp} 374207614Simp 375207614Simp 376207614Simp/* 377207614Simp * Receive a packet 378207614Simp */ 379241720Sedstatic jmp_buf timeoutbuf; 380207614Simp 381207614Simpstatic void 382207614Simptimeout(int sig __unused) 383207614Simp{ 384207614Simp 385207614Simp /* tftp_log(LOG_DEBUG, "Timeout\n"); Inside a signal handler... */ 386207614Simp longjmp(timeoutbuf, 1); 387207614Simp} 388207614Simp 389207614Simpint 390207614Simpreceive_packet(int peer, char *data, int size, struct sockaddr_storage *from, 391207614Simp int thistimeout) 392207614Simp{ 393207614Simp struct tftphdr *pkt; 394207614Simp struct sockaddr_storage from_local; 395207614Simp struct sockaddr_storage *pfrom; 396207614Simp socklen_t fromlen; 397207614Simp int n; 398339051Sasomers static int timed_out; 399207614Simp 400207614Simp if (debug&DEBUG_PACKETS) 401207614Simp tftp_log(LOG_DEBUG, 402207614Simp "Waiting %d seconds for packet", timeoutpacket); 403207614Simp 404207614Simp pkt = (struct tftphdr *)data; 405207614Simp 406207614Simp signal(SIGALRM, timeout); 407339051Sasomers timed_out = setjmp(timeoutbuf); 408207614Simp alarm(thistimeout); 409207614Simp 410339051Sasomers if (timed_out != 0) { 411207614Simp tftp_log(LOG_ERR, "receive_packet: timeout"); 412207614Simp alarm(0); 413207614Simp return (RP_TIMEOUT); 414207614Simp } 415207614Simp 416212665Simp pfrom = (from == NULL) ? &from_local : from; 417207614Simp fromlen = sizeof(*pfrom); 418207614Simp n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); 419207614Simp 420207614Simp alarm(0); 421207614Simp 422207614Simp DROPPACKETn("receive_packet", RP_TIMEOUT); 423207614Simp 424207614Simp if (n < 0) { 425207614Simp tftp_log(LOG_ERR, "receive_packet: timeout"); 426207614Simp return (RP_TIMEOUT); 427207614Simp } 428207614Simp 429207614Simp if (n < 0) { 430207614Simp /* No idea what could have happened if it isn't a timeout */ 431207614Simp tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno)); 432207614Simp return (RP_RECVFROM); 433207614Simp } 434207614Simp if (n < 4) { 435207614Simp tftp_log(LOG_ERR, 436207614Simp "receive_packet: packet too small (%d bytes)", n); 437207614Simp return (RP_TOOSMALL); 438207614Simp } 439207614Simp 440207614Simp pkt->th_opcode = ntohs((u_short)pkt->th_opcode); 441207614Simp if (pkt->th_opcode == DATA || 442207614Simp pkt->th_opcode == ACK) 443207614Simp pkt->th_block = ntohs((u_short)pkt->th_block); 444207614Simp 445207614Simp if (pkt->th_opcode == DATA && n > pktsize) { 446207614Simp tftp_log(LOG_ERR, "receive_packet: packet too big"); 447207614Simp return (RP_TOOBIG); 448207614Simp } 449207614Simp 450207614Simp if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr != 451207614Simp ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) { 452207614Simp tftp_log(LOG_ERR, 453207614Simp "receive_packet: received packet from wrong source"); 454207614Simp return (RP_WRONGSOURCE); 455207614Simp } 456207614Simp 457207614Simp if (pkt->th_opcode == ERROR) { 458231973Semaste tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR, 459231973Semaste "Got ERROR packet: %s", pkt->th_msg); 460207614Simp return (RP_ERROR); 461207614Simp } 462207614Simp 463207614Simp if (debug&DEBUG_PACKETS) 464207614Simp tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet", 465207614Simp n, packettype(pkt->th_opcode)); 466207614Simp 467207614Simp return n - 4; 468207614Simp} 469