📄 tftp.c
字号:
* up a new transfer through the server; otherwise return 0.
*/
int
tftpStartSrvrFilter(struct ether_header *ehdr,struct Udphdr *uhdr)
{
if ((TftpState == TFTPIDLE) || (TftpState == TFTPERROR) ||
(TftpState == TFTPTIMEOUT)) {
TftpTfsFname[0] = 0;
TftpRmtPort = ecs(uhdr->uh_sport);
tftpGotoState(TFTPACTIVE);
return(1);
}
/* If block is zero and the incoming WRQ request is from the same
* port as was previously recorded, then assume the ACK sent back
* to the requester was not received, and this is a WRQ that is
* being re-sent. That being the case, just send the Ack back.
*/
else if ((tftpPrevBlock == 0) && (TftpRmtPort == uhdr->uh_sport)) {
SendTFTPAck(ehdr,0);
return(0);
}
else {
printf(" (tftp busy)\n");
/* Note: the value of TftpState is not changed (final arg to
* SendTFTPErr is 0) to TFTPERROR. This is because
* we received a RRQ/WRQ request while processing a different
* TFTP transfer. We want to send the error response to the
* sender, but we don't want to stay in an error state because
* there is another valid TFTP transfer in progress.
*/
SendTFTPErr(ehdr,0,"TFTP server busy.",0);
return(0);
}
}
/* tftpTransferComplete():
* Print a message indicating that the transfer has completed.
*/
void
tftpTransferComplete(void)
{
if ((EtherVerbose & SHOW_TFTP_STATE) || (!MFLAGS_NOTFTPPRN()))
printf("TFTP transfer complete.\n");
}
/* processTFTP():
* This function handles the majority of the TFTP requests that a
* TFTP server must be able to handle. There is no real robust
* error handling, but it seems to work pretty well.
* Refer to Stevens' "UNIX Network Programming" chap 12 for details
* on TFTP.
* Note: During TFTP, promiscuity, broadcast & multicast reception
* are all turned off. This is done to speed up the file transfer.
*/
int
processTFTP(struct ether_header *ehdr,ushort size)
{
static uchar *oaddr;
struct ip *ihdr;
struct Udphdr *uhdr;
uchar *data;
int count;
ushort opcode, block, errcode;
char *comma, *tftpp, *filename, *mode, *errstring, msg[64];
if (TftpTurnedOff)
return(0);
ihdr = (struct ip *)(ehdr + 1);
uhdr = (struct Udphdr *)((char *)ihdr + IP_HLEN(ihdr));
tftpp = (char *)(uhdr + 1);
opcode = *(ushort *)tftpp;
switch (opcode) {
case ecs(TFTP_WRQ):
filename = tftpp+2;
if ((EtherVerbose & SHOW_TFTP_STATE) || (!MFLAGS_NOTFTPPRN()))
printf("TFTP rcvd WRQ: file <%s>\n", filename);
if (!tftpStartSrvrFilter(ehdr,uhdr))
return(0);
mode = filename;
while(*mode)
mode++;
mode++;
/* Destination of WRQ can be an address (0x...), environment
* variable ($...) or a TFS filename...
*/
if ((filename[0] == '$') && (getenv(&filename[1]))) {
TftpAddr = (uchar *)strtol(getenv(&filename[1]),(char **)0,0);
}
else if ((filename[0] == '0') && (filename[1] == 'x')) {
TftpAddr = (uchar *)strtol(filename,(char **)0,0);
}
else {
if (MFLAGS_NOTFTPOVW() && tfsstat(filename)) {
SendTFTPErr(ehdr,6,"File already exists.",1);
return(0);
}
TftpAddr = (uchar *)getAppRamStart();
strncpy(TftpTfsFname,filename,sizeof(TftpTfsFname)-1);
TftpTfsFname[sizeof(TftpTfsFname)-1] = 0;
}
TftpCount = -1; /* not used with WRQ, so clear it */
/* Convert mode to lower case... */
strtolower(mode);
if (!strcmp(mode,"netascii"))
TftpWrqMode = MODE_NETASCII;
else if (!strcmp(mode,"octet"))
TftpWrqMode = MODE_OCTET;
else {
SendTFTPErr(ehdr,0,"Mode not supported.",1);
TftpWrqMode = MODE_NULL;
TftpCount = -1;
return(0);
}
block = 0;
tftpPrevBlock = block;
oaddr = TftpAddr;
TftpChopCount = 0;
disableBroadcastReception();
break;
case ecs(TFTP_RRQ):
filename = tftpp+2;
if ((EtherVerbose & SHOW_TFTP_STATE) || (!MFLAGS_NOTFTPPRN()))
printf("TFTP rcvd RRQ: file <%s>\n",filename);
if (!tftpStartSrvrFilter(ehdr,uhdr))
return(0);
mode = filename;
while(*mode) mode++;
mode++;
comma = strchr(filename,',');
if (!comma) {
TFILE *tfp;
if (!strcmp(filename,".")) {
TftpLTMptr = TftpAddr = ls_to_mem();
if (TftpLTMptr)
TftpCount = strlen(TftpAddr);
else
TftpCount = 0;
}
else {
tfp = tfsstat(filename);
if (!tfp) {
SendTFTPErr(ehdr,0,"File not found",1);
TftpCount = -1;
return(0);
}
TftpAddr = (uchar *)TFS_BASE(tfp);
TftpCount = TFS_SIZE(tfp);
}
}
else {
*comma++ = 0;
if ((filename[0] == '$') && (getenv(&filename[1])))
TftpAddr = (uchar *)strtol(getenv(&filename[1]),(char **)0,0);
else
TftpAddr = (uchar *)strtol(filename,(char **)0,0);
TftpCount = strtol(comma,(char **)0,0);
}
if (strcmp(mode,"octet")) {
SendTFTPErr(ehdr,0,"Must use binary mode",1);
TftpCount = -1;
return(0);
}
block = tftpPrevBlock = 1;
disableBroadcastReception();
tftpGotoState(TFTPACTIVE);
SendTFTPData(ehdr,block,TftpAddr,TftpCount);
return(0);
case ecs(TFTP_DAT):
block = ecs(*(ushort *)(tftpp+2));
count = ecs(uhdr->uh_ulen) - (sizeof(struct Udphdr)+4);
if (EtherVerbose & SHOW_TFTP_STATE)
printf(" Rcvd TFTP_DAT (%d,blk=%d)\n",count,block);
else if (EtherVerbose & SHOW_TFTP_TICKER)
ticktock();
if (TftpState == TFTPSENTRRQ) { /* See notes in SendTFTPRRQ() */
tftpPrevBlock = 0;
if (block == 1) {
TftpRmtPort = ecs(uhdr->uh_sport);
tftpGotoState(TFTPACTIVE);
}
else {
SendTFTPErr(ehdr,0,"invalid block",1);
return(0);
}
}
/* Since we don't ACK the final TFTP_DAT from the server until after
* the file has been written, it is possible that we will receive
* a re-transmitted TFTP_DAT from the server. This is ignored by
* Sending another ACK...
*/
else if ((TftpState == TFTPIDLE) && (block == tftpPrevBlock)) {
SendTFTPAck(ehdr,block);
if (EtherVerbose & SHOW_TFTP_STATE)
printf(" (packet ignored)\n");
return(0);
}
else if (TftpState != TFTPACTIVE) {
SendTFTPErr(ehdr,0,"invalid state",1);
return(0);
}
if (ecs(uhdr->uh_sport) != TftpRmtPort) {
SendTFTPErr(ehdr,0,"invalid source port",0);
return(0);
}
if (block == tftpPrevBlock) { /* If block didn't increment, assume */
SendTFTPAck(ehdr,block); /* retry. Ack it and return here. */
return(0); /* Otherwise, if block != tftpPrevBlock+1, */
} /* return an error, and quit now. */
else if (block != tftpPrevBlock+1) {
SendTFTPErr(ehdr,0,"Unexpected block number",1);
TftpCount = -1;
return(0);
}
TftpCount += count;
oaddr = TftpAddr;
tftpPrevBlock = block;
data = (uchar *)(tftpp+4);
/* If count is less than TFTP_DATAMAX, this must be the last
* packet of the transfer, so clean up state here.
*/
if (count < TFTP_DATAMAX) {
enableBroadcastReception();
tftpGotoState(TFTPIDLE);
}
/* Copy data from enet buffer to TftpAddr location.
* If netascii mode is active, then this transfer is much
* slower...
* If not netascii, then we use s_memcpy() because it does
* a verification of each byte written and will abort as soon
* as a failure is detected.
*/
if (TftpWrqMode == MODE_NETASCII) {
int tmpcount = count;
while(tmpcount) {
if (*data == 0x0d) {
data++;
tmpcount--;
TftpChopCount++;
continue;
}
*TftpAddr = *data;
if (*TftpAddr != *data) {
sprintf(msg,"Write error at 0x%lx",(ulong)TftpAddr);
SendTFTPErr(ehdr,0,msg,1);
TftpCount = -1;
return(0);
}
TftpAddr++;
data++;
tmpcount--;
}
}
else {
#if INCLUDE_FLASH
if (InFlashSpace(TftpAddr,count)) {
SendTFTPErr(ehdr,0,"TFTP can't write directly to flash",1);
TftpCount = -1;
return(0);
}
#endif
if (s_memcpy(TftpAddr,data,count,0,0) != 0) {
sprintf(msg,"Write error at 0x%lx",(ulong)TftpAddr);
SendTFTPErr(ehdr,0,msg,1);
TftpCount = -1;
return(0);
}
TftpAddr += count;
}
/* Check for transfer complete (count < TFTP_DATAMAX)... */
if (count < TFTP_DATAMAX) {
if (TftpTfsFname[0]) {
char *fcomma, *icomma, *flags, *info;
int err;
/* If the transfer is complete and TftpTfsFname[0]
* is non-zero, then write the data to the specified
* TFS file... Note that a comma in the filename is
* used to find the start of (if any) the TFS flags
* string. A second comma, marks the info field.
*/
info = (char *)0;
flags = (char *)0;
fcomma = strchr(TftpTfsFname,',');
if (fcomma) {
icomma = strchr(fcomma+1,',');
if (icomma) {
*icomma = 0;
info = icomma+1;
}
if (tfsctrl(TFS_FATOB,(long)(fcomma+1),0) != -1) {
*fcomma = 0;
flags = fcomma+1;
}
else {
SendTFTPErr(ehdr,0,"Invalid flag spec.",1);
TftpTfsFname[0] = 0;
break;
}
}
if ((EtherVerbose & SHOW_TFTP_STATE) || (!MFLAGS_NOTFTPPRN()))
printf("TFTP adding file: '%s' to TFS.\n",TftpTfsFname);
err = tfsadd(TftpTfsFname,info,flags,
(char *)getAppRamStart(),TftpCount+1-TftpChopCount);
if (err != TFS_OKAY) {
sprintf(msg,"TFS err: %s",
(char *)tfsctrl(TFS_ERRMSG,err,0));
SendTFTPErr(ehdr,0,msg,1);
}
TftpTfsFname[0] = 0;
}
else {
int cnt;
char *addr;
/* If the transfer is complete and no file add is to
* be done, then we flush d-cache and invalidate
* i-cache across the memory space that was just
* copied to. This is necessary in case the
* binary data that was just transferred is code.
*/
cnt = TftpCount + 1;
addr = TftpAddr - cnt;
flushDcache(addr,cnt);
invalidateIcache(addr,cnt);
}
tftpTransferComplete();
}
break;
case ecs(TFTP_ACK):
block = ecs(*(ushort *)(tftpp+2));
if (TftpState != TFTPACTIVE) {
SendTFTPErr(ehdr,0,"Illegal server state for incoming TFTP_ACK",1);
return(0);
}
if (EtherVerbose & SHOW_TFTP_STATE)
printf(" Rcvd TFTP_ACK (blk#%d)\n",block);
if (block == tftpPrevBlock) {
if (TftpCount > TFTP_DATAMAX) {
TftpCount -= TFTP_DATAMAX;
TftpAddr += TFTP_DATAMAX;
SendTFTPData(ehdr,block+1,TftpAddr,TftpCount);
tftpPrevBlock++;
}
else if (TftpCount == TFTP_DATAMAX) {
TftpCount = 0;
tftpGotoState(TFTPIDLE);
SendTFTPData(ehdr,block+1,TftpAddr,0);
tftpPrevBlock++;
}
else {
TftpAddr += TftpCount;
TftpCount = 0;
tftpGotoState(TFTPIDLE);
if (TftpLTMptr) {
free(TftpLTMptr);
TftpLTMptr = (char *)0;
}
enableBroadcastReception();
tftpTransferComplete();
}
}
else if (block == tftpPrevBlock-1) {
SendTFTPData(ehdr,block+1,TftpAddr,TftpCount);
}
else {
SendTFTPErr(ehdr,0,"Blockno confused",1);
TftpCount = -1;
return(0);
}
return(0);
case ecs(TFTP_ERR):
errcode = ecs(*(ushort *)(tftpp+2));
errstring = tftpp+4;
if (EtherVerbose & SHOW_TFTP_STATE)
printf(" Rcvd TFTP_ERR #%d (%s)\n",errcode,errstring);
TftpCount = -1;
tftpGotoState(TFTPERROR);
strncpy(TftpErrString,errstring,sizeof(TftpErrString)-1);
TftpErrString[sizeof(TftpErrString)-1] = 0;
return(0);
default:
if (EtherVerbose & SHOW_TFTP_STATE)
printf(" Rcvd <%04x> unknown TFTP opcode\n", opcode);
TftpCount = -1;
return(-1);
}
SendTFTPAck(ehdr,block);
return(0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -