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