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