tftp-utils.c revision 215034
1207614Simp/* 2207614Simp * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 3207614Simp * 4207614Simp * Redistribution and use in source and binary forms, with or without 5207614Simp * modification, are permitted provided that the following conditions 6207614Simp * are met: 7207614Simp * 1. Redistributions of source code must retain the above copyright 8207614Simp * notice, this list of conditions and the following disclaimer. 9207614Simp * 2. Redistributions in binary form must reproduce the above copyright 10207614Simp * notice, this list of conditions and the following disclaimer in the 11207614Simp * documentation and/or other materials provided with the distribution. 12207614Simp * 13207614Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14207614Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15207614Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16207614Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17207614Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18207614Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19207614Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20207614Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21207614Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22207614Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23207614Simp * SUCH DAMAGE. 24207614Simp */ 25207614Simp 26207614Simp#include <sys/cdefs.h> 27207614Simp__FBSDID("$FreeBSD: head/libexec/tftpd/tftp-utils.c 215034 2010-11-09 10:59:09Z brucec $"); 28207614Simp 29207614Simp#include <sys/socket.h> 30207614Simp#include <sys/stat.h> 31207614Simp 32207614Simp#include <netinet/in.h> 33207614Simp#include <arpa/tftp.h> 34207614Simp 35207614Simp#include <stdarg.h> 36207614Simp#include <stdio.h> 37207614Simp#include <stdlib.h> 38207614Simp#include <string.h> 39207614Simp#include <syslog.h> 40207614Simp 41207614Simp#include "tftp-utils.h" 42207614Simp#include "tftp-io.h" 43207614Simp 44207614Simp/* 45207614Simp * Default values, can be changed later via the TFTP Options 46207614Simp */ 47207614Simpint timeoutpacket = TIMEOUT; 48207614Simpint timeoutnetwork = MAX_TIMEOUTS * TIMEOUT; 49207614Simpint maxtimeouts = MAX_TIMEOUTS; 50207614Simpuint16_t segsize = SEGSIZE; 51207614Simpuint16_t pktsize = SEGSIZE + 4; 52207614Simp 53207614Simpint acting_as_client; 54207614Simp 55207614Simp 56207614Simp/* 57207614Simp * Set timeout values for packet reception. The idea is that you 58207614Simp * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the 59207614Simp * first timeout) to 'timeoutnetwork' (i.e. the last timeout) 60207614Simp */ 61207614Simpint 62213099Smariussettimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused) 63207614Simp{ 64207614Simp int i; 65207614Simp 66207614Simp /* We cannot do impossible things */ 67207614Simp if (_timeoutpacket >= _timeoutnetwork) 68207614Simp return (0); 69207614Simp 70207614Simp maxtimeouts = 0; 71207614Simp i = _timeoutpacket; 72207614Simp while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) { 73207614Simp maxtimeouts++; 74207614Simp i += 5; 75207614Simp } 76207614Simp 77207614Simp timeoutpacket = _timeoutpacket; 78207614Simp timeoutnetwork = i; 79207614Simp return (1); 80207614Simp} 81207614Simp 82207614Simp/* translate IPv4 mapped IPv6 address to IPv4 address */ 83207614Simpvoid 84207614Simpunmappedaddr(struct sockaddr_in6 *sin6) 85207614Simp{ 86207614Simp struct sockaddr_in *sin4; 87207614Simp u_int32_t addr; 88207614Simp int port; 89207614Simp 90207614Simp if (sin6->sin6_family != AF_INET6 || 91207614Simp !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 92207614Simp return; 93207614Simp sin4 = (struct sockaddr_in *)sin6; 94213099Smarius memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr)); 95207614Simp port = sin6->sin6_port; 96207614Simp memset(sin4, 0, sizeof(struct sockaddr_in)); 97207614Simp sin4->sin_addr.s_addr = addr; 98207614Simp sin4->sin_port = port; 99207614Simp sin4->sin_family = AF_INET; 100207614Simp sin4->sin_len = sizeof(struct sockaddr_in); 101207614Simp} 102207614Simp 103215034Sbrucec/* Get a field from a \0 separated string */ 104207614Simpssize_t 105207614Simpget_field(int peer, char *buffer, ssize_t size) 106207614Simp{ 107207614Simp char *cp = buffer; 108207614Simp 109207614Simp while (cp < buffer + size) { 110207614Simp if (*cp == '\0') break; 111207614Simp cp++; 112207614Simp } 113207614Simp if (*cp != '\0') { 114207614Simp tftp_log(LOG_ERR, "Bad option - no trailing \\0 found"); 115207614Simp send_error(peer, EBADOP); 116207614Simp exit(1); 117207614Simp } 118207614Simp return (cp - buffer + 1); 119207614Simp} 120207614Simp 121207614Simp/* 122207614Simp * Logging functions 123207614Simp */ 124207614Simpint _tftp_logtostdout = 1; 125207614Simp 126207614Simpvoid 127207614Simptftp_openlog(const char *ident, int logopt, int facility) 128207614Simp{ 129207614Simp 130207614Simp _tftp_logtostdout = (ident == NULL); 131207614Simp if (_tftp_logtostdout == 0) 132207614Simp openlog(ident, logopt, facility); 133207614Simp} 134207614Simp 135207614Simpvoid 136207614Simptftp_closelog(void) 137207614Simp{ 138207614Simp 139207614Simp if (_tftp_logtostdout == 0) 140207614Simp closelog(); 141207614Simp} 142207614Simp 143207614Simpvoid 144207614Simptftp_log(int priority, const char *message, ...) 145207614Simp{ 146207614Simp va_list ap; 147207614Simp char *s; 148207614Simp 149207614Simp va_start(ap, message); 150207614Simp if (_tftp_logtostdout == 0) { 151207614Simp vasprintf(&s, message, ap); 152207614Simp syslog(priority, "%s", s); 153207614Simp } else { 154207614Simp vprintf(message, ap); 155207614Simp printf("\n"); 156207614Simp } 157207614Simp va_end(ap); 158207614Simp} 159207614Simp 160207614Simp/* 161207614Simp * Packet types 162207614Simp */ 163207614Simpstruct packettypes packettypes[] = { 164207614Simp { RRQ, "RRQ" }, 165207614Simp { WRQ, "WRQ" }, 166207614Simp { DATA, "DATA" }, 167207614Simp { ACK, "ACK" }, 168207614Simp { ERROR, "ERROR" }, 169207614Simp { OACK, "OACK" }, 170207614Simp { 0, NULL }, 171207614Simp}; 172207614Simp 173213099Smariusconst char * 174207614Simppackettype(int type) 175207614Simp{ 176207614Simp static char failed[100]; 177207614Simp int i = 0; 178207614Simp 179207614Simp while (packettypes[i].name != NULL) { 180207614Simp if (packettypes[i].value == type) 181207614Simp break; 182207614Simp i++; 183207614Simp } 184207614Simp if (packettypes[i].name != NULL) 185207614Simp return packettypes[i].name; 186207614Simp sprintf(failed, "unknown (type: %d)", type); 187207614Simp return (failed); 188207614Simp} 189207614Simp 190207614Simp/* 191207614Simp * Debugs 192207614Simp */ 193207614Simpint debug = DEBUG_NONE; 194207614Simpstruct debugs debugs[] = { 195207614Simp { DEBUG_PACKETS, "packet", "Packet debugging" }, 196207614Simp { DEBUG_SIMPLE, "simple", "Simple debugging" }, 197207614Simp { DEBUG_OPTIONS, "options", "Options debugging" }, 198207614Simp { DEBUG_ACCESS, "access", "TCPd access debugging" }, 199207614Simp { DEBUG_NONE, NULL, "No debugging" }, 200207614Simp}; 201207614Simpint packetdroppercentage = 0; 202207614Simp 203207614Simpint 204207614Simpdebug_find(char *s) 205207614Simp{ 206207614Simp int i = 0; 207207614Simp 208207614Simp while (debugs[i].name != NULL) { 209207614Simp if (strcasecmp(debugs[i].name, s) == 0) 210207614Simp break; 211207614Simp i++; 212207614Simp } 213207614Simp return (debugs[i].value); 214207614Simp} 215207614Simp 216207614Simpint 217207614Simpdebug_finds(char *s) 218207614Simp{ 219207614Simp int i = 0; 220207614Simp char *ps = s; 221207614Simp 222207614Simp while (s != NULL) { 223207614Simp ps = strchr(s, ' '); 224207614Simp if (ps != NULL) 225207614Simp *ps = '\0'; 226207614Simp i += debug_find(s); 227207614Simp if (ps != NULL) 228207614Simp *ps = ' '; 229207614Simp s = ps; 230207614Simp } 231207614Simp return (i); 232207614Simp} 233207614Simp 234213099Smariusconst char * 235207614Simpdebug_show(int d) 236207614Simp{ 237207614Simp static char s[100]; 238207614Simp int i = 0; 239207614Simp 240207614Simp s[0] = '\0'; 241207614Simp while (debugs[i].name != NULL) { 242207614Simp if (d&debugs[i].value) { 243207614Simp if (s[0] != '\0') 244207614Simp strcat(s, " "); 245207614Simp strcat(s, debugs[i].name); 246207614Simp } 247207614Simp i++; 248207614Simp } 249207614Simp if (s[0] != '\0') 250207614Simp return (s); 251207614Simp return ("none"); 252207614Simp} 253207614Simp 254207614Simp/* 255207614Simp * RP_ 256207614Simp */ 257207614Simpstruct rp_errors rp_errors[] = { 258207614Simp { RP_TIMEOUT, "Network timeout" }, 259207614Simp { RP_TOOSMALL, "Not enough data bytes" }, 260207614Simp { RP_WRONGSOURCE, "Invalid IP address of UDP port" }, 261207614Simp { RP_ERROR, "Error packet" }, 262207614Simp { RP_RECVFROM, "recvfrom() complained" }, 263207614Simp { RP_TOOBIG, "Too many data bytes" }, 264207614Simp { RP_NONE, NULL } 265207614Simp}; 266207614Simp 267207614Simpchar * 268207614Simprp_strerror(int error) 269207614Simp{ 270207614Simp static char s[100]; 271207614Simp int i = 0; 272207614Simp 273207614Simp while (rp_errors[i].desc != NULL) { 274207614Simp if (rp_errors[i].error == error) { 275207614Simp strcpy(s, rp_errors[i].desc); 276207614Simp } 277207614Simp i++; 278207614Simp } 279207614Simp if (s[0] == '\0') 280207614Simp sprintf(s, "unknown (error=%d)", error); 281207614Simp return (s); 282207614Simp} 283207614Simp 284207614Simp/* 285207614Simp * Performance figures 286207614Simp */ 287207614Simp 288207614Simpvoid 289207614Simpstats_init(struct tftp_stats *ts) 290207614Simp{ 291207614Simp 292207614Simp ts->amount = 0; 293207614Simp ts->rollovers = 0; 294207614Simp ts->retries = 0; 295207614Simp ts->blocks = 0; 296207614Simp ts->amount = 0; 297207614Simp gettimeofday(&(ts->tstart), NULL); 298207614Simp} 299207614Simp 300207614Simpvoid 301207614Simpprintstats(const char *direction, int verbose, struct tftp_stats *ts) 302207614Simp{ 303207614Simp double delta; /* compute delta in 1/10's second units */ 304207614Simp 305207614Simp delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) - 306207614Simp ((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000)); 307207614Simp delta = delta/10.; /* back to seconds */ 308207614Simp 309207614Simp printf("%s %zu bytes during %.1f seconds in %u blocks", 310207614Simp direction, ts->amount, delta, ts->blocks); 311207614Simp 312207614Simp if (ts->rollovers != 0) 313207614Simp printf(" with %d rollover%s", 314207614Simp ts->rollovers, ts->rollovers != 1 ? "s" : ""); 315207614Simp 316207614Simp if (verbose) 317207614Simp printf(" [%.0f bits/sec]", (ts->amount*8.)/delta); 318207614Simp putchar('\n'); 319207614Simp} 320