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