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