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 <errno.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <syslog.h> 44 #include <unistd.h> 45 46 #include "tftp-file.h" 47 #include "tftp-utils.h" 48 49 static FILE *file; 50 static int convert; 51 52 static char convbuffer[66000]; 53 static int gotcr = 0; 54 55 static size_t 56 convert_from_net(char *buffer, size_t count) 57 { 58 size_t i, n; 59 60 /* 61 * Convert all CR/LF to LF and all CR,NUL to CR 62 */ 63 64 n = 0; 65 for (i = 0; i < count; i++) { 66 67 if (gotcr == 0) { 68 convbuffer[n++] = buffer[i]; 69 gotcr = (buffer[i] == '\r'); 70 continue; 71 } 72 73 /* CR, NULL -> CR */ 74 if (buffer[i] == '\0') { 75 gotcr = 0; 76 continue; 77 } 78 79 /* CR, LF -> LF */ 80 if (buffer[i] == '\n') { 81 if (n == 0) { 82 if (ftell(file) != 0) { 83 fseek(file, -1, SEEK_END); 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 = 0; 112 113 if (init) { 114 newline = 0; 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) { 126 buffer[i++] = newline; 127 newline = 0; 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 alllowed (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 int 216 read_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 234 size_t 235 read_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 244 int 245 read_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 267 int 268 synchnet(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