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