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/socket.h> 32e7ff5475SWarner Losh #include <sys/stat.h> 33e7ff5475SWarner Losh 34e7ff5475SWarner Losh #include <netinet/in.h> 35e7ff5475SWarner Losh #include <arpa/tftp.h> 36e7ff5475SWarner Losh 37e7ff5475SWarner Losh #include <stdarg.h> 38e7ff5475SWarner Losh #include <stdio.h> 39e7ff5475SWarner Losh #include <stdlib.h> 40e7ff5475SWarner Losh #include <string.h> 41e7ff5475SWarner Losh #include <syslog.h> 42e7ff5475SWarner Losh 43e7ff5475SWarner Losh #include "tftp-utils.h" 44e7ff5475SWarner Losh #include "tftp-io.h" 45e7ff5475SWarner Losh 46e7ff5475SWarner Losh /* 47e7ff5475SWarner Losh * Default values, can be changed later via the TFTP Options 48e7ff5475SWarner Losh */ 49e7ff5475SWarner Losh int timeoutpacket = TIMEOUT; 50e7ff5475SWarner Losh int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT; 51e7ff5475SWarner Losh int maxtimeouts = MAX_TIMEOUTS; 52e7ff5475SWarner Losh uint16_t segsize = SEGSIZE; 53e7ff5475SWarner Losh uint16_t pktsize = SEGSIZE + 4; 54e7ff5475SWarner Losh 55e7ff5475SWarner Losh int acting_as_client; 56e7ff5475SWarner Losh 57e7ff5475SWarner Losh 58e7ff5475SWarner Losh /* 59e7ff5475SWarner Losh * Set timeout values for packet reception. The idea is that you 60e7ff5475SWarner Losh * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the 61e7ff5475SWarner Losh * first timeout) to 'timeoutnetwork' (i.e. the last timeout) 62e7ff5475SWarner Losh */ 63e7ff5475SWarner Losh int 6404ebad38SMarius Strobl settimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused) 65e7ff5475SWarner Losh { 66e7ff5475SWarner Losh int i; 67e7ff5475SWarner Losh 68e7ff5475SWarner Losh /* We cannot do impossible things */ 69e7ff5475SWarner Losh if (_timeoutpacket >= _timeoutnetwork) 70e7ff5475SWarner Losh return (0); 71e7ff5475SWarner Losh 72e7ff5475SWarner Losh maxtimeouts = 0; 73e7ff5475SWarner Losh i = _timeoutpacket; 74e7ff5475SWarner Losh while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) { 75e7ff5475SWarner Losh maxtimeouts++; 76e7ff5475SWarner Losh i += 5; 77e7ff5475SWarner Losh } 78e7ff5475SWarner Losh 79e7ff5475SWarner Losh timeoutpacket = _timeoutpacket; 80e7ff5475SWarner Losh timeoutnetwork = i; 81e7ff5475SWarner Losh return (1); 82e7ff5475SWarner Losh } 83e7ff5475SWarner Losh 84e7ff5475SWarner Losh /* translate IPv4 mapped IPv6 address to IPv4 address */ 85e7ff5475SWarner Losh void 86e7ff5475SWarner Losh unmappedaddr(struct sockaddr_in6 *sin6) 87e7ff5475SWarner Losh { 88e7ff5475SWarner Losh struct sockaddr_in *sin4; 89e7ff5475SWarner Losh u_int32_t addr; 90e7ff5475SWarner Losh int port; 91e7ff5475SWarner Losh 92e7ff5475SWarner Losh if (sin6->sin6_family != AF_INET6 || 93e7ff5475SWarner Losh !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 94e7ff5475SWarner Losh return; 95e7ff5475SWarner Losh sin4 = (struct sockaddr_in *)sin6; 9604ebad38SMarius Strobl memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr)); 97e7ff5475SWarner Losh port = sin6->sin6_port; 98e7ff5475SWarner Losh memset(sin4, 0, sizeof(struct sockaddr_in)); 99e7ff5475SWarner Losh sin4->sin_addr.s_addr = addr; 100e7ff5475SWarner Losh sin4->sin_port = port; 101e7ff5475SWarner Losh sin4->sin_family = AF_INET; 102e7ff5475SWarner Losh sin4->sin_len = sizeof(struct sockaddr_in); 103e7ff5475SWarner Losh } 104e7ff5475SWarner Losh 105b1ce21c6SRebecca Cran /* Get a field from a \0 separated string */ 106e7ff5475SWarner Losh ssize_t 107e7ff5475SWarner Losh get_field(int peer, char *buffer, ssize_t size) 108e7ff5475SWarner Losh { 109e7ff5475SWarner Losh char *cp = buffer; 110e7ff5475SWarner Losh 111e7ff5475SWarner Losh while (cp < buffer + size) { 112e7ff5475SWarner Losh if (*cp == '\0') break; 113e7ff5475SWarner Losh cp++; 114e7ff5475SWarner Losh } 115e7ff5475SWarner Losh if (*cp != '\0') { 116e7ff5475SWarner Losh tftp_log(LOG_ERR, "Bad option - no trailing \\0 found"); 117e7ff5475SWarner Losh send_error(peer, EBADOP); 118e7ff5475SWarner Losh exit(1); 119e7ff5475SWarner Losh } 120e7ff5475SWarner Losh return (cp - buffer + 1); 121e7ff5475SWarner Losh } 122e7ff5475SWarner Losh 123e7ff5475SWarner Losh /* 124e7ff5475SWarner Losh * Logging functions 125e7ff5475SWarner Losh */ 126ae824d80SEd Schouten static int _tftp_logtostdout = 1; 127e7ff5475SWarner Losh 128e7ff5475SWarner Losh void 129e7ff5475SWarner Losh tftp_openlog(const char *ident, int logopt, int facility) 130e7ff5475SWarner Losh { 131e7ff5475SWarner Losh 132e7ff5475SWarner Losh _tftp_logtostdout = (ident == NULL); 133e7ff5475SWarner Losh if (_tftp_logtostdout == 0) 134e7ff5475SWarner Losh openlog(ident, logopt, facility); 135e7ff5475SWarner Losh } 136e7ff5475SWarner Losh 137e7ff5475SWarner Losh void 138e7ff5475SWarner Losh tftp_closelog(void) 139e7ff5475SWarner Losh { 140e7ff5475SWarner Losh 141e7ff5475SWarner Losh if (_tftp_logtostdout == 0) 142e7ff5475SWarner Losh closelog(); 143e7ff5475SWarner Losh } 144e7ff5475SWarner Losh 145e7ff5475SWarner Losh void 146e7ff5475SWarner Losh tftp_log(int priority, const char *message, ...) 147e7ff5475SWarner Losh { 148e7ff5475SWarner Losh va_list ap; 149e7ff5475SWarner Losh char *s; 150e7ff5475SWarner Losh 151e7ff5475SWarner Losh va_start(ap, message); 152e7ff5475SWarner Losh if (_tftp_logtostdout == 0) { 153e7ff5475SWarner Losh vasprintf(&s, message, ap); 154e7ff5475SWarner Losh syslog(priority, "%s", s); 155e7ff5475SWarner Losh } else { 156e7ff5475SWarner Losh vprintf(message, ap); 157e7ff5475SWarner Losh printf("\n"); 158e7ff5475SWarner Losh } 159e7ff5475SWarner Losh va_end(ap); 160e7ff5475SWarner Losh } 161e7ff5475SWarner Losh 162e7ff5475SWarner Losh /* 163e7ff5475SWarner Losh * Packet types 164e7ff5475SWarner Losh */ 165e7ff5475SWarner Losh struct packettypes packettypes[] = { 166e7ff5475SWarner Losh { RRQ, "RRQ" }, 167e7ff5475SWarner Losh { WRQ, "WRQ" }, 168e7ff5475SWarner Losh { DATA, "DATA" }, 169e7ff5475SWarner Losh { ACK, "ACK" }, 170e7ff5475SWarner Losh { ERROR, "ERROR" }, 171e7ff5475SWarner Losh { OACK, "OACK" }, 172e7ff5475SWarner Losh { 0, NULL }, 173e7ff5475SWarner Losh }; 174e7ff5475SWarner Losh 17504ebad38SMarius Strobl const char * 176e7ff5475SWarner Losh packettype(int type) 177e7ff5475SWarner Losh { 178e7ff5475SWarner Losh static char failed[100]; 179e7ff5475SWarner Losh int i = 0; 180e7ff5475SWarner Losh 181e7ff5475SWarner Losh while (packettypes[i].name != NULL) { 182e7ff5475SWarner Losh if (packettypes[i].value == type) 183e7ff5475SWarner Losh break; 184e7ff5475SWarner Losh i++; 185e7ff5475SWarner Losh } 186e7ff5475SWarner Losh if (packettypes[i].name != NULL) 187e7ff5475SWarner Losh return packettypes[i].name; 188e7ff5475SWarner Losh sprintf(failed, "unknown (type: %d)", type); 189e7ff5475SWarner Losh return (failed); 190e7ff5475SWarner Losh } 191e7ff5475SWarner Losh 192e7ff5475SWarner Losh /* 193e7ff5475SWarner Losh * Debugs 194e7ff5475SWarner Losh */ 195e7ff5475SWarner Losh int debug = DEBUG_NONE; 196e7ff5475SWarner Losh struct debugs debugs[] = { 197e7ff5475SWarner Losh { DEBUG_PACKETS, "packet", "Packet debugging" }, 198e7ff5475SWarner Losh { DEBUG_SIMPLE, "simple", "Simple debugging" }, 199e7ff5475SWarner Losh { DEBUG_OPTIONS, "options", "Options debugging" }, 200e7ff5475SWarner Losh { DEBUG_ACCESS, "access", "TCPd access debugging" }, 201e7ff5475SWarner Losh { DEBUG_NONE, NULL, "No debugging" }, 202e7ff5475SWarner Losh }; 203e7ff5475SWarner Losh int packetdroppercentage = 0; 204e7ff5475SWarner Losh 205e7ff5475SWarner Losh int 206e7ff5475SWarner Losh debug_find(char *s) 207e7ff5475SWarner Losh { 208e7ff5475SWarner Losh int i = 0; 209e7ff5475SWarner Losh 210e7ff5475SWarner Losh while (debugs[i].name != NULL) { 211e7ff5475SWarner Losh if (strcasecmp(debugs[i].name, s) == 0) 212e7ff5475SWarner Losh break; 213e7ff5475SWarner Losh i++; 214e7ff5475SWarner Losh } 215e7ff5475SWarner Losh return (debugs[i].value); 216e7ff5475SWarner Losh } 217e7ff5475SWarner Losh 218e7ff5475SWarner Losh int 219e7ff5475SWarner Losh debug_finds(char *s) 220e7ff5475SWarner Losh { 221e7ff5475SWarner Losh int i = 0; 222e7ff5475SWarner Losh char *ps = s; 223e7ff5475SWarner Losh 224e7ff5475SWarner Losh while (s != NULL) { 225e7ff5475SWarner Losh ps = strchr(s, ' '); 226e7ff5475SWarner Losh if (ps != NULL) 227e7ff5475SWarner Losh *ps = '\0'; 228e7ff5475SWarner Losh i += debug_find(s); 229e7ff5475SWarner Losh if (ps != NULL) 230e7ff5475SWarner Losh *ps = ' '; 231e7ff5475SWarner Losh s = ps; 232e7ff5475SWarner Losh } 233e7ff5475SWarner Losh return (i); 234e7ff5475SWarner Losh } 235e7ff5475SWarner Losh 23604ebad38SMarius Strobl const char * 237e7ff5475SWarner Losh debug_show(int d) 238e7ff5475SWarner Losh { 239e7ff5475SWarner Losh static char s[100]; 240e7ff5475SWarner Losh int i = 0; 241e7ff5475SWarner Losh 242e7ff5475SWarner Losh s[0] = '\0'; 243e7ff5475SWarner Losh while (debugs[i].name != NULL) { 244e7ff5475SWarner Losh if (d&debugs[i].value) { 245e7ff5475SWarner Losh if (s[0] != '\0') 246e7ff5475SWarner Losh strcat(s, " "); 247e7ff5475SWarner Losh strcat(s, debugs[i].name); 248e7ff5475SWarner Losh } 249e7ff5475SWarner Losh i++; 250e7ff5475SWarner Losh } 251e7ff5475SWarner Losh if (s[0] != '\0') 252e7ff5475SWarner Losh return (s); 253e7ff5475SWarner Losh return ("none"); 254e7ff5475SWarner Losh } 255e7ff5475SWarner Losh 256e7ff5475SWarner Losh /* 257e7ff5475SWarner Losh * RP_ 258e7ff5475SWarner Losh */ 259e7ff5475SWarner Losh struct rp_errors rp_errors[] = { 260e7ff5475SWarner Losh { RP_TIMEOUT, "Network timeout" }, 261e7ff5475SWarner Losh { RP_TOOSMALL, "Not enough data bytes" }, 262e7ff5475SWarner Losh { RP_WRONGSOURCE, "Invalid IP address of UDP port" }, 263e7ff5475SWarner Losh { RP_ERROR, "Error packet" }, 264e7ff5475SWarner Losh { RP_RECVFROM, "recvfrom() complained" }, 265e7ff5475SWarner Losh { RP_TOOBIG, "Too many data bytes" }, 266e7ff5475SWarner Losh { RP_NONE, NULL } 267e7ff5475SWarner Losh }; 268e7ff5475SWarner Losh 269e7ff5475SWarner Losh char * 270e7ff5475SWarner Losh rp_strerror(int error) 271e7ff5475SWarner Losh { 272e7ff5475SWarner Losh static char s[100]; 2733c0fa265SAlan Somers size_t space = sizeof(s); 274e7ff5475SWarner Losh int i = 0; 275e7ff5475SWarner Losh 276e7ff5475SWarner Losh while (rp_errors[i].desc != NULL) { 277e7ff5475SWarner Losh if (rp_errors[i].error == error) { 2783c0fa265SAlan Somers strlcpy(s, rp_errors[i].desc, space); 2793c0fa265SAlan Somers space -= strlen(rp_errors[i].desc); 280e7ff5475SWarner Losh } 281e7ff5475SWarner Losh i++; 282e7ff5475SWarner Losh } 283e7ff5475SWarner Losh if (s[0] == '\0') 284e7ff5475SWarner Losh sprintf(s, "unknown (error=%d)", error); 285e7ff5475SWarner Losh return (s); 286e7ff5475SWarner Losh } 287e7ff5475SWarner Losh 288e7ff5475SWarner Losh /* 289e7ff5475SWarner Losh * Performance figures 290e7ff5475SWarner Losh */ 291e7ff5475SWarner Losh 292e7ff5475SWarner Losh void 293e7ff5475SWarner Losh stats_init(struct tftp_stats *ts) 294e7ff5475SWarner Losh { 295e7ff5475SWarner Losh 296e7ff5475SWarner Losh ts->amount = 0; 297e7ff5475SWarner Losh ts->rollovers = 0; 298e7ff5475SWarner Losh ts->retries = 0; 299e7ff5475SWarner Losh ts->blocks = 0; 300e7ff5475SWarner Losh ts->amount = 0; 301e7ff5475SWarner Losh gettimeofday(&(ts->tstart), NULL); 302e7ff5475SWarner Losh } 303e7ff5475SWarner Losh 304e7ff5475SWarner Losh void 305e7ff5475SWarner Losh printstats(const char *direction, int verbose, struct tftp_stats *ts) 306e7ff5475SWarner Losh { 307e7ff5475SWarner Losh double delta; /* compute delta in 1/10's second units */ 308e7ff5475SWarner Losh 309e7ff5475SWarner Losh delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) - 310e7ff5475SWarner Losh ((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000)); 311e7ff5475SWarner Losh delta = delta/10.; /* back to seconds */ 312e7ff5475SWarner Losh 313e7ff5475SWarner Losh printf("%s %zu bytes during %.1f seconds in %u blocks", 314e7ff5475SWarner Losh direction, ts->amount, delta, ts->blocks); 315e7ff5475SWarner Losh 316e7ff5475SWarner Losh if (ts->rollovers != 0) 317e7ff5475SWarner Losh printf(" with %d rollover%s", 318e7ff5475SWarner Losh ts->rollovers, ts->rollovers != 1 ? "s" : ""); 319e7ff5475SWarner Losh 320e7ff5475SWarner Losh if (verbose) 321e7ff5475SWarner Losh printf(" [%.0f bits/sec]", (ts->amount*8.)/delta); 322e7ff5475SWarner Losh putchar('\n'); 323e7ff5475SWarner Losh } 324