?? tftputil.c
字號:
/* tftputil.c
Copyright 1998-1999 by InterNiche Technologies, Inc. All rights reserved.
Copyright 1995 by NetPort Software.
Copyright 1986 by Carnegie Mellon
Copyright 1984 by the Massachusetts Institute of Technology
*/
#include "tftpport.h"
#include "tftp.h"
/* internal routine */
static void check_state(struct tfconn * cn);
struct tfconn * tftp_conns = NULL; /* list of active connections */
int ntftps = 0; /* number of connections */
/* Setup a TFTP connection block. */
struct tfconn *
tfmkcn(void)
{
struct tfconn * cn;
cn = (struct tfconn *)TFC_ALLOC(sizeof(struct tfconn));
if(!cn)
return NULL;
ntftps++;
cn->next = tftp_conns; /* add this tfconn to list */
tftp_conns = cn;
cn->tf_srtt = cn->tf_rt = TPS/2; /* init round trip timer with large-ish guess */
if (cn->tf_rt > TF_MAXTMO)
cn->tf_rt = TF_MAXTMO;
if (cn->tf_rt < TF_MINTMO)
cn->tf_rt = TF_MINTMO;
cn->tf_NR_last = 1; /* fake last packet Number retrys */
return cn;
}
/* Cleanup routine called when done */
void
tfcleanup(struct tfconn *cn)
{
struct tfconn * tmp, * last;
/* unlink cn from list */
tmp = tftp_conns;
last = NULL;
while(tmp) /* traverse list, looking of cn to free */
{
if(tmp == cn)
{
if(last) /* unlink from list */
last->next = tmp->next;
else /* was first in list, fix list pointer */
tftp_conns = tmp->next;
break;
}
last = tmp;
tmp = tmp->next;
}
if(!tmp) /* connection not in master list? */
{
dtrap("tftputil 1\n"); /* serious prog error..... */
}
if(cn->tf_fd != NULL)
vfclose(cn->tf_fd);
if(cn->tf_outbuf.data)
tftp_udpfree(cn);
if(cn->tf_conn != 0)
tftp_udpclose(cn->tf_conn);
TFC_FREE(cn);
ntftps--;
}
void
tftptmo(struct tfconn * cn)
{
TFTPBUF p;
cn->tf_tmo++;
/* if we have more tries... */
if((cn->tf_tries == 0) || (--cn->tf_tries))
{
int e;
cn->tf_rsnd++;
cn->tf_NR++;
/* do the actual retry. Don't use tf_write() since it will re-init
the round trip & retry count values. */
p = &cn->tf_outbuf;
e = tftp_udpsend(cn, p->data, cn->tf_lastlen);
if(e < 0)
{
if (cn->callback)
{
if (e == ENP_NOBUFFER)
cn->callback(TFC_BUFFER, cn, "UDP alloc failed");
else
cn->callback(TFC_UDPSEND, cn, "UDP send failed");
}
tfkill(cn); /* UDP send error, kill tftp session */
}
else /* udp retry packet sent OK */
{
cn->tf_rt <<= 1; /* double round trip est. */
if (cn->tf_rt > TF_MAXTMO)
cn->tf_rt = TF_MAXTMO;
cn->tf_tick = cticks + cn->tf_rt; /* set time to do next retry */
}
}
else
{
cn->tf_state = TIMEOUT;
}
}
/* tf_good() - called to do timing calcs after good receives */
void
tf_good(struct tfconn * cn)
{
unsigned long trt;
trt = cticks - cn->tf_sent; /* Measured round trip time */
/* set smoothed round trip time based on measured */
if((cn->tf_srtt > trt) && (cn->tf_srtt > 1))
cn->tf_srtt--;
else if((cn->tf_srtt) < trt)
cn->tf_srtt++;
/* set the retry time: experimental algorithm: */
trt = trt + cn->tf_srtt + 1;
if (trt < TF_MINTMO)
cn->tf_rt = TF_MINTMO;
else if (trt > TF_MAXTMO)
cn->tf_rt = TF_MAXTMO;
else
cn->tf_rt = trt;
cn->tf_NR_last = cn->tf_NR;
cn->tf_NR = 0;
}
/* tfdodata() - Process a data packet received for TFTP connection cn.
Handle out of sequence blocks and check on block length. If a block
is way to short (len < tftp header size) send back an error message
and abort the transfer. If the block is less than 512 bytes long, shut
down the transfer; we're done. Otherwise, just write it to disk (if necessary).
Returns TRUE if OK, FALSE if error.
*/
int /* BOOL */
tfdodata(struct tfconn * cn,
TFTPBUF p,
unsigned len)
{
char * data;
struct tfdata *ptfdat;
if(len < 4)
{
tfsnderr(cn, ERRTXT, "Bad len (too short)");
if (cn->callback)
cn->callback(TFC_BADLEN, cn, "short data from peer");
tfkill(cn);
return FALSE;
}
if(cn->tf_state != DATAWAIT)
{
tfsnderr(cn, ERRTXT, "Rcvd unexpected data block");
return FALSE;
}
/* point to tftp header at front of */
ptfdat = (struct tfdata *) p->data;
len -= 4; /* subtract length of header from data */
if(htons(ptfdat->tf_block) != cn->tf_expected)
{
/* We got a retransmission of a packet we have already tried to
ACK. If we retransmit the ACK, and the old ACK finally gets through also,
our correspondent will resend the next data block, and we will do the same
thing for it, on through to the end of the file. So we shouldn't retransmit
the ACK until we are convinced that the first one was actually lost. We will
become convinced if our own timeout waiting for the next data packet
expires. */
cn->tf_ous++;
return FALSE;
}
tf_good(cn); /* adjust timer values */
cn->tf_size += len;
cn->tf_flen = len;
/* write UDP data into file */
data = ptfdat->tf_data;
if((int)vfwrite(data, 1, len, cn->tf_fd) != (int)len)
{
tf_full(cn);
return FALSE;
}
/* Send the ack & move to next state */
tfsndack(cn);
if(len == NORMLEN)
cn->tf_state = DATAWAIT;
else /* sub-normal len (less than 512) indicates end of file */
cn->tf_state = RCVLASTDATA;
cn->tf_expected++;
return TRUE;
}
/* Handle disk full condition by sending error packet and killing
this connection. */
void
tf_full(struct tfconn *cn)
{
tfsnderr(cn, DISKFULL, " ");
tfkill(cn);
if(cn->callback)
cn->callback(TFC_DISKFULL, cn, tferrors[DISKFULL] );
}
/* Send an error packet. If the code is nonzero, put it in the packet.
Otherwise, copy the text into the packet. */
char * tferrors[] =
{
"See text",
"File not found",
"Access violation",
"Disk full",
"Illegal TFTP operation",
"Unknown transfer ID",
"File already exists",
"No such user"
};
void
tfsnderr(struct tfconn * cn,
unsigned code,
char *text)
{
unsigned len;
struct tferr *perr;
len = sizeof(struct tferr)-2;
tftp_udpbuffer(cn, sizeof(struct tfack) );
if(cn->tf_outbuf.data == NULL)
return;
perr = (struct tferr *)(cn->tf_outbuf.data);
perr->tf_op = htons(TF_ERROR);
perr->tf_code = htons(code);
if(code == ERRTXT)
{
strcpy(perr->tf_err, text);
len += strlen(text)+1;
}
else
{
strcpy(perr->tf_err, tferrors[code]);
len += strlen(tferrors[code]) + 1;
}
tftp_udpsend(cn, perr, len);
tftp_udpfree(cn); /* free now, there are no retrys on these */
}
/* Process an incoming error packet */
void
tfdoerr(struct tfconn *cn, TFTPBUF p, unsigned len)
{
struct tferr * perr;
int tferror; /* tftp protocol error */
char * errtxt; /* text of/for error */
/* if connection is dead, don't worry about this... */
if(cn->tf_state == DEAD || cn->tf_state == TERMINATED)
return;
tfkill(cn);
perr = (struct tferr *) p->data; /* overlay tftp error packet struct */
tferror = htons(perr->tf_code); /* get tftp error code (see rfc) */
if(tferror > 0 && tferror < 8) /* check for known range of errors */
errtxt = tferrors[tferror]; /* get text from array if known error */
else /* not know error, try to extract text from tftp packet */
{
errtxt = perr->tf_err;
if(*errtxt <= ' ' || *errtxt & 0x80 || (strlen(errtxt) > 100)) /* make sure it's printable */
errtxt = "bogus tftp error text";
}
if(cn->callback)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -