diff packages/redboot/current/src/net/tftp_client.c @ 115:6ed91473a1cd ecos-sw-2000-08-21

Merge from eCos master repository on 2000-08-21-22:40:54-BST
author jlarmour
date Fri, 25 Aug 2000 17:32:38 +0000
parents
children 6b5f480c6929
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/redboot/current/src/net/tftp_client.c
@@ -0,0 +1,364 @@
+//==========================================================================
+//
+//      net/tftp_client.c
+//
+//      Stand-alone TFTP support for RedBoot
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// -------------------------------------------                              
+// The contents of this file are subject to the Red Hat eCos Public License 
+// Version 1.1 (the "License"); you may not use this file except in         
+// compliance with the License.  You may obtain a copy of the License at    
+// http://www.redhat.com/                                                   
+//                                                                          
+// Software distributed under the License is distributed on an "AS IS"      
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
+// License for the specific language governing rights and limitations under 
+// the License.                                                             
+//                                                                          
+// The Original Code is eCos - Embedded Configurable Operating System,      
+// released September 30, 1998.                                             
+//                                                                          
+// The Initial Developer of the Original Code is Red Hat.                   
+// Portions created by Red Hat are                                          
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
+// All Rights Reserved.                                                     
+// -------------------------------------------                              
+//                                                                          
+//####COPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: gthomas
+// Date:         2000-07-14
+// Purpose:      
+// Description:  
+//              
+// This code is part of RedBoot (tm).
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+// TFTP client support
+
+#include <net/net.h>
+#include <net/tftp.h>
+#include <net/tftp_support.h>
+
+//
+// Read a file from a host into a local buffer.  Returns the
+// number of bytes actually read, or (-1) if an error occurs.
+// On error, *err will hold the reason.
+//
+int
+tftp_get(char *filename,
+         struct sockaddr_in *server,
+         char *buf,
+         int len,
+         int mode,
+         int *err)
+{
+    int res = 0;
+    int actual_len, data_len, recv_len;
+    static int get_port = 7700;
+    struct sockaddr_in local_addr, from_addr;
+    char data[SEGSIZE+sizeof(struct tftphdr)];
+    struct tftphdr *hdr = (struct tftphdr *)data;
+    char *cp, *fp;
+    struct timeval timeout;
+    int last_good_block = 0;
+    int total_timeouts = 0;
+
+    *err = 0;  // Just in case
+
+    // Create initial request
+    hdr->th_opcode = htons(RRQ);  // Read 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) {
+        fp = "OCTET";
+    } else {
+        *err = TFTP_INVALID;
+        return -1;
+    }
+    while (*fp) *cp++ = *fp++;
+    *cp++ = '\0';
+
+    memset((char *)&local_addr, 0, sizeof(local_addr));
+    local_addr.sin_family = AF_INET;
+    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    local_addr.sin_port = htons(get_port++);
+
+    if (server->sin_port == 0) {
+        server->sin_port = htons(TFTP_PORT);
+    }
+
+    // Send request
+    if (__udp_sendto(data, sizeof(data), server, &local_addr) < 0) {
+        // Problem sending request
+        *err = TFTP_NETERR;
+        return -1;
+    }
+
+    // Read data
+    fp = buf;
+    while (true) {
+        timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+        timeout.tv_usec = 0;
+        recv_len = sizeof(data);
+        if ((data_len = __udp_recvfrom(&data[0], recv_len, &from_addr, &local_addr,  &timeout)) < 0) {
+            // No data, try again
+            if ((++total_timeouts > TFTP_TIMEOUT_MAX) || (last_good_block == 0)) {
+                // Timeout - no data received
+                *err = TFTP_TIMEOUT;
+                return -1;
+            }
+            // Try resending last ACK
+            hdr->th_opcode = htons(ACK);
+            hdr->th_block = htons(last_good_block);
+            if (__udp_sendto(data, 4 /* FIXME */, &from_addr, &local_addr) < 0) {
+                // Problem sending request
+                *err = TFTP_NETERR;
+                return -1;
+            }
+        } else {
+            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;
+                    res += actual_len;
+                    while (data_len-- > 0) {
+                        if (len-- > 0) {
+                            *fp++ = *cp++;
+                        } else {
+                            // Buffer overflow
+                            *err = TFTP_TOOLARGE;
+                            return -1;
+                        }
+                    }
+                    last_good_block++;
+                }
+                // Send out the ACK
+                hdr->th_opcode = htons(ACK);
+                hdr->th_block = htons(last_good_block);
+                if (__udp_sendto(data, 4 /* FIXME */, &from_addr, &local_addr) < 0) {
+                    // Problem sending ACK
+                    *err = TFTP_NETERR;
+                    return -1;
+                }
+                if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
+                    // End of data
+                    return res;
+                }
+            } else 
+            if (ntohs(hdr->th_opcode) == ERROR) {
+                *err = ntohs(hdr->th_code);
+                return -1;
+            } else {
+                // What kind of packet is this?
+                *err = TFTP_PROTOCOL;
+                return -1;
+            }
+        }
+    }
+}
+
+static struct {
+    bool open;
+    int  total_timeouts;
+    int  last_good_block;
+    int  avail, actual_len;
+    struct sockaddr_in local_addr, from_addr;
+    char data[SEGSIZE+sizeof(struct tftphdr)];
+    char *bufp;
+} tftp_stream;
+
+int
+tftp_stream_open(char *filename,
+                 struct sockaddr_in *server,
+                 int mode,
+                 int *err)
+{
+    struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
+    char *cp, *fp;
+    static int get_port = 7700;
+
+    if (tftp_stream.open) {
+        *err = TFTP_INVALID;  // Already open
+        return -1;
+    }
+
+    // Create initial request
+    hdr->th_opcode = htons(RRQ);  // Read 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) {
+        fp = "OCTET";
+    } else {
+        *err = TFTP_INVALID;
+        return -1;
+    }
+    while (*fp) *cp++ = *fp++;
+    *cp++ = '\0';
+
+    memset((char *)&tftp_stream.local_addr, 0, sizeof(tftp_stream.local_addr));
+    tftp_stream.local_addr.sin_family = AF_INET;
+    tftp_stream.local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    tftp_stream.local_addr.sin_port = htons(get_port++);
+
+    if (server->sin_port == 0) {
+        server->sin_port = htons(TFTP_PORT);
+    }
+
+    // Send request
+    if (__udp_sendto(tftp_stream.data, sizeof(tftp_stream.data), 
+                     server, &tftp_stream.local_addr) < 0) {
+        // Problem sending request
+        *err = TFTP_NETERR;
+        return -1;
+    }
+
+    tftp_stream.open = true;
+    tftp_stream.avail = 0;
+    tftp_stream.actual_len = -1;
+    tftp_stream.last_good_block = 0;
+    tftp_stream.total_timeouts = 0;
+
+    return 0;
+}
+
+int
+tftp_stream_close(int *err)
+{
+    tftp_stream.open = false;
+    return 0;
+}
+
+int
+tftp_stream_read(char *buf,
+                 int len,
+                 int *err)
+{
+    int total_bytes = 0;
+    int size, recv_len, data_len;
+    struct timeval timeout;
+    struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
+
+    while (total_bytes < len) {
+        // Move any bytes which we've already read/buffered
+        if (tftp_stream.avail > 0) {
+            size = tftp_stream.avail;
+            if (size > (len - total_bytes)) size = len - total_bytes;
+            memcpy(buf, tftp_stream.bufp, size);
+            buf += size;
+            tftp_stream.bufp += size;
+            tftp_stream.avail -= size;
+            total_bytes += size;
+        } else {
+            if ((tftp_stream.actual_len >= 0) && (tftp_stream.actual_len < SEGSIZE)) {
+                // Out of data
+                break;
+            }
+            timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+            timeout.tv_usec = 0;
+            recv_len = sizeof(tftp_stream.data);
+            if ((data_len = __udp_recvfrom(&tftp_stream.data[0], recv_len, &tftp_stream.from_addr, 
+                                           &tftp_stream.local_addr,  &timeout)) < 0) {
+                // No data, try again
+                if ((++tftp_stream.total_timeouts > TFTP_TIMEOUT_MAX) || 
+                    (tftp_stream.last_good_block == 0)) {
+                    // Timeout - no data received
+                    *err = TFTP_TIMEOUT;
+                    return -1;
+                }
+                // Try resending last ACK
+                hdr->th_opcode = htons(ACK);
+                hdr->th_block = htons(tftp_stream.last_good_block);
+                if (__udp_sendto(tftp_stream.data, 4 /* FIXME */,
+                                 &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
+                    // Problem sending request
+                    *err = TFTP_NETERR;
+                    return -1;
+                }
+            } else {
+                if (ntohs(hdr->th_opcode) == DATA) {
+                    if (ntohs(hdr->th_block) == (tftp_stream.last_good_block+1)) {
+                        // Consume this data
+                        data_len -= 4;  /* Sizeof TFTP header */
+                        tftp_stream.avail = tftp_stream.actual_len = data_len;
+                        tftp_stream.bufp = hdr->th_data;
+                        tftp_stream.last_good_block++;
+                    }
+                    // Send out the ACK
+                    hdr->th_opcode = htons(ACK);
+                    hdr->th_block = htons(tftp_stream.last_good_block);
+                    if (__udp_sendto(tftp_stream.data, 4 /* FIXME */, 
+                                     &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
+                        // Problem sending ACK
+                        *err = TFTP_NETERR;
+                        return -1;
+                    }
+                } else {
+                    if (ntohs(hdr->th_opcode) == ERROR) {
+                        *err = ntohs(hdr->th_code);
+                        return -1;
+                    } else {
+                        // What kind of packet is this?
+                        *err = TFTP_PROTOCOL;
+                        return -1;
+                    }
+                }
+            }
+        }
+    }
+    return total_bytes;
+}
+
+char *
+tftp_error(int err)
+{
+    char *errmsg = "Unknown error";
+
+    switch (err) {
+    case TFTP_ENOTFOUND:
+        return "file not found";
+    case TFTP_EACCESS:
+        return "access violation";
+    case TFTP_ENOSPACE:
+        return "disk full or allocation exceeded";
+    case TFTP_EBADOP:
+        return "illegal TFTP operation";
+    case TFTP_EBADID:
+        return "unknown transfer ID";
+    case TFTP_EEXISTS:
+        return "file already exists";
+    case TFTP_ENOUSER:
+        return "no such user";
+    case TFTP_TIMEOUT:
+        return "operation timed out";
+    case TFTP_NETERR:
+        return "some sort of network error";
+    case TFTP_INVALID:
+        return "invalid parameter";
+    case TFTP_PROTOCOL:
+        return "protocol violation";
+    case TFTP_TOOLARGE:
+        return "file is larger than buffer";
+    }
+    return errmsg;
+}