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