1e7ff5475SWarner Losh /* 2e7ff5475SWarner Losh * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 3e7ff5475SWarner Losh * 4e7ff5475SWarner Losh * Redistribution and use in source and binary forms, with or without 5e7ff5475SWarner Losh * modification, are permitted provided that the following conditions 6e7ff5475SWarner Losh * are met: 7e7ff5475SWarner Losh * 1. Redistributions of source code must retain the above copyright 8e7ff5475SWarner Losh * notice, this list of conditions and the following disclaimer. 9e7ff5475SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 10e7ff5475SWarner Losh * notice, this list of conditions and the following disclaimer in the 11e7ff5475SWarner Losh * documentation and/or other materials provided with the distribution. 12e7ff5475SWarner Losh * 13e7ff5475SWarner Losh * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14e7ff5475SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15e7ff5475SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16e7ff5475SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17e7ff5475SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18e7ff5475SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19e7ff5475SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20e7ff5475SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21e7ff5475SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22e7ff5475SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23e7ff5475SWarner Losh * SUCH DAMAGE. 24e7ff5475SWarner Losh */ 25e7ff5475SWarner Losh 26e7ff5475SWarner Losh #include <sys/cdefs.h> 27e7ff5475SWarner Losh __FBSDID("$FreeBSD$"); 28e7ff5475SWarner Losh 29e7ff5475SWarner Losh #include <sys/stat.h> 30e7ff5475SWarner Losh #include <sys/types.h> 31e7ff5475SWarner Losh #include <sys/socket.h> 32e7ff5475SWarner Losh 33e7ff5475SWarner Losh #include <netinet/in.h> 34e7ff5475SWarner Losh #include <arpa/tftp.h> 35e7ff5475SWarner Losh #include <arpa/inet.h> 36e7ff5475SWarner Losh 37e7ff5475SWarner Losh #include <errno.h> 38e7ff5475SWarner Losh #include <setjmp.h> 39e7ff5475SWarner Losh #include <signal.h> 40e7ff5475SWarner Losh #include <stdio.h> 41e7ff5475SWarner Losh #include <stdlib.h> 42e7ff5475SWarner Losh #include <string.h> 43e7ff5475SWarner Losh #include <syslog.h> 44e7ff5475SWarner Losh #include <unistd.h> 45e7ff5475SWarner Losh 46e7ff5475SWarner Losh #include "tftp-file.h" 47e7ff5475SWarner Losh #include "tftp-io.h" 48e7ff5475SWarner Losh #include "tftp-utils.h" 49e7ff5475SWarner Losh #include "tftp-options.h" 50e7ff5475SWarner Losh 51e7ff5475SWarner Losh struct sockaddr_storage peer_sock; 52e7ff5475SWarner Losh struct sockaddr_storage me_sock; 53e7ff5475SWarner Losh 54e7ff5475SWarner Losh static int send_packet(int peer, uint16_t block, char *pkt, int size); 55e7ff5475SWarner Losh 56ae824d80SEd Schouten static struct errmsg { 57e7ff5475SWarner Losh int e_code; 58e7ff5475SWarner Losh const char *e_msg; 59e7ff5475SWarner Losh } errmsgs[] = { 60e7ff5475SWarner Losh { EUNDEF, "Undefined error code" }, 61e7ff5475SWarner Losh { ENOTFOUND, "File not found" }, 62e7ff5475SWarner Losh { EACCESS, "Access violation" }, 63e7ff5475SWarner Losh { ENOSPACE, "Disk full or allocation exceeded" }, 64e7ff5475SWarner Losh { EBADOP, "Illegal TFTP operation" }, 65e7ff5475SWarner Losh { EBADID, "Unknown transfer ID" }, 66e7ff5475SWarner Losh { EEXISTS, "File already exists" }, 67e7ff5475SWarner Losh { ENOUSER, "No such user" }, 68e7ff5475SWarner Losh { EOPTNEG, "Option negotiation" }, 69e7ff5475SWarner Losh { -1, NULL } 70e7ff5475SWarner Losh }; 71e7ff5475SWarner Losh 72e7ff5475SWarner Losh #define DROPPACKET(s) \ 73e7ff5475SWarner Losh if (packetdroppercentage != 0 && \ 74e7ff5475SWarner Losh random()%100 < packetdroppercentage) { \ 751acf0dbaSUlrich Spörlein tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ 76e7ff5475SWarner Losh return; \ 77e7ff5475SWarner Losh } 78e7ff5475SWarner Losh #define DROPPACKETn(s,n) \ 79e7ff5475SWarner Losh if (packetdroppercentage != 0 && \ 80e7ff5475SWarner Losh random()%100 < packetdroppercentage) { \ 811acf0dbaSUlrich Spörlein tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ 82e7ff5475SWarner Losh return (n); \ 83e7ff5475SWarner Losh } 84e7ff5475SWarner Losh 85e7ff5475SWarner Losh const char * 86e7ff5475SWarner Losh errtomsg(int error) 87e7ff5475SWarner Losh { 88e7ff5475SWarner Losh static char ebuf[40]; 89e7ff5475SWarner Losh struct errmsg *pe; 90e7ff5475SWarner Losh 91e7ff5475SWarner Losh if (error == 0) 92e7ff5475SWarner Losh return ("success"); 93e7ff5475SWarner Losh for (pe = errmsgs; pe->e_code >= 0; pe++) 94e7ff5475SWarner Losh if (pe->e_code == error) 95e7ff5475SWarner Losh return (pe->e_msg); 963496d72cSAntoine Brodin snprintf(ebuf, sizeof(ebuf), "error %d", error); 97e7ff5475SWarner Losh return (ebuf); 98e7ff5475SWarner Losh } 99e7ff5475SWarner Losh 100e7ff5475SWarner Losh static int 101e7ff5475SWarner Losh send_packet(int peer, uint16_t block, char *pkt, int size) 102e7ff5475SWarner Losh { 103e7ff5475SWarner Losh int i; 104e7ff5475SWarner Losh int t = 1; 105e7ff5475SWarner Losh 106e7ff5475SWarner Losh for (i = 0; i < 12 ; i++) { 107e7ff5475SWarner Losh DROPPACKETn("send_packet", 0); 108e7ff5475SWarner Losh 109*b713097aSMarius Strobl if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock, 110*b713097aSMarius Strobl peer_sock.ss_len) == size) { 111e7ff5475SWarner Losh if (i) 112e7ff5475SWarner Losh tftp_log(LOG_ERR, 113e7ff5475SWarner Losh "%s block %d, attempt %d successful", 114*b713097aSMarius Strobl packettype(ntohs(((struct tftphdr *) 115*b713097aSMarius Strobl (pkt))->th_opcode)), block, i); 116e7ff5475SWarner Losh return (0); 117e7ff5475SWarner Losh } 118e7ff5475SWarner Losh tftp_log(LOG_ERR, 119e7ff5475SWarner Losh "%s block %d, attempt %d failed (Error %d: %s)", 120e7ff5475SWarner Losh packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)), 121e7ff5475SWarner Losh block, i, errno, strerror(errno)); 122e7ff5475SWarner Losh sleep(t); 123e7ff5475SWarner Losh if (t < 32) 124e7ff5475SWarner Losh t <<= 1; 125e7ff5475SWarner Losh } 126e7ff5475SWarner Losh tftp_log(LOG_ERR, "send_packet: %s", strerror(errno)); 127e7ff5475SWarner Losh return (1); 128e7ff5475SWarner Losh } 129e7ff5475SWarner Losh 130e7ff5475SWarner Losh /* 131e7ff5475SWarner Losh * Send an ERROR packet (error message). 132e7ff5475SWarner Losh * Error code passed in is one of the 133e7ff5475SWarner Losh * standard TFTP codes, or a UNIX errno 134e7ff5475SWarner Losh * offset by 100. 135e7ff5475SWarner Losh */ 136e7ff5475SWarner Losh void 137e7ff5475SWarner Losh send_error(int peer, int error) 138e7ff5475SWarner Losh { 139e7ff5475SWarner Losh struct tftphdr *tp; 140e7ff5475SWarner Losh int length; 141e7ff5475SWarner Losh struct errmsg *pe; 142e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 143e7ff5475SWarner Losh 144e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 145663a6522SSean Bruno tftp_log(LOG_DEBUG, "Sending ERROR %d", error); 146e7ff5475SWarner Losh 147e7ff5475SWarner Losh DROPPACKET("send_error"); 148e7ff5475SWarner Losh 149e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 150e7ff5475SWarner Losh tp->th_opcode = htons((u_short)ERROR); 151e7ff5475SWarner Losh tp->th_code = htons((u_short)error); 152e7ff5475SWarner Losh for (pe = errmsgs; pe->e_code >= 0; pe++) 153e7ff5475SWarner Losh if (pe->e_code == error) 154e7ff5475SWarner Losh break; 155e7ff5475SWarner Losh if (pe->e_code < 0) { 156e7ff5475SWarner Losh pe->e_msg = strerror(error - 100); 157e7ff5475SWarner Losh tp->th_code = EUNDEF; /* set 'undef' errorcode */ 158e7ff5475SWarner Losh } 159e7ff5475SWarner Losh strcpy(tp->th_msg, pe->e_msg); 160e7ff5475SWarner Losh length = strlen(pe->e_msg); 161e7ff5475SWarner Losh tp->th_msg[length] = '\0'; 162e7ff5475SWarner Losh length += 5; 163e7ff5475SWarner Losh 164e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 165e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg); 166e7ff5475SWarner Losh 167e7ff5475SWarner Losh if (sendto(peer, buf, length, 0, 168e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length) 169e7ff5475SWarner Losh tftp_log(LOG_ERR, "send_error: %s", strerror(errno)); 170e7ff5475SWarner Losh } 171e7ff5475SWarner Losh 172e7ff5475SWarner Losh /* 173e7ff5475SWarner Losh * Send an WRQ packet (write request). 174e7ff5475SWarner Losh */ 175e7ff5475SWarner Losh int 176e7ff5475SWarner Losh send_wrq(int peer, char *filename, char *mode) 177e7ff5475SWarner Losh { 178e7ff5475SWarner Losh int n; 179e7ff5475SWarner Losh struct tftphdr *tp; 180e7ff5475SWarner Losh char *bp; 181e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 182e7ff5475SWarner Losh int size; 183e7ff5475SWarner Losh 184e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 185e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", 186e7ff5475SWarner Losh filename, mode 187e7ff5475SWarner Losh ); 188e7ff5475SWarner Losh 189e7ff5475SWarner Losh DROPPACKETn("send_wrq", 1); 190e7ff5475SWarner Losh 191e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 192e7ff5475SWarner Losh tp->th_opcode = htons((u_short)WRQ); 193e7ff5475SWarner Losh size = 2; 194e7ff5475SWarner Losh 195e7ff5475SWarner Losh bp = tp->th_stuff; 196e7ff5475SWarner Losh strcpy(bp, filename); 197e7ff5475SWarner Losh bp += strlen(filename); 198e7ff5475SWarner Losh *bp = 0; 199e7ff5475SWarner Losh bp++; 200e7ff5475SWarner Losh size += strlen(filename) + 1; 201e7ff5475SWarner Losh 202e7ff5475SWarner Losh strcpy(bp, mode); 203e7ff5475SWarner Losh bp += strlen(mode); 204e7ff5475SWarner Losh *bp = 0; 205e7ff5475SWarner Losh bp++; 206e7ff5475SWarner Losh size += strlen(mode) + 1; 207e7ff5475SWarner Losh 208e7ff5475SWarner Losh if (options_rfc_enabled) 209e7ff5475SWarner Losh size += make_options(peer, bp, sizeof(buf) - size); 210e7ff5475SWarner Losh 211e7ff5475SWarner Losh n = sendto(peer, buf, size, 0, 212e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len); 213e7ff5475SWarner Losh if (n != size) { 214e7ff5475SWarner Losh tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); 215e7ff5475SWarner Losh return (1); 216e7ff5475SWarner Losh } 217e7ff5475SWarner Losh return (0); 218e7ff5475SWarner Losh } 219e7ff5475SWarner Losh 220e7ff5475SWarner Losh /* 221e7ff5475SWarner Losh * Send an RRQ packet (write request). 222e7ff5475SWarner Losh */ 223e7ff5475SWarner Losh int 224e7ff5475SWarner Losh send_rrq(int peer, char *filename, char *mode) 225e7ff5475SWarner Losh { 226e7ff5475SWarner Losh int n; 227e7ff5475SWarner Losh struct tftphdr *tp; 228e7ff5475SWarner Losh char *bp; 229e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 230e7ff5475SWarner Losh int size; 231e7ff5475SWarner Losh 232e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 233e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", 234e7ff5475SWarner Losh filename, mode 235e7ff5475SWarner Losh ); 236e7ff5475SWarner Losh 237e7ff5475SWarner Losh DROPPACKETn("send_rrq", 1); 238e7ff5475SWarner Losh 239e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 240e7ff5475SWarner Losh tp->th_opcode = htons((u_short)RRQ); 241e7ff5475SWarner Losh size = 2; 242e7ff5475SWarner Losh 243e7ff5475SWarner Losh bp = tp->th_stuff; 244e7ff5475SWarner Losh strcpy(bp, filename); 245e7ff5475SWarner Losh bp += strlen(filename); 246e7ff5475SWarner Losh *bp = 0; 247e7ff5475SWarner Losh bp++; 248e7ff5475SWarner Losh size += strlen(filename) + 1; 249e7ff5475SWarner Losh 250e7ff5475SWarner Losh strcpy(bp, mode); 251e7ff5475SWarner Losh bp += strlen(mode); 252e7ff5475SWarner Losh *bp = 0; 253e7ff5475SWarner Losh bp++; 254e7ff5475SWarner Losh size += strlen(mode) + 1; 255e7ff5475SWarner Losh 256e7ff5475SWarner Losh if (options_rfc_enabled) { 257e7ff5475SWarner Losh options[OPT_TSIZE].o_request = strdup("0"); 258e7ff5475SWarner Losh size += make_options(peer, bp, sizeof(buf) - size); 259e7ff5475SWarner Losh } 260e7ff5475SWarner Losh 261e7ff5475SWarner Losh n = sendto(peer, buf, size, 0, 262e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len); 263e7ff5475SWarner Losh if (n != size) { 2643b6bd978SCraig Rodrigues tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno)); 265e7ff5475SWarner Losh return (1); 266e7ff5475SWarner Losh } 267e7ff5475SWarner Losh return (0); 268e7ff5475SWarner Losh } 269e7ff5475SWarner Losh 270e7ff5475SWarner Losh /* 271e7ff5475SWarner Losh * Send an OACK packet (option acknowledgement). 272e7ff5475SWarner Losh */ 273e7ff5475SWarner Losh int 274e7ff5475SWarner Losh send_oack(int peer) 275e7ff5475SWarner Losh { 276e7ff5475SWarner Losh struct tftphdr *tp; 277e7ff5475SWarner Losh int size, i, n; 278e7ff5475SWarner Losh char *bp; 279e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 280e7ff5475SWarner Losh 281e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 282e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending OACK"); 283e7ff5475SWarner Losh 284e7ff5475SWarner Losh DROPPACKETn("send_oack", 0); 285e7ff5475SWarner Losh 286e7ff5475SWarner Losh /* 287e7ff5475SWarner Losh * Send back an options acknowledgement (only the ones with 288e7ff5475SWarner Losh * a reply for) 289e7ff5475SWarner Losh */ 290e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 291e7ff5475SWarner Losh bp = buf + 2; 292e7ff5475SWarner Losh size = sizeof(buf) - 2; 293e7ff5475SWarner Losh tp->th_opcode = htons((u_short)OACK); 294e7ff5475SWarner Losh for (i = 0; options[i].o_type != NULL; i++) { 295e7ff5475SWarner Losh if (options[i].o_reply != NULL) { 296e7ff5475SWarner Losh n = snprintf(bp, size, "%s%c%s", options[i].o_type, 297e7ff5475SWarner Losh 0, options[i].o_reply); 298e7ff5475SWarner Losh bp += n+1; 299e7ff5475SWarner Losh size -= n+1; 300e7ff5475SWarner Losh if (size < 0) { 301e7ff5475SWarner Losh tftp_log(LOG_ERR, "oack: buffer overflow"); 302e7ff5475SWarner Losh exit(1); 303e7ff5475SWarner Losh } 304e7ff5475SWarner Losh } 305e7ff5475SWarner Losh } 306e7ff5475SWarner Losh size = bp - buf; 307e7ff5475SWarner Losh 308e7ff5475SWarner Losh if (sendto(peer, buf, size, 0, 309e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { 310e7ff5475SWarner Losh tftp_log(LOG_INFO, "send_oack: %s", strerror(errno)); 311e7ff5475SWarner Losh return (1); 312e7ff5475SWarner Losh } 313e7ff5475SWarner Losh 314e7ff5475SWarner Losh return (0); 315e7ff5475SWarner Losh } 316e7ff5475SWarner Losh 317e7ff5475SWarner Losh /* 318e7ff5475SWarner Losh * Send an ACK packet (acknowledgement). 319e7ff5475SWarner Losh */ 320e7ff5475SWarner Losh int 321e7ff5475SWarner Losh send_ack(int fp, uint16_t block) 322e7ff5475SWarner Losh { 323e7ff5475SWarner Losh struct tftphdr *tp; 324e7ff5475SWarner Losh int size; 325e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 326e7ff5475SWarner Losh 327e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 328e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending ACK for block %d", block); 329e7ff5475SWarner Losh 330e7ff5475SWarner Losh DROPPACKETn("send_ack", 0); 331e7ff5475SWarner Losh 332e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 333e7ff5475SWarner Losh size = sizeof(buf) - 2; 334e7ff5475SWarner Losh tp->th_opcode = htons((u_short)ACK); 335e7ff5475SWarner Losh tp->th_block = htons((u_short)block); 336e7ff5475SWarner Losh size = 4; 337e7ff5475SWarner Losh 338e7ff5475SWarner Losh if (sendto(fp, buf, size, 0, 339e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { 340e7ff5475SWarner Losh tftp_log(LOG_INFO, "send_ack: %s", strerror(errno)); 341e7ff5475SWarner Losh return (1); 342e7ff5475SWarner Losh } 343e7ff5475SWarner Losh 344e7ff5475SWarner Losh return (0); 345e7ff5475SWarner Losh } 346e7ff5475SWarner Losh 347e7ff5475SWarner Losh /* 348e7ff5475SWarner Losh * Send a DATA packet 349e7ff5475SWarner Losh */ 350e7ff5475SWarner Losh int 351e7ff5475SWarner Losh send_data(int peer, uint16_t block, char *data, int size) 352e7ff5475SWarner Losh { 353e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 354e7ff5475SWarner Losh struct tftphdr *pkt; 355e7ff5475SWarner Losh int n; 356e7ff5475SWarner Losh 357e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 358e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes", 359e7ff5475SWarner Losh block, size); 360e7ff5475SWarner Losh 361e7ff5475SWarner Losh DROPPACKETn("send_data", 0); 362e7ff5475SWarner Losh 363e7ff5475SWarner Losh pkt = (struct tftphdr *)buf; 364e7ff5475SWarner Losh 365e7ff5475SWarner Losh pkt->th_opcode = htons((u_short)DATA); 366e7ff5475SWarner Losh pkt->th_block = htons((u_short)block); 367e7ff5475SWarner Losh memcpy(pkt->th_data, data, size); 368e7ff5475SWarner Losh 369e7ff5475SWarner Losh n = send_packet(peer, block, (char *)pkt, size + 4); 370e7ff5475SWarner Losh return (n); 371e7ff5475SWarner Losh } 372e7ff5475SWarner Losh 373e7ff5475SWarner Losh 374e7ff5475SWarner Losh /* 375e7ff5475SWarner Losh * Receive a packet 376e7ff5475SWarner Losh */ 377ae824d80SEd Schouten static jmp_buf timeoutbuf; 378e7ff5475SWarner Losh 379e7ff5475SWarner Losh static void 380e7ff5475SWarner Losh timeout(int sig __unused) 381e7ff5475SWarner Losh { 382e7ff5475SWarner Losh 383e7ff5475SWarner Losh /* tftp_log(LOG_DEBUG, "Timeout\n"); Inside a signal handler... */ 384e7ff5475SWarner Losh longjmp(timeoutbuf, 1); 385e7ff5475SWarner Losh } 386e7ff5475SWarner Losh 387e7ff5475SWarner Losh int 388e7ff5475SWarner Losh receive_packet(int peer, char *data, int size, struct sockaddr_storage *from, 389e7ff5475SWarner Losh int thistimeout) 390e7ff5475SWarner Losh { 391e7ff5475SWarner Losh struct tftphdr *pkt; 392e7ff5475SWarner Losh struct sockaddr_storage from_local; 393e7ff5475SWarner Losh struct sockaddr_storage *pfrom; 394e7ff5475SWarner Losh socklen_t fromlen; 395e7ff5475SWarner Losh int n; 396e7ff5475SWarner Losh static int waiting; 397e7ff5475SWarner Losh 398e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 399e7ff5475SWarner Losh tftp_log(LOG_DEBUG, 400e7ff5475SWarner Losh "Waiting %d seconds for packet", timeoutpacket); 401e7ff5475SWarner Losh 402e7ff5475SWarner Losh pkt = (struct tftphdr *)data; 403e7ff5475SWarner Losh 404e7ff5475SWarner Losh waiting = 0; 405e7ff5475SWarner Losh signal(SIGALRM, timeout); 406e7ff5475SWarner Losh setjmp(timeoutbuf); 407e7ff5475SWarner Losh alarm(thistimeout); 408e7ff5475SWarner Losh 409e7ff5475SWarner Losh if (waiting > 0) { 410e7ff5475SWarner Losh alarm(0); 411e7ff5475SWarner Losh return (RP_TIMEOUT); 412e7ff5475SWarner Losh } 413e7ff5475SWarner Losh 414e7ff5475SWarner Losh if (waiting > 0) { 415e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: timeout"); 416e7ff5475SWarner Losh alarm(0); 417e7ff5475SWarner Losh return (RP_TIMEOUT); 418e7ff5475SWarner Losh } 419e7ff5475SWarner Losh 420e7ff5475SWarner Losh waiting++; 4213dc2fd3fSWarner Losh pfrom = (from == NULL) ? &from_local : from; 422e7ff5475SWarner Losh fromlen = sizeof(*pfrom); 423e7ff5475SWarner Losh n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); 424e7ff5475SWarner Losh 425e7ff5475SWarner Losh alarm(0); 426e7ff5475SWarner Losh 427e7ff5475SWarner Losh DROPPACKETn("receive_packet", RP_TIMEOUT); 428e7ff5475SWarner Losh 429e7ff5475SWarner Losh if (n < 0) { 430e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: timeout"); 431e7ff5475SWarner Losh return (RP_TIMEOUT); 432e7ff5475SWarner Losh } 433e7ff5475SWarner Losh 434e7ff5475SWarner Losh alarm(0); 435e7ff5475SWarner Losh 436e7ff5475SWarner Losh if (n < 0) { 437e7ff5475SWarner Losh /* No idea what could have happened if it isn't a timeout */ 438e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno)); 439e7ff5475SWarner Losh return (RP_RECVFROM); 440e7ff5475SWarner Losh } 441e7ff5475SWarner Losh if (n < 4) { 442e7ff5475SWarner Losh tftp_log(LOG_ERR, 443e7ff5475SWarner Losh "receive_packet: packet too small (%d bytes)", n); 444e7ff5475SWarner Losh return (RP_TOOSMALL); 445e7ff5475SWarner Losh } 446e7ff5475SWarner Losh 447e7ff5475SWarner Losh pkt->th_opcode = ntohs((u_short)pkt->th_opcode); 448e7ff5475SWarner Losh if (pkt->th_opcode == DATA || 449e7ff5475SWarner Losh pkt->th_opcode == ACK) 450e7ff5475SWarner Losh pkt->th_block = ntohs((u_short)pkt->th_block); 451e7ff5475SWarner Losh 452e7ff5475SWarner Losh if (pkt->th_opcode == DATA && n > pktsize) { 453e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: packet too big"); 454e7ff5475SWarner Losh return (RP_TOOBIG); 455e7ff5475SWarner Losh } 456e7ff5475SWarner Losh 457e7ff5475SWarner Losh if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr != 458e7ff5475SWarner Losh ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) { 459e7ff5475SWarner Losh tftp_log(LOG_ERR, 460e7ff5475SWarner Losh "receive_packet: received packet from wrong source"); 461e7ff5475SWarner Losh return (RP_WRONGSOURCE); 462e7ff5475SWarner Losh } 463e7ff5475SWarner Losh 464e7ff5475SWarner Losh if (pkt->th_opcode == ERROR) { 4659a2856b4SEd Maste tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR, 4669a2856b4SEd Maste "Got ERROR packet: %s", pkt->th_msg); 467e7ff5475SWarner Losh return (RP_ERROR); 468e7ff5475SWarner Losh } 469e7ff5475SWarner Losh 470e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 471e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet", 472e7ff5475SWarner Losh n, packettype(pkt->th_opcode)); 473e7ff5475SWarner Losh 474e7ff5475SWarner Losh return n - 4; 475e7ff5475SWarner Losh } 476