#include <network.h>
#include <arpa/tftp.h>
#include <tftp_support.h>
+#include <stdlib.h>
+#include <stdio.h>
#define min(x,y) (x<y ? x : y)
// On error, *err will hold the reason.
// This version uses the server name. This can be a name for DNS lookup
// or a dotty or colony number format for IPv4 or IPv6.
-int tftp_client_get(char *filename,
- char *server,
- int port,
- char *buf,
- int len,
- int mode,
- int *err) {
-
+static int tftp_client_get_inner(char *data,
+ const char * const filename,
+ const char * const server,
+ const int port,
+ char *buf,
+ int len,
+ const int mode,
+ int * const err
+#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET
+ ,int negotiate
+#endif
+ ) {
+
+ int blksize=SEGSIZE;
int result = 0;
int s=-1;
int actual_len, data_len;
int error;
struct sockaddr local_addr, from_addr;
- char data[SEGSIZE+sizeof(struct tftphdr)];
struct tftphdr *hdr = (struct tftphdr *)data;
- char *cp, *fp;
+ const char *fp;
+ char *cp, *bp;
struct timeval timeout;
unsigned short last_good_block = 0;
fd_set fds;
}
while (*fp) *cp++ = *fp++;
*cp++ = '\0';
+#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET
+ if (negotiate) {
+ fp="blksize";
+ while (*fp)
+ *cp++ = *fp++;
+ *cp++ = '\0';
+ cp+=sprintf(cp, "%d", CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET_SIZE);
+ *cp++ = '\0';
+ }
+#endif
memset(&hints,0,sizeof(hints));
hints.ai_family = PF_UNSPEC;
addrinfo = res;
while (addrinfo) {
s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
- addrinfo->ai_protocol);
+ addrinfo->ai_protocol);
if (s >= 0) {
- memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
- switch(addrinfo->ai_addr->sa_family) {
- case AF_INET: {
- struct sockaddr_in * saddr =
- (struct sockaddr_in *) addrinfo->ai_addr;
- struct sockaddr_in * laddr =
- (struct sockaddr_in *) &local_addr;
- if (port) {
- saddr->sin_port = htons(port);
- }
- laddr->sin_port = htons(get_port++);
- laddr->sin_addr.s_addr = INADDR_ANY;
- break;
- }
+ memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
+ switch(addrinfo->ai_addr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in * saddr =
+ (struct sockaddr_in *) addrinfo->ai_addr;
+ struct sockaddr_in * laddr =
+ (struct sockaddr_in *) &local_addr;
+ if (port) {
+ saddr->sin_port = htons(port);
+ }
+ laddr->sin_port = htons(get_port++);
+ laddr->sin_addr.s_addr = INADDR_ANY;
+ break;
+ }
#ifdef CYGPKG_NET_INET6
- case AF_INET6: {
- struct sockaddr_in6 * saddr =
- (struct sockaddr_in6 *) addrinfo->ai_addr;
- struct sockaddr_in6 * laddr =
- (struct sockaddr_in6 *) &local_addr;
- if (port) {
- saddr->sin6_port = htons(port);
- }
- laddr->sin6_port = htons(get_port++);
- laddr->sin6_addr = in6addr_any;
- break;
- }
+ case AF_INET6: {
+ struct sockaddr_in6 * saddr =
+ (struct sockaddr_in6 *) addrinfo->ai_addr;
+ struct sockaddr_in6 * laddr =
+ (struct sockaddr_in6 *) &local_addr;
+ if (port) {
+ saddr->sin6_port = htons(port);
+ }
+ laddr->sin6_port = htons(get_port++);
+ laddr->sin6_addr = in6addr_any;
+ break;
+ }
#endif
- default:
- *err = TFTP_NETERR;
- goto out;
- }
+ default:
+ *err = TFTP_NETERR;
+ goto out;
+ }
- if (bind(s,&local_addr,addrinfo->ai_addrlen) < 0) {
+ if (bind(s,&local_addr,addrinfo->ai_addrlen) < 0) {
*err = TFTP_NETERR;
goto out;
}
-
- // Send request
- if (sendto(s, data, (int)(cp-data), 0,
- addrinfo->ai_addr,
- addrinfo->ai_addrlen) < 0) {
- // Problem sending request
- *err = TFTP_NETERR;
- goto nextaddr;
- }
+
+ // Send request
+ if (sendto(s, data, (int)(cp-data), 0,
+ addrinfo->ai_addr,
+ addrinfo->ai_addrlen) < 0) {
+ // Problem sending request
+ *err = TFTP_NETERR;
+ goto nextaddr;
+ }
- // Read data
- fp = buf;
- while (true) {
- timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
- timeout.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(s, &fds);
- if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
+ // Read data
+ bp = buf;
+ while (true) {
+ timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+ timeout.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
total_timeouts++;
if ((last_good_block == 0) && (total_timeouts > TFTP_RETRIES_MAX)) {
- // Timeout - no data received. Probably no server.
- *err = TFTP_TIMEOUT;
- goto nextaddr;
+ // Timeout - no data received. Probably no server.
+ *err = TFTP_TIMEOUT;
+ goto nextaddr;
}
- if (total_timeouts > TFTP_TIMEOUT_MAX) {
+ if (total_timeouts > TFTP_TIMEOUT_MAX) {
// Timeout - have received data. Network problem?
*err = TFTP_TIMEOUT;
goto out;
- }
+ }
if (last_good_block == 0 ) {
// Send request
goto out;
}
}
- } else {
- recv_len = sizeof(data);
- from_len = sizeof(from_addr);
- if ((data_len = recvfrom(s, &data, recv_len, 0,
- &from_addr, &from_len)) < 0) {
- // What happened?
- *err = TFTP_NETERR;
- goto out;
- }
- if (ntohs(hdr->th_opcode) == DATA) {
- actual_len = 0;
- if (ntohs(hdr->th_block) == (last_good_block+1)) {
- // Consume this data
- cp = hdr->th_data;
- data_len -= 4; /* Sizeof TFTP header */
- actual_len = data_len;
- result += actual_len;
- while (data_len-- > 0) {
- if (len-- > 0) {
- *fp++ = *cp++;
- } else {
- // Buffer overflow
- *err = TFTP_TOOLARGE;
- goto out;
- }
- }
- last_good_block++;
- } else {
- // To prevent an out-of-sequence packet from
- // terminating transmission prematurely, set
- // actual_len to a full size packet.
- actual_len = SEGSIZE;
- }
- // Send out the ACK
- hdr->th_opcode = htons(ACK);
- hdr->th_block = htons(last_good_block);
- if (sendto(s, data, 4 /* FIXME */, 0,
- &from_addr, from_len) < 0) {
- // Problem sending request
- *err = TFTP_NETERR;
- goto out;
- }
- // A short packet marks the end of the file.
- if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
- // End of data
- close(s);
- freeaddrinfo(res);
- return result;
- }
- } else
- if (ntohs(hdr->th_opcode) == ERROR) {
- *err = ntohs(hdr->th_code);
- goto out;
- } else {
- // What kind of packet is this?
- *err = TFTP_PROTOCOL;
- goto out;
- }
- }
- }
+ } else {
+ recv_len = blksize+sizeof(struct tftphdr);
+ from_len = sizeof(from_addr);
+ if ((data_len = recvfrom(s, data, recv_len, 0,
+ &from_addr, &from_len)) < 0) {
+ // What happened?
+ *err = TFTP_NETERR;
+ goto out;
+ }
+#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET
+ if (ntohs(hdr->th_opcode) == OACK) {
+ // We can have only *one* option, the one we sent..
+ if (strncmp(data+2, "blksize", data_len)==0) {
+ blksize=atol(data+2+strlen("blksize")+1);
+ } else {
+ // option ignored, use default.
+ }
+ // Send out the ACK
+ hdr->th_opcode = htons(ACK);
+ hdr->th_block = htons(last_good_block);
+ if (sendto(s, data, 4 /* FIXME */, 0,
+ &from_addr, from_len) < 0) {
+ // Problem sending request
+ *err = TFTP_NETERR;
+ goto out;
+ }
+ } else
+#endif
+ if (ntohs(hdr->th_opcode) == DATA) {
+ actual_len = 0;
+ if (ntohs(hdr->th_block) == (last_good_block+1)) {
+ // Consume this data
+ cp = hdr->th_data;
+ data_len -= 4; /* Sizeof TFTP header */
+ actual_len = data_len;
+ result += actual_len;
+ if (len<data_len)
+ {
+ // Buffer overflow
+ *err = TFTP_TOOLARGE;
+ goto out;
+ }
+ memcpy(bp, cp, data_len);
+ bp+=data_len;
+ len-=data_len;
+ last_good_block++;
+ } else {
+ // To prevent an out-of-sequence packet from
+ // terminating transmission prematurely, set
+ // actual_len to a full size packet.
+ actual_len = blksize;
+ }
+ // Send out the ACK
+ hdr->th_opcode = htons(ACK);
+ hdr->th_block = htons(last_good_block);
+ if (sendto(s, data, 4 /* FIXME */, 0,
+ &from_addr, from_len) < 0) {
+ // Problem sending request
+ *err = TFTP_NETERR;
+ goto out;
+ }
+ // A short packet marks the end of the file.
+ /* 4 = Sizeof TFTP header */
+ if ((actual_len >= 0) && (actual_len < blksize)) {
+ // End of data
+ close(s);
+ freeaddrinfo(res);
+ return result;
+ }
+ } else
+ if (ntohs(hdr->th_opcode) == ERROR) {
+ *err = ntohs(hdr->th_code);
+ goto out;
+ } else {
+ // What kind of packet is this?
+ *err = TFTP_PROTOCOL;
+ goto out;
+ }
+ }
+ }
}
// If we got here, it means there was a problem connecting to the
// server. Try the next address returned by getaddrinfo
nextaddr:
if (-1 != s) {
- close(s);
+ close(s);
}
addrinfo=addrinfo->ai_next;
}
freeaddrinfo(res);
return -1;
}
+
+
+int tftp_client_get(const char * const filename,
+ const char * const server,
+ const int port,
+ char *buf,
+ int len,
+ const int mode,
+ int * const err) {
+ int result;
+#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET
+ char *data = malloc(CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET_SIZE+
+ sizeof(struct tftphdr));
+ if (data==NULL) {
+ *err=TFTP_ENOSPACE;
+ return -1;
+ }
+#else
+ char data[SEGSIZE+sizeof(struct tftphdr)];
+#endif
+ result=tftp_client_get_inner(data, filename, server,
+ port, buf, len, mode, err
+#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET
+ ,1
+#endif
+ );
+ if (result<0)
+ {
+#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET
+ // try without negotiating packet size. The serves that do
+ // not support options negotiation may or may not ignore the
+ // options. If they return an error in the case of options
+ // this code path will try without packet size negotiation.
+ result=tftp_client_get_inner(data, filename, server,
+ port, buf, len, mode, err,
+ 0);
+#endif
+ }
+
+#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET
+ free(data);
+#endif
+
+ return result;
+}
+
//
// Read a file from a host into a local buffer. Returns the
// number of bytes actually read, or (-1) if an error occurs.
//
// Depreciated. Use tftp_client_get instead.
int
-tftp_get(char *filename,
- struct sockaddr_in *server,
+tftp_get(const char * const filename,
+ const struct sockaddr_in * const server,
char *buf,
int len,
- int mode,
- int *err)
+ const int mode,
+ int * const err)
{
char server_name[20];
char *ret;
int port;
ret = inet_ntop(AF_INET, (void *)&server->sin_addr,
- server_name, sizeof(server_name));
+ server_name, sizeof(server_name));
if (NULL == ret) {
*err = TFTP_NETERR;
return -1;
// Send data to a file on a server via TFTP.
//
int
-tftp_put(char *filename,
- struct sockaddr_in *server,
- char *buf,
+tftp_put(const char * const filename,
+ const struct sockaddr_in * const server,
+ const char *buf,
int len,
- int mode,
- int *err)
+ const int mode,
+ int * const err)
{
char server_name[20];
char *ret;
int port;
ret = inet_ntop(AF_INET, (void *)&server->sin_addr,
- server_name, sizeof(server_name));
+ server_name, sizeof(server_name));
if (NULL == ret) {
*err = TFTP_NETERR;
return -1;
// On error, *err will hold the reason.
// This version uses the server name. This can be a name for DNS lookup
// or a dotty or colony number format for IPv4 or IPv6.
-int tftp_client_put(char *filename,
- char *server,
- int port,
- char *buf,
- int len,
- int mode,
- int *err) {
+int tftp_client_put(const char * const filename,
+ const char * const server,
+ const int port,
+ const char *buf,
+ int len,
+ const int mode,
+ int * const err) {
int result = 0;
int s = -1, actual_len, data_len;
struct sockaddr local_addr, from_addr;
char data[SEGSIZE+sizeof(struct tftphdr)];
struct tftphdr *hdr = (struct tftphdr *)data;
- char *cp, *fp, *sfp;
+ const char *fp, *sfp;
+ char *cp;
struct timeval timeout;
unsigned short last_good_block = 0;
fd_set fds;
addrinfo = res;
while (addrinfo) {
s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
- addrinfo->ai_protocol);
+ addrinfo->ai_protocol);
if (s >= 0) {
- memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
- switch(addrinfo->ai_addr->sa_family) {
- case AF_INET: {
- struct sockaddr_in * saddr =
- (struct sockaddr_in *) addrinfo->ai_addr;
- struct sockaddr_in * laddr =
- (struct sockaddr_in *) &local_addr;
- if (port) {
- saddr->sin_port = htons(port);
- }
- laddr->sin_port = htons(put_port++);
- laddr->sin_addr.s_addr = INADDR_ANY;
- break;
- }
+ memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
+ switch(addrinfo->ai_addr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in * saddr =
+ (struct sockaddr_in *) addrinfo->ai_addr;
+ struct sockaddr_in * laddr =
+ (struct sockaddr_in *) &local_addr;
+ if (port) {
+ saddr->sin_port = htons(port);
+ }
+ laddr->sin_port = htons(put_port++);
+ laddr->sin_addr.s_addr = INADDR_ANY;
+ break;
+ }
#ifdef CYGPKG_NET_INET6
- case AF_INET6: {
- struct sockaddr_in6 * saddr =
- (struct sockaddr_in6 *) addrinfo->ai_addr;
- struct sockaddr_in6 * laddr =
- (struct sockaddr_in6 *) &local_addr;
- if (port) {
- saddr->sin6_port = htons(port);
- }
- laddr->sin6_port = htons(put_port++);
- laddr->sin6_addr = in6addr_any;
- break;
- }
+ case AF_INET6: {
+ struct sockaddr_in6 * saddr =
+ (struct sockaddr_in6 *) addrinfo->ai_addr;
+ struct sockaddr_in6 * laddr =
+ (struct sockaddr_in6 *) &local_addr;
+ if (port) {
+ saddr->sin6_port = htons(port);
+ }
+ laddr->sin6_port = htons(put_port++);
+ laddr->sin6_addr = in6addr_any;
+ break;
+ }
#endif
- default:
- *err = TFTP_NETERR;
- goto out;
- }
- if (bind(s,
- (struct sockaddr *)&local_addr,
- addrinfo->ai_addrlen) < 0) {
- // Problem setting up my end
- *err = TFTP_NETERR;
- goto out;
- }
+ default:
+ *err = TFTP_NETERR;
+ goto out;
+ }
+ if (bind(s,
+ (struct sockaddr *)&local_addr,
+ addrinfo->ai_addrlen) < 0) {
+ // Problem setting up my end
+ *err = TFTP_NETERR;
+ goto out;
+ }
- while (1) {
- // Create initial request
- hdr->th_opcode = htons(WRQ); // Create/write file
- cp = (char *)&hdr->th_stuff;
- fp = filename;
- while (*fp) *cp++ = *fp++;
- *cp++ = '\0';
- if (mode == TFTP_NETASCII) {
+ while (1) {
+ // Create initial request
+ hdr->th_opcode = htons(WRQ); // Create/write file
+ cp = (char *)&hdr->th_stuff;
+ fp = filename;
+ while (*fp) *cp++ = *fp++;
+ *cp++ = '\0';
+ if (mode == TFTP_NETASCII) {
fp = "NETASCII";
- } else if (mode == TFTP_OCTET) {
+ } else if (mode == TFTP_OCTET) {
fp = "OCTET";
- } else {
+ } else {
*err = TFTP_INVALID;
- goto out;
- }
- while (*fp) *cp++ = *fp++;
- *cp++ = '\0';
- // Send request
- if (sendto(s, data, (int)(cp-data), 0,
- addrinfo->ai_addr,
- addrinfo->ai_addrlen) < 0) {
+ goto out;
+ }
+ while (*fp) *cp++ = *fp++;
+ *cp++ = '\0';
+ // Send request
+ if (sendto(s, data, (int)(cp-data), 0,
+ addrinfo->ai_addr,
+ addrinfo->ai_addrlen) < 0) {
// Problem sending request
*err = TFTP_NETERR;
- goto nextaddr;
- }
- // Wait for ACK
- timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
- timeout.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(s, &fds);
- if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
+ goto nextaddr;
+ }
+ // Wait for ACK
+ timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+ timeout.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
if (++total_timeouts > TFTP_RETRIES_MAX) {
- // Timeout - no ACK received
- *err = TFTP_TIMEOUT;
- goto nextaddr;
+ // Timeout - no ACK received
+ *err = TFTP_TIMEOUT;
+ goto nextaddr;
}
- } else {
+ } else {
recv_len = sizeof(data);
from_len = sizeof(from_addr);
if ((data_len = recvfrom(s, &data, recv_len, 0,
&from_addr, &from_len)) < 0) {
- // What happened?
- *err = TFTP_NETERR;
- goto out;
+ // What happened?
+ *err = TFTP_NETERR;
+ goto out;
}
if (ntohs(hdr->th_opcode) == ACK) {
- // Write request accepted - start sending data
- break;
+ // Write request accepted - start sending data
+ break;
} else
- if (ntohs(hdr->th_opcode) == ERROR) {
+ if (ntohs(hdr->th_opcode) == ERROR) {
*err = ntohs(hdr->th_code);
goto out;
- } else {
+ } else {
// What kind of packet is this?
- goto out;
- }
- }
- }
-
- // Send data
- sfp = buf;
- last_good_block = 1;
- while (result < len) {
- // Build packet of data to send
- data_len = min(SEGSIZE, len-result);
- hdr->th_opcode = htons(DATA);
- hdr->th_block = htons(last_good_block);
- cp = hdr->th_data;
- fp = sfp;
- actual_len = data_len + 4;
- // FIXME - what about "netascii" data?
- while (data_len-- > 0) *cp++ = *fp++;
- // Send data packet
- if (sendto(s, data, actual_len, 0,
- &from_addr, from_len) < 0) {
+ goto out;
+ }
+ }
+ }
+
+ // Send data
+ sfp = buf;
+ last_good_block = 1;
+ while (result < len) {
+ // Build packet of data to send
+ data_len = min(SEGSIZE, len-result);
+ hdr->th_opcode = htons(DATA);
+ hdr->th_block = htons(last_good_block);
+ cp = hdr->th_data;
+ fp = sfp;
+ actual_len = data_len + 4;
+ // FIXME - what about "netascii" data?
+ while (data_len-- > 0) *cp++ = *fp++;
+ // Send data packet
+ if (sendto(s, data, actual_len, 0,
+ &from_addr, from_len) < 0) {
// Problem sending request
*err = TFTP_NETERR;
- goto out;
- }
- // Wait for ACK
- timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
- timeout.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(s, &fds);
- if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
+ goto out;
+ }
+ // Wait for ACK
+ timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+ timeout.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
if (++total_timeouts > TFTP_TIMEOUT_MAX) {
- // Timeout - no data received
- *err = TFTP_TIMEOUT;
- goto out;
+ // Timeout - no data received
+ *err = TFTP_TIMEOUT;
+ goto out;
}
- } else {
+ } else {
recv_len = sizeof(data);
from_len = sizeof(from_addr);
if ((data_len = recvfrom(s, &data, recv_len, 0,
&from_addr, &from_len)) < 0) {
- // What happened?
- *err = TFTP_NETERR;
- goto out;
+ // What happened?
+ *err = TFTP_NETERR;
+ goto out;
}
if (ntohs(hdr->th_opcode) == ACK) {
- if (ntohs(hdr->th_block) == last_good_block) {
- // Advance pointers, etc
- sfp = fp;
- result += (actual_len - 4);
- last_good_block++;
- } else {
- diag_printf("Send block #%d, got ACK for #%d\n",
- last_good_block, ntohs(hdr->th_block));
- }
+ if (ntohs(hdr->th_block) == last_good_block) {
+ // Advance pointers, etc
+ sfp = fp;
+ result += (actual_len - 4);
+ last_good_block++;
+ } else {
+ diag_printf("Send block #%d, got ACK for #%d\n",
+ last_good_block, ntohs(hdr->th_block));
+ }
} else
- if (ntohs(hdr->th_opcode) == ERROR) {
+ if (ntohs(hdr->th_opcode) == ERROR) {
*err = ntohs(hdr->th_code);
goto out;
- } else {
+ } else {
// What kind of packet is this?
*err = TFTP_PROTOCOL;
- goto out;
- }
- }
- }
- close (s);
- return result;
+ goto out;
+ }
+ }
+ }
+ close (s);
+ return result;
}
// If we got here, it means there was a problem connecting to the
// server. Try the next address returned by getaddrinfo
nextaddr:
if (-1 != s) {
- close(s);
+ close(s);
}
addrinfo=addrinfo->ai_next;
}