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/types.h> 29 #include <sys/ioctl.h> 30 #include <sys/socket.h> 31 #include <sys/stat.h> 32 33 #include <netinet/in.h> 34 #include <arpa/tftp.h> 35 36 #include <assert.h> 37 #include <errno.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <syslog.h> 42 #include <unistd.h> 43 44 #include "tftp-file.h" 45 #include "tftp-utils.h" 46 47 static FILE *file; 48 static int convert; 49 50 static char convbuffer[66000]; 51 static int gotcr = 0; 52 53 static size_t 54 convert_from_net(char *buffer, size_t count) 55 { 56 size_t i, n; 57 58 /* 59 * Convert all CR/LF to LF and all CR,NUL to CR 60 */ 61 62 n = 0; 63 for (i = 0; i < count; i++) { 64 65 if (gotcr == 0) { 66 convbuffer[n++] = buffer[i]; 67 gotcr = (buffer[i] == '\r'); 68 continue; 69 } 70 71 /* CR, NULL -> CR */ 72 if (buffer[i] == '\0') { 73 gotcr = 0; 74 continue; 75 } 76 77 /* CR, LF -> LF */ 78 if (buffer[i] == '\n') { 79 if (n == 0) { 80 if (ftell(file) != 0) { 81 int r = fseek(file, -1, SEEK_END); 82 assert(r == 0); 83 convbuffer[n++] = '\n'; 84 } else { 85 /* This shouldn't happen */ 86 tftp_log(LOG_ERR, 87 "Received LF as first character"); 88 abort(); 89 } 90 } else 91 convbuffer[n-1] = '\n'; 92 gotcr = 0; 93 continue; 94 } 95 96 /* Everything else just accept as is */ 97 convbuffer[n++] = buffer[i]; 98 gotcr = (buffer[i] == '\r'); 99 continue; 100 } 101 102 return fwrite(convbuffer, 1, n, file); 103 } 104 105 static size_t 106 convert_to_net(char *buffer, size_t count, int init) 107 { 108 size_t i; 109 static size_t n = 0, in = 0; 110 static int newline = -1; 111 112 if (init) { 113 newline = -1; 114 n = 0; 115 in = 0; 116 return 0 ; 117 } 118 119 /* 120 * Convert all LF to CR,LF and all CR to CR,NUL 121 */ 122 i = 0; 123 124 if (newline != -1) { 125 buffer[i++] = newline; 126 newline = -1; 127 } 128 129 while (i < count) { 130 if (n == in) { 131 /* When done we're done */ 132 if (feof(file)) break; 133 134 /* Otherwise read another bunch */ 135 in = fread(convbuffer, 1, count, file); 136 if (in == 0) break; 137 n = 0; 138 } 139 140 /* CR -> CR,NULL */ 141 if (convbuffer[n] == '\r') { 142 buffer[i++] = '\r'; 143 buffer[i++] = '\0'; 144 n++; 145 continue; 146 } 147 148 /* LF -> CR,LF */ 149 if (convbuffer[n] == '\n') { 150 buffer[i++] = '\r'; 151 buffer[i++] = '\n'; 152 n++; 153 continue; 154 } 155 156 buffer[i++] = convbuffer[n++]; 157 } 158 159 if (i > count) { 160 /* 161 * Whoops... that isn't allowed (but it will happen 162 * when there is a CR or LF at the end of the buffer) 163 */ 164 newline = buffer[i-1]; 165 } 166 167 if (i < count) { 168 /* We are done! */ 169 return i; 170 } else 171 return count; 172 173 } 174 175 int 176 write_init(int fd, FILE *f, const char *mode) 177 { 178 179 if (f == NULL) { 180 file = fdopen(fd, "w"); 181 if (file == NULL) { 182 int en = errno; 183 tftp_log(LOG_ERR, "fdopen() failed: %s", 184 strerror(errno)); 185 return en; 186 } 187 } else 188 file = f; 189 convert = !strcmp(mode, "netascii"); 190 return 0; 191 } 192 193 size_t 194 write_file(char *buffer, int count) 195 { 196 197 if (convert == 0) 198 return fwrite(buffer, 1, count, file); 199 200 return convert_from_net(buffer, count); 201 } 202 203 int 204 write_close(void) 205 { 206 207 if (fclose(file) != 0) { 208 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 209 return 1; 210 } 211 return 0; 212 } 213 214 off_t 215 tell_file(void) 216 { 217 218 return ftello(file); 219 } 220 221 int 222 seek_file(off_t offset) 223 { 224 225 return fseeko(file, offset, SEEK_SET); 226 } 227 228 int 229 read_init(int fd, FILE *f, const char *mode) 230 { 231 232 convert_to_net(NULL, 0, 1); 233 if (f == NULL) { 234 file = fdopen(fd, "r"); 235 if (file == NULL) { 236 int en = errno; 237 tftp_log(LOG_ERR, "fdopen() failed: %s", 238 strerror(errno)); 239 return en; 240 } 241 } else 242 file = f; 243 convert = !strcmp(mode, "netascii"); 244 return 0; 245 } 246 247 size_t 248 read_file(char *buffer, int count) 249 { 250 251 if (convert == 0) 252 return fread(buffer, 1, count, file); 253 254 return convert_to_net(buffer, count, 0); 255 } 256 257 int 258 read_close(void) 259 { 260 261 if (fclose(file) != 0) { 262 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 263 return 1; 264 } 265 return 0; 266 } 267 268 269 /* When an error has occurred, it is possible that the two sides 270 * are out of synch. Ie: that what I think is the other side's 271 * response to packet N is really their response to packet N-1. 272 * 273 * So, to try to prevent that, we flush all the input queued up 274 * for us on the network connection on our host. 275 * 276 * We return the number of packets we flushed (mostly for reporting 277 * when trace is active). 278 */ 279 280 int 281 synchnet(int peer) /* socket to flush */ 282 { 283 int i, j = 0; 284 char rbuf[MAXPKTSIZE]; 285 struct sockaddr_storage from; 286 socklen_t fromlen; 287 288 while (1) { 289 (void) ioctl(peer, FIONREAD, &i); 290 if (i) { 291 j++; 292 fromlen = sizeof from; 293 (void) recvfrom(peer, rbuf, sizeof (rbuf), 0, 294 (struct sockaddr *)&from, &fromlen); 295 } else { 296 return(j); 297 } 298 } 299 } 300