//==========================================================================
|
//==========================================================================
|
//
|
//
|
// lib/tftp_client.c
|
// lib/tftp_client.c
|
//
|
//
|
// TFTP client support
|
// TFTP client support
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// eCos is free software; you can redistribute it and/or modify it under
|
// the terms of the GNU General Public License as published by the Free
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 or (at your option) any later version.
|
// Software Foundation; either version 2 or (at your option) any later version.
|
//
|
//
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// As a special exception, if other files instantiate templates or use macros
|
// or inline functions from this file, or you compile this file and link it
|
// or inline functions from this file, or you compile this file and link it
|
// with other works to produce a work based on this file, this file does not
|
// with other works to produce a work based on this file, this file does not
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// License. However the source code for this file must still be made available
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// in accordance with section (3) of the GNU General Public License.
|
//
|
//
|
// This exception does not invalidate any other reasons why a work based on
|
// This exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): gthomas
|
// Author(s): gthomas
|
// Contributors: gthomas
|
// Contributors: gthomas
|
// Date: 2000-04-06
|
// Date: 2000-04-06
|
// Purpose:
|
// Purpose:
|
// Description:
|
// Description:
|
//
|
//
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
|
|
// TFTP client support
|
// TFTP client support
|
|
|
#include <network.h>
|
#include <network.h>
|
#include <arpa/tftp.h>
|
#include <arpa/tftp.h>
|
#include <tftp_support.h>
|
#include <tftp_support.h>
|
|
|
#define min(x,y) (x<y ? x : y)
|
#define min(x,y) (x<y ? x : y)
|
|
|
//
|
//
|
// Read a file from a host into a local buffer. Returns the
|
// Read a file from a host into a local buffer. Returns the
|
// number of bytes actually read, or (-1) if an error occurs.
|
// number of bytes actually read, or (-1) if an error occurs.
|
// On error, *err will hold the reason.
|
// On error, *err will hold the reason.
|
//
|
//
|
int
|
int
|
tftp_get(char *filename,
|
tftp_get(char *filename,
|
struct sockaddr_in *server,
|
struct sockaddr_in *server,
|
char *buf,
|
char *buf,
|
int len,
|
int len,
|
int mode,
|
int mode,
|
int *err)
|
int *err)
|
{
|
{
|
int res = 0;
|
int res = 0;
|
int s, actual_len, data_len, recv_len, from_len;
|
int s, actual_len, data_len, recv_len, from_len;
|
static int get_port = 7700;
|
static int get_port = 7700;
|
struct sockaddr_in local_addr, server_addr, from_addr;
|
struct sockaddr_in local_addr, server_addr, from_addr;
|
char data[SEGSIZE+sizeof(struct tftphdr)];
|
char data[SEGSIZE+sizeof(struct tftphdr)];
|
struct tftphdr *hdr = (struct tftphdr *)data;
|
struct tftphdr *hdr = (struct tftphdr *)data;
|
char *cp, *fp;
|
char *cp, *fp;
|
struct timeval timeout;
|
struct timeval timeout;
|
unsigned short last_good_block = 0;
|
unsigned short last_good_block = 0;
|
struct servent *server_info;
|
struct servent *server_info;
|
fd_set fds;
|
fd_set fds;
|
int total_timeouts = 0;
|
int total_timeouts = 0;
|
|
|
*err = 0; // Just in case
|
*err = 0; // Just in case
|
|
|
// Create initial request
|
// Create initial request
|
hdr->th_opcode = htons(RRQ); // Read file
|
hdr->th_opcode = htons(RRQ); // Read file
|
cp = (char *)&hdr->th_stuff;
|
cp = (char *)&hdr->th_stuff;
|
fp = filename;
|
fp = filename;
|
while (*fp) *cp++ = *fp++;
|
while (*fp) *cp++ = *fp++;
|
*cp++ = '\0';
|
*cp++ = '\0';
|
if (mode == TFTP_NETASCII) {
|
if (mode == TFTP_NETASCII) {
|
fp = "NETASCII";
|
fp = "NETASCII";
|
} else if (mode == TFTP_OCTET) {
|
} else if (mode == TFTP_OCTET) {
|
fp = "OCTET";
|
fp = "OCTET";
|
} else {
|
} else {
|
*err = TFTP_INVALID;
|
*err = TFTP_INVALID;
|
return -1;
|
return -1;
|
}
|
}
|
while (*fp) *cp++ = *fp++;
|
while (*fp) *cp++ = *fp++;
|
*cp++ = '\0';
|
*cp++ = '\0';
|
server_info = getservbyname("tftp", "udp");
|
server_info = getservbyname("tftp", "udp");
|
if (server_info == (struct servent *)0) {
|
if (server_info == (struct servent *)0) {
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
return -1;
|
return -1;
|
}
|
}
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
if (s < 0) {
|
if (s < 0) {
|
// Couldn't open a communications channel
|
// Couldn't open a communications channel
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
return -1;
|
return -1;
|
}
|
}
|
memset((char *)&local_addr, 0, sizeof(local_addr));
|
memset((char *)&local_addr, 0, sizeof(local_addr));
|
local_addr.sin_family = AF_INET;
|
local_addr.sin_family = AF_INET;
|
local_addr.sin_len = sizeof(local_addr);
|
local_addr.sin_len = sizeof(local_addr);
|
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
local_addr.sin_port = htons(get_port++);
|
local_addr.sin_port = htons(get_port++);
|
if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
|
if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
|
// Problem setting up my end
|
// Problem setting up my end
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
memset((char *)&server_addr, 0, sizeof(server_addr));
|
memset((char *)&server_addr, 0, sizeof(server_addr));
|
server_addr.sin_family = AF_INET;
|
server_addr.sin_family = AF_INET;
|
server_addr.sin_len = sizeof(server_addr);
|
server_addr.sin_len = sizeof(server_addr);
|
server_addr.sin_addr = server->sin_addr;
|
server_addr.sin_addr = server->sin_addr;
|
if (server->sin_port == 0) {
|
if (server->sin_port == 0) {
|
server_addr.sin_port = server_info->s_port; // Network order already
|
server_addr.sin_port = server_info->s_port; // Network order already
|
} else {
|
} else {
|
server_addr.sin_port = server->sin_port;
|
server_addr.sin_port = server->sin_port;
|
}
|
}
|
|
|
// Send request
|
// Send request
|
if (sendto(s, data, sizeof(data), 0,
|
if (sendto(s, data, sizeof(data), 0,
|
(struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
(struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
// Problem sending request
|
// Problem sending request
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
|
|
// Read data
|
// Read data
|
fp = buf;
|
fp = buf;
|
while (true) {
|
while (true) {
|
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
|
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
|
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
FD_SET(s, &fds);
|
FD_SET(s, &fds);
|
if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
|
if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
|
if ((++total_timeouts > TFTP_TIMEOUT_MAX) || (last_good_block == 0)) {
|
if ((++total_timeouts > TFTP_TIMEOUT_MAX) || (last_good_block == 0)) {
|
// Timeout - no data received
|
// Timeout - no data received
|
*err = TFTP_TIMEOUT;
|
*err = TFTP_TIMEOUT;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
// Try resending last ACK
|
// Try resending last ACK
|
hdr->th_opcode = htons(ACK);
|
hdr->th_opcode = htons(ACK);
|
hdr->th_block = htons(last_good_block);
|
hdr->th_block = htons(last_good_block);
|
if (sendto(s, data, 4 /* FIXME */, 0,
|
if (sendto(s, data, 4 /* FIXME */, 0,
|
(struct sockaddr *)&from_addr, from_len) < 0) {
|
(struct sockaddr *)&from_addr, from_len) < 0) {
|
// Problem sending request
|
// Problem sending request
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
} else {
|
} else {
|
recv_len = sizeof(data);
|
recv_len = sizeof(data);
|
from_len = sizeof(from_addr);
|
from_len = sizeof(from_addr);
|
if ((data_len = recvfrom(s, &data, recv_len, 0,
|
if ((data_len = recvfrom(s, &data, recv_len, 0,
|
(struct sockaddr *)&from_addr, &from_len)) < 0) {
|
(struct sockaddr *)&from_addr, &from_len)) < 0) {
|
// What happened?
|
// What happened?
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
if (ntohs(hdr->th_opcode) == DATA) {
|
if (ntohs(hdr->th_opcode) == DATA) {
|
actual_len = 0;
|
actual_len = 0;
|
if (ntohs(hdr->th_block) == (last_good_block+1)) {
|
if (ntohs(hdr->th_block) == (last_good_block+1)) {
|
// Consume this data
|
// Consume this data
|
cp = hdr->th_data;
|
cp = hdr->th_data;
|
data_len -= 4; /* Sizeof TFTP header */
|
data_len -= 4; /* Sizeof TFTP header */
|
actual_len = data_len;
|
actual_len = data_len;
|
res += actual_len;
|
res += actual_len;
|
while (data_len-- > 0) {
|
while (data_len-- > 0) {
|
if (len-- > 0) {
|
if (len-- > 0) {
|
*fp++ = *cp++;
|
*fp++ = *cp++;
|
} else {
|
} else {
|
// Buffer overflow
|
// Buffer overflow
|
*err = TFTP_TOOLARGE;
|
*err = TFTP_TOOLARGE;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
last_good_block++;
|
last_good_block++;
|
}
|
}
|
// Send out the ACK
|
// Send out the ACK
|
hdr->th_opcode = htons(ACK);
|
hdr->th_opcode = htons(ACK);
|
hdr->th_block = htons(last_good_block);
|
hdr->th_block = htons(last_good_block);
|
if (sendto(s, data, 4 /* FIXME */, 0,
|
if (sendto(s, data, 4 /* FIXME */, 0,
|
(struct sockaddr *)&from_addr, from_len) < 0) {
|
(struct sockaddr *)&from_addr, from_len) < 0) {
|
// Problem sending request
|
// Problem sending request
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
|
if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
|
// End of data
|
// End of data
|
close(s);
|
close(s);
|
return res;
|
return res;
|
}
|
}
|
} else
|
} else
|
if (ntohs(hdr->th_opcode) == ERROR) {
|
if (ntohs(hdr->th_opcode) == ERROR) {
|
*err = ntohs(hdr->th_code);
|
*err = ntohs(hdr->th_code);
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
} else {
|
} else {
|
// What kind of packet is this?
|
// What kind of packet is this?
|
*err = TFTP_PROTOCOL;
|
*err = TFTP_PROTOCOL;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
//
|
//
|
// Send data to a file on a server via TFTP.
|
// Send data to a file on a server via TFTP.
|
//
|
//
|
int
|
int
|
tftp_put(char *filename,
|
tftp_put(char *filename,
|
struct sockaddr_in *server,
|
struct sockaddr_in *server,
|
char *buf,
|
char *buf,
|
int len,
|
int len,
|
int mode,
|
int mode,
|
int *err)
|
int *err)
|
{
|
{
|
int res = 0;
|
int res = 0;
|
int s, actual_len, data_len, recv_len, from_len;
|
int s, actual_len, data_len, recv_len, from_len;
|
static int put_port = 7800;
|
static int put_port = 7800;
|
struct sockaddr_in local_addr, server_addr, from_addr;
|
struct sockaddr_in local_addr, server_addr, from_addr;
|
char data[SEGSIZE+sizeof(struct tftphdr)];
|
char data[SEGSIZE+sizeof(struct tftphdr)];
|
struct tftphdr *hdr = (struct tftphdr *)data;
|
struct tftphdr *hdr = (struct tftphdr *)data;
|
char *cp, *fp, *sfp;
|
char *cp, *fp, *sfp;
|
struct timeval timeout;
|
struct timeval timeout;
|
unsigned short last_good_block = 0;
|
unsigned short last_good_block = 0;
|
struct servent *server_info;
|
struct servent *server_info;
|
fd_set fds;
|
fd_set fds;
|
int total_timeouts = 0;
|
int total_timeouts = 0;
|
|
|
*err = 0; // Just in case
|
*err = 0; // Just in case
|
|
|
server_info = getservbyname("tftp", "udp");
|
server_info = getservbyname("tftp", "udp");
|
if (server_info == (struct servent *)0) {
|
if (server_info == (struct servent *)0) {
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
return -1;
|
return -1;
|
}
|
}
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
if (s < 0) {
|
if (s < 0) {
|
// Couldn't open a communications channel
|
// Couldn't open a communications channel
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
return -1;
|
return -1;
|
}
|
}
|
memset((char *)&local_addr, 0, sizeof(local_addr));
|
memset((char *)&local_addr, 0, sizeof(local_addr));
|
local_addr.sin_family = AF_INET;
|
local_addr.sin_family = AF_INET;
|
local_addr.sin_len = sizeof(local_addr);
|
local_addr.sin_len = sizeof(local_addr);
|
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
local_addr.sin_port = htons(put_port++);
|
local_addr.sin_port = htons(put_port++);
|
if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
|
if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
|
// Problem setting up my end
|
// Problem setting up my end
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
memset((char *)&server_addr, 0, sizeof(server_addr));
|
memset((char *)&server_addr, 0, sizeof(server_addr));
|
server_addr.sin_family = AF_INET;
|
server_addr.sin_family = AF_INET;
|
server_addr.sin_len = sizeof(server_addr);
|
server_addr.sin_len = sizeof(server_addr);
|
server_addr.sin_addr = server->sin_addr;
|
server_addr.sin_addr = server->sin_addr;
|
if (server->sin_port == 0) {
|
if (server->sin_port == 0) {
|
server_addr.sin_port = server_info->s_port; // Network order already
|
server_addr.sin_port = server_info->s_port; // Network order already
|
} else {
|
} else {
|
server_addr.sin_port = server->sin_port;
|
server_addr.sin_port = server->sin_port;
|
}
|
}
|
|
|
while (1) {
|
while (1) {
|
// Create initial request
|
// Create initial request
|
hdr->th_opcode = htons(WRQ); // Create/write file
|
hdr->th_opcode = htons(WRQ); // Create/write file
|
cp = (char *)&hdr->th_stuff;
|
cp = (char *)&hdr->th_stuff;
|
fp = filename;
|
fp = filename;
|
while (*fp) *cp++ = *fp++;
|
while (*fp) *cp++ = *fp++;
|
*cp++ = '\0';
|
*cp++ = '\0';
|
if (mode == TFTP_NETASCII) {
|
if (mode == TFTP_NETASCII) {
|
fp = "NETASCII";
|
fp = "NETASCII";
|
} else if (mode == TFTP_OCTET) {
|
} else if (mode == TFTP_OCTET) {
|
fp = "OCTET";
|
fp = "OCTET";
|
} else {
|
} else {
|
*err = TFTP_INVALID;
|
*err = TFTP_INVALID;
|
return -1;
|
return -1;
|
}
|
}
|
while (*fp) *cp++ = *fp++;
|
while (*fp) *cp++ = *fp++;
|
*cp++ = '\0';
|
*cp++ = '\0';
|
// Send request
|
// Send request
|
if (sendto(s, data, sizeof(data), 0,
|
if (sendto(s, data, sizeof(data), 0,
|
(struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
(struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
// Problem sending request
|
// Problem sending request
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
// Wait for ACK
|
// Wait for ACK
|
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
|
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
|
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
FD_SET(s, &fds);
|
FD_SET(s, &fds);
|
if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
|
if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
|
if (++total_timeouts > TFTP_TIMEOUT_MAX) {
|
if (++total_timeouts > TFTP_TIMEOUT_MAX) {
|
// Timeout - no ACK received
|
// Timeout - no ACK received
|
*err = TFTP_TIMEOUT;
|
*err = TFTP_TIMEOUT;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
} else {
|
} else {
|
recv_len = sizeof(data);
|
recv_len = sizeof(data);
|
from_len = sizeof(from_addr);
|
from_len = sizeof(from_addr);
|
if ((data_len = recvfrom(s, &data, recv_len, 0,
|
if ((data_len = recvfrom(s, &data, recv_len, 0,
|
(struct sockaddr *)&from_addr, &from_len)) < 0) {
|
(struct sockaddr *)&from_addr, &from_len)) < 0) {
|
// What happened?
|
// What happened?
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
if (ntohs(hdr->th_opcode) == ACK) {
|
if (ntohs(hdr->th_opcode) == ACK) {
|
// Write request accepted - start sending data
|
// Write request accepted - start sending data
|
break;
|
break;
|
} else
|
} else
|
if (ntohs(hdr->th_opcode) == ERROR) {
|
if (ntohs(hdr->th_opcode) == ERROR) {
|
*err = ntohs(hdr->th_code);
|
*err = ntohs(hdr->th_code);
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
} else {
|
} else {
|
// What kind of packet is this?
|
// What kind of packet is this?
|
*err = TFTP_PROTOCOL;
|
*err = TFTP_PROTOCOL;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
// Send data
|
// Send data
|
sfp = buf;
|
sfp = buf;
|
last_good_block = 1;
|
last_good_block = 1;
|
while (res < len) {
|
while (res < len) {
|
// Build packet of data to send
|
// Build packet of data to send
|
data_len = min(SEGSIZE, len-res);
|
data_len = min(SEGSIZE, len-res);
|
hdr->th_opcode = htons(DATA);
|
hdr->th_opcode = htons(DATA);
|
hdr->th_block = htons(last_good_block);
|
hdr->th_block = htons(last_good_block);
|
cp = hdr->th_data;
|
cp = hdr->th_data;
|
fp = sfp;
|
fp = sfp;
|
actual_len = data_len + 4;
|
actual_len = data_len + 4;
|
// FIXME - what about "netascii" data?
|
// FIXME - what about "netascii" data?
|
while (data_len-- > 0) *cp++ = *fp++;
|
while (data_len-- > 0) *cp++ = *fp++;
|
// Send data packet
|
// Send data packet
|
if (sendto(s, data, actual_len, 0,
|
if (sendto(s, data, actual_len, 0,
|
(struct sockaddr *)&from_addr, from_len) < 0) {
|
(struct sockaddr *)&from_addr, from_len) < 0) {
|
// Problem sending request
|
// Problem sending request
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
// Wait for ACK
|
// Wait for ACK
|
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
|
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
|
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
FD_SET(s, &fds);
|
FD_SET(s, &fds);
|
if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
|
if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
|
if (++total_timeouts > TFTP_TIMEOUT_MAX) {
|
if (++total_timeouts > TFTP_TIMEOUT_MAX) {
|
// Timeout - no data received
|
// Timeout - no data received
|
*err = TFTP_TIMEOUT;
|
*err = TFTP_TIMEOUT;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
} else {
|
} else {
|
recv_len = sizeof(data);
|
recv_len = sizeof(data);
|
from_len = sizeof(from_addr);
|
from_len = sizeof(from_addr);
|
if ((data_len = recvfrom(s, &data, recv_len, 0,
|
if ((data_len = recvfrom(s, &data, recv_len, 0,
|
(struct sockaddr *)&from_addr, &from_len)) < 0) {
|
(struct sockaddr *)&from_addr, &from_len)) < 0) {
|
// What happened?
|
// What happened?
|
*err = TFTP_NETERR;
|
*err = TFTP_NETERR;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
if (ntohs(hdr->th_opcode) == ACK) {
|
if (ntohs(hdr->th_opcode) == ACK) {
|
if (ntohs(hdr->th_block) == last_good_block) {
|
if (ntohs(hdr->th_block) == last_good_block) {
|
// Advance pointers, etc
|
// Advance pointers, etc
|
sfp = fp;
|
sfp = fp;
|
res += (actual_len - 4);
|
res += (actual_len - 4);
|
last_good_block++;
|
last_good_block++;
|
} else {
|
} else {
|
diag_printf("Send block #%d, got ACK for #%d\n",
|
diag_printf("Send block #%d, got ACK for #%d\n",
|
last_good_block, ntohs(hdr->th_block));
|
last_good_block, ntohs(hdr->th_block));
|
}
|
}
|
} else
|
} else
|
if (ntohs(hdr->th_opcode) == ERROR) {
|
if (ntohs(hdr->th_opcode) == ERROR) {
|
*err = ntohs(hdr->th_code);
|
*err = ntohs(hdr->th_code);
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
} else {
|
} else {
|
// What kind of packet is this?
|
// What kind of packet is this?
|
*err = TFTP_PROTOCOL;
|
*err = TFTP_PROTOCOL;
|
close(s);
|
close(s);
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
}
|
}
|
close(s);
|
close(s);
|
return res;
|
return res;
|
}
|
}
|
|
|
// EOF tftp_client.c
|
// EOF tftp_client.c
|
|
|