diff --git a/libexec/tftpd/tftp-io.c b/libexec/tftpd/tftp-io.c index 245d73ffb871..4504946e910f 100644 --- a/libexec/tftpd/tftp-io.c +++ b/libexec/tftpd/tftp-io.c @@ -1,470 +1,470 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tftp-file.h" #include "tftp-io.h" #include "tftp-utils.h" #include "tftp-options.h" struct sockaddr_storage peer_sock; struct sockaddr_storage me_sock; static int send_packet(int peer, uint16_t block, char *pkt, int size); static struct errmsg { int e_code; const char *e_msg; } errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, { EACCESS, "Access violation" }, { ENOSPACE, "Disk full or allocation exceeded" }, { EBADOP, "Illegal TFTP operation" }, { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, { EOPTNEG, "Option negotiation" }, { -1, NULL } }; #define DROPPACKET(s) \ if (packetdroppercentage != 0 && \ random()%100 < packetdroppercentage) { \ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ return; \ } #define DROPPACKETn(s,n) \ if (packetdroppercentage != 0 && \ random()%100 < packetdroppercentage) { \ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ return (n); \ } const char * errtomsg(int error) { static char ebuf[40]; struct errmsg *pe; if (error == 0) return ("success"); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) return (pe->e_msg); snprintf(ebuf, sizeof(ebuf), "error %d", error); return (ebuf); } static int send_packet(int peer, uint16_t block, char *pkt, int size) { int i; int t = 1; for (i = 0; i < 12 ; i++) { DROPPACKETn("send_packet", 0); if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) == size) { if (i) tftp_log(LOG_ERR, "%s block %d, attempt %d successful", packettype(ntohs(((struct tftphdr *) (pkt))->th_opcode)), block, i); return (0); } tftp_log(LOG_ERR, "%s block %d, attempt %d failed (Error %d: %s)", packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)), block, i, errno, strerror(errno)); sleep(t); if (t < 32) t <<= 1; } tftp_log(LOG_ERR, "send_packet: %s", strerror(errno)); return (1); } /* * Send an ERROR packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */ void send_error(int peer, int error) { struct tftphdr *tp; int length; struct errmsg *pe; char buf[MAXPKTSIZE]; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ERROR %d", error); DROPPACKET("send_error"); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ERROR); tp->th_code = htons((u_short)error); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) break; if (pe->e_code < 0) { pe->e_msg = strerror(error - 100); tp->th_code = EUNDEF; /* set 'undef' errorcode */ } strcpy(tp->th_msg, pe->e_msg); length = strlen(pe->e_msg); tp->th_msg[length] = '\0'; length += 5; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg); if (sendto(peer, buf, length, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length) tftp_log(LOG_ERR, "send_error: %s", strerror(errno)); } /* * Send an WRQ packet (write request). */ int send_wrq(int peer, char *filename, char *mode) { int n; struct tftphdr *tp; char *bp; char buf[MAXPKTSIZE]; int size; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", filename, mode ); DROPPACKETn("send_wrq", 0); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)WRQ); size = offsetof(struct tftphdr, th_stuff); bp = tp->th_stuff; strlcpy(bp, filename, sizeof(buf) - size); bp += strlen(filename); *bp = 0; bp++; size += strlen(filename) + 1; strlcpy(bp, mode, sizeof(buf) - size); bp += strlen(mode); *bp = 0; bp++; size += strlen(mode) + 1; if (options_rfc_enabled) size += make_options(peer, bp, sizeof(buf) - size); n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); return (1); } return (0); } /* * Send an RRQ packet (write request). */ int send_rrq(int peer, char *filename, char *mode) { int n; struct tftphdr *tp; char *bp; char buf[MAXPKTSIZE]; int size; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", filename, mode ); DROPPACKETn("send_rrq", 0); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)RRQ); size = offsetof(struct tftphdr, th_stuff); bp = tp->th_stuff; strlcpy(bp, filename, sizeof(buf) - size); bp += strlen(filename); *bp = 0; bp++; size += strlen(filename) + 1; strlcpy(bp, mode, sizeof(buf) - size); bp += strlen(mode); *bp = 0; bp++; size += strlen(mode) + 1; if (options_rfc_enabled) { - options[OPT_TSIZE].o_request = strdup("0"); + options_set_request(OPT_TSIZE, "0"); size += make_options(peer, bp, sizeof(buf) - size); } n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno)); return (1); } return (0); } /* * Send an OACK packet (option acknowledgement). */ int send_oack(int peer) { struct tftphdr *tp; int size, i, n; char *bp; char buf[MAXPKTSIZE]; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending OACK"); DROPPACKETn("send_oack", 0); /* * Send back an options acknowledgement (only the ones with * a reply for) */ tp = (struct tftphdr *)buf; bp = buf + 2; size = sizeof(buf) - 2; tp->th_opcode = htons((u_short)OACK); for (i = 0; options[i].o_type != NULL; i++) { if (options[i].o_reply != NULL) { n = snprintf(bp, size, "%s%c%s", options[i].o_type, 0, options[i].o_reply); bp += n+1; size -= n+1; if (size < 0) { tftp_log(LOG_ERR, "oack: buffer overflow"); exit(1); } } } size = bp - buf; if (sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { tftp_log(LOG_INFO, "send_oack: %s", strerror(errno)); return (1); } return (0); } /* * Send an ACK packet (acknowledgement). */ int send_ack(int fp, uint16_t block) { struct tftphdr *tp; int size; char buf[MAXPKTSIZE]; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ACK for block %d", block); DROPPACKETn("send_ack", 0); tp = (struct tftphdr *)buf; size = sizeof(buf) - 2; tp->th_opcode = htons((u_short)ACK); tp->th_block = htons((u_short)block); size = 4; if (sendto(fp, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { tftp_log(LOG_INFO, "send_ack: %s", strerror(errno)); return (1); } return (0); } /* * Send a DATA packet */ int send_data(int peer, uint16_t block, char *data, int size) { char buf[MAXPKTSIZE]; struct tftphdr *pkt; int n; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes", block, size); DROPPACKETn("send_data", 0); pkt = (struct tftphdr *)buf; pkt->th_opcode = htons((u_short)DATA); pkt->th_block = htons((u_short)block); memcpy(pkt->th_data, data, size); n = send_packet(peer, block, (char *)pkt, size + 4); return (n); } /* * Receive a packet */ static jmp_buf timeoutbuf; static void timeout(int sig __unused) { /* tftp_log(LOG_DEBUG, "Timeout\n"); Inside a signal handler... */ longjmp(timeoutbuf, 1); } int receive_packet(int peer, char *data, int size, struct sockaddr_storage *from, int thistimeout) { struct tftphdr *pkt; struct sockaddr_storage from_local; struct sockaddr_storage *pfrom; socklen_t fromlen; int n; static int timed_out; if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Waiting %d seconds for packet", timeoutpacket); pkt = (struct tftphdr *)data; signal(SIGALRM, timeout); timed_out = setjmp(timeoutbuf); alarm(thistimeout); if (timed_out != 0) { tftp_log(LOG_ERR, "receive_packet: timeout"); alarm(0); return (RP_TIMEOUT); } pfrom = (from == NULL) ? &from_local : from; fromlen = sizeof(*pfrom); n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); alarm(0); DROPPACKETn("receive_packet", RP_TIMEOUT); if (n < 0) { tftp_log(LOG_ERR, "receive_packet: timeout"); return (RP_TIMEOUT); } if (n < 0) { /* No idea what could have happened if it isn't a timeout */ tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno)); return (RP_RECVFROM); } if (n < 4) { tftp_log(LOG_ERR, "receive_packet: packet too small (%d bytes)", n); return (RP_TOOSMALL); } pkt->th_opcode = ntohs((u_short)pkt->th_opcode); if (pkt->th_opcode == DATA || pkt->th_opcode == ACK) pkt->th_block = ntohs((u_short)pkt->th_block); if (pkt->th_opcode == DATA && n > pktsize) { tftp_log(LOG_ERR, "receive_packet: packet too big"); return (RP_TOOBIG); } if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr != ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) { tftp_log(LOG_ERR, "receive_packet: received packet from wrong source"); return (RP_WRONGSOURCE); } if (pkt->th_opcode == ERROR) { tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR, "Got ERROR packet: %s", pkt->th_msg); return (RP_ERROR); } if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet", n, packettype(pkt->th_opcode)); return n - 4; } diff --git a/libexec/tftpd/tftp-options.c b/libexec/tftpd/tftp-options.c index 01c47e66e9a5..cc902c7d2110 100644 --- a/libexec/tftpd/tftp-options.c +++ b/libexec/tftpd/tftp-options.c @@ -1,426 +1,479 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include #include #include "tftp-utils.h" #include "tftp-io.h" #include "tftp-options.h" /* * Option handlers */ struct options options[] = { { "tsize", NULL, NULL, NULL /* option_tsize */, 1 }, { "timeout", NULL, NULL, option_timeout, 1 }, { "blksize", NULL, NULL, option_blksize, 1 }, { "blksize2", NULL, NULL, option_blksize2, 0 }, { "rollover", NULL, NULL, option_rollover, 0 }, { "windowsize", NULL, NULL, option_windowsize, 1 }, { NULL, NULL, NULL, NULL, 0 } }; /* By default allow them */ int options_rfc_enabled = 1; int options_extra_enabled = 1; +int +options_set_request(enum opt_enum opt, const char *fmt, ...) +{ + va_list ap; + char *str; + int ret; + + if (fmt == NULL) { + str = NULL; + } else { + va_start(ap, fmt); + ret = vasprintf(&str, fmt, ap); + va_end(ap); + if (ret < 0) + return (ret); + } + if (options[opt].o_request != NULL && + options[opt].o_request != options[opt].o_reply) + free(options[opt].o_request); + options[opt].o_request = str; + return (0); +} + +int +options_set_reply(enum opt_enum opt, const char *fmt, ...) +{ + va_list ap; + char *str; + int ret; + + if (fmt == NULL) { + str = NULL; + } else { + va_start(ap, fmt); + ret = vasprintf(&str, fmt, ap); + va_end(ap); + if (ret < 0) + return (ret); + } + if (options[opt].o_reply != NULL && + options[opt].o_reply != options[opt].o_request) + free(options[opt].o_reply); + options[opt].o_reply = str; + return (0); +} + +static void +options_set_reply_equal_request(enum opt_enum opt) +{ + + if (options[opt].o_reply != NULL && + options[opt].o_reply != options[opt].o_request) + free(options[opt].o_reply); + options[opt].o_reply = options[opt].o_request; +} + /* * Rules for the option handlers: * - If there is no o_request, there will be no processing. * * For servers * - Logging is done as warnings. * - The handler exit()s if there is a serious problem with the * values submitted in the option. * * For clients * - Logging is done as errors. After all, the server shouldn't * return rubbish. * - The handler returns if there is a serious problem with the * values submitted in the option. * - Sending the EBADOP packets is done by the handler. */ int option_tsize(int peer __unused, struct tftphdr *tp __unused, int mode, struct stat *stbuf) { if (options[OPT_TSIZE].o_request == NULL) return (0); if (mode == RRQ) - asprintf(&options[OPT_TSIZE].o_reply, - "%ju", (uintmax_t)stbuf->st_size); + options_set_reply(OPT_TSIZE, "%ju", stbuf->st_size); else /* XXX Allows writes of all sizes. */ - options[OPT_TSIZE].o_reply = - strdup(options[OPT_TSIZE].o_request); + options_set_reply_equal_request(OPT_TSIZE); return (0); } int option_timeout(int peer) { int to; if (options[OPT_TIMEOUT].o_request == NULL) return (0); to = atoi(options[OPT_TIMEOUT].o_request); if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) { tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, "Received bad value for timeout. " "Should be between %d and %d, received %d", TIMEOUT_MIN, TIMEOUT_MAX, to); send_error(peer, EBADOP); if (acting_as_client) return (1); exit(1); } else { timeoutpacket = to; - options[OPT_TIMEOUT].o_reply = - strdup(options[OPT_TIMEOUT].o_request); + options_set_reply_equal_request(OPT_TIMEOUT); } settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts); if (debug & DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting timeout to '%s'", options[OPT_TIMEOUT].o_reply); return (0); } int option_rollover(int peer) { if (options[OPT_ROLLOVER].o_request == NULL) return (0); if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0 && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) { tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, "Bad value for rollover, " "should be either 0 or 1, received '%s', " "ignoring request", options[OPT_ROLLOVER].o_request); if (acting_as_client) { send_error(peer, EBADOP); return (1); } return (0); } - options[OPT_ROLLOVER].o_reply = - strdup(options[OPT_ROLLOVER].o_request); + options_set_reply_equal_request(OPT_ROLLOVER); if (debug & DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting rollover to '%s'", options[OPT_ROLLOVER].o_reply); return (0); } int option_blksize(int peer) { u_long maxdgram; size_t len; if (options[OPT_BLKSIZE].o_request == NULL) return (0); /* maximum size of an UDP packet according to the system */ len = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &len, NULL, 0) < 0) { tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); return (acting_as_client ? 1 : 0); } int size = atoi(options[OPT_BLKSIZE].o_request); if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { if (acting_as_client) { tftp_log(LOG_ERR, "Invalid blocksize (%d bytes), aborting", size); send_error(peer, EBADOP); return (1); } else { tftp_log(LOG_WARNING, "Invalid blocksize (%d bytes), ignoring request", size); return (0); } } if (size > (int)maxdgram) { if (acting_as_client) { tftp_log(LOG_ERR, "Invalid blocksize (%d bytes), " "net.inet.udp.maxdgram sysctl limits it to " "%ld bytes.\n", size, maxdgram); send_error(peer, EBADOP); return (1); } else { tftp_log(LOG_WARNING, "Invalid blocksize (%d bytes), " "net.inet.udp.maxdgram sysctl limits it to " "%ld bytes.\n", size, maxdgram); size = maxdgram; /* No reason to return */ } } - asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size); + options_set_reply(OPT_BLKSIZE, "%d", size); segsize = size; pktsize = size + 4; if (debug & DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting blksize to '%s'", options[OPT_BLKSIZE].o_reply); return (0); } int option_blksize2(int peer __unused) { u_long maxdgram; int size, i; size_t len; int sizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 0 }; if (options[OPT_BLKSIZE2].o_request == NULL) return (0); /* maximum size of an UDP packet according to the system */ len = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &len, NULL, 0) < 0) { tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); return (acting_as_client ? 1 : 0); } size = atoi(options[OPT_BLKSIZE2].o_request); for (i = 0; sizes[i] != 0; i++) { if (size == sizes[i]) break; } if (sizes[i] == 0) { tftp_log(LOG_INFO, "Invalid blocksize2 (%d bytes), ignoring request", size); return (acting_as_client ? 1 : 0); } if (size > (int)maxdgram) { for (i = 0; sizes[i+1] != 0; i++) { if ((int)maxdgram < sizes[i+1]) break; } tftp_log(LOG_INFO, "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram " "sysctl limits it to %ld bytes.\n", size, maxdgram); size = sizes[i]; /* No need to return */ } - asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size); + options_set_reply(OPT_BLKSIZE2, "%d", size); segsize = size; pktsize = size + 4; if (debug & DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'", options[OPT_BLKSIZE2].o_reply); return (0); } int option_windowsize(int peer) { int size; if (options[OPT_WINDOWSIZE].o_request == NULL) return (0); size = atoi(options[OPT_WINDOWSIZE].o_request); if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) { if (acting_as_client) { tftp_log(LOG_ERR, "Invalid windowsize (%d blocks), aborting", size); send_error(peer, EBADOP); return (1); } else { tftp_log(LOG_WARNING, "Invalid windowsize (%d blocks), ignoring request", size); return (0); } } /* XXX: Should force a windowsize of 1 for non-seekable files. */ - asprintf(&options[OPT_WINDOWSIZE].o_reply, "%d", size); + options_set_reply(OPT_WINDOWSIZE, "%d", size); windowsize = size; if (debug & DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting windowsize to '%s'", options[OPT_WINDOWSIZE].o_reply); return (0); } /* * Append the available options to the header */ uint16_t make_options(int peer __unused, char *buffer, uint16_t size) { int i; char *value; const char *option; uint16_t length; uint16_t returnsize = 0; if (!options_rfc_enabled) return (0); for (i = 0; options[i].o_type != NULL; i++) { if (options[i].rfc == 0 && !options_extra_enabled) continue; option = options[i].o_type; if (acting_as_client) value = options[i].o_request; else value = options[i].o_reply; if (value == NULL) continue; length = strlen(value) + strlen(option) + 2; if (size <= length) { tftp_log(LOG_ERR, "Running out of option space for " "option '%s' with value '%s': " "needed %d bytes, got %d bytes", option, value, size, length); continue; } sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000'); size -= length; buffer += length; returnsize += length; } return (returnsize); } /* * Parse the received options in the header */ int parse_options(int peer, char *buffer, uint16_t size) { int i, options_failed; char *c, *cp, *option, *value; if (!options_rfc_enabled) return (0); /* Parse the options */ cp = buffer; options_failed = 0; while (size > 0) { option = cp; i = get_field(peer, cp, size); cp += i; value = cp; i = get_field(peer, cp, size); cp += i; /* We are at the end */ if (*option == '\0') break; if (debug & DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "option: '%s' value: '%s'", option, value); for (c = option; *c; c++) if (isupper(*c)) *c = tolower(*c); for (i = 0; options[i].o_type != NULL; i++) { if (strcmp(option, options[i].o_type) == 0) { if (!acting_as_client) - options[i].o_request = value; + options_set_request(i, "%s", value); if (!options_extra_enabled && !options[i].rfc) { tftp_log(LOG_INFO, "Option '%s' with value '%s' found " "but it is not an RFC option", option, value); continue; } if (options[i].o_handler) options_failed += (options[i].o_handler)(peer); break; } } if (options[i].o_type == NULL) tftp_log(LOG_WARNING, "Unknown option: '%s'", option); size -= strlen(option) + strlen(value) + 2; } return (options_failed); } /* * Set some default values in the options */ void init_options(void) { - options[OPT_ROLLOVER].o_request = strdup("0"); + options_set_request(OPT_ROLLOVER, "0"); } diff --git a/libexec/tftpd/tftp-options.h b/libexec/tftpd/tftp-options.h index cb387dba46c0..569d88d3c6d1 100644 --- a/libexec/tftpd/tftp-options.h +++ b/libexec/tftpd/tftp-options.h @@ -1,66 +1,71 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Options */ void init_options(void); uint16_t make_options(int peer, char *buffer, uint16_t size); int parse_options(int peer, char *buffer, uint16_t size); /* Call back functions */ int option_tsize(int peer, struct tftphdr *, int, struct stat *); int option_timeout(int peer); int option_blksize(int peer); int option_blksize2(int peer); int option_rollover(int peer); int option_windowsize(int peer); extern int options_extra_enabled; extern int options_rfc_enabled; struct options { const char *o_type; char *o_request; char *o_reply; int (*o_handler)(int peer); int rfc; }; extern struct options options[]; enum opt_enum { OPT_TSIZE = 0, OPT_TIMEOUT, OPT_BLKSIZE, OPT_BLKSIZE2, OPT_ROLLOVER, OPT_WINDOWSIZE, }; + +int options_set_request(enum opt_enum, const char *, ...) + __printflike(2, 3); +int options_set_reply(enum opt_enum, const char *, ...) + __printflike(2, 3); diff --git a/usr.bin/tftp/main.c b/usr.bin/tftp/main.c index b6d7bd2a0dcc..cfd486fb8418 100644 --- a/usr.bin/tftp/main.c +++ b/usr.bin/tftp/main.c @@ -1,1103 +1,1097 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #if 0 #ifndef lint static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; #endif #endif #include __FBSDID("$FreeBSD$"); /* Many bug fixes are from Jim Guyton */ /* * TFTP User Program -- Command Interface. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tftp-utils.h" #include "tftp-io.h" #include "tftp-options.h" #include "tftp.h" #define MAXLINE (2 * MAXPATHLEN) #define TIMEOUT 5 /* secs between rexmt's */ typedef struct sockaddr_storage peeraddr; static int connected; static char mode[32]; static jmp_buf toplevel; volatile int txrx_error; static int peer; #define MAX_MARGV 20 static int margc; static char *margv[MAX_MARGV]; int verbose; static char *port = NULL; static void get(int, char **); static void help(int, char **); static void intr(int); static void modecmd(int, char **); static void put(int, char **); static void quit(int, char **); static void setascii(int, char **); static void setbinary(int, char **); static void setpeer0(char *, const char *); static void setpeer(int, char **); static void settimeoutpacket(int, char **); static void settimeoutnetwork(int, char **); static void setdebug(int, char **); static void setverbose(int, char **); static void showstatus(int, char **); static void setblocksize(int, char **); static void setblocksize2(int, char **); static void setoptions(int, char **); static void setrollover(int, char **); static void setpacketdrop(int, char **); static void setwindowsize(int, char **); static void command(bool, EditLine *, History *, HistEvent *) __dead2; static const char *command_prompt(void); static void urihandling(char *URI); static void getusage(char *); static void makeargv(char *line); static void putusage(char *); static void settftpmode(const char *); static char *tail(char *); static struct cmd *getcmd(char *); #define HELPINDENT (sizeof("connect")) struct cmd { const char *name; void (*handler)(int, char **); const char *help; }; static struct cmd cmdtab[] = { { "connect", setpeer, "connect to remote tftp" }, { "mode", modecmd, "set file transfer mode" }, { "put", put, "send file" }, { "get", get, "receive file" }, { "quit", quit, "exit tftp" }, { "verbose", setverbose, "toggle verbose mode" }, { "status", showstatus, "show current status" }, { "binary", setbinary, "set mode to octet" }, { "ascii", setascii, "set mode to netascii" }, { "rexmt", settimeoutpacket, "set per-packet retransmission timeout[-]" }, { "timeout", settimeoutnetwork, "set total retransmission timeout" }, { "trace", setdebug, "enable 'debug packet'[-]" }, { "debug", setdebug, "enable verbose output" }, { "blocksize", setblocksize, "set blocksize[*]" }, { "blocksize2", setblocksize2, "set blocksize as a power of 2[**]" }, { "rollover", setrollover, "rollover after 64K packets[**]" }, { "options", setoptions, "enable or disable RFC2347 style options" }, { "help", help, "print help information" }, { "packetdrop", setpacketdrop, "artificial packetloss feature" }, { "windowsize", setwindowsize, "set windowsize[*]" }, { "?", help, "print help information" }, { NULL, NULL, NULL } }; static struct modes { const char *m_name; const char *m_mode; } modes[] = { { "ascii", "netascii" }, { "netascii", "netascii" }, { "binary", "octet" }, { "image", "octet" }, { "octet", "octet" }, { NULL, NULL } }; int main(int argc, char *argv[]) { HistEvent he; static EditLine *el; static History *hist; bool interactive; acting_as_client = 1; peer = -1; strcpy(mode, "octet"); signal(SIGINT, intr); interactive = isatty(STDIN_FILENO); if (interactive) { el = el_init("tftp", stdin, stdout, stderr); hist = history_init(); history(hist, &he, H_SETSIZE, 100); el_set(el, EL_HIST, history, hist); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_PROMPT, command_prompt); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); } if (argc > 1) { if (setjmp(toplevel) != 0) exit(txrx_error); if (strncmp(argv[1], "tftp://", 7) == 0) { urihandling(argv[1]); exit(txrx_error); } setpeer(argc, argv); } if (setjmp(toplevel) != 0) { if (interactive) el_reset(el); (void)putchar('\n'); } init_options(); command(interactive, el, hist, &he); } /* * RFC3617 handling of TFTP URIs: * * tftpURI = "tftp://" host "/" file [ mode ] * mode = ";" "mode=" ( "netascii" / "octet" ) * file = *( unreserved / escaped ) * host = * unreserved = * escaped = * * We are cheating a little bit by allowing any mode as specified in the * modes table defined earlier on in this file and mapping it on the real * mode. */ static void urihandling(char *URI) { char uri[ARG_MAX]; char *host = NULL; char *path = NULL; char *opts = NULL; const char *tmode = "octet"; char *s; char line[MAXLINE]; int i; strlcpy(uri, URI, ARG_MAX); host = uri + 7; if ((s = strchr(host, '/')) == NULL) { fprintf(stderr, "Invalid URI: Couldn't find / after hostname\n"); exit(1); } *s = '\0'; path = s + 1; if ((s = strchr(path, ';')) != NULL) { *s = '\0'; opts = s + 1; if (strncmp(opts, "mode=", 5) == 0) { tmode = opts; tmode += 5; for (i = 0; modes[i].m_name != NULL; i++) { if (strcmp(modes[i].m_name, tmode) == 0) break; } if (modes[i].m_name == NULL) { fprintf(stderr, "Invalid mode: '%s'\n", mode); exit(1); } settftpmode(modes[i].m_mode); } } else { settftpmode("octet"); } setpeer0(host, NULL); sprintf(line, "get %s", path); makeargv(line); get(margc, margv); } static char hostname[MAXHOSTNAMELEN]; static void setpeer0(char *host, const char *lport) { struct addrinfo hints, *res0, *res; int error; const char *cause = "unknown"; if (connected) { close(peer); peer = -1; } connected = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_CANONNAME; if (!lport) lport = "tftp"; error = getaddrinfo(host, lport, &hints, &res0); if (error) { warnx("%s", gai_strerror(error)); return; } for (res = res0; res; res = res->ai_next) { if (res->ai_addrlen > sizeof(peeraddr)) continue; peer = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (peer < 0) { cause = "socket"; continue; } memset(&peer_sock, 0, sizeof(peer_sock)); peer_sock.ss_family = res->ai_family; peer_sock.ss_len = res->ai_addrlen; if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) { cause = "bind"; close(peer); peer = -1; continue; } break; } if (peer < 0) warn("%s", cause); else { /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ memcpy(&peer_sock, res->ai_addr, res->ai_addrlen); if (res->ai_canonname) { (void) strlcpy(hostname, res->ai_canonname, sizeof(hostname)); } else (void) strlcpy(hostname, host, sizeof(hostname)); connected = 1; } freeaddrinfo(res0); } static void setpeer(int argc, char *argv[]) { char line[MAXLINE]; if (argc < 2) { strcpy(line, "Connect "); printf("(to) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if ((argc < 2) || (argc > 3)) { printf("usage: %s [host [port]]\n", argv[0]); return; } if (argc == 3) { port = argv[2]; setpeer0(argv[1], argv[2]); } else setpeer0(argv[1], NULL); } static void modecmd(int argc, char *argv[]) { struct modes *p; const char *sep; if (argc < 2) { printf("Using %s mode to transfer files.\n", mode); return; } if (argc == 2) { for (p = modes; p->m_name; p++) if (strcmp(argv[1], p->m_name) == 0) break; if (p->m_name) { settftpmode(p->m_mode); return; } printf("%s: unknown mode\n", argv[1]); /* drop through and print usage message */ } printf("usage: %s [", argv[0]); sep = " "; for (p = modes; p->m_name != NULL; p++) { printf("%s%s", sep, p->m_name); if (*sep == ' ') sep = " | "; } printf(" ]\n"); return; } static void setbinary(int argc __unused, char *argv[] __unused) { settftpmode("octet"); } static void setascii(int argc __unused, char *argv[] __unused) { settftpmode("netascii"); } static void settftpmode(const char *newmode) { strlcpy(mode, newmode, sizeof(mode)); if (verbose) printf("mode set to %s\n", mode); } /* * Send file(s). */ static void put(int argc, char *argv[]) { int fd; int n; char *cp, *targ, *path; char line[MAXLINE]; struct stat sb; if (argc < 2) { strcpy(line, "send "); printf("(file) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc < 2) { putusage(argv[0]); return; } targ = argv[argc - 1]; if (strrchr(argv[argc - 1], ':')) { char *lcp; for (n = 1; n < argc - 1; n++) if (strchr(argv[n], ':')) { putusage(argv[0]); return; } lcp = argv[argc - 1]; targ = strrchr(lcp, ':'); *targ++ = 0; if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { lcp[strlen(lcp) - 1] = '\0'; lcp++; } setpeer0(lcp, NULL); } if (!connected) { printf("No target machine specified.\n"); return; } if (argc < 4) { cp = argc == 2 ? tail(targ) : argv[1]; fd = open(cp, O_RDONLY); if (fd < 0) { warn("%s", cp); return; } if (fstat(fd, &sb) < 0) { warn("%s", cp); close(fd); return; } - asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); + options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size); if (verbose) printf("putting %s to %s:%s [%s]\n", cp, hostname, targ, mode); xmitfile(peer, port, fd, targ, mode); close(fd); return; } /* this assumes the target is a directory */ /* on a remote unix system. hmmmm. */ for (n = 1; n < argc - 1; n++) { if (asprintf(&path, "%s/%s", targ, tail(argv[n])) < 0) err(1, "malloc"); fd = open(argv[n], O_RDONLY); if (fd < 0) { warn("%s", argv[n]); free(path); continue; } if (fstat(fd, &sb) < 0) { warn("%s", argv[n]); close(fd); free(path); continue; } - asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); + options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size); if (verbose) printf("putting %s to %s:%s [%s]\n", argv[n], hostname, path, mode); xmitfile(peer, port, fd, path, mode); close(fd); free(path); } } static void putusage(char *s) { printf("usage: %s file [remotename]\n", s); printf(" %s file host:remotename\n", s); printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", s); } /* * Receive file(s). */ static void get(int argc, char *argv[]) { int fd; int n; char *cp; char *src; char line[MAXLINE]; if (argc < 2) { strcpy(line, "get "); printf("(files) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc < 2) { getusage(argv[0]); return; } if (!connected) { for (n = 1; n < argc ; n++) if (strrchr(argv[n], ':') == 0) { printf("No remote host specified and " "no host given for file '%s'\n", argv[n]); getusage(argv[0]); return; } } for (n = 1; n < argc ; n++) { src = strrchr(argv[n], ':'); if (src == NULL) src = argv[n]; else { char *lcp; *src++ = 0; lcp = argv[n]; if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { lcp[strlen(lcp) - 1] = '\0'; lcp++; } setpeer0(lcp, NULL); if (!connected) continue; } if (argc < 4) { cp = argc == 3 ? argv[2] : tail(src); fd = creat(cp, 0644); if (fd < 0) { warn("%s", cp); return; } if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); recvfile(peer, port, fd, src, mode); break; } cp = tail(src); /* new .. jdg */ fd = creat(cp, 0644); if (fd < 0) { warn("%s", cp); continue; } if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); recvfile(peer, port, fd, src, mode); } } static void getusage(char *s) { printf("usage: %s file [localname]\n", s); printf(" %s [host:]file [localname]\n", s); printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); } static void settimeoutpacket(int argc, char *argv[]) { int t; char line[MAXLINE]; if (argc < 2) { strcpy(line, "Packet timeout "); printf("(value) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc != 2) { printf("usage: %s value\n", argv[0]); return; } t = atoi(argv[1]); if (t < 0) { printf("%s: bad value\n", argv[1]); return; } settimeouts(t, timeoutnetwork, maxtimeouts); } static void settimeoutnetwork(int argc, char *argv[]) { int t; char line[MAXLINE]; if (argc < 2) { strcpy(line, "Network timeout "); printf("(value) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc != 2) { printf("usage: %s value\n", argv[0]); return; } t = atoi(argv[1]); if (t < 0) { printf("%s: bad value\n", argv[1]); return; } settimeouts(timeoutpacket, t, maxtimeouts); } static void showstatus(int argc __unused, char *argv[] __unused) { printf("Remote host: %s\n", connected ? hostname : "none specified yet"); printf("RFC2347 Options support: %s\n", options_rfc_enabled ? "enabled" : "disabled"); printf("Non-RFC defined options support: %s\n", options_extra_enabled ? "enabled" : "disabled"); printf("Mode: %s\n", mode); printf("Verbose: %s\n", verbose ? "on" : "off"); printf("Debug: %s\n", debug_show(debug)); printf("Artificial packetloss: %d in 100 packets\n", packetdroppercentage); printf("Segment size: %d bytes\n", segsize); printf("Network timeout: %d seconds\n", timeoutpacket); printf("Maximum network timeout: %d seconds\n", timeoutnetwork); printf("Maximum timeouts: %d \n", maxtimeouts); } static void intr(int dummy __unused) { signal(SIGALRM, SIG_IGN); alarm(0); longjmp(toplevel, -1); } static char * tail(char *filename) { char *s; while (*filename) { s = strrchr(filename, '/'); if (s == NULL) break; if (s[1]) return (s + 1); *s = '\0'; } return (filename); } static const char * command_prompt(void) { return ("tftp> "); } /* * Command parser. */ static void command(bool interactive, EditLine *el, History *hist, HistEvent *hep) { struct cmd *c; const char *bp; char *cp; int len, num; char line[MAXLINE]; for (;;) { if (interactive) { if ((bp = el_gets(el, &num)) == NULL || num == 0) exit(0); len = MIN(MAXLINE, num); memcpy(line, bp, len); line[len - 1] = '\0'; history(hist, hep, H_ENTER, bp); } else { line[0] = 0; if (fgets(line, sizeof line , stdin) == NULL) { if (feof(stdin)) { exit(txrx_error); } else { continue; } } } if ((cp = strchr(line, '\n'))) *cp = '\0'; if (line[0] == 0) continue; makeargv(line); if (margc == 0) continue; c = getcmd(margv[0]); if (c == (struct cmd *)-1) { printf("?Ambiguous command\n"); continue; } if (c == NULL) { printf("?Invalid command\n"); continue; } (*c->handler)(margc, margv); } } static struct cmd * getcmd(char *name) { const char *p, *q; struct cmd *c, *found; int nmatches, longest; longest = 0; nmatches = 0; found = 0; for (c = cmdtab; (p = c->name) != NULL; c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return (c); if (!*q) { /* the name was a prefix */ if (q - name > longest) { longest = q - name; nmatches = 1; found = c; } else if (q - name == longest) nmatches++; } } if (nmatches > 1) return ((struct cmd *)-1); return (found); } /* * Slice a string up into argc/argv. */ static void makeargv(char *line) { char *cp; char **argp = margv; margc = 0; if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) { while (isspace(*cp)) cp++; if (*cp == '\0') break; *argp++ = cp; margc += 1; while (*cp != '\0' && !isspace(*cp)) cp++; if (*cp == '\0') break; *cp++ = '\0'; } *argp++ = 0; } static void quit(int argc __unused, char *argv[] __unused) { exit(txrx_error); } /* * Help command. */ static void help(int argc, char *argv[]) { struct cmd *c; if (argc == 1) { printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->name; c++) printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); printf("\n[-] : You shouldn't use these ones anymore.\n"); printf("[*] : RFC2347 options support required.\n"); printf("[**] : Non-standard RFC2347 option.\n"); return; } while (--argc > 0) { char *arg; arg = *++argv; c = getcmd(arg); if (c == (struct cmd *)-1) printf("?Ambiguous help command: %s\n", arg); else if (c == (struct cmd *)0) printf("?Invalid help command: %s\n", arg); else printf("%s\n", c->help); } } static void setverbose(int argc __unused, char *argv[] __unused) { verbose = !verbose; printf("Verbose mode %s.\n", verbose ? "on" : "off"); } static void setoptions(int argc, char *argv[]) { if (argc == 2) { if (strcasecmp(argv[1], "enable") == 0 || strcasecmp(argv[1], "on") == 0) { options_extra_enabled = 1; options_rfc_enabled = 1; } if (strcasecmp(argv[1], "disable") == 0 || strcasecmp(argv[1], "off") == 0) { options_extra_enabled = 0; options_rfc_enabled = 0; } if (strcasecmp(argv[1], "extra") == 0) options_extra_enabled = !options_extra_enabled; } printf("Support for RFC2347 style options are now %s.\n", options_rfc_enabled ? "enabled" : "disabled"); printf("Support for non-RFC defined options are now %s.\n", options_extra_enabled ? "enabled" : "disabled"); printf("\nThe following options are available:\n" "\toptions on : enable support for RFC2347 style options\n" "\toptions off : disable support for RFC2347 style options\n" "\toptions extra : toggle support for non-RFC defined options\n" ); } static void setrollover(int argc, char *argv[]) { if (argc == 2) { if (strcasecmp(argv[1], "never") == 0 || strcasecmp(argv[1], "none") == 0) { - free(options[OPT_ROLLOVER].o_request); - options[OPT_ROLLOVER].o_request = NULL; + options_set_request(OPT_ROLLOVER, NULL); } if (strcasecmp(argv[1], "1") == 0) { - free(options[OPT_ROLLOVER].o_request); - options[OPT_ROLLOVER].o_request = strdup("1"); + options_set_request(OPT_ROLLOVER, "1"); } if (strcasecmp(argv[1], "0") == 0) { - free(options[OPT_ROLLOVER].o_request); - options[OPT_ROLLOVER].o_request = strdup("0"); + options_set_request(OPT_ROLLOVER, "0"); } } printf("Support for the rollover options is %s.\n", options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled"); if (options[OPT_ROLLOVER].o_request != NULL) printf("Block rollover will be to block %s.\n", options[OPT_ROLLOVER].o_request); printf("\nThe following rollover options are available:\n" "\trollover 0 : rollover to block zero (default)\n" "\trollover 1 : rollover to block one\n" "\trollover never : do not support the rollover option\n" "\trollover none : do not support the rollover option\n" ); } static void setdebug(int argc, char *argv[]) { int i; if (argc != 1) { i = 1; while (i < argc) debug ^= debug_find(argv[i++]); } printf("The following debugging is enabled: %s\n", debug_show(debug)); printf("\nThe following debugs are available:\n"); i = 0; while (debugs[i].name != NULL) { printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc); i++; } } static void setblocksize(int argc, char *argv[]) { if (!options_rfc_enabled) printf("RFC2347 style options are not enabled " "(but proceeding anyway)\n"); if (argc != 1) { int size = atoi(argv[1]); size_t max; u_long maxdgram; max = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &max, NULL, 0) < 0) { perror("sysctl: net.inet.udp.maxdgram"); return; } if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { printf("Blocksize should be between %d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX); return; } else if (size > (int)maxdgram - 4) { printf("Blocksize can't be bigger than %ld bytes due " "to the net.inet.udp.maxdgram sysctl limitation.\n", maxdgram - 4); - asprintf(&options[OPT_BLKSIZE].o_request, - "%ld", maxdgram - 4); + options_set_request(OPT_BLKSIZE, "%ld", maxdgram - 4); } else { - asprintf(&options[OPT_BLKSIZE].o_request, "%d", size); + options_set_request(OPT_BLKSIZE, "%d", size); } } printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request); } static void setblocksize2(int argc, char *argv[]) { if (!options_rfc_enabled || !options_extra_enabled) printf( "RFC2347 style or non-RFC defined options are not enabled " "(but proceeding anyway)\n"); if (argc != 1) { int size = atoi(argv[1]); int i; size_t max; u_long maxdgram; int sizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 0 }; max = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &max, NULL, 0) < 0) { perror("sysctl: net.inet.udp.maxdgram"); return; } for (i = 0; sizes[i] != 0; i++) { if (sizes[i] == size) break; } if (sizes[i] == 0) { printf("Blocksize2 should be a power of two between " "8 and 32768.\n"); return; } if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { printf("Blocksize2 should be between " "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX); return; } else if (size > (int)maxdgram - 4) { printf("Blocksize2 can't be bigger than %ld bytes due " "to the net.inet.udp.maxdgram sysctl limitation.\n", maxdgram - 4); for (i = 0; sizes[i+1] != 0; i++) { if ((int)maxdgram < sizes[i+1]) break; } - asprintf(&options[OPT_BLKSIZE2].o_request, - "%d", sizes[i]); + options_set_request(OPT_BLKSIZE2, "%d", sizes[i]); } else { - asprintf(&options[OPT_BLKSIZE2].o_request, "%d", size); + options_set_request(OPT_BLKSIZE2, "%d", size); } } printf("Blocksize2 is now %s bytes.\n", options[OPT_BLKSIZE2].o_request); } static void setpacketdrop(int argc, char *argv[]) { if (argc != 1) packetdroppercentage = atoi(argv[1]); printf("Randomly %d in 100 packets will be dropped\n", packetdroppercentage); } static void setwindowsize(int argc, char *argv[]) { if (!options_rfc_enabled) printf("RFC2347 style options are not enabled " "(but proceeding anyway)\n"); if (argc != 1) { int size = atoi(argv[1]); if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) { printf("Windowsize should be between %d and %d " "blocks.\n", WINDOWSIZE_MIN, WINDOWSIZE_MAX); return; } else { - asprintf(&options[OPT_WINDOWSIZE].o_request, "%d", - size); + options_set_request(OPT_WINDOWSIZE, "%d", size); } } printf("Windowsize is now %s blocks.\n", options[OPT_WINDOWSIZE].o_request); }