tftp-io.c revision 212665
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: head/libexec/tftpd/tftp-io.c 212665 2010-09-15 15:38:47Z imp $"); 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 37207614Simp#include <errno.h> 38207614Simp#include <setjmp.h> 39207614Simp#include <signal.h> 40207614Simp#include <stdio.h> 41207614Simp#include <stdlib.h> 42207614Simp#include <string.h> 43207614Simp#include <syslog.h> 44207614Simp#include <unistd.h> 45207614Simp 46207614Simp#include "tftp-file.h" 47207614Simp#include "tftp-io.h" 48207614Simp#include "tftp-utils.h" 49207614Simp#include "tftp-options.h" 50207614Simp 51207614Simpstruct sockaddr_storage peer_sock; 52207614Simpstruct sockaddr_storage me_sock; 53207614Simp 54207614Simpstatic int send_packet(int peer, uint16_t block, char *pkt, int size); 55207614Simp 56207614Simpstruct errmsg { 57207614Simp int e_code; 58207614Simp const char *e_msg; 59207614Simp} errmsgs[] = { 60207614Simp { EUNDEF, "Undefined error code" }, 61207614Simp { ENOTFOUND, "File not found" }, 62207614Simp { EACCESS, "Access violation" }, 63207614Simp { ENOSPACE, "Disk full or allocation exceeded" }, 64207614Simp { EBADOP, "Illegal TFTP operation" }, 65207614Simp { EBADID, "Unknown transfer ID" }, 66207614Simp { EEXISTS, "File already exists" }, 67207614Simp { ENOUSER, "No such user" }, 68207614Simp { EOPTNEG, "Option negotiation" }, 69207614Simp { -1, NULL } 70207614Simp}; 71207614Simp 72207614Simp#define DROPPACKET(s) \ 73207614Simp if (packetdroppercentage != 0 && \ 74207614Simp random()%100 < packetdroppercentage) { \ 75207614Simp tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s); \ 76207614Simp return; \ 77207614Simp } 78207614Simp#define DROPPACKETn(s,n) \ 79207614Simp if (packetdroppercentage != 0 && \ 80207614Simp random()%100 < packetdroppercentage) { \ 81207614Simp tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s); \ 82207614Simp return (n); \ 83207614Simp } 84207614Simp 85207614Simpconst char * 86207614Simperrtomsg(int error) 87207614Simp{ 88207614Simp static char ebuf[40]; 89207614Simp struct errmsg *pe; 90207614Simp char buf[MAXPKTSIZE]; 91207614Simp 92207614Simp if (error == 0) 93207614Simp return ("success"); 94207614Simp for (pe = errmsgs; pe->e_code >= 0; pe++) 95207614Simp if (pe->e_code == error) 96207614Simp return (pe->e_msg); 97207614Simp snprintf(ebuf, sizeof(buf), "error %d", error); 98207614Simp return (ebuf); 99207614Simp} 100207614Simp 101207614Simpstatic int 102207614Simpsend_packet(int peer, uint16_t block, char *pkt, int size) 103207614Simp{ 104207614Simp int i; 105207614Simp int t = 1; 106207614Simp 107207614Simp for (i = 0; i < 12 ; i++) { 108207614Simp DROPPACKETn("send_packet", 0); 109207614Simp 110207614Simp if (sendto(peer, pkt, size, 0, 111207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len) 112207614Simp == size) { 113207614Simp if (i) 114207614Simp tftp_log(LOG_ERR, 115207614Simp "%s block %d, attempt %d successful", 116207614Simp block, i); 117207614Simp return (0); 118207614Simp } 119207614Simp tftp_log(LOG_ERR, 120207614Simp "%s block %d, attempt %d failed (Error %d: %s)", 121207614Simp packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)), 122207614Simp block, i, errno, strerror(errno)); 123207614Simp sleep(t); 124207614Simp if (t < 32) 125207614Simp t <<= 1; 126207614Simp } 127207614Simp tftp_log(LOG_ERR, "send_packet: %s", strerror(errno)); 128207614Simp return (1); 129207614Simp} 130207614Simp 131207614Simp/* 132207614Simp * Send an ERROR packet (error message). 133207614Simp * Error code passed in is one of the 134207614Simp * standard TFTP codes, or a UNIX errno 135207614Simp * offset by 100. 136207614Simp */ 137207614Simpvoid 138207614Simpsend_error(int peer, int error) 139207614Simp{ 140207614Simp struct tftphdr *tp; 141207614Simp int length; 142207614Simp struct errmsg *pe; 143207614Simp char buf[MAXPKTSIZE]; 144207614Simp 145207614Simp if (debug&DEBUG_PACKETS) 146207614Simp tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error); 147207614Simp 148207614Simp DROPPACKET("send_error"); 149207614Simp 150207614Simp tp = (struct tftphdr *)buf; 151207614Simp tp->th_opcode = htons((u_short)ERROR); 152207614Simp tp->th_code = htons((u_short)error); 153207614Simp for (pe = errmsgs; pe->e_code >= 0; pe++) 154207614Simp if (pe->e_code == error) 155207614Simp break; 156207614Simp if (pe->e_code < 0) { 157207614Simp pe->e_msg = strerror(error - 100); 158207614Simp tp->th_code = EUNDEF; /* set 'undef' errorcode */ 159207614Simp } 160207614Simp strcpy(tp->th_msg, pe->e_msg); 161207614Simp length = strlen(pe->e_msg); 162207614Simp tp->th_msg[length] = '\0'; 163207614Simp length += 5; 164207614Simp 165207614Simp if (debug&DEBUG_PACKETS) 166207614Simp tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg); 167207614Simp 168207614Simp if (sendto(peer, buf, length, 0, 169207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length) 170207614Simp tftp_log(LOG_ERR, "send_error: %s", strerror(errno)); 171207614Simp} 172207614Simp 173207614Simp/* 174207614Simp * Send an WRQ packet (write request). 175207614Simp */ 176207614Simpint 177207614Simpsend_wrq(int peer, char *filename, char *mode) 178207614Simp{ 179207614Simp int n; 180207614Simp struct tftphdr *tp; 181207614Simp char *bp; 182207614Simp char buf[MAXPKTSIZE]; 183207614Simp int size; 184207614Simp 185207614Simp if (debug&DEBUG_PACKETS) 186207614Simp tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", 187207614Simp filename, mode 188207614Simp ); 189207614Simp 190207614Simp DROPPACKETn("send_wrq", 1); 191207614Simp 192207614Simp tp = (struct tftphdr *)buf; 193207614Simp tp->th_opcode = htons((u_short)WRQ); 194207614Simp size = 2; 195207614Simp 196207614Simp bp = tp->th_stuff; 197207614Simp strcpy(bp, filename); 198207614Simp bp += strlen(filename); 199207614Simp *bp = 0; 200207614Simp bp++; 201207614Simp size += strlen(filename) + 1; 202207614Simp 203207614Simp strcpy(bp, mode); 204207614Simp bp += strlen(mode); 205207614Simp *bp = 0; 206207614Simp bp++; 207207614Simp size += strlen(mode) + 1; 208207614Simp 209207614Simp if (options_rfc_enabled) 210207614Simp size += make_options(peer, bp, sizeof(buf) - size); 211207614Simp 212207614Simp n = sendto(peer, buf, size, 0, 213207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len); 214207614Simp if (n != size) { 215207614Simp tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); 216207614Simp return (1); 217207614Simp } 218207614Simp return (0); 219207614Simp} 220207614Simp 221207614Simp/* 222207614Simp * Send an RRQ packet (write request). 223207614Simp */ 224207614Simpint 225207614Simpsend_rrq(int peer, char *filename, char *mode) 226207614Simp{ 227207614Simp int n; 228207614Simp struct tftphdr *tp; 229207614Simp char *bp; 230207614Simp char buf[MAXPKTSIZE]; 231207614Simp int size; 232207614Simp 233207614Simp if (debug&DEBUG_PACKETS) 234207614Simp tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", 235207614Simp filename, mode 236207614Simp ); 237207614Simp 238207614Simp DROPPACKETn("send_rrq", 1); 239207614Simp 240207614Simp tp = (struct tftphdr *)buf; 241207614Simp tp->th_opcode = htons((u_short)RRQ); 242207614Simp size = 2; 243207614Simp 244207614Simp bp = tp->th_stuff; 245207614Simp strcpy(bp, filename); 246207614Simp bp += strlen(filename); 247207614Simp *bp = 0; 248207614Simp bp++; 249207614Simp size += strlen(filename) + 1; 250207614Simp 251207614Simp strcpy(bp, mode); 252207614Simp bp += strlen(mode); 253207614Simp *bp = 0; 254207614Simp bp++; 255207614Simp size += strlen(mode) + 1; 256207614Simp 257207614Simp if (options_rfc_enabled) { 258207614Simp options[OPT_TSIZE].o_request = strdup("0"); 259207614Simp size += make_options(peer, bp, sizeof(buf) - size); 260207614Simp } 261207614Simp 262207614Simp n = sendto(peer, buf, size, 0, 263207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len); 264207614Simp if (n != size) { 265207614Simp tftp_log(LOG_ERR, "send_rrq: %s", n, strerror(errno)); 266207614Simp return (1); 267207614Simp } 268207614Simp return (0); 269207614Simp} 270207614Simp 271207614Simp/* 272207614Simp * Send an OACK packet (option acknowledgement). 273207614Simp */ 274207614Simpint 275207614Simpsend_oack(int peer) 276207614Simp{ 277207614Simp struct tftphdr *tp; 278207614Simp int size, i, n; 279207614Simp char *bp; 280207614Simp char buf[MAXPKTSIZE]; 281207614Simp 282207614Simp if (debug&DEBUG_PACKETS) 283207614Simp tftp_log(LOG_DEBUG, "Sending OACK"); 284207614Simp 285207614Simp DROPPACKETn("send_oack", 0); 286207614Simp 287207614Simp /* 288207614Simp * Send back an options acknowledgement (only the ones with 289207614Simp * a reply for) 290207614Simp */ 291207614Simp tp = (struct tftphdr *)buf; 292207614Simp bp = buf + 2; 293207614Simp size = sizeof(buf) - 2; 294207614Simp tp->th_opcode = htons((u_short)OACK); 295207614Simp for (i = 0; options[i].o_type != NULL; i++) { 296207614Simp if (options[i].o_reply != NULL) { 297207614Simp n = snprintf(bp, size, "%s%c%s", options[i].o_type, 298207614Simp 0, options[i].o_reply); 299207614Simp bp += n+1; 300207614Simp size -= n+1; 301207614Simp if (size < 0) { 302207614Simp tftp_log(LOG_ERR, "oack: buffer overflow"); 303207614Simp exit(1); 304207614Simp } 305207614Simp } 306207614Simp } 307207614Simp size = bp - buf; 308207614Simp 309207614Simp if (sendto(peer, buf, size, 0, 310207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { 311207614Simp tftp_log(LOG_INFO, "send_oack: %s", strerror(errno)); 312207614Simp return (1); 313207614Simp } 314207614Simp 315207614Simp return (0); 316207614Simp} 317207614Simp 318207614Simp/* 319207614Simp * Send an ACK packet (acknowledgement). 320207614Simp */ 321207614Simpint 322207614Simpsend_ack(int fp, uint16_t block) 323207614Simp{ 324207614Simp struct tftphdr *tp; 325207614Simp int size; 326207614Simp char *bp; 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 bp = buf + 2; 336207614Simp size = sizeof(buf) - 2; 337207614Simp tp->th_opcode = htons((u_short)ACK); 338207614Simp tp->th_block = htons((u_short)block); 339207614Simp size = 4; 340207614Simp 341207614Simp if (sendto(fp, buf, size, 0, 342207614Simp (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { 343207614Simp tftp_log(LOG_INFO, "send_ack: %s", strerror(errno)); 344207614Simp return (1); 345207614Simp } 346207614Simp 347207614Simp return (0); 348207614Simp} 349207614Simp 350207614Simp/* 351207614Simp * Send a DATA packet 352207614Simp */ 353207614Simpint 354207614Simpsend_data(int peer, uint16_t block, char *data, int size) 355207614Simp{ 356207614Simp char buf[MAXPKTSIZE]; 357207614Simp struct tftphdr *pkt; 358207614Simp int n; 359207614Simp 360207614Simp if (debug&DEBUG_PACKETS) 361207614Simp tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes", 362207614Simp block, size); 363207614Simp 364207614Simp DROPPACKETn("send_data", 0); 365207614Simp 366207614Simp pkt = (struct tftphdr *)buf; 367207614Simp 368207614Simp pkt->th_opcode = htons((u_short)DATA); 369207614Simp pkt->th_block = htons((u_short)block); 370207614Simp memcpy(pkt->th_data, data, size); 371207614Simp 372207614Simp n = send_packet(peer, block, (char *)pkt, size + 4); 373207614Simp return (n); 374207614Simp} 375207614Simp 376207614Simp 377207614Simp/* 378207614Simp * Receive a packet 379207614Simp */ 380207614Simpjmp_buf timeoutbuf; 381207614Simp 382207614Simpstatic void 383207614Simptimeout(int sig __unused) 384207614Simp{ 385207614Simp 386207614Simp /* tftp_log(LOG_DEBUG, "Timeout\n"); Inside a signal handler... */ 387207614Simp longjmp(timeoutbuf, 1); 388207614Simp} 389207614Simp 390207614Simpint 391207614Simpreceive_packet(int peer, char *data, int size, struct sockaddr_storage *from, 392207614Simp int thistimeout) 393207614Simp{ 394207614Simp struct tftphdr *pkt; 395207614Simp struct sockaddr_storage from_local; 396207614Simp struct sockaddr_storage *pfrom; 397207614Simp socklen_t fromlen; 398207614Simp int n; 399207614Simp static int waiting; 400207614Simp 401207614Simp if (debug&DEBUG_PACKETS) 402207614Simp tftp_log(LOG_DEBUG, 403207614Simp "Waiting %d seconds for packet", timeoutpacket); 404207614Simp 405207614Simp pkt = (struct tftphdr *)data; 406207614Simp 407207614Simp waiting = 0; 408207614Simp signal(SIGALRM, timeout); 409207614Simp setjmp(timeoutbuf); 410207614Simp alarm(thistimeout); 411207614Simp 412207614Simp if (waiting > 0) { 413207614Simp alarm(0); 414207614Simp return (RP_TIMEOUT); 415207614Simp } 416207614Simp 417207614Simp if (waiting > 0) { 418207614Simp tftp_log(LOG_ERR, "receive_packet: timeout"); 419207614Simp alarm(0); 420207614Simp return (RP_TIMEOUT); 421207614Simp } 422207614Simp 423207614Simp waiting++; 424212665Simp pfrom = (from == NULL) ? &from_local : from; 425207614Simp fromlen = sizeof(*pfrom); 426207614Simp n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); 427207614Simp 428207614Simp alarm(0); 429207614Simp 430207614Simp DROPPACKETn("receive_packet", RP_TIMEOUT); 431207614Simp 432207614Simp if (n < 0) { 433207614Simp tftp_log(LOG_ERR, "receive_packet: timeout"); 434207614Simp return (RP_TIMEOUT); 435207614Simp } 436207614Simp 437207614Simp alarm(0); 438207614Simp 439207614Simp if (n < 0) { 440207614Simp /* No idea what could have happened if it isn't a timeout */ 441207614Simp tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno)); 442207614Simp return (RP_RECVFROM); 443207614Simp } 444207614Simp if (n < 4) { 445207614Simp tftp_log(LOG_ERR, 446207614Simp "receive_packet: packet too small (%d bytes)", n); 447207614Simp return (RP_TOOSMALL); 448207614Simp } 449207614Simp 450207614Simp pkt->th_opcode = ntohs((u_short)pkt->th_opcode); 451207614Simp if (pkt->th_opcode == DATA || 452207614Simp pkt->th_opcode == ACK) 453207614Simp pkt->th_block = ntohs((u_short)pkt->th_block); 454207614Simp 455207614Simp if (pkt->th_opcode == DATA && n > pktsize) { 456207614Simp tftp_log(LOG_ERR, "receive_packet: packet too big"); 457207614Simp return (RP_TOOBIG); 458207614Simp } 459207614Simp 460207614Simp if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr != 461207614Simp ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) { 462207614Simp tftp_log(LOG_ERR, 463207614Simp "receive_packet: received packet from wrong source"); 464207614Simp return (RP_WRONGSOURCE); 465207614Simp } 466207614Simp 467207614Simp if (pkt->th_opcode == ERROR) { 468207614Simp tftp_log(LOG_ERR, "Got ERROR packet: %s", pkt->th_msg); 469207614Simp return (RP_ERROR); 470207614Simp } 471207614Simp 472207614Simp if (debug&DEBUG_PACKETS) 473207614Simp tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet", 474207614Simp n, packettype(pkt->th_opcode)); 475207614Simp 476207614Simp return n - 4; 477207614Simp} 478