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