version 1.9, 2003/09/24 20:21:40 |
version 1.10, 2006/07/12 16:58:51 |
|
|
#if 0 |
#if 0 |
static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; |
static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; |
#endif |
#endif |
static const char rcsid[] = "$OpenBSD$"; |
static const char rcsid[] = |
|
"$OpenBSD$"; |
#endif /* not lint */ |
#endif /* not lint */ |
|
|
/* Simple minded read-ahead/write-behind subroutines for tftp user and |
/* |
server. Written originally with multiple buffers in mind, but current |
* Simple minded read-ahead/write-behind subroutines for tftp user and |
implementation has two buffer logic wired in. |
* server. Written originally with multiple buffers in mind, but current |
|
* implementation has two buffer logic wired in. |
Todo: add some sort of final error check so when the write-buffer |
* |
is finally flushed, the caller can detect if the disk filled up |
* Todo: add some sort of final error check so when the write-buffer |
(or had an i/o error) and return a nak to the other side. |
* is finally flushed, the caller can detect if the disk filled up |
|
* (or had an i/o error) and return a nak to the other side. |
Jim Guyton 10/85 |
* |
|
* Jim Guyton 10/85 |
*/ |
*/ |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
|
|
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <arpa/tftp.h> |
#include <arpa/tftp.h> |
|
|
|
|
|
|
#include "tftpsubs.h" |
#include "tftpsubs.h" |
|
|
#define PKTSIZE SEGSIZE+4 /* should be moved to tftp.h */ |
#define PKTSIZE SEGSIZE + 4 /* should be moved to tftp.h */ |
|
/* values for bf.counter */ |
|
#define BF_ALLOC -3 /* alloc'd but not yet filled */ |
|
#define BF_FREE -2 /* free */ |
|
/* [-1 .. SEGSIZE] = size of data in the data buffer */ |
|
|
|
static struct tftphdr *rw_init(int); |
|
|
struct bf { |
struct bf { |
int counter; /* size of data in buffer, or flag */ |
int counter; /* size of data in buffer, or flag */ |
char buf[PKTSIZE]; /* room for data packet */ |
char buf[PKTSIZE]; /* room for data packet */ |
} bfs[2]; |
} bfs[2]; |
|
|
/* Values for bf.counter */ |
static int nextone; /* index of next buffer to use */ |
#define BF_ALLOC -3 /* alloc'd but not yet filled */ |
static int current; /* index of buffer in use */ |
#define BF_FREE -2 /* free */ |
|
/* [-1 .. SEGSIZE] = size of data in the data buffer */ |
|
|
|
static int nextone; /* index of next buffer to use */ |
|
static int current; /* index of buffer in use */ |
|
|
|
/* control flags for crlf conversions */ |
/* control flags for crlf conversions */ |
int newline = 0; /* fillbuf: in middle of newline expansion */ |
int newline = 0; /* fillbuf: in middle of newline expansion */ |
int prevchar = -1; /* putbuf: previous char (cr check) */ |
int prevchar = -1; /* putbuf: previous char (cr check) */ |
|
|
static struct tftphdr *rw_init(int); |
|
|
|
struct tftphdr * |
struct tftphdr * |
w_init(void) |
w_init(void) |
{ |
{ |
return rw_init(0); /* write-behind */ |
return (rw_init(0)); /* write-behind */ |
} |
} |
|
|
struct tftphdr * |
struct tftphdr * |
r_init(void) |
r_init(void) |
{ |
{ |
return rw_init(1); /* read-ahead */ |
return (rw_init(1)); /* read-ahead */ |
} |
} |
|
|
/* init for either read-ahead or write-behind */ |
/* |
/* zero for write-behind, one for read-head */ |
* Init for either read-ahead or write-behind. |
|
* Zero for write-behind, one for read-head. |
|
*/ |
static struct tftphdr * |
static struct tftphdr * |
rw_init(int x) |
rw_init(int x) |
{ |
{ |
newline = 0; /* init crlf flag */ |
newline = 0; /* init crlf flag */ |
prevchar = -1; |
prevchar = -1; |
bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ |
bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ |
current = 0; |
current = 0; |
bfs[1].counter = BF_FREE; |
bfs[1].counter = BF_FREE; |
nextone = x; /* ahead or behind? */ |
nextone = x; /* ahead or behind? */ |
return (struct tftphdr *)bfs[0].buf; |
|
|
return ((struct tftphdr *)bfs[0].buf); |
} |
} |
|
|
|
/* |
/* Have emptied current buffer by sending to net and getting ack. |
* Have emptied current buffer by sending to net and getting ack. |
Free it and return next buffer filled with data. |
* Free it and return next buffer filled with data. |
*/ |
*/ |
int |
int |
readit(FILE *file, struct tftphdr **dpp, int convert) |
readit(FILE *file, struct tftphdr **dpp, int convert) |
{ |
{ |
struct bf *b; |
struct bf *b; |
|
|
bfs[current].counter = BF_FREE; /* free old one */ |
bfs[current].counter = BF_FREE; /* free old one */ |
current = !current; /* "incr" current */ |
current = !current; /* "incr" current */ |
|
|
b = &bfs[current]; /* look at new buffer */ |
b = &bfs[current]; /* look at new buffer */ |
if (b->counter == BF_FREE) /* if it's empty */ |
if (b->counter == BF_FREE) /* if it's empty */ |
read_ahead(file, convert); /* fill it */ |
read_ahead(file, convert); /* fill it */ |
/* assert(b->counter != BF_FREE);*//* check */ |
/* assert(b->counter != BF_FREE); */ /* check */ |
*dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ |
*dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ |
return b->counter; |
|
|
return (b->counter); |
} |
} |
|
|
/* |
/* |
* fill the input buffer, doing ascii conversions if requested |
* Fill the input buffer, doing ascii conversions if requested. |
* conversions are lf -> cr,lf and cr -> cr, nul |
* Conversions are lf -> cr, lf and cr -> cr, nul. |
*/ |
*/ |
void |
void |
read_ahead(FILE *file, int convert) |
read_ahead(FILE *file, int convert) |
{ |
{ |
int i; |
int i; |
char *p; |
char *p; |
int c; |
int c; |
struct bf *b; |
struct bf *b; |
struct tftphdr *dp; |
struct tftphdr *dp; |
|
|
b = &bfs[nextone]; /* look at "next" buffer */ |
b = &bfs[nextone]; /* look at "next" buffer */ |
if (b->counter != BF_FREE) /* nop if not free */ |
if (b->counter != BF_FREE) /* nop if not free */ |
return; |
return; |
nextone = !nextone; /* "incr" next buffer ptr */ |
nextone = !nextone; /* "incr" next buffer ptr */ |
|
|
dp = (struct tftphdr *)b->buf; |
dp = (struct tftphdr *)b->buf; |
|
|
|
|
} |
} |
|
|
p = dp->th_data; |
p = dp->th_data; |
for (i = 0 ; i < SEGSIZE; i++) { |
for (i = 0; i < SEGSIZE; i++) { |
if (newline) { |
if (newline) { |
if (prevchar == '\n') |
if (prevchar == '\n') |
c = '\n'; /* lf to cr,lf */ |
c = '\n'; /* lf to cr, lf */ |
else |
else |
c = '\0'; /* cr to cr,nul */ |
c = '\0'; /* cr to cr, nul */ |
newline = 0; |
newline = 0; |
} else { |
} else { |
c = getc(file); |
c = getc(file); |
if (c == EOF) break; |
if (c == EOF) |
|
break; |
if (c == '\n' || c == '\r') { |
if (c == '\n' || c == '\r') { |
prevchar = c; |
prevchar = c; |
c = '\r'; |
c = '\r'; |
|
|
b->counter = (int)(p - dp->th_data); |
b->counter = (int)(p - dp->th_data); |
} |
} |
|
|
/* Update count associated with the buffer, get new buffer |
/* |
from the queue. Calls write_behind only if next buffer not |
* Update count associated with the buffer, get new buffer |
available. |
* from the queue. Calls write_behind only if next buffer not |
|
* available. |
*/ |
*/ |
int |
int |
writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) |
writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) |
{ |
{ |
bfs[current].counter = ct; /* set size of data to write */ |
bfs[current].counter = ct; /* set size of data to write */ |
current = !current; /* switch to other buffer */ |
current = !current; /* switch to other buffer */ |
if (bfs[current].counter != BF_FREE) /* if not free */ |
if (bfs[current].counter != BF_FREE) /* if not free */ |
(void)write_behind(file, convert); /* flush it */ |
/* flush it */ |
bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ |
(void)write_behind(file, convert); |
|
bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ |
*dpp = (struct tftphdr *)bfs[current].buf; |
*dpp = (struct tftphdr *)bfs[current].buf; |
return ct; /* this is a lie of course */ |
|
|
return (ct); /* this is a lie of course */ |
} |
} |
|
|
/* |
/* |
* Output a buffer to a file, converting from netascii if requested. |
* Output a buffer to a file, converting from netascii if requested. |
* CR,NUL -> CR and CR,LF => LF. |
* CR, NUL -> CR and CR, LF -> LF. |
* Note spec is undefined if we get CR as last byte of file or a |
* Note spec is undefined if we get CR as last byte of file or a |
* CR followed by anything else. In this case we leave it alone. |
* CR followed by anything else. In this case we leave it alone. |
n */ |
*/ |
int |
int |
write_behind(FILE *file, int convert) |
write_behind(FILE *file, int convert) |
{ |
{ |
char *buf; |
char *buf; |
int count; |
int count; |
int ct; |
int ct; |
char *p; |
char *p; |
int c; /* current character */ |
int c; /* current character */ |
struct bf *b; |
struct bf *b; |
struct tftphdr *dp; |
struct tftphdr *dp; |
|
|
b = &bfs[nextone]; |
b = &bfs[nextone]; |
if (b->counter < -1) /* anything to flush? */ |
if (b->counter < -1) /* anything to flush? */ |
return 0; /* just nop if nothing to do */ |
return (0); /* just nop if nothing to do */ |
|
|
count = b->counter; /* remember byte count */ |
count = b->counter; /* remember byte count */ |
b->counter = BF_FREE; /* reset flag */ |
b->counter = BF_FREE; /* reset flag */ |
dp = (struct tftphdr *)b->buf; |
dp = (struct tftphdr *)b->buf; |
nextone = !nextone; /* incr for next time */ |
nextone = !nextone; /* incr for next time */ |
buf = dp->th_data; |
buf = dp->th_data; |
|
|
if (count <= 0) return -1; /* nak logic? */ |
if (count <= 0) /* nak logic? */ |
|
return (-1); |
|
|
if (convert == 0) |
if (convert == 0) |
return write(fileno(file), buf, count); |
return (write(fileno(file), buf, count)); |
|
|
p = buf; |
p = buf; |
ct = count; |
ct = count; |
while (ct--) { /* loop over the buffer */ |
while (ct--) { /* loop over the buffer */ |
c = *p++; /* pick up a character */ |
c = *p++; /* pick up a character */ |
if (prevchar == '\r') { /* if prev char was cr */ |
if (prevchar == '\r') { /* if prev char was cr */ |
if (c == '\n') /* if have cr,lf then just */ |
if (c == '\n') /* if have cr,lf then just */ |
fseek(file, -1, 1); /* smash lf on top of the cr */ |
/* smash lf on top of the cr */ |
else if (c == '\0') /* if have cr,nul then */ |
fseek(file, -1, 1); |
goto skipit; /* just skip over the putc */ |
else if (c == '\0') /* if have cr,nul then */ |
/* else just fall through and allow it */ |
goto skipit; /* just skip over the putc */ |
|
/* FALLTHROUGH */ |
} |
} |
putc(c, file); |
putc(c, file); |
skipit: |
skipit: |
prevchar = c; |
prevchar = c; |
} |
} |
return count; |
|
|
return (count); |
} |
} |
|
|
|
/* |
/* When an error has occurred, it is possible that the two sides |
* When an error has occurred, it is possible that the two sides |
* are out of synch. Ie: that what I think is the other side's |
* are out of synch. Ie: that what I think is the other side's |
* response to packet N is really their response to packet N-1. |
* response to packet N is really their response to packet N-1. |
* |
* |
|
|
* We return the number of packets we flushed (mostly for reporting |
* We return the number of packets we flushed (mostly for reporting |
* when trace is active). |
* when trace is active). |
*/ |
*/ |
|
|
int |
int |
synchnet(int f) |
synchnet(int f) |
{ |
{ |
int i, j = 0; |
int i, j = 0; |
char rbuf[PKTSIZE]; |
char rbuf[PKTSIZE]; |
struct sockaddr_in from; |
struct sockaddr_in from; |
socklen_t fromlen; |
socklen_t fromlen; |
|
|
while (1) { |
for (;;) { |
(void) ioctl(f, FIONREAD, &i); |
(void)ioctl(f, FIONREAD, &i); |
if (i) { |
if (i) { |
j++; |
j++; |
fromlen = sizeof from; |
fromlen = sizeof(from); |
(void) recvfrom(f, rbuf, sizeof (rbuf), 0, |
(void)recvfrom(f, rbuf, sizeof(rbuf), 0, |
(struct sockaddr *)&from, &fromlen); |
(struct sockaddr *)&from, &fromlen); |
} else { |
} else |
return(j); |
return (j); |
} |
|
} |
} |
} |
} |