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