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