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