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 #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 48 static FILE *file; 49 static int convert; 50 51 static char convbuffer[66000]; 52 static int gotcr = 0; 53 54 static size_t 55 convert_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 106 static size_t 107 convert_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 176 int 177 write_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 194 size_t 195 write_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 204 int 205 write_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 215 off_t 216 tell_file(void) 217 { 218 219 return ftello(file); 220 } 221 222 int 223 seek_file(off_t offset) 224 { 225 226 return fseeko(file, offset, SEEK_SET); 227 } 228 229 int 230 read_init(int fd, FILE *f, const char *mode) 231 { 232 233 convert_to_net(NULL, 0, 1); 234 if (f == NULL) { 235 file = fdopen(fd, "r"); 236 if (file == NULL) { 237 int en = errno; 238 tftp_log(LOG_ERR, "fdopen() failed: %s", 239 strerror(errno)); 240 return en; 241 } 242 } else 243 file = f; 244 convert = !strcmp(mode, "netascii"); 245 return 0; 246 } 247 248 size_t 249 read_file(char *buffer, int count) 250 { 251 252 if (convert == 0) 253 return fread(buffer, 1, count, file); 254 255 return convert_to_net(buffer, count, 0); 256 } 257 258 int 259 read_close(void) 260 { 261 262 if (fclose(file) != 0) { 263 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 264 return 1; 265 } 266 return 0; 267 } 268 269 270 /* When an error has occurred, it is possible that the two sides 271 * are out of synch. Ie: that what I think is the other side's 272 * response to packet N is really their response to packet N-1. 273 * 274 * So, to try to prevent that, we flush all the input queued up 275 * for us on the network connection on our host. 276 * 277 * We return the number of packets we flushed (mostly for reporting 278 * when trace is active). 279 */ 280 281 int 282 synchnet(int peer) /* socket to flush */ 283 { 284 int i, j = 0; 285 char rbuf[MAXPKTSIZE]; 286 struct sockaddr_storage from; 287 socklen_t fromlen; 288 289 while (1) { 290 (void) ioctl(peer, FIONREAD, &i); 291 if (i) { 292 j++; 293 fromlen = sizeof from; 294 (void) recvfrom(peer, rbuf, sizeof (rbuf), 0, 295 (struct sockaddr *)&from, &fromlen); 296 } else { 297 return(j); 298 } 299 } 300 } 301