1207614Simp/* 2207614Simp * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 3207614Simp * 4207614Simp * Redistribution and use in source and binary forms, with or without 5207614Simp * modification, are permitted provided that the following conditions 6207614Simp * are met: 7207614Simp * 1. Redistributions of source code must retain the above copyright 8207614Simp * notice, this list of conditions and the following disclaimer. 9207614Simp * 2. Redistributions in binary form must reproduce the above copyright 10207614Simp * notice, this list of conditions and the following disclaimer in the 11207614Simp * documentation and/or other materials provided with the distribution. 12207614Simp * 13207614Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14207614Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15207614Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16207614Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17207614Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18207614Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19207614Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20207614Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21207614Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22207614Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23207614Simp * SUCH DAMAGE. 24207614Simp */ 25207614Simp 26207614Simp#include <sys/cdefs.h> 27207614Simp__FBSDID("$FreeBSD: stable/11/libexec/tftpd/tftp-file.c 339057 2018-10-01 16:04:07Z asomers $"); 28207614Simp 29207614Simp#include <sys/types.h> 30223487Srodrigc#include <sys/ioctl.h> 31223487Srodrigc#include <sys/socket.h> 32207614Simp#include <sys/stat.h> 33207614Simp 34207614Simp#include <netinet/in.h> 35207614Simp#include <arpa/tftp.h> 36207614Simp 37339051Sasomers#include <assert.h> 38207614Simp#include <errno.h> 39207614Simp#include <stdio.h> 40207614Simp#include <stdlib.h> 41207614Simp#include <string.h> 42207614Simp#include <syslog.h> 43207614Simp#include <unistd.h> 44207614Simp 45207614Simp#include "tftp-file.h" 46207614Simp#include "tftp-utils.h" 47207614Simp 48207614Simpstatic FILE *file; 49207614Simpstatic int convert; 50207614Simp 51207614Simpstatic char convbuffer[66000]; 52207614Simpstatic int gotcr = 0; 53207614Simp 54207614Simpstatic size_t 55207614Simpconvert_from_net(char *buffer, size_t count) 56207614Simp{ 57207614Simp size_t i, n; 58207614Simp 59207614Simp /* 60207614Simp * Convert all CR/LF to LF and all CR,NUL to CR 61207614Simp */ 62207614Simp 63207614Simp n = 0; 64207614Simp for (i = 0; i < count; i++) { 65207614Simp 66207614Simp if (gotcr == 0) { 67207614Simp convbuffer[n++] = buffer[i]; 68207614Simp gotcr = (buffer[i] == '\r'); 69207614Simp continue; 70207614Simp } 71207614Simp 72207614Simp /* CR, NULL -> CR */ 73207614Simp if (buffer[i] == '\0') { 74207614Simp gotcr = 0; 75207614Simp continue; 76207614Simp } 77207614Simp 78207614Simp /* CR, LF -> LF */ 79207614Simp if (buffer[i] == '\n') { 80207614Simp if (n == 0) { 81207614Simp if (ftell(file) != 0) { 82339051Sasomers int r = fseek(file, -1, SEEK_END); 83339051Sasomers assert(r == 0); 84207614Simp convbuffer[n++] = '\n'; 85207614Simp } else { 86207614Simp /* This shouldn't happen */ 87207614Simp tftp_log(LOG_ERR, 88207614Simp "Received LF as first character"); 89207614Simp abort(); 90207614Simp } 91207614Simp } else 92207614Simp convbuffer[n-1] = '\n'; 93207614Simp gotcr = 0; 94207614Simp continue; 95207614Simp } 96207614Simp 97207614Simp /* Everything else just accept as is */ 98207614Simp convbuffer[n++] = buffer[i]; 99207614Simp gotcr = (buffer[i] == '\r'); 100207614Simp continue; 101207614Simp } 102207614Simp 103207614Simp return fwrite(convbuffer, 1, n, file); 104207614Simp} 105207614Simp 106207614Simpstatic size_t 107207614Simpconvert_to_net(char *buffer, size_t count, int init) 108207614Simp{ 109207614Simp size_t i; 110213099Smarius static size_t n = 0, in = 0; 111339057Sasomers static int newline = -1; 112207614Simp 113207614Simp if (init) { 114339057Sasomers newline = -1; 115207614Simp n = 0; 116213099Smarius in = 0; 117207614Simp return 0 ; 118207614Simp } 119207614Simp 120207614Simp /* 121207614Simp * Convert all LF to CR,LF and all CR to CR,NUL 122207614Simp */ 123207614Simp i = 0; 124207614Simp 125339057Sasomers if (newline != -1) { 126207614Simp buffer[i++] = newline; 127339057Sasomers newline = -1; 128207614Simp } 129207614Simp 130207614Simp while (i < count) { 131213099Smarius if (n == in) { 132207614Simp /* When done we're done */ 133207614Simp if (feof(file)) break; 134207614Simp 135207614Simp /* Otherwise read another bunch */ 136213099Smarius in = fread(convbuffer, 1, count, file); 137213099Smarius if (in == 0) break; 138207614Simp n = 0; 139207614Simp } 140207614Simp 141207614Simp /* CR -> CR,NULL */ 142207614Simp if (convbuffer[n] == '\r') { 143207614Simp buffer[i++] = '\r'; 144207614Simp buffer[i++] = '\0'; 145207614Simp n++; 146207614Simp continue; 147207614Simp } 148207614Simp 149207614Simp /* LF -> CR,LF */ 150207614Simp if (convbuffer[n] == '\n') { 151207614Simp buffer[i++] = '\r'; 152207614Simp buffer[i++] = '\n'; 153207614Simp n++; 154207614Simp continue; 155207614Simp } 156207614Simp 157207614Simp buffer[i++] = convbuffer[n++]; 158207614Simp } 159207614Simp 160207614Simp if (i > count) { 161207614Simp /* 162339057Sasomers * Whoops... that isn't allowed (but it will happen 163207614Simp * when there is a CR or LF at the end of the buffer) 164207614Simp */ 165207614Simp newline = buffer[i-1]; 166207614Simp } 167207614Simp 168207614Simp if (i < count) { 169207614Simp /* We are done! */ 170207614Simp return i; 171207614Simp } else 172207614Simp return count; 173207614Simp 174207614Simp} 175207614Simp 176207614Simpint 177207614Simpwrite_init(int fd, FILE *f, const char *mode) 178207614Simp{ 179207614Simp 180207614Simp if (f == NULL) { 181207614Simp file = fdopen(fd, "w"); 182207614Simp if (file == NULL) { 183207614Simp int en = errno; 184207614Simp tftp_log(LOG_ERR, "fdopen() failed: %s", 185207614Simp strerror(errno)); 186207614Simp return en; 187207614Simp } 188207614Simp } else 189207614Simp file = f; 190207614Simp convert = !strcmp(mode, "netascii"); 191207614Simp return 0; 192207614Simp} 193207614Simp 194207614Simpsize_t 195207614Simpwrite_file(char *buffer, int count) 196207614Simp{ 197207614Simp 198207614Simp if (convert == 0) 199207614Simp return fwrite(buffer, 1, count, file); 200207614Simp 201207614Simp return convert_from_net(buffer, count); 202207614Simp} 203207614Simp 204207614Simpint 205207614Simpwrite_close(void) 206207614Simp{ 207207614Simp 208207614Simp if (fclose(file) != 0) { 209207614Simp tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 210207614Simp return 1; 211207614Simp } 212207614Simp return 0; 213207614Simp} 214207614Simp 215207614Simpint 216207614Simpread_init(int fd, FILE *f, const char *mode) 217207614Simp{ 218207614Simp 219207614Simp convert_to_net(NULL, 0, 1); 220207614Simp if (f == NULL) { 221207614Simp file = fdopen(fd, "r"); 222207614Simp if (file == NULL) { 223207614Simp int en = errno; 224207614Simp tftp_log(LOG_ERR, "fdopen() failed: %s", 225207614Simp strerror(errno)); 226207614Simp return en; 227207614Simp } 228207614Simp } else 229207614Simp file = f; 230207614Simp convert = !strcmp(mode, "netascii"); 231207614Simp return 0; 232207614Simp} 233207614Simp 234207614Simpsize_t 235207614Simpread_file(char *buffer, int count) 236207614Simp{ 237207614Simp 238207614Simp if (convert == 0) 239207614Simp return fread(buffer, 1, count, file); 240207614Simp 241207614Simp return convert_to_net(buffer, count, 0); 242207614Simp} 243207614Simp 244207614Simpint 245207614Simpread_close(void) 246207614Simp{ 247207614Simp 248207614Simp if (fclose(file) != 0) { 249207614Simp tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 250207614Simp return 1; 251207614Simp } 252207614Simp return 0; 253207614Simp} 254207614Simp 255207614Simp 256223487Srodrigc/* When an error has occurred, it is possible that the two sides 257223487Srodrigc * are out of synch. Ie: that what I think is the other side's 258223487Srodrigc * response to packet N is really their response to packet N-1. 259223487Srodrigc * 260223487Srodrigc * So, to try to prevent that, we flush all the input queued up 261223487Srodrigc * for us on the network connection on our host. 262223487Srodrigc * 263223487Srodrigc * We return the number of packets we flushed (mostly for reporting 264223487Srodrigc * when trace is active). 265223487Srodrigc */ 266223487Srodrigc 267207614Simpint 268223487Srodrigcsynchnet(int peer) /* socket to flush */ 269207614Simp{ 270223487Srodrigc int i, j = 0; 271223487Srodrigc char rbuf[MAXPKTSIZE]; 272223487Srodrigc struct sockaddr_storage from; 273223487Srodrigc socklen_t fromlen; 274207614Simp 275223487Srodrigc while (1) { 276223487Srodrigc (void) ioctl(peer, FIONREAD, &i); 277223487Srodrigc if (i) { 278223487Srodrigc j++; 279223487Srodrigc fromlen = sizeof from; 280223487Srodrigc (void) recvfrom(peer, rbuf, sizeof (rbuf), 0, 281223487Srodrigc (struct sockaddr *)&from, &fromlen); 282223487Srodrigc } else { 283223487Srodrigc return(j); 284223487Srodrigc } 285223487Srodrigc } 286207614Simp} 287