/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (C) 2008 Edwin Groothuis. All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/types.h>28#include <sys/ioctl.h>29#include <sys/socket.h>30#include <sys/stat.h>3132#include <netinet/in.h>33#include <arpa/tftp.h>3435#include <assert.h>36#include <errno.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <syslog.h>41#include <unistd.h>4243#include "tftp-file.h"44#include "tftp-utils.h"4546static FILE *file;47static int convert;4849static char convbuffer[66000];50static int gotcr = 0;5152static size_t53convert_from_net(char *buffer, size_t count)54{55size_t i, n;5657/*58* Convert all CR/LF to LF and all CR,NUL to CR59*/6061n = 0;62for (i = 0; i < count; i++) {6364if (gotcr == 0) {65convbuffer[n++] = buffer[i];66gotcr = (buffer[i] == '\r');67continue;68}6970/* CR, NULL -> CR */71if (buffer[i] == '\0') {72gotcr = 0;73continue;74}7576/* CR, LF -> LF */77if (buffer[i] == '\n') {78if (n == 0) {79if (ftell(file) != 0) {80int r = fseek(file, -1, SEEK_END);81assert(r == 0);82convbuffer[n++] = '\n';83} else {84/* This shouldn't happen */85tftp_log(LOG_ERR,86"Received LF as first character");87abort();88}89} else90convbuffer[n-1] = '\n';91gotcr = 0;92continue;93}9495/* Everything else just accept as is */96convbuffer[n++] = buffer[i];97gotcr = (buffer[i] == '\r');98continue;99}100101return fwrite(convbuffer, 1, n, file);102}103104static size_t105convert_to_net(char *buffer, size_t count, int init)106{107size_t i;108static size_t n = 0, in = 0;109static int newline = -1;110111if (init) {112newline = -1;113n = 0;114in = 0;115return 0 ;116}117118/*119* Convert all LF to CR,LF and all CR to CR,NUL120*/121i = 0;122123if (newline != -1) {124buffer[i++] = newline;125newline = -1;126}127128while (i < count) {129if (n == in) {130/* When done we're done */131if (feof(file)) break;132133/* Otherwise read another bunch */134in = fread(convbuffer, 1, count, file);135if (in == 0) break;136n = 0;137}138139/* CR -> CR,NULL */140if (convbuffer[n] == '\r') {141buffer[i++] = '\r';142buffer[i++] = '\0';143n++;144continue;145}146147/* LF -> CR,LF */148if (convbuffer[n] == '\n') {149buffer[i++] = '\r';150buffer[i++] = '\n';151n++;152continue;153}154155buffer[i++] = convbuffer[n++];156}157158if (i > count) {159/*160* Whoops... that isn't allowed (but it will happen161* when there is a CR or LF at the end of the buffer)162*/163newline = buffer[i-1];164}165166if (i < count) {167/* We are done! */168return i;169} else170return count;171172}173174int175write_init(int fd, FILE *f, const char *mode)176{177178if (f == NULL) {179file = fdopen(fd, "w");180if (file == NULL) {181int en = errno;182tftp_log(LOG_ERR, "fdopen() failed: %s",183strerror(errno));184return en;185}186} else187file = f;188convert = !strcmp(mode, "netascii");189return 0;190}191192size_t193write_file(char *buffer, int count)194{195196if (convert == 0)197return fwrite(buffer, 1, count, file);198199return convert_from_net(buffer, count);200}201202int203write_close(void)204{205206if (fclose(file) != 0) {207tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));208return 1;209}210return 0;211}212213off_t214tell_file(void)215{216217return ftello(file);218}219220int221seek_file(off_t offset)222{223224return fseeko(file, offset, SEEK_SET);225}226227int228read_init(int fd, FILE *f, const char *mode)229{230231convert_to_net(NULL, 0, 1);232if (f == NULL) {233file = fdopen(fd, "r");234if (file == NULL) {235int en = errno;236tftp_log(LOG_ERR, "fdopen() failed: %s",237strerror(errno));238return en;239}240} else241file = f;242convert = !strcmp(mode, "netascii");243return 0;244}245246size_t247read_file(char *buffer, int count)248{249250if (convert == 0)251return fread(buffer, 1, count, file);252253return convert_to_net(buffer, count, 0);254}255256int257read_close(void)258{259260if (fclose(file) != 0) {261tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));262return 1;263}264return 0;265}266267268/* When an error has occurred, it is possible that the two sides269* are out of synch. Ie: that what I think is the other side's270* response to packet N is really their response to packet N-1.271*272* So, to try to prevent that, we flush all the input queued up273* for us on the network connection on our host.274*275* We return the number of packets we flushed (mostly for reporting276* when trace is active).277*/278279int280synchnet(int peer) /* socket to flush */281{282int i, j = 0;283char rbuf[MAXPKTSIZE];284struct sockaddr_storage from;285socklen_t fromlen;286287while (1) {288(void) ioctl(peer, FIONREAD, &i);289if (i) {290j++;291fromlen = sizeof from;292(void) recvfrom(peer, rbuf, sizeof (rbuf), 0,293(struct sockaddr *)&from, &fromlen);294} else {295return(j);296}297}298}299300301