uchar_ptr TFTP_read ( uint_32_ptr size /* [OUT] number of bytes read, or error code */ ) { /* Body */ uint_32 sock; sockaddr_in remote_addr; uint_16 remote_size; uint_16 ack_block; uint_16 pkt_op, pkt_block; uint_32 pkt_size; uint_32 time_left; boolean expired; #if TFTP_TIMEOUT_RETRIES uint_32 retries = 0; #endif ack_block = ntohs(TFTP_config.ACK.BLOCK); TFTP_timeout_restart(&TFTP_config.TIMEOUT); for (;;) { /* Check for timeout */ time_left = TFTP_timeout_left(&TFTP_config.TIMEOUT, &expired); if (expired) { #if TFTP_TIMEOUT_RETRIES retries++; if (retries > TFTP_TIMEOUT_RETRIES) { *size = RTCSERR_TFTP_TIMEOUT; TFTP_close(); return NULL; } /* Endif */ #endif /* Retransmit the last packet */ TFTP_RESEND(); } /* Endif */ /* Wait for a packet */ sock = TFTP_WAIT(time_left); /* Timeout -- retransmit last packet */ if (sock != TFTP_config.SOCK) { continue; } /* Endif */ remote_size = sizeof(remote_addr); pkt_size = TFTP_RECV(TFTP_config.PACKET); pkt_op = ntohs(TFTP_config.PACKET.HEAD.OP); pkt_block = ntohs(TFTP_config.PACKET.HEAD.BLOCK); /* Check source address of received packet */ if (remote_addr.sin_addr.s_addr != TFTP_config.SADDR.sin_addr.s_addr) { continue; } /* Endif */ /* Validate source port */ if (ack_block && remote_addr.sin_port != TFTP_config.SADDR.sin_port) { TFTP_SEND(TFTP_config.SOCK, _tftp_error_tid, remote_addr); continue; } /* Endif */ /* Check size of received packet */ if (pkt_size < sizeof(TFTP_HEADER)) { TFTP_SEND(TFTP_config.SOCK, _tftp_error_op, remote_addr); *size = RTCSERR_TFTP_ERROR + TFTPERR_ILLEGAL_OP; TFTP_close(); return NULL; } /* Endif */ /* Check for error packet */ if (pkt_op == TFTPOP_ERROR) { *size = RTCSERR_TFTP_ERROR + pkt_block; TFTP_close(); return NULL; } /* Endif */ /* Check for data packet */ if ((pkt_op != TFTPOP_DATA) || (pkt_size > sizeof(TFTP_PACKET)) || (pkt_block < ack_block) || (pkt_block > ack_block+1)) { TFTP_SEND(TFTP_config.SOCK, _tftp_error_op, remote_addr); *size = RTCSERR_TFTP_ERROR + TFTPERR_ILLEGAL_OP; TFTP_close(); return NULL; } /* Endif */ /* Check for retransmitted packet */ if (pkt_block == ack_block) { TFTP_timeout_restart(&TFTP_config.TIMEOUT); TFTP_SEND(TFTP_config.SOCK, TFTP_config.ACK, TFTP_config.SADDR); continue; } /* Endif */ /* We have the next packet */ break; } /* Endfor */ /* Update the adaptive timeout */ TFTP_timeout_update(&TFTP_config.TIMEOUT); /* Free the original RRQ */ if (!ack_block) { TFTP_config.SADDR.sin_port = remote_addr.sin_port; _mem_free(TFTP_config.RRQ_PTR); TFTP_config.RRQ_PTR = NULL; } /* Endif */ /* ACK it */ ack_block++; htons(TFTP_config.ACK.BLOCK, ack_block); TFTP_SEND(TFTP_config.SOCK, TFTP_config.ACK, TFTP_config.SADDR); TFTP_config.LAST = (pkt_size < sizeof(TFTP_PACKET)); /* Return the data */ *size = pkt_size - sizeof(TFTP_HEADER); return TFTP_config.PACKET.DATA; } /* Endbody */
uint_32 TFTP_open ( pointer ft_data /* [IN] the address and filename of the bootimage */ ) { /* Body */ TFTP_DATA_STRUCT_PTR tftp; sockaddr_in local_addr; char_ptr str_ptr; uchar_ptr packet; uint_32 sock; uint_32 error; uint_32 fn_len, fm_len; uint_32 pkt_len; tftp = (TFTP_DATA_STRUCT_PTR) ft_data; /* Get a socket */ sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == RTCS_SOCKET_ERROR) { return RTCSERR_TFTP_SOCKET; } /* Endif */ /* Must be initialized in order for TFTP_close to be able to release socket and memory */ TFTP_config.SOCK = sock; TFTP_config.RRQ_PTR = NULL; /* Bind it */ local_addr.sin_family = AF_INET; local_addr.sin_port = 0; local_addr.sin_addr.s_addr = INADDR_ANY; error = bind(sock, &local_addr, sizeof(local_addr)); if (error != RTCS_OK) { TFTP_close(); return error; } /* Endif */ if ( (tftp->FILENAME == NULL) || (tftp->FILEMODE == NULL) ){ TFTP_close(); return TFTPERR_FILE_NOT_FOUND; } /* Endif */ /* Prepare a read request (RRQ) */ str_ptr = tftp->FILENAME; while (*str_ptr++) {}; fn_len = str_ptr - tftp->FILENAME; str_ptr = tftp->FILEMODE; while (*str_ptr++) {}; fm_len = str_ptr - tftp->FILEMODE; pkt_len = 2 + fn_len + fm_len; packet = RTCS_mem_alloc(pkt_len); if (packet == NULL) { TFTP_close(); return RTCSERR_TFTP_RRQ_ALLOC; } /* Endif */ _mem_set_type(packet, MEM_TYPE_TFTP_PACKET); TFTP_config.RRQ_PTR = packet; htons(packet, TFTPOP_RRQ); _mem_copy(tftp->FILENAME, &packet[ 2], fn_len); _mem_copy(tftp->FILEMODE, &packet[fn_len+2], fm_len); /* Send the RRQ */ TFTP_config.SOCK = sock; TFTP_config.SADDR.sin_family = AF_INET; TFTP_config.SADDR.sin_port = IPPORT_TFTP; TFTP_config.SADDR.sin_addr.s_addr = tftp->SERVER; TFTP_timeout_init(&TFTP_config.TIMEOUT); error = sendto(sock, packet, pkt_len, 0, &TFTP_config.SADDR, sizeof(TFTP_config.SADDR)); if (error != pkt_len) { TFTP_close(); return RTCSERR_TFTP_RRQ_SEND; } /* Endif */ /* Initialize the TFTP client */ TFTP_config.RRQ_PTR = packet; TFTP_config.RRQ_LEN = pkt_len; TFTP_config.LAST = FALSE; htons(TFTP_config.ACK.OP, TFTPOP_ACK); htons(TFTP_config.ACK.BLOCK, 0); return RTCS_OK; } /* Endbody */
unsigned char *TFTP_read ( uint32_t *size /* [OUT] number of bytes read, or error code */ ) { /* Body */ uint32_t sock; sockaddr_in remote_addr; uint16_t remote_size; uint16_t ack_block; uint16_t pkt_op, pkt_block; uint32_t pkt_size; uint32_t time_left; bool expired; #if TFTP_TIMEOUT_RETRIES uint32_t retries = 0; #endif ack_block = mqx_ntohs(TFTP_config.ACK.BLOCK); TFTP_timeout_restart(&TFTP_config.TIMEOUT); for (;;) { /* Check for timeout */ time_left = TFTP_timeout_left(&TFTP_config.TIMEOUT, &expired); if (expired) { #if TFTP_TIMEOUT_RETRIES retries++; if (retries > TFTP_TIMEOUT_RETRIES) { *size = RTCSERR_TFTP_TIMEOUT; TFTP_close(); return NULL; } /* Endif */ #endif /* Retransmit the last packet */ TFTP_RESEND(); } /* Endif */ /* Wait for a packet */ sock = TFTP_WAIT(time_left); /* Timeout -- retransmit last packet */ if (sock != TFTP_config.SOCK) { continue; } /* Endif */ remote_size = sizeof(remote_addr); pkt_size = TFTP_RECV(TFTP_config.PACKET); pkt_op = mqx_ntohs(TFTP_config.PACKET.HEAD.OP); pkt_block = mqx_ntohs(TFTP_config.PACKET.HEAD.BLOCK); /* Check source address of received packet */ if (remote_addr.sin_addr.s_addr != TFTP_config.SADDR.sin_addr.s_addr) { continue; } /* Endif */ /* Validate source port */ if (ack_block && remote_addr.sin_port != TFTP_config.SADDR.sin_port) { TFTP_SEND(TFTP_config.SOCK, _tftp_error_tid, remote_addr); continue; } /* Endif */ /* Check size of received packet */ if (pkt_size < sizeof(TFTP_HEADER)) { TFTP_SEND(TFTP_config.SOCK, _tftp_error_op, remote_addr); *size = RTCSERR_TFTP_ERROR + TFTPERR_ILLEGAL_OP; TFTP_close(); return NULL; } /* Endif */ /* Check for error packet */ if (pkt_op == TFTPOP_ERROR) { *size = RTCSERR_TFTP_ERROR + pkt_block; TFTP_close(); return NULL; } /* Endif */ /* Check for data packet */ if ((pkt_op != TFTPOP_DATA) || (pkt_size > sizeof(TFTP_PACKET))) { TFTP_SEND(TFTP_config.SOCK, _tftp_error_op, remote_addr); *size = RTCSERR_TFTP_ERROR + TFTPERR_ILLEGAL_OP; TFTP_close(); return NULL; } /* Endif */ /* Check for retransmitted packet */ if (pkt_block == ack_block) { TFTP_timeout_restart(&TFTP_config.TIMEOUT); TFTP_SEND(TFTP_config.SOCK, TFTP_config.ACK, TFTP_config.SADDR); continue; } /* Endif */ /* Aknowledge also packets with lower id, some servers do retransmit them until they get an ack */ if (pkt_block < ack_block) { TFTP_timeout_restart(&TFTP_config.TIMEOUT); mqx_htons(TFTP_config.ACK.BLOCK, pkt_block); TFTP_SEND(TFTP_config.SOCK, TFTP_config.ACK, TFTP_config.SADDR); mqx_htons(TFTP_config.ACK.BLOCK, ack_block); /* Restore id of last acknowledged packet */ continue; } /* Endif */ /* Drop unexpected packets */ if (pkt_block > ack_block+1) { /* Some server do send more than one packet at a time, these will eventually retransmitted */ continue; } /* We have the next packet */ break; } /* Endfor */ /* Update the adaptive timeout */ TFTP_timeout_update(&TFTP_config.TIMEOUT); /* Free the original RRQ */ if (!ack_block) { TFTP_config.SADDR.sin_port = remote_addr.sin_port; _mem_free(TFTP_config.RRQ_PTR); TFTP_config.RRQ_PTR = NULL; } /* Endif */ /* ACK it */ ack_block++; mqx_htons(TFTP_config.ACK.BLOCK, ack_block); TFTP_SEND(TFTP_config.SOCK, TFTP_config.ACK, TFTP_config.SADDR); TFTP_config.LAST = (pkt_size < sizeof(TFTP_PACKET)); /* Return the data */ *size = pkt_size - sizeof(TFTP_HEADER); return TFTP_config.PACKET.DATA; } /* Endbody */