tftp-file.c revision 207614
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: head/libexec/tftpd/tftp-file.c 207614 2010-05-04 13:07:40Z imp $"); 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 45static FILE *file; 46static int convert; 47 48static char convbuffer[66000]; 49static int gotcr = 0; 50 51static size_t 52convert_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 102static size_t 103convert_to_net(char *buffer, size_t count, int init) 104{ 105 size_t i; 106 static size_t n = 0, read = 0; 107 static int newline = 0; 108 109 if (init) { 110 newline = 0; 111 n = 0; 112 read = 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 == read) { 128 /* When done we're done */ 129 if (feof(file)) break; 130 131 /* Otherwise read another bunch */ 132 read = fread(convbuffer, 1, count, file); 133 if (read == 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 172int 173write_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 190size_t 191write_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 200int 201write_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 211int 212read_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 230size_t 231read_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 240int 241read_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 252int 253synchnet(int peer) 254{ 255 256 return 0; 257} 258