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/stat.h> 31 32 #include <netinet/in.h> 33 #include <arpa/tftp.h> 34 35 #include <errno.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <syslog.h> 40 #include <unistd.h> 41 42 #include "tftp-file.h" 43 #include "tftp-utils.h" 44 45 static FILE *file; 46 static int convert; 47 48 static char convbuffer[66000]; 49 static int gotcr = 0; 50 51 static size_t 52 convert_from_net(char *buffer, size_t count) 53 { 54 size_t i, n; 55 56 /* 57 * Convert all CR/LF to LF and all CR,NUL to CR 58 */ 59 60 n = 0; 61 for (i = 0; i < count; i++) { 62 63 if (gotcr == 0) { 64 convbuffer[n++] = buffer[i]; 65 gotcr = (buffer[i] == '\r'); 66 continue; 67 } 68 69 /* CR, NULL -> CR */ 70 if (buffer[i] == '\0') { 71 gotcr = 0; 72 continue; 73 } 74 75 /* CR, LF -> LF */ 76 if (buffer[i] == '\n') { 77 if (n == 0) { 78 if (ftell(file) != 0) { 79 fseek(file, -1, SEEK_END); 80 convbuffer[n++] = '\n'; 81 } else { 82 /* This shouldn't happen */ 83 tftp_log(LOG_ERR, 84 "Received LF as first character"); 85 abort(); 86 } 87 } else 88 convbuffer[n-1] = '\n'; 89 gotcr = 0; 90 continue; 91 } 92 93 /* Everything else just accept as is */ 94 convbuffer[n++] = buffer[i]; 95 gotcr = (buffer[i] == '\r'); 96 continue; 97 } 98 99 return fwrite(convbuffer, 1, n, file); 100 } 101 102 static size_t 103 convert_to_net(char *buffer, size_t count, int init) 104 { 105 size_t i; 106 static size_t n = 0, in = 0; 107 static int newline = 0; 108 109 if (init) { 110 newline = 0; 111 n = 0; 112 in = 0; 113 return 0 ; 114 } 115 116 /* 117 * Convert all LF to CR,LF and all CR to CR,NUL 118 */ 119 i = 0; 120 121 if (newline) { 122 buffer[i++] = newline; 123 newline = 0; 124 } 125 126 while (i < count) { 127 if (n == in) { 128 /* When done we're done */ 129 if (feof(file)) break; 130 131 /* Otherwise read another bunch */ 132 in = fread(convbuffer, 1, count, file); 133 if (in == 0) break; 134 n = 0; 135 } 136 137 /* CR -> CR,NULL */ 138 if (convbuffer[n] == '\r') { 139 buffer[i++] = '\r'; 140 buffer[i++] = '\0'; 141 n++; 142 continue; 143 } 144 145 /* LF -> CR,LF */ 146 if (convbuffer[n] == '\n') { 147 buffer[i++] = '\r'; 148 buffer[i++] = '\n'; 149 n++; 150 continue; 151 } 152 153 buffer[i++] = convbuffer[n++]; 154 } 155 156 if (i > count) { 157 /* 158 * Whoops... that isn't alllowed (but it will happen 159 * when there is a CR or LF at the end of the buffer) 160 */ 161 newline = buffer[i-1]; 162 } 163 164 if (i < count) { 165 /* We are done! */ 166 return i; 167 } else 168 return count; 169 170 } 171 172 int 173 write_init(int fd, FILE *f, const char *mode) 174 { 175 176 if (f == NULL) { 177 file = fdopen(fd, "w"); 178 if (file == NULL) { 179 int en = errno; 180 tftp_log(LOG_ERR, "fdopen() failed: %s", 181 strerror(errno)); 182 return en; 183 } 184 } else 185 file = f; 186 convert = !strcmp(mode, "netascii"); 187 return 0; 188 } 189 190 size_t 191 write_file(char *buffer, int count) 192 { 193 194 if (convert == 0) 195 return fwrite(buffer, 1, count, file); 196 197 return convert_from_net(buffer, count); 198 } 199 200 int 201 write_close(void) 202 { 203 204 if (fclose(file) != 0) { 205 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 206 return 1; 207 } 208 return 0; 209 } 210 211 int 212 read_init(int fd, FILE *f, const char *mode) 213 { 214 215 convert_to_net(NULL, 0, 1); 216 if (f == NULL) { 217 file = fdopen(fd, "r"); 218 if (file == NULL) { 219 int en = errno; 220 tftp_log(LOG_ERR, "fdopen() failed: %s", 221 strerror(errno)); 222 return en; 223 } 224 } else 225 file = f; 226 convert = !strcmp(mode, "netascii"); 227 return 0; 228 } 229 230 size_t 231 read_file(char *buffer, int count) 232 { 233 234 if (convert == 0) 235 return fread(buffer, 1, count, file); 236 237 return convert_to_net(buffer, count, 0); 238 } 239 240 int 241 read_close(void) 242 { 243 244 if (fclose(file) != 0) { 245 tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); 246 return 1; 247 } 248 return 0; 249 } 250 251 252 int 253 synchnet(int peer __unused) 254 { 255 256 return 0; 257 } 258