diff packages/net/common/current/src/tftp_client.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children 42332443fea2
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/net/common/current/src/tftp_client.c
@@ -0,0 +1,402 @@
+//==========================================================================
+//
+//      lib/tftp_client.c
+//
+//      TFTP client support
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD or other sources,
+// and are covered by the appropriate copyright disclaimers included herein.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: gthomas
+// Date:         2000-04-06
+// Purpose:      
+// Description:  
+//              
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+// TFTP client support
+
+#include <network.h>
+#include <arpa/tftp.h>
+#include <tftp_support.h>
+
+#define min(x,y) (x<y ? x : y)
+
+//
+// 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 s, actual_len, data_len, recv_len, from_len;
+    static int get_port = 7700;
+    struct sockaddr_in local_addr, server_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;
+    struct servent *server_info;
+    fd_set fds;
+    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';
+    server_info = getservbyname("tftp", "udp");
+    if (server_info == (struct servent *)0) {
+        *err = TFTP_NETERR;
+        return -1;
+    }
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0) {
+        // Couldn't open a communications channel
+        *err = TFTP_NETERR;
+        return -1;
+    }
+    memset((char *)&local_addr, 0, sizeof(local_addr));
+    local_addr.sin_family = AF_INET;
+    local_addr.sin_len = sizeof(local_addr);
+    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    local_addr.sin_port = htons(get_port++);
+    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
+        // Problem setting up my end
+        *err = TFTP_NETERR;
+        close(s);
+        return -1;
+    }
+    memset((char *)&server_addr, 0, sizeof(server_addr));
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_len = sizeof(server_addr);
+    server_addr.sin_addr = server->sin_addr;
+    if (server->sin_port == 0) {
+        server_addr.sin_port = server_info->s_port; // Network order already
+    } else {
+        server_addr.sin_port = server->sin_port;
+    }
+
+    // Send request
+    if (sendto(s, data, sizeof(data), 0, 
+               (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
+        // Problem sending request
+        *err = TFTP_NETERR;
+        close(s);
+        return -1;
+    }
+
+    // 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) {
+            if ((++total_timeouts > TFTP_TIMEOUT_MAX) || (last_good_block == 0)) {
+                // Timeout - no data received
+                *err = TFTP_TIMEOUT;
+                close(s);
+                return -1;
+            }
+            // Try resending last ACK
+            hdr->th_opcode = htons(ACK);
+            hdr->th_block = htons(last_good_block);
+            if (sendto(s, data, 4 /* FIXME */, 0, 
+                       (struct sockaddr *)&from_addr, from_len) < 0) {
+                // Problem sending request
+                *err = TFTP_NETERR;
+                close(s);
+                return -1;
+            }
+        } else {
+            recv_len = sizeof(data);
+            from_len = sizeof(from_addr);
+            if ((data_len = recvfrom(s, &data, recv_len, 0, 
+                                     (struct sockaddr *)&from_addr, &from_len)) < 0) {
+                // What happened?
+                *err = TFTP_NETERR;
+                close(s);
+                return -1;
+            }
+            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;
+                            close(s);
+                            return -1;
+                        }
+                    }
+                    last_good_block++;
+                }
+                // Send out the ACK
+                hdr->th_opcode = htons(ACK);
+                hdr->th_block = htons(last_good_block);
+                if (sendto(s, data, 4 /* FIXME */, 0, 
+                           (struct sockaddr *)&from_addr, from_len) < 0) {
+                    // Problem sending request
+                    *err = TFTP_NETERR;
+                    close(s);
+                    return -1;
+                }
+                if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
+                    // End of data
+                    close(s);
+                    return res;
+                }
+            } else 
+            if (ntohs(hdr->th_opcode) == ERROR) {
+                *err = ntohs(hdr->th_code);
+                close(s);
+                return -1;
+            } else {
+                // What kind of packet is this?
+                *err = TFTP_PROTOCOL;
+                close(s);
+                return -1;
+            }
+        }
+    }
+}
+
+//
+// Send data to a file on a server via TFTP.
+//
+int
+tftp_put(char *filename,
+         struct sockaddr_in *server,
+         char *buf,
+         int len,
+         int mode,
+         int *err)
+{
+    int res = 0;
+    int s, actual_len, data_len, recv_len, from_len;
+    static int put_port = 7800;
+    struct sockaddr_in local_addr, server_addr, from_addr;
+    char data[SEGSIZE+sizeof(struct tftphdr)];
+    struct tftphdr *hdr = (struct tftphdr *)data;
+    char *cp, *fp, *sfp;
+    struct timeval timeout;
+    int last_good_block = 0;
+    struct servent *server_info;
+    fd_set fds;
+    int total_timeouts = 0;
+
+    *err = 0;  // Just in case
+
+    server_info = getservbyname("tftp", "udp");
+    if (server_info == (struct servent *)0) {
+        *err = TFTP_NETERR;
+        return -1;
+    }
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0) {
+        // Couldn't open a communications channel
+        *err = TFTP_NETERR;
+        return -1;
+    }
+    memset((char *)&local_addr, 0, sizeof(local_addr));
+    local_addr.sin_family = AF_INET;
+    local_addr.sin_len = sizeof(local_addr);
+    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    local_addr.sin_port = htons(put_port++);
+    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
+        // Problem setting up my end
+        *err = TFTP_NETERR;
+        close(s);
+        return -1;
+    }
+    memset((char *)&server_addr, 0, sizeof(server_addr));
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_len = sizeof(server_addr);
+    server_addr.sin_addr = server->sin_addr;
+    if (server->sin_port == 0) {
+        server_addr.sin_port = server_info->s_port; // Network order already
+    } else {
+        server_addr.sin_port = server->sin_port;
+    }
+
+    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) {
+            fp = "OCTET";
+        } else {
+            *err = TFTP_INVALID;
+            return -1;
+        }
+        while (*fp) *cp++ = *fp++;
+        *cp++ = '\0';
+        // Send request
+        if (sendto(s, data, sizeof(data), 0, 
+                   (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
+            // Problem sending request
+            *err = TFTP_NETERR;
+            close(s);
+            return -1;
+        }
+        // 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 ACK received
+                *err = TFTP_TIMEOUT;
+                close(s);
+                return -1;
+            }
+        } else {
+            recv_len = sizeof(data);
+            from_len = sizeof(from_addr);
+            if ((data_len = recvfrom(s, &data, recv_len, 0, 
+                                     (struct sockaddr *)&from_addr, &from_len)) < 0) {
+                // What happened?
+                *err = TFTP_NETERR;
+                close(s);
+                return -1;
+            }
+            if (ntohs(hdr->th_opcode) == ACK) {
+                // Write request accepted - start sending data
+                break;
+            } else 
+            if (ntohs(hdr->th_opcode) == ERROR) {
+                *err = ntohs(hdr->th_code);
+                close(s);
+                return -1;
+            } else {
+                // What kind of packet is this?
+                *err = TFTP_PROTOCOL;
+                close(s);
+                return -1;
+            }
+        }
+    }
+
+    // Send data
+    sfp = buf;
+    last_good_block = 1;
+    while (res < len) {
+        // Build packet of data to send
+        data_len = min(SEGSIZE, len-res);
+        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, 
+                   (struct sockaddr *)&from_addr, from_len) < 0) {
+            // Problem sending request
+            *err = TFTP_NETERR;
+            close(s);
+            return -1;
+        }
+        // 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;
+                close(s);
+                return -1;
+            }
+        } else {
+            recv_len = sizeof(data);
+            from_len = sizeof(from_addr);
+            if ((data_len = recvfrom(s, &data, recv_len, 0, 
+                                     (struct sockaddr *)&from_addr, &from_len)) < 0) {
+                // What happened?
+                *err = TFTP_NETERR;
+                close(s);
+                return -1;
+            }
+            if (ntohs(hdr->th_opcode) == ACK) {
+                if (ntohs(hdr->th_block) == last_good_block) {
+                    // Advance pointers, etc
+                    sfp = fp;
+                    res += (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) {
+                *err = ntohs(hdr->th_code);
+                close(s);
+                return -1;
+            } else {
+                // What kind of packet is this?
+                *err = TFTP_PROTOCOL;
+                close(s);
+                return -1;
+            }
+        }
+    }
+    close(s);
+    return res;
+}
+