DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode, const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv) { struct mbuf *m = NULL; PTFTPIPHDR pTftpIpHeader = NULL; LogFlowFunc(("ENTER: errorcode: %RX16, msg: %s\n", errorcode, msg)); m = slirpTftpMbufAlloc(pData); if (!m) { LogFlowFunc(("LEAVE: Can't allocate mbuf\n")); return -1; } m->m_data += if_maxlinkhdr; m->m_len = sizeof(TFTPIPHDR) + strlen(msg) + 1; /* ending zero */ m->m_pkthdr.header = mtod(m, void *); pTftpIpHeader = mtod(m, PTFTPIPHDR); pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_ERROR); pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(errorcode); m_copyback(pData, m, sizeof(TFTPIPHDR), strlen(msg) + 1 /* copy ending zerro*/, (c_caddr_t)msg); tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv); tftpSessionTerminate(pTftpSession); LogFlowFuncLeave(); return 0; }
static int tftpSendData(PNATState pData, PTFTPSESSION pTftpSession, uint16_t u16Block, PCTFTPIPHDR pcTftpIpHeaderRecv) { struct mbuf *m; PTFTPIPHDR pTftpIpHeader; int cbRead = 0; int rc = VINF_SUCCESS; if (u16Block == pTftpSession->cTftpAck) pTftpSession->cTftpAck++; else { tftpSendError(pData, pTftpSession, 6, "ACK is wrong", pcTftpIpHeaderRecv); tftpSessionTerminate(pTftpSession); return -1; } m = slirpTftpMbufAlloc(pData); if (!m) return -1; m->m_data += if_maxlinkhdr; m->m_pkthdr.header = mtod(m, void *); pTftpIpHeader = mtod(m, PTFTPIPHDR); m->m_len = sizeof(TFTPIPHDR); pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_DATA); pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(pTftpSession->cTftpAck); rc = tftpReadDataBlock(pData, pTftpSession, (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t), &cbRead); if (RT_SUCCESS(rc)) { pTftpSession->cbTransfered += cbRead; m->m_len += cbRead; tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv); if (cbRead > 0) tftpSessionUpdate(pData, pTftpSession); else tftpSessionTerminate(pTftpSession); } else { m_freem(pData, m); tftpSendError(pData, pTftpSession, 1, "File not found", pcTftpIpHeaderRecv); /* send "file not found" error back */ return -1; } return 0; }
// tftpReSync() // // Attempt to ReSync a failed transfer // int tftpReSyncLocal( TFTP *pTftp, int dir ) { int rc = 0; //printf("in resynch, returning\n"); //return 0; // Fluch pending input packets tftpFlushPackets( pTftp ); // Abort if too many Sync errors pTftp->MaxSyncError--; if( pTftp->MaxSyncError == 0 ) { rc = TFTPERROR_FAILED; goto ABORT; // Too Many Sync Errors } // Back up expected block if (dir==0) pTftp->NextBlock--; // Resend last packet - if we're on block ZERO, resend the initial // request. if( !pTftp->NextBlock ) tftpXRQBuild( pTftp, dir ); else if (dir==0) tftpACKBuild( pTftp ); else /* was last block of file ack'd ?*/ if( pTftp->BufferUsed <0 ) // last packet did not make it there //tftpDATABuild(pTftp, &pTftp->Buffer[(pTftp->NextBlock-1)*SEGSIZE], pTftp->Length+SEGSIZE); tftpDATABuild(pTftp, &pTftp->Buffer[pTftp->BufferSize-pTftp->BufferUsed-SEGSIZE], pTftp->Length+SEGSIZE); else //tftpDATABuild(pTftp, &pTftp->Buffer[(pTftp->NextBlock-1)*SEGSIZE], SEGSIZE ); tftpDATABuild(pTftp, &pTftp->Buffer[pTftp->BufferSize-pTftp->BufferUsed-SEGSIZE], SEGSIZE ); // Send the packet rc = tftpSend( pTftp ); if( rc < 0 ) goto ABORT; if (dir==0) pTftp->NextBlock++; // Increment next expected BLOCK //pTftp->NextBlock++; // Increment next expected BLOCK ABORT: return(rc); }
// // tftpPutFile() // // Called to sent a file using TFTP. Called with TFTP parameter // structure initialized. // // Returns: // 1 - If file was sucessfully transferred // // <0 - Error // static int tftpPutFile( TFTP *pTftp ) { int rc = 0; // Build the request packet tftpXRQBuild( pTftp , 1 /* 1==write */); // Send the request packet rc = tftpSend( pTftp ); if( rc < 0 ) goto ABORT; // // Now look for ACKs and then send data // pTftp->MaxSyncError = MAX_SYNC_TRIES; // set sync error counter pTftp->NextBlock = 0; // first ACK block expected is "0" for(;;) { //printf("tftpPutFile() - reading response\n"); // Try and get a reply packet rc = tftpReadPacket( pTftp ); if( rc < 0 ) { printf("tftpPutFile: abort from readPacket\n"); goto ABORT; } // Process the reply packet //printf("tftpPutFile: processing rcv pkt\n"); rc = tftpProcessPacket( pTftp, 1 ); if( rc < 0 ){ printf(" .. abort from tftpProcessPkt\n"); goto ABORT; } // If done, break out of loop if( rc == 1 ) break; } rc = 1; ABORT: return(rc); }
// // tftpGetFile() // // Called to receive a file using TFTP. Called with TFTP parameter // structure initialized. // // Returns: // 1 - If file was sucessfully transferred // 0 - If the file was transferred but too large for the buffer // <0 - Error // static int tftpGetFile( TFTP *pTftp ) { int rc = 0; // Build the request packet tftpXRQBuild( pTftp, 0 ); // Send the request packet rc = tftpSend( pTftp ); if( rc < 0 ) goto ABORT; // // Now look for response packets // pTftp->MaxSyncError = MAX_SYNC_TRIES; // set sync error counter pTftp->NextBlock = 1; // first block expected is "1" for(;;) { // Try and get a reply packet rc = tftpReadPacket( pTftp ); if( rc < 0 ) goto ABORT; // Process the reply packet rc = tftpProcessPacket( pTftp, 0 ); if( rc < 0 ) goto ABORT; // If done, break out of loop if( rc == 1 ) break; } // If the receive buffer was too small, return 0, else return 1 if( pTftp->BufferUsed < pTftp->FileSize ) rc = 0; else rc = 1; ABORT: return(rc); }
DECLINLINE(int) tftpSendOACK(PNATState pData, PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeaderRecv) { struct mbuf *m; PTFTPIPHDR pTftpIpHeader; int rc = VINF_SUCCESS; rc = tftpSessionEvaluateOptions(pData, pTftpSession); if (RT_FAILURE(rc)) { tftpSendError(pData, pTftpSession, 2, "Internal Error (blksize evaluation)", pcTftpIpHeaderRecv); LogFlowFuncLeave(); return -1; } m = slirpTftpMbufAlloc(pData); if (!m) return -1; m->m_data += if_maxlinkhdr; m->m_pkthdr.header = mtod(m, void *); pTftpIpHeader = mtod(m, PTFTPIPHDR); m->m_len = sizeof(TFTPIPHDR) - sizeof(uint16_t); /* no u16TftpOpCode */ pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_OACK); if (pTftpSession->OptionBlkSize.fRequested) { if (pTftpSession->OptionBlkSize.u64Value > UINT16_MAX) rc = VERR_INVALID_PARAMETER; else rc = tftpAddOptionToOACK(pData, m, "blksize", pTftpSession->OptionBlkSize.u64Value); } if ( RT_SUCCESS(rc) && pTftpSession->OptionTSize.fRequested) rc = tftpAddOptionToOACK(pData, m, "tsize", pTftpSession->OptionTSize.u64Value); rc = tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv); return RT_SUCCESS(rc) ? 0 : -1; }
// tftpProcessPacket() // // Processes a data packet obtained from ReadPacket() // // Returns: // 1 - Operation Complete // 0 - Operation Progressing // <0 - Error Condition // static int tftpProcessPacket( TFTP *pTftp, int dir /* 0= reading, 1=writing */ ) { int rc = 0, done = 0; UINT16 OpCode; UINT16 ServerBlock; UINT32 CopySize; struct tftphdr *ReadBuffer; char * dataPtr; ReadBuffer = (struct tftphdr *)pTftp->PacketBuffer; // Check for a bad packet - resynch on error if( !pTftp->Length ) return( tftpReSyncLocal( pTftp, dir ) ); OpCode = (UINT16) ntohs(ReadBuffer->opcode); //printf("processpacket = opcode = %d\n",OpCode); switch (OpCode) { case ERROR : // Copy the error code pTftp->ErrorCode = (UINT16) ntohs(ReadBuffer->block); // If the buffer is large enough, copy the error message pTftp->Length -= TFTP_HEADER; // Get payload length // Copy file data if room left in buffer is large enough if( pTftp->BufferSize >= pTftp->Length ) { bcopy( ReadBuffer->data, pTftp->Buffer, (int)pTftp->Length ); pTftp->BufferUsed = pTftp->Length; } else pTftp->BufferUsed = 0; rc = TFTPERROR_ERRORREPLY; break; case ACK: ServerBlock = (UINT16) ntohs(ReadBuffer->block); //printf("** ACK %d recv\n",ServerBlock); if (pTftp->NextBlock != ServerBlock) { //printf("write resynch error %d %d\n",pTftp->NextBlock, ServerBlock); rc = tftpReSyncLocal( pTftp, 1 ); //pTftp->Length = 0; break; } // If this is the first block, reset our port number.?? if( pTftp->NextBlock == 0) { pTftp->peeraddr.sin_port = pTftp->tmpaddr.sin_port; // Update TID //printf(" new dest port = %d %x\n", pTftp->tmpaddr.sin_port,pTftp->tmpaddr.sin_port); } // ACK matches what we sent pTftp->MaxSyncError = MAX_SYNC_TRIES; // reset Sync Counter /* was last block of file ack'd ?*/ if( pTftp->BufferUsed <0 ) //if( pTftp->BufferUsed <=0 ) { done = 1; //printf("** done = 1, should be exiting\n"); break; } dataPtr = &pTftp->Buffer[pTftp->BufferSize-pTftp->BufferUsed]; /*not last block, see where we are in file to send */ pTftp->Length = SEGSIZE; if (pTftp->BufferUsed< SEGSIZE) { pTftp->Length = pTftp->BufferUsed; pTftp->BufferUsed-=SEGSIZE; //pTftp->BufferUsed = 0; } else { pTftp->BufferUsed-=pTftp->Length; } //dataPtr = &pTftp->Buffer[pTftp->NextBlock*SEGSIZE]; CopySize = pTftp->Length; // Increment next BLOCK pTftp->NextBlock++; //printf("** sending next block: len=%d, bu=%d, dp=%x, CS=%d, blk=%d\n", pTftp->Length, pTftp->BufferUsed, dataPtr, CopySize, // pTftp->NextBlock); //send data block tftpDATABuild( pTftp ,dataPtr, CopySize); rc = tftpSend( pTftp ); if( rc < 0 ) { /*printf("process ack, rc<0 from tftpSend (data)\n");*/ break;} // Our done flag is the return code on success rc = done; break; case DATA : // Received Data, verify BLOCK correct ServerBlock = (UINT16) ntohs(ReadBuffer->block); // If this is not the block we're expecting, resync if (pTftp->NextBlock != ServerBlock) { rc = tftpReSyncLocal( pTftp , 0); pTftp->Length = 0; break; } // If this is the first block, reset our port number. if( pTftp->NextBlock == 1 ) pTftp->peeraddr.sin_port = pTftp->tmpaddr.sin_port; // Update TID // Block is for me! pTftp->MaxSyncError = MAX_SYNC_TRIES; // reset Sync Counter pTftp->Length -= TFTP_HEADER; // Get payload length CopySize = pTftp->Length; // Copy length pTftp->FileSize += CopySize; // Track the file length // Copy file data if room left in buffer is large enough if( pTftp->BufferSize > pTftp->BufferUsed ) { if( (pTftp->BufferSize - pTftp->BufferUsed) < CopySize) CopySize = pTftp->BufferSize - pTftp->BufferUsed; if( CopySize ) { // Add it to our receive buffer bcopy( ReadBuffer->data, (pTftp->Buffer+pTftp->BufferUsed), (int)CopySize ); // Track the number of bytes used pTftp->BufferUsed += CopySize; } } // If we received a partial block, we're done if( pTftp->Length < SEGSIZE ) done = 1; // Need to acknowledge this block tftpACKBuild( pTftp ); rc = tftpSend( pTftp ); if( rc < 0 ) break; // Increment next expected BLOCK pTftp->NextBlock++; // Our done flag is the return code on success rc = done; break; default: rc = TFTPERROR_FAILED; break; } return(done==1 ? 1: rc); }