| 1 |
//========================================================================== |
|---|
| 2 |
// |
|---|
| 3 |
// net/tftp_client.c |
|---|
| 4 |
// |
|---|
| 5 |
// Stand-alone TFTP support for RedBoot |
|---|
| 6 |
// |
|---|
| 7 |
//========================================================================== |
|---|
| 8 |
//####ECOSGPLCOPYRIGHTBEGIN#### |
|---|
| 9 |
// ------------------------------------------- |
|---|
| 10 |
// This file is part of eCos, the Embedded Configurable Operating System. |
|---|
| 11 |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
|---|
| 12 |
// Copyright (C) 2002, 2003 Gary Thomas |
|---|
| 13 |
// |
|---|
| 14 |
// eCos is free software; you can redistribute it and/or modify it under |
|---|
| 15 |
// the terms of the GNU General Public License as published by the Free |
|---|
| 16 |
// Software Foundation; either version 2 or (at your option) any later version. |
|---|
| 17 |
// |
|---|
| 18 |
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY |
|---|
| 19 |
// WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 20 |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|---|
| 21 |
// for more details. |
|---|
| 22 |
// |
|---|
| 23 |
// You should have received a copy of the GNU General Public License along |
|---|
| 24 |
// with eCos; if not, write to the Free Software Foundation, Inc., |
|---|
| 25 |
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
|---|
| 26 |
// |
|---|
| 27 |
// As a special exception, if other files instantiate templates or use macros |
|---|
| 28 |
// or inline functions from this file, or you compile this file and link it |
|---|
| 29 |
// with other works to produce a work based on this file, this file does not |
|---|
| 30 |
// by itself cause the resulting work to be covered by the GNU General Public |
|---|
| 31 |
// License. However the source code for this file must still be made available |
|---|
| 32 |
// in accordance with section (3) of the GNU General Public License. |
|---|
| 33 |
// |
|---|
| 34 |
// This exception does not invalidate any other reasons why a work based on |
|---|
| 35 |
// this file might be covered by the GNU General Public License. |
|---|
| 36 |
// |
|---|
| 37 |
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. |
|---|
| 38 |
// at http://sources.redhat.com/ecos/ecos-license/ |
|---|
| 39 |
// ------------------------------------------- |
|---|
| 40 |
//####ECOSGPLCOPYRIGHTEND#### |
|---|
| 41 |
//========================================================================== |
|---|
| 42 |
//#####DESCRIPTIONBEGIN#### |
|---|
| 43 |
// |
|---|
| 44 |
// Author(s): gthomas |
|---|
| 45 |
// Contributors: gthomas |
|---|
| 46 |
// Date: 2000-07-14 |
|---|
| 47 |
// Purpose: |
|---|
| 48 |
// Description: |
|---|
| 49 |
// |
|---|
| 50 |
// This code is part of RedBoot (tm). |
|---|
| 51 |
// |
|---|
| 52 |
//####DESCRIPTIONEND#### |
|---|
| 53 |
// |
|---|
| 54 |
//========================================================================== |
|---|
| 55 |
|
|---|
| 56 |
// TFTP client support |
|---|
| 57 |
|
|---|
| 58 |
#include <redboot.h> // have_net |
|---|
| 59 |
#include <net/net.h> |
|---|
| 60 |
#include <net/tftp.h> |
|---|
| 61 |
#include <net/tftp_support.h> |
|---|
| 62 |
|
|---|
| 63 |
// So we remember which ports have been used |
|---|
| 64 |
static int get_port = 7700; |
|---|
| 65 |
|
|---|
| 66 |
static struct { |
|---|
| 67 |
bool open; |
|---|
| 68 |
int total_timeouts, packets_received; |
|---|
| 69 |
unsigned long last_good_block; |
|---|
| 70 |
int avail, actual_len; |
|---|
| 71 |
struct sockaddr_in local_addr, from_addr; |
|---|
| 72 |
char data[SEGSIZE + sizeof(struct tftphdr)]; |
|---|
| 73 |
char *bufp; |
|---|
| 74 |
} tftp_stream; |
|---|
| 75 |
|
|---|
| 76 |
int tftp_stream_open(connection_info_t * info, int *err) |
|---|
| 77 |
{ |
|---|
| 78 |
struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data; |
|---|
| 79 |
char *cp, *fp; |
|---|
| 80 |
char test_buf; |
|---|
| 81 |
|
|---|
| 82 |
if (!have_net || tftp_stream.open) { |
|---|
| 83 |
*err = TFTP_INVALID; // Already open |
|---|
| 84 |
return -1; |
|---|
| 85 |
} |
|---|
| 86 |
// Create initial request |
|---|
| 87 |
hdr->th_opcode = htons(RRQ); // Read file |
|---|
| 88 |
cp = (char *)&hdr->th_stuff; |
|---|
| 89 |
fp = info->filename; |
|---|
| 90 |
while (*fp) |
|---|
| 91 |
*cp++ = *fp++; |
|---|
| 92 |
*cp++ = '\0'; |
|---|
| 93 |
// Since this is used for downloading data, OCTET (binary) is the |
|---|
| 94 |
// only mode that makes sense. |
|---|
| 95 |
fp = "OCTET"; |
|---|
| 96 |
while (*fp) |
|---|
| 97 |
*cp++ = *fp++; |
|---|
| 98 |
*cp++ = '\0'; |
|---|
| 99 |
|
|---|
| 100 |
memset((char *)&tftp_stream.local_addr, 0, |
|---|
| 101 |
sizeof(tftp_stream.local_addr)); |
|---|
| 102 |
tftp_stream.local_addr.sin_family = AF_INET; |
|---|
| 103 |
tftp_stream.local_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|---|
| 104 |
tftp_stream.local_addr.sin_port = htons(get_port++); |
|---|
| 105 |
|
|---|
| 106 |
if (info->server->sin_port == 0) { |
|---|
| 107 |
info->server->sin_port = htons(TFTP_PORT); |
|---|
| 108 |
} else { |
|---|
| 109 |
info->server->sin_port = htons(info->server->sin_port); |
|---|
| 110 |
} |
|---|
| 111 |
|
|---|
| 112 |
// Send request - note: RFC 1350 (TFTP rev 2) indicates that this should be |
|---|
| 113 |
// only as long as required to hold the request, with the nul terminator. |
|---|
| 114 |
// Some servers silently go to lunch if the request is not the correct size. |
|---|
| 115 |
if (__udp_sendto(tftp_stream.data, cp - (char *)hdr, |
|---|
| 116 |
info->server, &tftp_stream.local_addr) < 0) { |
|---|
| 117 |
// Problem sending request |
|---|
| 118 |
*err = TFTP_NETERR; |
|---|
| 119 |
return -1; |
|---|
| 120 |
} |
|---|
| 121 |
|
|---|
| 122 |
tftp_stream.open = true; |
|---|
| 123 |
tftp_stream.avail = 0; |
|---|
| 124 |
tftp_stream.actual_len = -1; |
|---|
| 125 |
tftp_stream.last_good_block = 0; |
|---|
| 126 |
tftp_stream.total_timeouts = 0; |
|---|
| 127 |
tftp_stream.from_addr.sin_port = 0; |
|---|
| 128 |
tftp_stream.packets_received = 0; |
|---|
| 129 |
|
|---|
| 130 |
// Try and read the first byte [block] since no errors are |
|---|
| 131 |
// reported until then. |
|---|
| 132 |
if (tftp_stream_read(&test_buf, 1, err) == 1) { |
|---|
| 133 |
// Back up [rewind] over this datum |
|---|
| 134 |
tftp_stream.bufp--; |
|---|
| 135 |
tftp_stream.avail++; |
|---|
| 136 |
return 0; // Open and first read successful |
|---|
| 137 |
} else { |
|---|
| 138 |
tftp_stream.open = false; |
|---|
| 139 |
return -1; // Couldn't read |
|---|
| 140 |
} |
|---|
| 141 |
} |
|---|
| 142 |
|
|---|
| 143 |
static int tftp_ack(int *err) |
|---|
| 144 |
{ |
|---|
| 145 |
struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data; |
|---|
| 146 |
// ACK last packet so server can shut down |
|---|
| 147 |
if (tftp_stream.packets_received > 0) { |
|---|
| 148 |
hdr->th_opcode = htons(ACK); |
|---|
| 149 |
hdr->th_block = |
|---|
| 150 |
htons((cyg_uint16) tftp_stream.last_good_block & 0xFFFF); |
|---|
| 151 |
if (__udp_sendto(tftp_stream.data, 4 /* FIXME */ , |
|---|
| 152 |
&tftp_stream.from_addr, |
|---|
| 153 |
&tftp_stream.local_addr) < 0) { |
|---|
| 154 |
// Problem sending ACK |
|---|
| 155 |
*err = TFTP_NETERR; |
|---|
| 156 |
return -1; |
|---|
| 157 |
} |
|---|
| 158 |
} |
|---|
| 159 |
return 0; |
|---|
| 160 |
} |
|---|
| 161 |
|
|---|
| 162 |
void tftp_stream_close(int *err) |
|---|
| 163 |
{ |
|---|
| 164 |
tftp_ack(err); |
|---|
| 165 |
tftp_stream.open = false; |
|---|
| 166 |
} |
|---|
| 167 |
|
|---|
| 168 |
int tftp_stream_read(char *buf, int len, int *err) |
|---|
| 169 |
{ |
|---|
| 170 |
int total_bytes = 0; |
|---|
| 171 |
int size, recv_len, data_len; |
|---|
| 172 |
struct timeval timeout; |
|---|
| 173 |
struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data; |
|---|
| 174 |
|
|---|
| 175 |
while (total_bytes < len) { |
|---|
| 176 |
// Move any bytes which we've already read/buffered |
|---|
| 177 |
if (tftp_stream.avail > 0) { |
|---|
| 178 |
size = tftp_stream.avail; |
|---|
| 179 |
if (size > (len - total_bytes)) |
|---|
| 180 |
size = len - total_bytes; |
|---|
| 181 |
memcpy(buf, tftp_stream.bufp, size); |
|---|
| 182 |
buf += size; |
|---|
| 183 |
tftp_stream.bufp += size; |
|---|
| 184 |
tftp_stream.avail -= size; |
|---|
| 185 |
total_bytes += size; |
|---|
| 186 |
} else { |
|---|
| 187 |
if (tftp_ack(err) < 0) { |
|---|
| 188 |
return -1; |
|---|
| 189 |
} |
|---|
| 190 |
if ((tftp_stream.actual_len >= 0) |
|---|
| 191 |
&& (tftp_stream.actual_len < SEGSIZE)) { |
|---|
| 192 |
// Out of data |
|---|
| 193 |
break; |
|---|
| 194 |
} |
|---|
| 195 |
timeout.tv_sec = |
|---|
| 196 |
(tftp_stream.last_good_block == |
|---|
| 197 |
0) ? 10 * |
|---|
| 198 |
TFTP_TIMEOUT_PERIOD : TFTP_TIMEOUT_PERIOD; |
|---|
| 199 |
timeout.tv_usec = 0; |
|---|
| 200 |
recv_len = sizeof(tftp_stream.data); |
|---|
| 201 |
if ((data_len = |
|---|
| 202 |
__udp_recvfrom(&tftp_stream.data[0], recv_len, |
|---|
| 203 |
&tftp_stream.from_addr, |
|---|
| 204 |
&tftp_stream.local_addr, |
|---|
| 205 |
&timeout)) < 0) { |
|---|
| 206 |
// No data, try again |
|---|
| 207 |
diag_printf("TFTP timed out %d/%d\n", |
|---|
| 208 |
tftp_stream.total_timeouts + 1, |
|---|
| 209 |
TFTP_TIMEOUT_MAX); |
|---|
| 210 |
if ((++tftp_stream.total_timeouts > |
|---|
| 211 |
TFTP_TIMEOUT_MAX) |
|---|
| 212 |
|| (tftp_stream.last_good_block == 0)) { |
|---|
| 213 |
// Timeout - no data received |
|---|
| 214 |
*err = TFTP_TIMEOUT; |
|---|
| 215 |
return -1; |
|---|
| 216 |
} |
|---|
| 217 |
// Send out the ACK for the last block - maybe server will retry |
|---|
| 218 |
if (tftp_ack(err) < 0) { |
|---|
| 219 |
return -1; |
|---|
| 220 |
} |
|---|
| 221 |
} else { |
|---|
| 222 |
tftp_stream.packets_received++; |
|---|
| 223 |
if (ntohs(hdr->th_opcode) == DATA) { |
|---|
| 224 |
if (ntohs(hdr->th_block) == |
|---|
| 225 |
(cyg_uint16) ((tftp_stream.last_good_block + 1) & 0xFFFF)) { |
|---|
| 226 |
// Consume this data |
|---|
| 227 |
data_len -= 4; /* Sizeof TFTP header */ |
|---|
| 228 |
tftp_stream.avail = |
|---|
| 229 |
tftp_stream.actual_len = |
|---|
| 230 |
data_len; |
|---|
| 231 |
tftp_stream.bufp = hdr->th_data; |
|---|
| 232 |
tftp_stream.last_good_block++; |
|---|
| 233 |
} |
|---|
| 234 |
} else { |
|---|
| 235 |
if (ntohs(hdr->th_opcode) == ERROR) { |
|---|
| 236 |
*err = ntohs(hdr->th_code); |
|---|
| 237 |
return -1; |
|---|
| 238 |
} else { |
|---|
| 239 |
// What kind of packet is this? |
|---|
| 240 |
*err = TFTP_PROTOCOL; |
|---|
| 241 |
return -1; |
|---|
| 242 |
} |
|---|
| 243 |
} |
|---|
| 244 |
} |
|---|
| 245 |
} |
|---|
| 246 |
} |
|---|
| 247 |
return total_bytes; |
|---|
| 248 |
} |
|---|
| 249 |
|
|---|
| 250 |
char *tftp_error(int err) |
|---|
| 251 |
{ |
|---|
| 252 |
char *errmsg = "Unknown error"; |
|---|
| 253 |
|
|---|
| 254 |
switch (err) { |
|---|
| 255 |
case TFTP_ENOTFOUND: |
|---|
| 256 |
return "file not found"; |
|---|
| 257 |
case TFTP_EACCESS: |
|---|
| 258 |
return "access violation"; |
|---|
| 259 |
case TFTP_ENOSPACE: |
|---|
| 260 |
return "disk full or allocation exceeded"; |
|---|
| 261 |
case TFTP_EBADOP: |
|---|
| 262 |
return "illegal TFTP operation"; |
|---|
| 263 |
case TFTP_EBADID: |
|---|
| 264 |
return "unknown transfer ID"; |
|---|
| 265 |
case TFTP_EEXISTS: |
|---|
| 266 |
return "file already exists"; |
|---|
| 267 |
case TFTP_ENOUSER: |
|---|
| 268 |
return "no such user"; |
|---|
| 269 |
case TFTP_TIMEOUT: |
|---|
| 270 |
return "operation timed out"; |
|---|
| 271 |
case TFTP_NETERR: |
|---|
| 272 |
return "some sort of network error"; |
|---|
| 273 |
case TFTP_INVALID: |
|---|
| 274 |
return "invalid parameter"; |
|---|
| 275 |
case TFTP_PROTOCOL: |
|---|
| 276 |
return "protocol violation"; |
|---|
| 277 |
case TFTP_TOOLARGE: |
|---|
| 278 |
return "file is larger than buffer"; |
|---|
| 279 |
} |
|---|
| 280 |
return errmsg; |
|---|
| 281 |
} |
|---|
| 282 |
|
|---|
| 283 |
// |
|---|
| 284 |
// RedBoot interface |
|---|
| 285 |
// |
|---|
| 286 |
GETC_IO_FUNCS(tftp_io, tftp_stream_open, tftp_stream_close, |
|---|
| 287 |
0, tftp_stream_read, tftp_error); |
|---|
| 288 |
RedBoot_load(tftp, tftp_io, true, true, 0); |
|---|