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 56e7ff5475SWarner Losh 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) { \ 75*1acf0dbaSUlrich 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) { \ 81*1acf0dbaSUlrich 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 char buf[MAXPKTSIZE]; 91e7ff5475SWarner Losh 92e7ff5475SWarner Losh if (error == 0) 93e7ff5475SWarner Losh return ("success"); 94e7ff5475SWarner Losh for (pe = errmsgs; pe->e_code >= 0; pe++) 95e7ff5475SWarner Losh if (pe->e_code == error) 96e7ff5475SWarner Losh return (pe->e_msg); 97e7ff5475SWarner Losh snprintf(ebuf, sizeof(buf), "error %d", error); 98e7ff5475SWarner Losh return (ebuf); 99e7ff5475SWarner Losh } 100e7ff5475SWarner Losh 101e7ff5475SWarner Losh static int 102e7ff5475SWarner Losh send_packet(int peer, uint16_t block, char *pkt, int size) 103e7ff5475SWarner Losh { 104e7ff5475SWarner Losh int i; 105e7ff5475SWarner Losh int t = 1; 106e7ff5475SWarner Losh 107e7ff5475SWarner Losh for (i = 0; i < 12 ; i++) { 108e7ff5475SWarner Losh DROPPACKETn("send_packet", 0); 109e7ff5475SWarner Losh 110e7ff5475SWarner Losh if (sendto(peer, pkt, size, 0, 111e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len) 112e7ff5475SWarner Losh == size) { 113e7ff5475SWarner Losh if (i) 114e7ff5475SWarner Losh tftp_log(LOG_ERR, 115e7ff5475SWarner Losh "%s block %d, attempt %d successful", 116e7ff5475SWarner Losh block, i); 117e7ff5475SWarner Losh return (0); 118e7ff5475SWarner Losh } 119e7ff5475SWarner Losh tftp_log(LOG_ERR, 120e7ff5475SWarner Losh "%s block %d, attempt %d failed (Error %d: %s)", 121e7ff5475SWarner Losh packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)), 122e7ff5475SWarner Losh block, i, errno, strerror(errno)); 123e7ff5475SWarner Losh sleep(t); 124e7ff5475SWarner Losh if (t < 32) 125e7ff5475SWarner Losh t <<= 1; 126e7ff5475SWarner Losh } 127e7ff5475SWarner Losh tftp_log(LOG_ERR, "send_packet: %s", strerror(errno)); 128e7ff5475SWarner Losh return (1); 129e7ff5475SWarner Losh } 130e7ff5475SWarner Losh 131e7ff5475SWarner Losh /* 132e7ff5475SWarner Losh * Send an ERROR packet (error message). 133e7ff5475SWarner Losh * Error code passed in is one of the 134e7ff5475SWarner Losh * standard TFTP codes, or a UNIX errno 135e7ff5475SWarner Losh * offset by 100. 136e7ff5475SWarner Losh */ 137e7ff5475SWarner Losh void 138e7ff5475SWarner Losh send_error(int peer, int error) 139e7ff5475SWarner Losh { 140e7ff5475SWarner Losh struct tftphdr *tp; 141e7ff5475SWarner Losh int length; 142e7ff5475SWarner Losh struct errmsg *pe; 143e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 144e7ff5475SWarner Losh 145e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 146e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error); 147e7ff5475SWarner Losh 148e7ff5475SWarner Losh DROPPACKET("send_error"); 149e7ff5475SWarner Losh 150e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 151e7ff5475SWarner Losh tp->th_opcode = htons((u_short)ERROR); 152e7ff5475SWarner Losh tp->th_code = htons((u_short)error); 153e7ff5475SWarner Losh for (pe = errmsgs; pe->e_code >= 0; pe++) 154e7ff5475SWarner Losh if (pe->e_code == error) 155e7ff5475SWarner Losh break; 156e7ff5475SWarner Losh if (pe->e_code < 0) { 157e7ff5475SWarner Losh pe->e_msg = strerror(error - 100); 158e7ff5475SWarner Losh tp->th_code = EUNDEF; /* set 'undef' errorcode */ 159e7ff5475SWarner Losh } 160e7ff5475SWarner Losh strcpy(tp->th_msg, pe->e_msg); 161e7ff5475SWarner Losh length = strlen(pe->e_msg); 162e7ff5475SWarner Losh tp->th_msg[length] = '\0'; 163e7ff5475SWarner Losh length += 5; 164e7ff5475SWarner Losh 165e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 166e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg); 167e7ff5475SWarner Losh 168e7ff5475SWarner Losh if (sendto(peer, buf, length, 0, 169e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length) 170e7ff5475SWarner Losh tftp_log(LOG_ERR, "send_error: %s", strerror(errno)); 171e7ff5475SWarner Losh } 172e7ff5475SWarner Losh 173e7ff5475SWarner Losh /* 174e7ff5475SWarner Losh * Send an WRQ packet (write request). 175e7ff5475SWarner Losh */ 176e7ff5475SWarner Losh int 177e7ff5475SWarner Losh send_wrq(int peer, char *filename, char *mode) 178e7ff5475SWarner Losh { 179e7ff5475SWarner Losh int n; 180e7ff5475SWarner Losh struct tftphdr *tp; 181e7ff5475SWarner Losh char *bp; 182e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 183e7ff5475SWarner Losh int size; 184e7ff5475SWarner Losh 185e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 186e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", 187e7ff5475SWarner Losh filename, mode 188e7ff5475SWarner Losh ); 189e7ff5475SWarner Losh 190e7ff5475SWarner Losh DROPPACKETn("send_wrq", 1); 191e7ff5475SWarner Losh 192e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 193e7ff5475SWarner Losh tp->th_opcode = htons((u_short)WRQ); 194e7ff5475SWarner Losh size = 2; 195e7ff5475SWarner Losh 196e7ff5475SWarner Losh bp = tp->th_stuff; 197e7ff5475SWarner Losh strcpy(bp, filename); 198e7ff5475SWarner Losh bp += strlen(filename); 199e7ff5475SWarner Losh *bp = 0; 200e7ff5475SWarner Losh bp++; 201e7ff5475SWarner Losh size += strlen(filename) + 1; 202e7ff5475SWarner Losh 203e7ff5475SWarner Losh strcpy(bp, mode); 204e7ff5475SWarner Losh bp += strlen(mode); 205e7ff5475SWarner Losh *bp = 0; 206e7ff5475SWarner Losh bp++; 207e7ff5475SWarner Losh size += strlen(mode) + 1; 208e7ff5475SWarner Losh 209e7ff5475SWarner Losh if (options_rfc_enabled) 210e7ff5475SWarner Losh size += make_options(peer, bp, sizeof(buf) - size); 211e7ff5475SWarner Losh 212e7ff5475SWarner Losh n = sendto(peer, buf, size, 0, 213e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len); 214e7ff5475SWarner Losh if (n != size) { 215e7ff5475SWarner Losh tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); 216e7ff5475SWarner Losh return (1); 217e7ff5475SWarner Losh } 218e7ff5475SWarner Losh return (0); 219e7ff5475SWarner Losh } 220e7ff5475SWarner Losh 221e7ff5475SWarner Losh /* 222e7ff5475SWarner Losh * Send an RRQ packet (write request). 223e7ff5475SWarner Losh */ 224e7ff5475SWarner Losh int 225e7ff5475SWarner Losh send_rrq(int peer, char *filename, char *mode) 226e7ff5475SWarner Losh { 227e7ff5475SWarner Losh int n; 228e7ff5475SWarner Losh struct tftphdr *tp; 229e7ff5475SWarner Losh char *bp; 230e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 231e7ff5475SWarner Losh int size; 232e7ff5475SWarner Losh 233e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 234e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", 235e7ff5475SWarner Losh filename, mode 236e7ff5475SWarner Losh ); 237e7ff5475SWarner Losh 238e7ff5475SWarner Losh DROPPACKETn("send_rrq", 1); 239e7ff5475SWarner Losh 240e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 241e7ff5475SWarner Losh tp->th_opcode = htons((u_short)RRQ); 242e7ff5475SWarner Losh size = 2; 243e7ff5475SWarner Losh 244e7ff5475SWarner Losh bp = tp->th_stuff; 245e7ff5475SWarner Losh strcpy(bp, filename); 246e7ff5475SWarner Losh bp += strlen(filename); 247e7ff5475SWarner Losh *bp = 0; 248e7ff5475SWarner Losh bp++; 249e7ff5475SWarner Losh size += strlen(filename) + 1; 250e7ff5475SWarner Losh 251e7ff5475SWarner Losh strcpy(bp, mode); 252e7ff5475SWarner Losh bp += strlen(mode); 253e7ff5475SWarner Losh *bp = 0; 254e7ff5475SWarner Losh bp++; 255e7ff5475SWarner Losh size += strlen(mode) + 1; 256e7ff5475SWarner Losh 257e7ff5475SWarner Losh if (options_rfc_enabled) { 258e7ff5475SWarner Losh options[OPT_TSIZE].o_request = strdup("0"); 259e7ff5475SWarner Losh size += make_options(peer, bp, sizeof(buf) - size); 260e7ff5475SWarner Losh } 261e7ff5475SWarner Losh 262e7ff5475SWarner Losh n = sendto(peer, buf, size, 0, 263e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len); 264e7ff5475SWarner Losh if (n != size) { 2653b6bd978SCraig Rodrigues tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno)); 266e7ff5475SWarner Losh return (1); 267e7ff5475SWarner Losh } 268e7ff5475SWarner Losh return (0); 269e7ff5475SWarner Losh } 270e7ff5475SWarner Losh 271e7ff5475SWarner Losh /* 272e7ff5475SWarner Losh * Send an OACK packet (option acknowledgement). 273e7ff5475SWarner Losh */ 274e7ff5475SWarner Losh int 275e7ff5475SWarner Losh send_oack(int peer) 276e7ff5475SWarner Losh { 277e7ff5475SWarner Losh struct tftphdr *tp; 278e7ff5475SWarner Losh int size, i, n; 279e7ff5475SWarner Losh char *bp; 280e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 281e7ff5475SWarner Losh 282e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 283e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending OACK"); 284e7ff5475SWarner Losh 285e7ff5475SWarner Losh DROPPACKETn("send_oack", 0); 286e7ff5475SWarner Losh 287e7ff5475SWarner Losh /* 288e7ff5475SWarner Losh * Send back an options acknowledgement (only the ones with 289e7ff5475SWarner Losh * a reply for) 290e7ff5475SWarner Losh */ 291e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 292e7ff5475SWarner Losh bp = buf + 2; 293e7ff5475SWarner Losh size = sizeof(buf) - 2; 294e7ff5475SWarner Losh tp->th_opcode = htons((u_short)OACK); 295e7ff5475SWarner Losh for (i = 0; options[i].o_type != NULL; i++) { 296e7ff5475SWarner Losh if (options[i].o_reply != NULL) { 297e7ff5475SWarner Losh n = snprintf(bp, size, "%s%c%s", options[i].o_type, 298e7ff5475SWarner Losh 0, options[i].o_reply); 299e7ff5475SWarner Losh bp += n+1; 300e7ff5475SWarner Losh size -= n+1; 301e7ff5475SWarner Losh if (size < 0) { 302e7ff5475SWarner Losh tftp_log(LOG_ERR, "oack: buffer overflow"); 303e7ff5475SWarner Losh exit(1); 304e7ff5475SWarner Losh } 305e7ff5475SWarner Losh } 306e7ff5475SWarner Losh } 307e7ff5475SWarner Losh size = bp - buf; 308e7ff5475SWarner Losh 309e7ff5475SWarner Losh if (sendto(peer, buf, size, 0, 310e7ff5475SWarner Losh (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { 311e7ff5475SWarner Losh tftp_log(LOG_INFO, "send_oack: %s", strerror(errno)); 312e7ff5475SWarner Losh return (1); 313e7ff5475SWarner Losh } 314e7ff5475SWarner Losh 315e7ff5475SWarner Losh return (0); 316e7ff5475SWarner Losh } 317e7ff5475SWarner Losh 318e7ff5475SWarner Losh /* 319e7ff5475SWarner Losh * Send an ACK packet (acknowledgement). 320e7ff5475SWarner Losh */ 321e7ff5475SWarner Losh int 322e7ff5475SWarner Losh send_ack(int fp, uint16_t block) 323e7ff5475SWarner Losh { 324e7ff5475SWarner Losh struct tftphdr *tp; 325e7ff5475SWarner Losh int size; 326e7ff5475SWarner Losh char *bp; 327e7ff5475SWarner Losh char buf[MAXPKTSIZE]; 328e7ff5475SWarner Losh 329e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 330e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending ACK for block %d", block); 331e7ff5475SWarner Losh 332e7ff5475SWarner Losh DROPPACKETn("send_ack", 0); 333e7ff5475SWarner Losh 334e7ff5475SWarner Losh tp = (struct tftphdr *)buf; 335e7ff5475SWarner Losh bp = buf + 2; 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 */ 380e7ff5475SWarner Losh 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; 399e7ff5475SWarner Losh static int waiting; 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 waiting = 0; 408e7ff5475SWarner Losh signal(SIGALRM, timeout); 409e7ff5475SWarner Losh setjmp(timeoutbuf); 410e7ff5475SWarner Losh alarm(thistimeout); 411e7ff5475SWarner Losh 412e7ff5475SWarner Losh if (waiting > 0) { 413e7ff5475SWarner Losh alarm(0); 414e7ff5475SWarner Losh return (RP_TIMEOUT); 415e7ff5475SWarner Losh } 416e7ff5475SWarner Losh 417e7ff5475SWarner Losh if (waiting > 0) { 418e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: timeout"); 419e7ff5475SWarner Losh alarm(0); 420e7ff5475SWarner Losh return (RP_TIMEOUT); 421e7ff5475SWarner Losh } 422e7ff5475SWarner Losh 423e7ff5475SWarner Losh waiting++; 4243dc2fd3fSWarner Losh pfrom = (from == NULL) ? &from_local : from; 425e7ff5475SWarner Losh fromlen = sizeof(*pfrom); 426e7ff5475SWarner Losh n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); 427e7ff5475SWarner Losh 428e7ff5475SWarner Losh alarm(0); 429e7ff5475SWarner Losh 430e7ff5475SWarner Losh DROPPACKETn("receive_packet", RP_TIMEOUT); 431e7ff5475SWarner Losh 432e7ff5475SWarner Losh if (n < 0) { 433e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: timeout"); 434e7ff5475SWarner Losh return (RP_TIMEOUT); 435e7ff5475SWarner Losh } 436e7ff5475SWarner Losh 437e7ff5475SWarner Losh alarm(0); 438e7ff5475SWarner Losh 439e7ff5475SWarner Losh if (n < 0) { 440e7ff5475SWarner Losh /* No idea what could have happened if it isn't a timeout */ 441e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno)); 442e7ff5475SWarner Losh return (RP_RECVFROM); 443e7ff5475SWarner Losh } 444e7ff5475SWarner Losh if (n < 4) { 445e7ff5475SWarner Losh tftp_log(LOG_ERR, 446e7ff5475SWarner Losh "receive_packet: packet too small (%d bytes)", n); 447e7ff5475SWarner Losh return (RP_TOOSMALL); 448e7ff5475SWarner Losh } 449e7ff5475SWarner Losh 450e7ff5475SWarner Losh pkt->th_opcode = ntohs((u_short)pkt->th_opcode); 451e7ff5475SWarner Losh if (pkt->th_opcode == DATA || 452e7ff5475SWarner Losh pkt->th_opcode == ACK) 453e7ff5475SWarner Losh pkt->th_block = ntohs((u_short)pkt->th_block); 454e7ff5475SWarner Losh 455e7ff5475SWarner Losh if (pkt->th_opcode == DATA && n > pktsize) { 456e7ff5475SWarner Losh tftp_log(LOG_ERR, "receive_packet: packet too big"); 457e7ff5475SWarner Losh return (RP_TOOBIG); 458e7ff5475SWarner Losh } 459e7ff5475SWarner Losh 460e7ff5475SWarner Losh if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr != 461e7ff5475SWarner Losh ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) { 462e7ff5475SWarner Losh tftp_log(LOG_ERR, 463e7ff5475SWarner Losh "receive_packet: received packet from wrong source"); 464e7ff5475SWarner Losh return (RP_WRONGSOURCE); 465e7ff5475SWarner Losh } 466e7ff5475SWarner Losh 467e7ff5475SWarner Losh if (pkt->th_opcode == ERROR) { 468e7ff5475SWarner Losh tftp_log(LOG_ERR, "Got ERROR packet: %s", pkt->th_msg); 469e7ff5475SWarner Losh return (RP_ERROR); 470e7ff5475SWarner Losh } 471e7ff5475SWarner Losh 472e7ff5475SWarner Losh if (debug&DEBUG_PACKETS) 473e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet", 474e7ff5475SWarner Losh n, packettype(pkt->th_opcode)); 475e7ff5475SWarner Losh 476e7ff5475SWarner Losh return n - 4; 477e7ff5475SWarner Losh } 478