1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 /* 41 * Simple minded read-ahead/write-behind subroutines for tftp user and 42 * server. Written originally with multiple buffers in mind, but current 43 * implementation has two buffer logic wired in. 44 * 45 * Todo: add some sort of final error check so when the write-buffer 46 * is finally flushed, the caller can detect if the disk filled up 47 * (or had an i/o error) and return a nak to the other side. 48 */ 49 50 #include <sys/types.h> 51 #include <sys/socket.h> 52 #include <sys/ioctl.h> 53 #include <sys/filio.h> 54 55 #include <netinet/in.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <unistd.h> 59 #include <signal.h> 60 #include <errno.h> 61 #include <poll.h> 62 #include <string.h> 63 64 #include "tftpcommon.h" 65 66 struct errmsg errmsgs[] = { 67 { EUNDEF, "Undefined error code" }, 68 { ENOTFOUND, "File not found" }, 69 { EACCESS, "Access violation" }, 70 { ENOSPACE, "Disk full or allocation exceeded" }, 71 { EBADOP, "Illegal TFTP operation" }, 72 { EBADID, "Unknown transfer ID" }, 73 { EEXISTS, "File already exists" }, 74 { ENOUSER, "No such user" }, 75 { EOPTNEG, "Option negotiation error" }, 76 { -1, NULL } 77 }; 78 79 static struct bf { 80 int counter; /* size of data in buffer, or flag */ 81 tftpbuf buf; /* room for data packet */ 82 } bfs[2]; 83 84 extern int blocksize; /* Number of data bytes in a DATA packet */ 85 /* Values for bf.counter */ 86 #define BF_ALLOC -3 /* alloc'd but not yet filled */ 87 #define BF_FREE -2 /* free */ 88 /* [-1 .. blocksize] = size of data in the data buffer */ 89 90 static int nextone; /* index of next buffer to use */ 91 static int current; /* index of buffer in use */ 92 93 /* control flags for crlf conversions */ 94 static int newline = 0; /* fillbuf: in middle of newline expansion */ 95 static int prevchar = -1; /* putbuf: previous char (cr check) */ 96 97 static struct tftphdr *rw_init(int); 98 99 struct tftphdr *w_init() { return (rw_init(0)); } /* write-behind */ 100 struct tftphdr *r_init() { return (rw_init(1)); } /* read-ahead */ 101 102 /* 103 * Init for either read-ahead or write-behind. 104 * x is zero for write-behind, one for read-head. 105 */ 106 static struct tftphdr * 107 rw_init(int x) 108 { 109 newline = 0; /* init crlf flag */ 110 prevchar = -1; 111 bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ 112 current = 0; 113 bfs[1].counter = BF_FREE; 114 nextone = x; /* ahead or behind? */ 115 return (&bfs[0].buf.tb_hdr); 116 } 117 118 119 /* 120 * Have emptied current buffer by sending to net and getting ack. 121 * Free it and return next buffer filled with data. 122 */ 123 int 124 readit(FILE *file, struct tftphdr **dpp, int convert) 125 { 126 struct bf *b; 127 128 bfs[current].counter = BF_FREE; /* free old one */ 129 current = !current; /* "incr" current */ 130 131 b = &bfs[current]; /* look at new buffer */ 132 if (b->counter == BF_FREE) /* if it's empty */ 133 read_ahead(file, convert); /* fill it */ 134 *dpp = &b->buf.tb_hdr; /* set caller's ptr */ 135 return (b->counter); 136 } 137 138 /* 139 * fill the input buffer, doing ascii conversions if requested 140 * conversions are lf -> cr,lf and cr -> cr, nul 141 */ 142 void 143 read_ahead(FILE *file, int convert) 144 { 145 int i; 146 char *p; 147 int c; 148 struct bf *b; 149 struct tftphdr *dp; 150 151 b = &bfs[nextone]; /* look at "next" buffer */ 152 if (b->counter != BF_FREE) /* nop if not free */ 153 return; 154 nextone = !nextone; /* "incr" next buffer ptr */ 155 156 dp = &b->buf.tb_hdr; 157 158 if (!convert) { 159 b->counter = fread(dp->th_data, sizeof (char), blocksize, 160 file); 161 if (ferror(file)) 162 b->counter = -1; 163 return; 164 } 165 166 p = dp->th_data; 167 for (i = 0; i < blocksize; i++) { 168 if (newline) { 169 if (prevchar == '\n') 170 c = '\n'; /* lf to cr,lf */ 171 else c = '\0'; /* cr to cr,nul */ 172 newline = 0; 173 } else { 174 c = getc(file); 175 if (c == EOF) break; 176 if (c == '\n' || c == '\r') { 177 prevchar = c; 178 c = '\r'; 179 newline = 1; 180 } 181 } 182 *p++ = c; 183 } 184 b->counter = (int)(p - dp->th_data); 185 } 186 187 /* 188 * Update count associated with the buffer, get new buffer 189 * from the queue. Calls write_behind only if next buffer not 190 * available. 191 */ 192 int 193 writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) 194 { 195 bfs[current].counter = ct; /* set size of data to write */ 196 current = !current; /* switch to other buffer */ 197 if (bfs[current].counter != BF_FREE) /* if not free */ 198 if (write_behind(file, convert) < 0) /* flush it */ 199 ct = -1; 200 bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ 201 *dpp = &bfs[current].buf.tb_hdr; 202 return (ct); /* this is a lie of course */ 203 } 204 205 /* 206 * Output a buffer to a file, converting from netascii if requested. 207 * CR,NUL -> CR and CR,LF => LF. 208 * Note spec is undefined if we get CR as last byte of file or a 209 * CR followed by anything else. In this case we leave it alone. 210 */ 211 int 212 write_behind(FILE *file, int convert) 213 { 214 char *buf; 215 int count; 216 int ct; 217 char *p; 218 int c; /* current character */ 219 struct bf *b; 220 struct tftphdr *dp; 221 222 b = &bfs[nextone]; 223 if (b->counter < -1) /* anything to flush? */ 224 return (0); /* just nop if nothing to do */ 225 226 count = b->counter; /* remember byte count */ 227 b->counter = BF_FREE; /* reset flag */ 228 dp = &b->buf.tb_hdr; 229 nextone = !nextone; /* incr for next time */ 230 buf = dp->th_data; 231 232 if (count <= 0) 233 return (0); /* nak logic? */ 234 235 if (!convert) { 236 size_t left = count; 237 238 while (left > 0) { 239 size_t written; 240 241 written = fwrite(buf, sizeof (char), left, file); 242 if (ferror(file)) { 243 /* Retry if we were interrupted by a signal. */ 244 if (errno == EINTR) 245 continue; 246 return (-1); 247 } 248 if (written == 0) 249 return (-1); 250 251 left -= written; 252 buf += written; 253 } 254 255 return (count); 256 } 257 258 p = buf; 259 ct = count; 260 while (ct--) { /* loop over the buffer */ 261 c = *p++; /* pick up a character */ 262 if (prevchar == '\r') { /* if prev char was cr */ 263 if (c == '\n') { /* if have cr,lf then just */ 264 /* smash lf on top of the cr */ 265 if (fseek(file, -1, SEEK_CUR) < 0) 266 return (-1); 267 } else { 268 if (c == '\0') { 269 /* 270 * If we have cr,nul then 271 * just skip over the putc. 272 */ 273 prevchar = 0; 274 continue; 275 } 276 } 277 /* else just fall through and allow it */ 278 } 279 if (putc(c, file) == EOF) 280 return (-1); 281 prevchar = c; 282 } 283 return (count); 284 } 285 286 287 /* 288 * When an error has occurred, it is possible that the two sides 289 * are out of synch. Ie: that what I think is the other side's 290 * response to packet N is really their response to packet N-1. 291 * 292 * So, to try to prevent that, we flush all the input queued up 293 * for us on the network connection on our host. 294 * 295 * We return the number of packets we flushed (mostly for reporting 296 * when trace is active) or -1 in case of an error. 297 */ 298 299 int 300 synchnet(int socket) 301 { 302 struct pollfd pfd; 303 int packets; 304 305 pfd.fd = socket; 306 pfd.events = POLLRDNORM; 307 for (packets = 0; ; packets++) { 308 char buf; 309 struct sockaddr_in6 from; 310 socklen_t fromlen; 311 312 if (poll(&pfd, 1, 0) <= 0) 313 break; 314 315 /* 316 * A one byte buffer is enough because recvfrom() will 317 * discard the remaining data of the packet. 318 */ 319 fromlen = sizeof (from); 320 if (recvfrom(socket, &buf, sizeof (buf), 0, 321 (struct sockaddr *)&from, &fromlen) < 0) 322 return (-1); 323 } 324 325 return (packets); 326 } 327 328 /* 329 * Return a pointer to the next field in string s, or return NULL if no 330 * terminating NUL is found for the current field before end. 331 */ 332 char * 333 next_field(const char *s, const char *end) 334 { 335 if (s < end) { 336 s = memchr(s, 0, end - s); 337 if (s != NULL) 338 return ((char *)s + 1); 339 } 340 return (NULL); 341 } 342 343 /* 344 * Print to stream options in the format option_name=option_value 345 */ 346 void 347 print_options(FILE *stream, char *opts, int len) 348 { 349 char *cp, *optname, *optval; 350 char *endopts = opts + len; 351 int first = 1; 352 353 /* 354 * Ignore null padding, appended by broken TFTP clients to 355 * requests which don't include options. 356 */ 357 cp = opts; 358 while ((cp < endopts) && (*cp == '\0')) 359 cp++; 360 if (cp == endopts) 361 return; 362 363 while (opts < endopts) { 364 optname = opts; 365 if ((optval = next_field(optname, endopts)) == NULL) { 366 (void) putc('?', stream); 367 return; 368 } 369 if (first) 370 first = 0; 371 else 372 (void) putc(' ', stream); 373 (void) fputs(optname, stream); 374 if ((opts = next_field(optval, endopts)) == NULL) { 375 (void) putc('?', stream); 376 return; 377 } 378 (void) fprintf(stream, "=%s", optval); 379 } 380 } 381 382 /* 383 * Turn off the alarm timer and ensure any pending SIGALRM signal is ignored. 384 */ 385 void 386 cancel_alarm(void) 387 { 388 (void) alarm(0); 389 (void) signal(SIGALRM, SIG_IGN); 390 (void) sigrelse(SIGALRM); 391 } 392