1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 34 #include <netinet/in.h> 35 #include <arpa/tftp.h> 36 37 #include <stdarg.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <syslog.h> 42 43 #include "tftp-utils.h" 44 #include "tftp-io.h" 45 46 /* 47 * Default values, can be changed later via the TFTP Options 48 */ 49 int timeoutpacket = TIMEOUT; 50 int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT; 51 int maxtimeouts = MAX_TIMEOUTS; 52 uint16_t segsize = SEGSIZE; 53 uint16_t pktsize = SEGSIZE + 4; 54 55 int acting_as_client; 56 57 58 /* 59 * Set timeout values for packet reception. The idea is that you 60 * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the 61 * first timeout) to 'timeoutnetwork' (i.e. the last timeout) 62 */ 63 int 64 settimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused) 65 { 66 int i; 67 68 /* We cannot do impossible things */ 69 if (_timeoutpacket >= _timeoutnetwork) 70 return (0); 71 72 maxtimeouts = 0; 73 i = _timeoutpacket; 74 while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) { 75 maxtimeouts++; 76 i += 5; 77 } 78 79 timeoutpacket = _timeoutpacket; 80 timeoutnetwork = i; 81 return (1); 82 } 83 84 /* translate IPv4 mapped IPv6 address to IPv4 address */ 85 void 86 unmappedaddr(struct sockaddr_in6 *sin6) 87 { 88 struct sockaddr_in *sin4; 89 u_int32_t addr; 90 int port; 91 92 if (sin6->sin6_family != AF_INET6 || 93 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 94 return; 95 sin4 = (struct sockaddr_in *)sin6; 96 memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr)); 97 port = sin6->sin6_port; 98 memset(sin4, 0, sizeof(struct sockaddr_in)); 99 sin4->sin_addr.s_addr = addr; 100 sin4->sin_port = port; 101 sin4->sin_family = AF_INET; 102 sin4->sin_len = sizeof(struct sockaddr_in); 103 } 104 105 /* Get a field from a \0 separated string */ 106 ssize_t 107 get_field(int peer, char *buffer, ssize_t size) 108 { 109 char *cp = buffer; 110 111 while (cp < buffer + size) { 112 if (*cp == '\0') break; 113 cp++; 114 } 115 if (*cp != '\0') { 116 tftp_log(LOG_ERR, "Bad option - no trailing \\0 found"); 117 send_error(peer, EBADOP); 118 exit(1); 119 } 120 return (cp - buffer + 1); 121 } 122 123 /* 124 * Logging functions 125 */ 126 static int _tftp_logtostdout = 1; 127 128 void 129 tftp_openlog(const char *ident, int logopt, int facility) 130 { 131 132 _tftp_logtostdout = (ident == NULL); 133 if (_tftp_logtostdout == 0) 134 openlog(ident, logopt, facility); 135 } 136 137 void 138 tftp_closelog(void) 139 { 140 141 if (_tftp_logtostdout == 0) 142 closelog(); 143 } 144 145 void 146 tftp_log(int priority, const char *message, ...) 147 { 148 va_list ap; 149 char *s; 150 151 va_start(ap, message); 152 if (_tftp_logtostdout == 0) { 153 vasprintf(&s, message, ap); 154 syslog(priority, "%s", s); 155 } else { 156 vprintf(message, ap); 157 printf("\n"); 158 } 159 va_end(ap); 160 } 161 162 /* 163 * Packet types 164 */ 165 struct packettypes packettypes[] = { 166 { RRQ, "RRQ" }, 167 { WRQ, "WRQ" }, 168 { DATA, "DATA" }, 169 { ACK, "ACK" }, 170 { ERROR, "ERROR" }, 171 { OACK, "OACK" }, 172 { 0, NULL }, 173 }; 174 175 const char * 176 packettype(int type) 177 { 178 static char failed[100]; 179 int i = 0; 180 181 while (packettypes[i].name != NULL) { 182 if (packettypes[i].value == type) 183 break; 184 i++; 185 } 186 if (packettypes[i].name != NULL) 187 return packettypes[i].name; 188 sprintf(failed, "unknown (type: %d)", type); 189 return (failed); 190 } 191 192 /* 193 * Debugs 194 */ 195 int debug = DEBUG_NONE; 196 struct debugs debugs[] = { 197 { DEBUG_PACKETS, "packet", "Packet debugging" }, 198 { DEBUG_SIMPLE, "simple", "Simple debugging" }, 199 { DEBUG_OPTIONS, "options", "Options debugging" }, 200 { DEBUG_ACCESS, "access", "TCPd access debugging" }, 201 { DEBUG_NONE, NULL, "No debugging" }, 202 }; 203 int packetdroppercentage = 0; 204 205 int 206 debug_find(char *s) 207 { 208 int i = 0; 209 210 while (debugs[i].name != NULL) { 211 if (strcasecmp(debugs[i].name, s) == 0) 212 break; 213 i++; 214 } 215 return (debugs[i].value); 216 } 217 218 int 219 debug_finds(char *s) 220 { 221 int i = 0; 222 char *ps = s; 223 224 while (s != NULL) { 225 ps = strchr(s, ' '); 226 if (ps != NULL) 227 *ps = '\0'; 228 i += debug_find(s); 229 if (ps != NULL) 230 *ps = ' '; 231 s = ps; 232 } 233 return (i); 234 } 235 236 const char * 237 debug_show(int d) 238 { 239 static char s[100]; 240 int i = 0; 241 242 s[0] = '\0'; 243 while (debugs[i].name != NULL) { 244 if (d&debugs[i].value) { 245 if (s[0] != '\0') 246 strcat(s, " "); 247 strcat(s, debugs[i].name); 248 } 249 i++; 250 } 251 if (s[0] != '\0') 252 return (s); 253 return ("none"); 254 } 255 256 /* 257 * RP_ 258 */ 259 struct rp_errors rp_errors[] = { 260 { RP_TIMEOUT, "Network timeout" }, 261 { RP_TOOSMALL, "Not enough data bytes" }, 262 { RP_WRONGSOURCE, "Invalid IP address of UDP port" }, 263 { RP_ERROR, "Error packet" }, 264 { RP_RECVFROM, "recvfrom() complained" }, 265 { RP_TOOBIG, "Too many data bytes" }, 266 { RP_NONE, NULL } 267 }; 268 269 char * 270 rp_strerror(int error) 271 { 272 static char s[100]; 273 int i = 0; 274 275 while (rp_errors[i].desc != NULL) { 276 if (rp_errors[i].error == error) { 277 strcpy(s, rp_errors[i].desc); 278 } 279 i++; 280 } 281 if (s[0] == '\0') 282 sprintf(s, "unknown (error=%d)", error); 283 return (s); 284 } 285 286 /* 287 * Performance figures 288 */ 289 290 void 291 stats_init(struct tftp_stats *ts) 292 { 293 294 ts->amount = 0; 295 ts->rollovers = 0; 296 ts->retries = 0; 297 ts->blocks = 0; 298 ts->amount = 0; 299 gettimeofday(&(ts->tstart), NULL); 300 } 301 302 void 303 printstats(const char *direction, int verbose, struct tftp_stats *ts) 304 { 305 double delta; /* compute delta in 1/10's second units */ 306 307 delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) - 308 ((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000)); 309 delta = delta/10.; /* back to seconds */ 310 311 printf("%s %zu bytes during %.1f seconds in %u blocks", 312 direction, ts->amount, delta, ts->blocks); 313 314 if (ts->rollovers != 0) 315 printf(" with %d rollover%s", 316 ts->rollovers, ts->rollovers != 1 ? "s" : ""); 317 318 if (verbose) 319 printf(" [%.0f bits/sec]", (ts->amount*8.)/delta); 320 putchar('\n'); 321 } 322