1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 50 static FILE *file; 51 static int convert; 52 53 static char convbuffer[66000]; 54 static int gotcr = 0; 55 56 static size_t 57 convert_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 108 static size_t 109 convert_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 178 int 179 write_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 196 size_t 197 write_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 206 int 207 write_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 217 off_t 218 tell_file(void) 219 { 220 221 return ftello(file); 222 } 223 224 int 225 seek_file(off_t offset) 226 { 227 228 return fseeko(file, offset, SEEK_SET); 229 } 230 231 int 232 read_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 250 size_t 251 read_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 260 int 261 read_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 283 int 284 synchnet(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