Bring in new files from edwin's tftp
This commit is contained in:
parent
11666ce50a
commit
e7ff54750b
257
libexec/tftpd/tftp-file.c
Normal file
257
libexec/tftpd/tftp-file.c
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/tftp.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "tftp-file.h"
|
||||
#include "tftp-utils.h"
|
||||
|
||||
static FILE *file;
|
||||
static int convert;
|
||||
|
||||
static char convbuffer[66000];
|
||||
static int gotcr = 0;
|
||||
|
||||
static size_t
|
||||
convert_from_net(char *buffer, size_t count)
|
||||
{
|
||||
size_t i, n;
|
||||
|
||||
/*
|
||||
* Convert all CR/LF to LF and all CR,NUL to CR
|
||||
*/
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
|
||||
if (gotcr == 0) {
|
||||
convbuffer[n++] = buffer[i];
|
||||
gotcr = (buffer[i] == '\r');
|
||||
continue;
|
||||
}
|
||||
|
||||
/* CR, NULL -> CR */
|
||||
if (buffer[i] == '\0') {
|
||||
gotcr = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* CR, LF -> LF */
|
||||
if (buffer[i] == '\n') {
|
||||
if (n == 0) {
|
||||
if (ftell(file) != 0) {
|
||||
fseek(file, -1, SEEK_END);
|
||||
convbuffer[n++] = '\n';
|
||||
} else {
|
||||
/* This shouldn't happen */
|
||||
tftp_log(LOG_ERR,
|
||||
"Received LF as first character");
|
||||
abort();
|
||||
}
|
||||
} else
|
||||
convbuffer[n-1] = '\n';
|
||||
gotcr = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Everything else just accept as is */
|
||||
convbuffer[n++] = buffer[i];
|
||||
gotcr = (buffer[i] == '\r');
|
||||
continue;
|
||||
}
|
||||
|
||||
return fwrite(convbuffer, 1, n, file);
|
||||
}
|
||||
|
||||
static size_t
|
||||
convert_to_net(char *buffer, size_t count, int init)
|
||||
{
|
||||
size_t i;
|
||||
static size_t n = 0, read = 0;
|
||||
static int newline = 0;
|
||||
|
||||
if (init) {
|
||||
newline = 0;
|
||||
n = 0;
|
||||
read = 0;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert all LF to CR,LF and all CR to CR,NUL
|
||||
*/
|
||||
i = 0;
|
||||
|
||||
if (newline) {
|
||||
buffer[i++] = newline;
|
||||
newline = 0;
|
||||
}
|
||||
|
||||
while (i < count) {
|
||||
if (n == read) {
|
||||
/* When done we're done */
|
||||
if (feof(file)) break;
|
||||
|
||||
/* Otherwise read another bunch */
|
||||
read = fread(convbuffer, 1, count, file);
|
||||
if (read == 0) break;
|
||||
n = 0;
|
||||
}
|
||||
|
||||
/* CR -> CR,NULL */
|
||||
if (convbuffer[n] == '\r') {
|
||||
buffer[i++] = '\r';
|
||||
buffer[i++] = '\0';
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* LF -> CR,LF */
|
||||
if (convbuffer[n] == '\n') {
|
||||
buffer[i++] = '\r';
|
||||
buffer[i++] = '\n';
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer[i++] = convbuffer[n++];
|
||||
}
|
||||
|
||||
if (i > count) {
|
||||
/*
|
||||
* Whoops... that isn't alllowed (but it will happen
|
||||
* when there is a CR or LF at the end of the buffer)
|
||||
*/
|
||||
newline = buffer[i-1];
|
||||
}
|
||||
|
||||
if (i < count) {
|
||||
/* We are done! */
|
||||
return i;
|
||||
} else
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
write_init(int fd, FILE *f, const char *mode)
|
||||
{
|
||||
|
||||
if (f == NULL) {
|
||||
file = fdopen(fd, "w");
|
||||
if (file == NULL) {
|
||||
int en = errno;
|
||||
tftp_log(LOG_ERR, "fdopen() failed: %s",
|
||||
strerror(errno));
|
||||
return en;
|
||||
}
|
||||
} else
|
||||
file = f;
|
||||
convert = !strcmp(mode, "netascii");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
write_file(char *buffer, int count)
|
||||
{
|
||||
|
||||
if (convert == 0)
|
||||
return fwrite(buffer, 1, count, file);
|
||||
|
||||
return convert_from_net(buffer, count);
|
||||
}
|
||||
|
||||
int
|
||||
write_close(void)
|
||||
{
|
||||
|
||||
if (fclose(file) != 0) {
|
||||
tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
read_init(int fd, FILE *f, const char *mode)
|
||||
{
|
||||
|
||||
convert_to_net(NULL, 0, 1);
|
||||
if (f == NULL) {
|
||||
file = fdopen(fd, "r");
|
||||
if (file == NULL) {
|
||||
int en = errno;
|
||||
tftp_log(LOG_ERR, "fdopen() failed: %s",
|
||||
strerror(errno));
|
||||
return en;
|
||||
}
|
||||
} else
|
||||
file = f;
|
||||
convert = !strcmp(mode, "netascii");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
read_file(char *buffer, int count)
|
||||
{
|
||||
|
||||
if (convert == 0)
|
||||
return fread(buffer, 1, count, file);
|
||||
|
||||
return convert_to_net(buffer, count, 0);
|
||||
}
|
||||
|
||||
int
|
||||
read_close(void)
|
||||
{
|
||||
|
||||
if (fclose(file) != 0) {
|
||||
tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
synchnet(int peer)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
37
libexec/tftpd/tftp-file.h
Normal file
37
libexec/tftpd/tftp-file.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
int write_init(int fd, FILE *f, const char *mode);
|
||||
size_t write_file(char *buffer, int count);
|
||||
int write_close(void);
|
||||
|
||||
int read_init(int fd, FILE *f, const char *mode);
|
||||
size_t read_file(char *buffer, int count);
|
||||
int read_close(void);
|
||||
|
||||
int synchnet(int peer);
|
478
libexec/tftpd/tftp-io.c
Normal file
478
libexec/tftpd/tftp-io.c
Normal file
@ -0,0 +1,478 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/tftp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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);
|
||||
|
||||
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, "Artifical packet drop in %s", s); \
|
||||
return; \
|
||||
}
|
||||
#define DROPPACKETn(s,n) \
|
||||
if (packetdroppercentage != 0 && \
|
||||
random()%100 < packetdroppercentage) { \
|
||||
tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s); \
|
||||
return (n); \
|
||||
}
|
||||
|
||||
const char *
|
||||
errtomsg(int error)
|
||||
{
|
||||
static char ebuf[40];
|
||||
struct errmsg *pe;
|
||||
char buf[MAXPKTSIZE];
|
||||
|
||||
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(buf), "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",
|
||||
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: %s", 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", 1);
|
||||
|
||||
tp = (struct tftphdr *)buf;
|
||||
tp->th_opcode = htons((u_short)WRQ);
|
||||
size = 2;
|
||||
|
||||
bp = tp->th_stuff;
|
||||
strcpy(bp, filename);
|
||||
bp += strlen(filename);
|
||||
*bp = 0;
|
||||
bp++;
|
||||
size += strlen(filename) + 1;
|
||||
|
||||
strcpy(bp, mode);
|
||||
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", 1);
|
||||
|
||||
tp = (struct tftphdr *)buf;
|
||||
tp->th_opcode = htons((u_short)RRQ);
|
||||
size = 2;
|
||||
|
||||
bp = tp->th_stuff;
|
||||
strcpy(bp, filename);
|
||||
bp += strlen(filename);
|
||||
*bp = 0;
|
||||
bp++;
|
||||
size += strlen(filename) + 1;
|
||||
|
||||
strcpy(bp, mode);
|
||||
bp += strlen(mode);
|
||||
*bp = 0;
|
||||
bp++;
|
||||
size += strlen(mode) + 1;
|
||||
|
||||
if (options_rfc_enabled) {
|
||||
options[OPT_TSIZE].o_request = strdup("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: %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 *bp;
|
||||
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;
|
||||
bp = buf + 2;
|
||||
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
|
||||
*/
|
||||
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 waiting;
|
||||
|
||||
pfrom = (from == NULL) ? &from_local : from;
|
||||
|
||||
if (debug&DEBUG_PACKETS)
|
||||
tftp_log(LOG_DEBUG,
|
||||
"Waiting %d seconds for packet", timeoutpacket);
|
||||
|
||||
pkt = (struct tftphdr *)data;
|
||||
|
||||
waiting = 0;
|
||||
signal(SIGALRM, timeout);
|
||||
setjmp(timeoutbuf);
|
||||
alarm(thistimeout);
|
||||
|
||||
if (waiting > 0) {
|
||||
alarm(0);
|
||||
return (RP_TIMEOUT);
|
||||
}
|
||||
|
||||
if (waiting > 0) {
|
||||
tftp_log(LOG_ERR, "receive_packet: timeout");
|
||||
alarm(0);
|
||||
return (RP_TIMEOUT);
|
||||
}
|
||||
|
||||
waiting++;
|
||||
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);
|
||||
}
|
||||
|
||||
alarm(0);
|
||||
|
||||
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(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;
|
||||
}
|
47
libexec/tftpd/tftp-io.h
Normal file
47
libexec/tftpd/tftp-io.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#define RP_NONE 0
|
||||
#define RP_RECVFROM -1
|
||||
#define RP_TOOSMALL -2
|
||||
#define RP_ERROR -3
|
||||
#define RP_WRONGSOURCE -4
|
||||
#define RP_TIMEOUT -5
|
||||
#define RP_TOOBIG -6
|
||||
|
||||
const char *errtomsg(int);
|
||||
void send_error(int peer, int);
|
||||
int send_wrq(int peer, char *, char *);
|
||||
int send_rrq(int peer, char *, char *);
|
||||
int send_oack(int peer);
|
||||
int send_ack(int peer, unsigned short);
|
||||
int send_data(int peer, uint16_t, char *, int);
|
||||
int receive_packet(int peer, char *, int, struct sockaddr_storage *, int);
|
||||
|
||||
extern struct sockaddr_storage peer_sock;
|
||||
extern struct sockaddr_storage me_sock;
|
390
libexec/tftpd/tftp-options.c
Normal file
390
libexec/tftpd/tftp-options.c
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/tftp.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#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 },
|
||||
{ NULL, NULL, NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
/* By default allow them */
|
||||
int options_rfc_enabled = 1;
|
||||
int options_extra_enabled = 1;
|
||||
|
||||
/*
|
||||
* 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, struct tftphdr *tp, int mode, struct stat *stbuf)
|
||||
{
|
||||
|
||||
if (options[OPT_TSIZE].o_request == NULL)
|
||||
return (0);
|
||||
|
||||
if (mode == RRQ)
|
||||
asprintf(&options[OPT_TSIZE].o_reply,
|
||||
"%ju", stbuf->st_size);
|
||||
else
|
||||
/* XXX Allows writes of all sizes. */
|
||||
options[OPT_TSIZE].o_reply =
|
||||
strdup(options[OPT_TSIZE].o_request);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
option_timeout(int peer)
|
||||
{
|
||||
|
||||
if (options[OPT_TIMEOUT].o_request == NULL)
|
||||
return (0);
|
||||
|
||||
int 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 %s",
|
||||
TIMEOUT_MIN, TIMEOUT_MAX);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
if (debug&DEBUG_OPTIONS)
|
||||
tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
|
||||
options[OPT_ROLLOVER].o_reply);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
option_blksize(int peer)
|
||||
{
|
||||
int *maxdgram;
|
||||
char maxbuffer[100];
|
||||
size_t len;
|
||||
|
||||
if (options[OPT_BLKSIZE].o_request == NULL)
|
||||
return (0);
|
||||
|
||||
/* maximum size of an UDP packet according to the system */
|
||||
len = sizeof(maxbuffer);
|
||||
if (sysctlbyname("net.inet.udp.maxdgram",
|
||||
maxbuffer, &len, NULL, 0) < 0) {
|
||||
tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
|
||||
return (acting_as_client ? 1 : 0);
|
||||
}
|
||||
maxdgram = (int *)maxbuffer;
|
||||
|
||||
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 > *maxdgram) {
|
||||
if (acting_as_client) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Invalid blocksize (%d bytes), "
|
||||
"net.inet.udp.maxdgram sysctl limits it to "
|
||||
"%d 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 "
|
||||
"%d bytes.\n", size, *maxdgram);
|
||||
size = *maxdgram;
|
||||
/* No reason to return */
|
||||
}
|
||||
}
|
||||
|
||||
asprintf(&options[OPT_BLKSIZE].o_reply, "%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)
|
||||
{
|
||||
int *maxdgram;
|
||||
char maxbuffer[100];
|
||||
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(maxbuffer);
|
||||
if (sysctlbyname("net.inet.udp.maxdgram",
|
||||
maxbuffer, &len, NULL, 0) < 0) {
|
||||
tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
|
||||
return (acting_as_client ? 1 : 0);
|
||||
}
|
||||
maxdgram = (int *)maxbuffer;
|
||||
|
||||
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 > *maxdgram) {
|
||||
for (i = 0; sizes[i+1] != 0; i++) {
|
||||
if (*maxdgram < sizes[i+1]) break;
|
||||
}
|
||||
tftp_log(LOG_INFO,
|
||||
"Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
|
||||
"sysctl limits it to %d bytes.\n", size, *maxdgram);
|
||||
size = sizes[i];
|
||||
/* No need to return */
|
||||
}
|
||||
|
||||
asprintf(&options[OPT_BLKSIZE2].o_reply, "%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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Append the available options to the header
|
||||
*/
|
||||
uint16_t
|
||||
make_options(int peer, 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;
|
||||
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");
|
||||
}
|
62
libexec/tftpd/tftp-options.h
Normal file
62
libexec/tftpd/tftp-options.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__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);
|
||||
|
||||
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,
|
||||
};
|
318
libexec/tftpd/tftp-transfer.c
Normal file
318
libexec/tftpd/tftp-transfer.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/tftp.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "tftp-file.h"
|
||||
#include "tftp-io.h"
|
||||
#include "tftp-utils.h"
|
||||
#include "tftp-options.h"
|
||||
#include "tftp-transfer.h"
|
||||
|
||||
/*
|
||||
* Send a file via the TFTP data session.
|
||||
*/
|
||||
void
|
||||
tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
|
||||
{
|
||||
struct tftphdr *rp;
|
||||
int size, n_data, n_ack, try;
|
||||
uint16_t oldblock;
|
||||
char sendbuffer[MAXPKTSIZE];
|
||||
char recvbuffer[MAXPKTSIZE];
|
||||
|
||||
rp = (struct tftphdr *)recvbuffer;
|
||||
*block = 1;
|
||||
ts->amount = 0;
|
||||
do {
|
||||
if (debug&DEBUG_SIMPLE)
|
||||
tftp_log(LOG_DEBUG, "Sending block %d", *block);
|
||||
|
||||
size = read_file(sendbuffer, segsize);
|
||||
if (size < 0) {
|
||||
tftp_log(LOG_ERR, "read_file returned %d", size);
|
||||
send_error(peer, errno + 100);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
for (try = 0; ; try++) {
|
||||
n_data = send_data(peer, *block, sendbuffer, size);
|
||||
if (n_data > 0) {
|
||||
if (try == maxtimeouts) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Cannot send DATA packet #%d, "
|
||||
"giving up", *block);
|
||||
return;
|
||||
}
|
||||
tftp_log(LOG_ERR,
|
||||
"Cannot send DATA packet #%d, trying again",
|
||||
*block);
|
||||
continue;
|
||||
}
|
||||
|
||||
n_ack = receive_packet(peer, recvbuffer,
|
||||
MAXPKTSIZE, NULL, timeoutpacket);
|
||||
if (n_ack < 0) {
|
||||
if (n_ack == RP_TIMEOUT) {
|
||||
if (try == maxtimeouts) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Timeout #%d send ACK %d "
|
||||
"giving up", try, *block);
|
||||
return;
|
||||
}
|
||||
tftp_log(LOG_WARNING,
|
||||
"Timeout #%d on ACK %d",
|
||||
try, *block);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Either read failure or ERROR packet */
|
||||
if (debug&DEBUG_SIMPLE)
|
||||
tftp_log(LOG_ERR, "Aborting: %s",
|
||||
rp_strerror(n_ack));
|
||||
goto abort;
|
||||
}
|
||||
if (rp->th_opcode == ACK) {
|
||||
ts->blocks++;
|
||||
if (rp->th_block == *block) {
|
||||
ts->amount += size;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Re-synchronize with the other side */
|
||||
(void) synchnet(peer);
|
||||
if (rp->th_block == (*block - 1)) {
|
||||
ts->retries++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
oldblock = *block;
|
||||
(*block)++;
|
||||
if (oldblock > *block) {
|
||||
if (options[OPT_ROLLOVER].o_request == NULL) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Block rollover but not allowed.");
|
||||
send_error(peer, EBADOP);
|
||||
gettimeofday(&(ts->tstop), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
*block = atoi(options[OPT_ROLLOVER].o_request);
|
||||
ts->rollovers++;
|
||||
}
|
||||
gettimeofday(&(ts->tstop), NULL);
|
||||
} while (size == segsize);
|
||||
abort:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a file via the TFTP data session.
|
||||
*
|
||||
* - It could be that the first block has already arrived while
|
||||
* trying to figure out if we were receiving options or not. In
|
||||
* that case it is passed to this function.
|
||||
*/
|
||||
void
|
||||
tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
|
||||
struct tftphdr *firstblock, size_t fb_size)
|
||||
{
|
||||
struct tftphdr *rp;
|
||||
uint16_t oldblock;
|
||||
int n_data, n_ack, writesize, i, retry;
|
||||
char recvbuffer[MAXPKTSIZE];
|
||||
|
||||
ts->amount = 0;
|
||||
|
||||
if (firstblock != NULL) {
|
||||
writesize = write_file(firstblock->th_data, fb_size);
|
||||
ts->amount += writesize;
|
||||
for (i = 0; ; i++) {
|
||||
n_ack = send_ack(peer, *block);
|
||||
if (n_ack > 0) {
|
||||
if (i == maxtimeouts) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Cannot send ACK packet #%d, "
|
||||
"giving up", *block);
|
||||
return;
|
||||
}
|
||||
tftp_log(LOG_ERR,
|
||||
"Cannot send ACK packet #%d, trying again",
|
||||
*block);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (fb_size != segsize) {
|
||||
gettimeofday(&(ts->tstop), NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rp = (struct tftphdr *)recvbuffer;
|
||||
do {
|
||||
oldblock = *block;
|
||||
(*block)++;
|
||||
if (oldblock > *block) {
|
||||
if (options[OPT_ROLLOVER].o_request == NULL) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Block rollover but not allowed.");
|
||||
send_error(peer, EBADOP);
|
||||
gettimeofday(&(ts->tstop), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
*block = atoi(options[OPT_ROLLOVER].o_request);
|
||||
ts->rollovers++;
|
||||
}
|
||||
|
||||
for (retry = 0; ; retry++) {
|
||||
if (debug&DEBUG_SIMPLE)
|
||||
tftp_log(LOG_DEBUG,
|
||||
"Receiving DATA block %d", *block);
|
||||
|
||||
n_data = receive_packet(peer, recvbuffer,
|
||||
MAXPKTSIZE, NULL, timeoutpacket);
|
||||
if (n_data < 0) {
|
||||
if (retry == maxtimeouts) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Timeout #%d on DATA block %d, "
|
||||
"giving up", retry, *block);
|
||||
return;
|
||||
}
|
||||
if (n_data == RP_TIMEOUT) {
|
||||
tftp_log(LOG_WARNING,
|
||||
"Timeout #%d on DATA block %d",
|
||||
retry, *block);
|
||||
send_ack(peer, oldblock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Either read failure or ERROR packet */
|
||||
if (debug&DEBUG_SIMPLE)
|
||||
tftp_log(LOG_DEBUG, "Aborting: %s",
|
||||
rp_strerror(n_data));
|
||||
goto abort;
|
||||
}
|
||||
if (rp->th_opcode == DATA) {
|
||||
ts->blocks++;
|
||||
|
||||
if (rp->th_block == *block)
|
||||
break;
|
||||
|
||||
tftp_log(LOG_WARNING,
|
||||
"Expected DATA block %d, got block %d",
|
||||
*block, rp->th_block);
|
||||
|
||||
/* Re-synchronize with the other side */
|
||||
(void) synchnet(peer);
|
||||
if (rp->th_block == (*block-1)) {
|
||||
tftp_log(LOG_INFO, "Trying to sync");
|
||||
*block = oldblock;
|
||||
ts->retries++;
|
||||
goto send_ack; /* rexmit */
|
||||
}
|
||||
|
||||
} else {
|
||||
tftp_log(LOG_WARNING,
|
||||
"Expected DATA block, got %s block",
|
||||
packettype(rp->th_opcode));
|
||||
}
|
||||
}
|
||||
|
||||
if (n_data > 0) {
|
||||
writesize = write_file(rp->th_data, n_data);
|
||||
ts->amount += writesize;
|
||||
if (writesize <= 0) {
|
||||
tftp_log(LOG_ERR,
|
||||
"write_file returned %d", writesize);
|
||||
if (writesize < 0)
|
||||
send_error(peer, errno + 100);
|
||||
else
|
||||
send_error(peer, ENOSPACE);
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
send_ack:
|
||||
for (i = 0; ; i++) {
|
||||
n_ack = send_ack(peer, *block);
|
||||
if (n_ack > 0) {
|
||||
|
||||
if (i == maxtimeouts) {
|
||||
tftp_log(LOG_ERR,
|
||||
"Cannot send ACK packet #%d, "
|
||||
"giving up", *block);
|
||||
return;
|
||||
}
|
||||
|
||||
tftp_log(LOG_ERR,
|
||||
"Cannot send ACK packet #%d, trying again",
|
||||
*block);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
gettimeofday(&(ts->tstop), NULL);
|
||||
} while (n_data == segsize);
|
||||
|
||||
/* Don't do late packet management for the client implementation */
|
||||
if (acting_as_client)
|
||||
return;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
n_data = receive_packet(peer, (char *)rp, pktsize,
|
||||
NULL, timeoutpacket);
|
||||
if (n_data <= 0)
|
||||
break;
|
||||
if (n_data > 0 &&
|
||||
rp->th_opcode == DATA && /* and got a data block */
|
||||
*block == rp->th_block) /* then my last ack was lost */
|
||||
send_ack(peer, *block); /* resend final ack */
|
||||
}
|
||||
|
||||
abort:
|
||||
return;
|
||||
}
|
31
libexec/tftpd/tftp-transfer.h
Normal file
31
libexec/tftpd/tftp-transfer.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
void tftp_send(int peer, uint16_t *block, struct tftp_stats *tp);
|
||||
void tftp_receive(int peer, uint16_t *block, struct tftp_stats *tp,
|
||||
struct tftphdr *firstblock, size_t fb_size);
|
320
libexec/tftpd/tftp-utils.c
Normal file
320
libexec/tftpd/tftp-utils.c
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/tftp.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "tftp-utils.h"
|
||||
#include "tftp-io.h"
|
||||
|
||||
/*
|
||||
* Default values, can be changed later via the TFTP Options
|
||||
*/
|
||||
int timeoutpacket = TIMEOUT;
|
||||
int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
|
||||
int maxtimeouts = MAX_TIMEOUTS;
|
||||
uint16_t segsize = SEGSIZE;
|
||||
uint16_t pktsize = SEGSIZE + 4;
|
||||
|
||||
int acting_as_client;
|
||||
|
||||
|
||||
/*
|
||||
* Set timeout values for packet reception. The idea is that you
|
||||
* get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the
|
||||
* first timeout) to 'timeoutnetwork' (i.e. the last timeout)
|
||||
*/
|
||||
int
|
||||
settimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* We cannot do impossible things */
|
||||
if (_timeoutpacket >= _timeoutnetwork)
|
||||
return (0);
|
||||
|
||||
maxtimeouts = 0;
|
||||
i = _timeoutpacket;
|
||||
while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) {
|
||||
maxtimeouts++;
|
||||
i += 5;
|
||||
}
|
||||
|
||||
timeoutpacket = _timeoutpacket;
|
||||
timeoutnetwork = i;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* translate IPv4 mapped IPv6 address to IPv4 address */
|
||||
void
|
||||
unmappedaddr(struct sockaddr_in6 *sin6)
|
||||
{
|
||||
struct sockaddr_in *sin4;
|
||||
u_int32_t addr;
|
||||
int port;
|
||||
|
||||
if (sin6->sin6_family != AF_INET6 ||
|
||||
!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
|
||||
return;
|
||||
sin4 = (struct sockaddr_in *)sin6;
|
||||
addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
|
||||
port = sin6->sin6_port;
|
||||
memset(sin4, 0, sizeof(struct sockaddr_in));
|
||||
sin4->sin_addr.s_addr = addr;
|
||||
sin4->sin_port = port;
|
||||
sin4->sin_family = AF_INET;
|
||||
sin4->sin_len = sizeof(struct sockaddr_in);
|
||||
}
|
||||
|
||||
/* Get a field from a \0 seperated string */
|
||||
ssize_t
|
||||
get_field(int peer, char *buffer, ssize_t size)
|
||||
{
|
||||
char *cp = buffer;
|
||||
|
||||
while (cp < buffer + size) {
|
||||
if (*cp == '\0') break;
|
||||
cp++;
|
||||
}
|
||||
if (*cp != '\0') {
|
||||
tftp_log(LOG_ERR, "Bad option - no trailing \\0 found");
|
||||
send_error(peer, EBADOP);
|
||||
exit(1);
|
||||
}
|
||||
return (cp - buffer + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Logging functions
|
||||
*/
|
||||
int _tftp_logtostdout = 1;
|
||||
|
||||
void
|
||||
tftp_openlog(const char *ident, int logopt, int facility)
|
||||
{
|
||||
|
||||
_tftp_logtostdout = (ident == NULL);
|
||||
if (_tftp_logtostdout == 0)
|
||||
openlog(ident, logopt, facility);
|
||||
}
|
||||
|
||||
void
|
||||
tftp_closelog(void)
|
||||
{
|
||||
|
||||
if (_tftp_logtostdout == 0)
|
||||
closelog();
|
||||
}
|
||||
|
||||
void
|
||||
tftp_log(int priority, const char *message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *s;
|
||||
|
||||
va_start(ap, message);
|
||||
if (_tftp_logtostdout == 0) {
|
||||
vasprintf(&s, message, ap);
|
||||
syslog(priority, "%s", s);
|
||||
} else {
|
||||
vprintf(message, ap);
|
||||
printf("\n");
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Packet types
|
||||
*/
|
||||
struct packettypes packettypes[] = {
|
||||
{ RRQ, "RRQ" },
|
||||
{ WRQ, "WRQ" },
|
||||
{ DATA, "DATA" },
|
||||
{ ACK, "ACK" },
|
||||
{ ERROR, "ERROR" },
|
||||
{ OACK, "OACK" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
char *
|
||||
packettype(int type)
|
||||
{
|
||||
static char failed[100];
|
||||
int i = 0;
|
||||
|
||||
while (packettypes[i].name != NULL) {
|
||||
if (packettypes[i].value == type)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
if (packettypes[i].name != NULL)
|
||||
return packettypes[i].name;
|
||||
sprintf(failed, "unknown (type: %d)", type);
|
||||
return (failed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugs
|
||||
*/
|
||||
int debug = DEBUG_NONE;
|
||||
struct debugs debugs[] = {
|
||||
{ DEBUG_PACKETS, "packet", "Packet debugging" },
|
||||
{ DEBUG_SIMPLE, "simple", "Simple debugging" },
|
||||
{ DEBUG_OPTIONS, "options", "Options debugging" },
|
||||
{ DEBUG_ACCESS, "access", "TCPd access debugging" },
|
||||
{ DEBUG_NONE, NULL, "No debugging" },
|
||||
};
|
||||
int packetdroppercentage = 0;
|
||||
|
||||
int
|
||||
debug_find(char *s)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (debugs[i].name != NULL) {
|
||||
if (strcasecmp(debugs[i].name, s) == 0)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
return (debugs[i].value);
|
||||
}
|
||||
|
||||
int
|
||||
debug_finds(char *s)
|
||||
{
|
||||
int i = 0;
|
||||
char *ps = s;
|
||||
|
||||
while (s != NULL) {
|
||||
ps = strchr(s, ' ');
|
||||
if (ps != NULL)
|
||||
*ps = '\0';
|
||||
i += debug_find(s);
|
||||
if (ps != NULL)
|
||||
*ps = ' ';
|
||||
s = ps;
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
char *
|
||||
debug_show(int d)
|
||||
{
|
||||
static char s[100];
|
||||
int i = 0;
|
||||
|
||||
s[0] = '\0';
|
||||
while (debugs[i].name != NULL) {
|
||||
if (d&debugs[i].value) {
|
||||
if (s[0] != '\0')
|
||||
strcat(s, " ");
|
||||
strcat(s, debugs[i].name);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (s[0] != '\0')
|
||||
return (s);
|
||||
return ("none");
|
||||
}
|
||||
|
||||
/*
|
||||
* RP_
|
||||
*/
|
||||
struct rp_errors rp_errors[] = {
|
||||
{ RP_TIMEOUT, "Network timeout" },
|
||||
{ RP_TOOSMALL, "Not enough data bytes" },
|
||||
{ RP_WRONGSOURCE, "Invalid IP address of UDP port" },
|
||||
{ RP_ERROR, "Error packet" },
|
||||
{ RP_RECVFROM, "recvfrom() complained" },
|
||||
{ RP_TOOBIG, "Too many data bytes" },
|
||||
{ RP_NONE, NULL }
|
||||
};
|
||||
|
||||
char *
|
||||
rp_strerror(int error)
|
||||
{
|
||||
static char s[100];
|
||||
int i = 0;
|
||||
|
||||
while (rp_errors[i].desc != NULL) {
|
||||
if (rp_errors[i].error == error) {
|
||||
strcpy(s, rp_errors[i].desc);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (s[0] == '\0')
|
||||
sprintf(s, "unknown (error=%d)", error);
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performance figures
|
||||
*/
|
||||
|
||||
void
|
||||
stats_init(struct tftp_stats *ts)
|
||||
{
|
||||
|
||||
ts->amount = 0;
|
||||
ts->rollovers = 0;
|
||||
ts->retries = 0;
|
||||
ts->blocks = 0;
|
||||
ts->amount = 0;
|
||||
gettimeofday(&(ts->tstart), NULL);
|
||||
}
|
||||
|
||||
void
|
||||
printstats(const char *direction, int verbose, struct tftp_stats *ts)
|
||||
{
|
||||
double delta; /* compute delta in 1/10's second units */
|
||||
|
||||
delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) -
|
||||
((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000));
|
||||
delta = delta/10.; /* back to seconds */
|
||||
|
||||
printf("%s %zu bytes during %.1f seconds in %u blocks",
|
||||
direction, ts->amount, delta, ts->blocks);
|
||||
|
||||
if (ts->rollovers != 0)
|
||||
printf(" with %d rollover%s",
|
||||
ts->rollovers, ts->rollovers != 1 ? "s" : "");
|
||||
|
||||
if (verbose)
|
||||
printf(" [%.0f bits/sec]", (ts->amount*8.)/delta);
|
||||
putchar('\n');
|
||||
}
|
||||
|
124
libexec/tftpd/tftp-utils.h
Normal file
124
libexec/tftpd/tftp-utils.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
*/
|
||||
#define TIMEOUT 5
|
||||
#define MAX_TIMEOUTS 5
|
||||
|
||||
/* Generic values */
|
||||
#define MAXSEGSIZE 65464 /* Maximum size of the data segment */
|
||||
#define MAXPKTSIZE (MAXSEGSIZE + 4) /* Maximum size of the packet */
|
||||
|
||||
/* For the blksize option */
|
||||
#define BLKSIZE_MIN 8 /* Minumum size of the data segment */
|
||||
#define BLKSIZE_MAX MAXSEGSIZE /* Maximum size of the data segment */
|
||||
|
||||
/* For the timeout option */
|
||||
#define TIMEOUT_MIN 0 /* Minumum timeout value */
|
||||
#define TIMEOUT_MAX 255 /* Maximum timeout value */
|
||||
#define MIN_TIMEOUTS 3
|
||||
|
||||
extern int timeoutpacket;
|
||||
extern int timeoutnetwork;
|
||||
extern int maxtimeouts;
|
||||
int settimeouts(int timeoutpacket, int timeoutnetwork, int maxtimeouts);
|
||||
|
||||
extern uint16_t segsize;
|
||||
extern uint16_t pktsize;
|
||||
|
||||
extern int acting_as_client;
|
||||
|
||||
/*
|
||||
*/
|
||||
void unmappedaddr(struct sockaddr_in6 *sin6);
|
||||
ssize_t get_field(int peer, char *buffer, ssize_t size);
|
||||
|
||||
/*
|
||||
* Packet types
|
||||
*/
|
||||
struct packettypes {
|
||||
int value;
|
||||
char *name;
|
||||
};
|
||||
extern struct packettypes packettypes[];
|
||||
char *packettype(int);
|
||||
|
||||
/*
|
||||
* RP_
|
||||
*/
|
||||
struct rp_errors {
|
||||
int error;
|
||||
char *desc;
|
||||
};
|
||||
extern struct rp_errors rp_errors[];
|
||||
char *rp_strerror(int error);
|
||||
|
||||
/*
|
||||
* Debug features
|
||||
*/
|
||||
#define DEBUG_NONE 0x0000
|
||||
#define DEBUG_PACKETS 0x0001
|
||||
#define DEBUG_SIMPLE 0x0002
|
||||
#define DEBUG_OPTIONS 0x0004
|
||||
#define DEBUG_ACCESS 0x0008
|
||||
struct debugs {
|
||||
int value;
|
||||
char *name;
|
||||
char *desc;
|
||||
};
|
||||
extern int debug;
|
||||
extern struct debugs debugs[];
|
||||
extern int packetdroppercentage;
|
||||
int debug_find(char *s);
|
||||
int debug_finds(char *s);
|
||||
char *debug_show(int d);
|
||||
|
||||
/*
|
||||
* Log routines
|
||||
*/
|
||||
#define DEBUG(s) tftp_log(LOG_DEBUG, "%s", s)
|
||||
extern int tftp_logtostdout;
|
||||
void tftp_openlog(const char *ident, int logopt, int facility);
|
||||
void tftp_closelog(void);
|
||||
void tftp_log(int priority, const char *message, ...);
|
||||
|
||||
/*
|
||||
* Performance figures
|
||||
*/
|
||||
struct tftp_stats {
|
||||
size_t amount;
|
||||
int rollovers;
|
||||
uint32_t blocks;
|
||||
int retries;
|
||||
struct timeval tstart;
|
||||
struct timeval tstop;
|
||||
};
|
||||
|
||||
void stats_init(struct tftp_stats *ts);
|
||||
void printstats(const char *direction, int verbose, struct tftp_stats *ts);
|
Loading…
Reference in New Issue
Block a user