1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD$"); 30 31#include <sys/types.h> 32#include <sys/ioctl.h> 33#include <sys/socket.h> 34#include <sys/stat.h> 35 36#include <netinet/in.h> 37#include <arpa/tftp.h> 38 39#include <assert.h> 40#include <errno.h> 41#include <stdio.h> 42#include <stdlib.h> 43#include <string.h> 44#include <syslog.h> 45#include <unistd.h> 46 47#include "tftp-file.h" 48#include "tftp-utils.h" 49 50static FILE *file; 51static int convert; 52 53static char convbuffer[66000]; 54static int gotcr = 0; 55 56static size_t 57convert_from_net(char *buffer, size_t count) 58{ 59 size_t i, n; 60 61 /* 62 * Convert all CR/LF to LF and all CR,NUL to CR 63 */ 64 65 n = 0; 66 for (i = 0; i < count; i++) { 67 68 if (gotcr == 0) { 69 convbuffer[n++] = buffer[i]; 70 gotcr = (buffer[i] == '\r'); 71 continue; 72 } 73 74 /* CR, NULL -> CR */ 75 if (buffer[i] == '\0') { 76 gotcr = 0; 77 continue; 78 } 79 80 /* CR, LF -> LF */ 81 if (buffer[i] == '\n') { 82 if (n == 0) { 83 if (ftell(file) != 0) { 84 int r = fseek(file, -1, SEEK_END); 85 assert(r == 0); 86 convbuffer[n++] = '\n'; 87 } else { 88 /* This shouldn't happen */ 89 tftp_log(LOG_ERR, 90 "Received LF as first character"); 91 abort(); 92 } 93 } else 94 convbuffer[n-1] = '\n'; 95 gotcr = 0; 96 continue; 97 } 98 99 /* Everything else just accept as is */ 100 convbuffer[n++] = buffer[i]; 101 gotcr = (buffer[i] == '\r'); 102 continue; 103 } 104 105 return fwrite(convbuffer, 1, n, file); 106} 107 108static size_t 109convert_to_net(char *buffer, size_t count, int init) 110{ 111 size_t i; 112 static size_t n = 0, in = 0; 113 static int newline = -1; 114 115 if (init) { 116 newline = -1; 117 n = 0; 118 in = 0; 119 return 0 ; 120 } 121 122 /* 123 * Convert all LF to CR,LF and all CR to CR,NUL 124 */ 125 i = 0; 126 127 if (newline != -1) { 128 buffer[i++] = newline; 129 newline = -1; 130 } 131 132 while (i < count) { 133 if (n == in) { 134 /* When done we're done */ 135 if (feof(file)) break; 136 137 /* Otherwise read another bunch */ 138 in = fread(convbuffer, 1, count, file); 139 if (in == 0) break; 140 n = 0; 141 } 142 143 /* CR -> CR,NULL */ 144 if (convbuffer[n] == '\r') { 145 buffer[i++] = '\r'; 146 buffer[i++] = '\0'; 147 n++; 148 continue; 149 } 150 151 /* LF -> CR,LF */ 152 if (convbuffer[n] == '\n') { 153 buffer[i++] = '\r'; 154 buffer[i++] = '\n'; 155 n++; 156 continue; 157 } 158 159 buffer[i++] = convbuffer[n++]; 160 } 161 162 if (i > count) { 163 /* 164 * Whoops... that isn't allowed (but it will happen 165 * when there is a CR or LF at the end of the buffer) 166 */ 167 newline = buffer[i-1]; 168 } 169 170 if (i < count) { 171 /* We are done! */ 172 return i; 173 } else 174 return count; 175 176} 177 178int 179write_init(int fd, FILE *f, const char *mode) 180{ 181 182 if (f == NULL) { 183 file = fdopen(fd, "w"); 184 if (file == NULL) { 185 int en = errno; 186 tftp_log(LOG_ERR, "fdopen() failed: %s", 187 strerror(errno)); 188 return en; 189 } 190 } else 191 file = f; 192 convert = !strcmp(mode, "netascii"); 193 return 0; 194} 195 196size_t 197write_file(char *buffer, int count) 198{ 199 200 if (convert == 0) 201 return fwrite(buffer, 1, count, file); 202 203 return convert_from_net(buffer, count); 204} 205 206int 207write_close(void) 208{ 209 210 if (fclose(file) != 0) { 211 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 212 return 1; 213 } 214 return 0; 215} 216 217off_t 218tell_file(void) 219{ 220 221 return ftello(file); 222} 223 224int 225seek_file(off_t offset) 226{ 227 228 return fseeko(file, offset, SEEK_SET); 229} 230 231int 232read_init(int fd, FILE *f, const char *mode) 233{ 234 235 convert_to_net(NULL, 0, 1); 236 if (f == NULL) { 237 file = fdopen(fd, "r"); 238 if (file == NULL) { 239 int en = errno; 240 tftp_log(LOG_ERR, "fdopen() failed: %s", 241 strerror(errno)); 242 return en; 243 } 244 } else 245 file = f; 246 convert = !strcmp(mode, "netascii"); 247 return 0; 248} 249 250size_t 251read_file(char *buffer, int count) 252{ 253 254 if (convert == 0) 255 return fread(buffer, 1, count, file); 256 257 return convert_to_net(buffer, count, 0); 258} 259 260int 261read_close(void) 262{ 263 264 if (fclose(file) != 0) { 265 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 266 return 1; 267 } 268 return 0; 269} 270 271 272/* When an error has occurred, it is possible that the two sides 273 * are out of synch. Ie: that what I think is the other side's 274 * response to packet N is really their response to packet N-1. 275 * 276 * So, to try to prevent that, we flush all the input queued up 277 * for us on the network connection on our host. 278 * 279 * We return the number of packets we flushed (mostly for reporting 280 * when trace is active). 281 */ 282 283int 284synchnet(int peer) /* socket to flush */ 285{ 286 int i, j = 0; 287 char rbuf[MAXPKTSIZE]; 288 struct sockaddr_storage from; 289 socklen_t fromlen; 290 291 while (1) { 292 (void) ioctl(peer, FIONREAD, &i); 293 if (i) { 294 j++; 295 fromlen = sizeof from; 296 (void) recvfrom(peer, rbuf, sizeof (rbuf), 0, 297 (struct sockaddr *)&from, &fromlen); 298 } else { 299 return(j); 300 } 301 } 302} 303