[syslinux:lwip] pxe: add support for FTP

syslinux-bot for H. Peter Anvin hpa at zytor.com
Sun Apr 24 21:03:23 PDT 2011


Commit-ID:  5fbdb5eba5640f1147474fafa73fbb66c9a406a9
Gitweb:     http://syslinux.zytor.com/commit/5fbdb5eba5640f1147474fafa73fbb66c9a406a9
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Sun, 24 Apr 2011 13:49:30 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Sun, 24 Apr 2011 13:49:30 -0700

pxe: add support for FTP

Add support for the FTP protocol.

Signed-off-by: H. Peter Anvin <hpa at zytor.com>


---
 core/fs/pxe/ftp.c |  267 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 core/fs/pxe/pxe.c |    3 +-
 core/fs/pxe/pxe.h |    9 ++-
 3 files changed, 276 insertions(+), 3 deletions(-)

diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c
new file mode 100644
index 0000000..0c03355
--- /dev/null
+++ b/core/fs/pxe/ftp.c
@@ -0,0 +1,267 @@
+/*
+ * ftp.c
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include <netinet/in.h>
+#include <lwip/api.h>
+#include "pxe.h"
+#include "thread.h"
+#include "strbuf.h"
+#include "url.h"
+
+static int ftp_cmd_response(struct inode *inode, const char *cmd,
+			    const char *cmd_arg,
+			    uint8_t *pasv_data, int *pn_ptr)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    int c;
+    int pos, code;
+    int pb, pn;
+    bool ps;
+    bool first_line, done;
+    err_t err;
+    char cmd_buf[4096];
+    int cmd_len;
+    const char *p;
+    char *q;
+
+    if (cmd) {
+	cmd_len = strlcpy(cmd_buf, cmd, sizeof cmd_buf);
+	if (cmd_len >= sizeof cmd_buf - 3)
+	    return -1;
+	q = cmd_buf + cmd_len;
+
+	if (cmd_arg) {
+	    p = cmd_arg;
+
+	    *q++ = ' ';
+	    cmd_len++;
+	    while (*p) {
+		if (++cmd_len < sizeof cmd_buf) *q++ = *p;
+		if (*p == '\r')
+		    if (++cmd_len < sizeof cmd_buf) *q++ = '\0';
+		p++;
+	    }
+
+	    if (cmd_len >= sizeof cmd_buf - 2)
+		return -1;
+	}
+
+	*q++ = '\r';
+	*q++ = '\n';
+	cmd_len += 2;
+
+	err = netconn_write(socket->conn, cmd_buf, cmd_len, NETCONN_COPY);
+	if (err)
+	    return -1;
+    }
+
+    pos = code = pn = pb = 0;
+    ps = false;
+    first_line = true;
+    done = false;
+
+    while ((c = pxe_getc(inode)) >= 0) {
+	//printf("%c", c);
+
+	if (c == '\n') {
+	    pos = 0;
+	    if (done) {
+		if (pn) {
+		    pn += ps;
+		    if (pn_ptr)
+			*pn_ptr = pn;
+		}
+		//printf("FTP response: %u\n", code);
+		return code;
+	    }
+	    first_line = false;
+	    continue;
+	}
+
+	switch (pos++) {
+	case 0:
+	case 1:
+	case 2:
+	    if (c < '0' || c > '9') {
+		if (first_line)
+		    return -1;
+		else
+		    pos = 4;	/* Skip this line */
+	    } else {
+		code = (code*10) + (c - '0');
+	    }
+	    break;
+
+	case 3:
+	    pn = pb = 0;
+	    ps = false;
+	    if (c == ' ')
+		done = true;
+	    else if (c == '-')
+		done = false;
+	    else if (first_line)
+		return -1;
+	    else
+		done = false;
+	    break;
+
+	default:
+	    if (pasv_data) {
+		if (c >= '0' && c <= '9') {
+		    pb = (pb*10) + (c-'0');
+		    if (pn < 6)
+			pasv_data[pn] = pb;
+		    ps = true;
+		} else if (c == ',') {
+		    pn++;
+		    pb = 0;
+		    ps = false;
+		} else if (pn) {
+		    pn += ps;
+		    if (pn_ptr)
+			*pn_ptr = pn;
+		    pn = pb = 0;
+		    ps = false;
+		}
+	    }
+	    break;
+	}
+    }
+
+    return -1;
+}
+
+static void ftp_free(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+
+    if (socket->ctl) {
+	tcp_close_file(socket->ctl);
+	free_socket(socket->ctl);
+	socket->ctl = NULL;
+    }
+    tcp_close_file(inode);
+}
+
+static void ftp_close_file(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket  = PVT(inode);
+    struct pxe_pvt_inode *ctlsock;
+    int resp;
+
+    //printf("In ftp_close_file\n");
+
+    ctlsock = socket->ctl ? PVT(socket->ctl) : NULL;
+    if (ctlsock->conn) {
+	resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL);
+	while (resp == 226) {
+	    resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
+	}
+    }
+    ftp_free(inode);
+}
+
+void ftp_open(struct url_info *url, struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    struct pxe_pvt_inode *ctlsock;
+    struct ip_addr addr;
+    uint8_t pasv_data[6];
+    int pasv_bytes;
+    int resp;
+    err_t err;
+
+    inode->size = 0;
+
+    if (!url->port)
+	url->port = 21;
+
+    url_unescape(url->path, 0);
+
+    socket->fill_buffer = tcp_fill_buffer;
+    socket->close = ftp_close_file;
+
+    /* Allocate a socket for the control connection */
+    socket->ctl = alloc_inode(inode->fs, 0, sizeof(struct pxe_pvt_inode));
+    if (!socket->ctl)
+	return;
+    ctlsock = PVT(socket->ctl);
+    ctlsock->fill_buffer = tcp_fill_buffer;
+    ctlsock->close = tcp_close_file;
+
+    ctlsock->conn = netconn_new(NETCONN_TCP);
+    if (!ctlsock->conn)
+	goto err_free;
+    addr.addr = url->ip;
+    err = netconn_connect(ctlsock->conn, &addr, url->port);
+    if (err)
+	goto err_delete;
+
+    do {
+	resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
+    } while (resp == 120);
+    if (resp != 220)
+	goto err_disconnect;
+
+    if (!url->user)
+	url->user = "anonymous";
+    if (!url->passwd)
+	url->passwd = "pxelinux@";
+
+    resp = ftp_cmd_response(socket->ctl, "USER", url->user, NULL, NULL);
+    if (resp != 202 && resp != 230) {
+	if (resp != 331)
+	    goto err_disconnect;
+
+	resp = ftp_cmd_response(socket->ctl, "PASS", url->passwd, NULL, NULL);
+	if (resp != 230)
+	    goto err_disconnect;
+    }
+
+    resp = ftp_cmd_response(socket->ctl, "TYPE", "I", NULL, NULL);
+    if (resp != 200)
+	goto err_disconnect;
+
+    resp = ftp_cmd_response(socket->ctl, "PASV", NULL, pasv_data, &pasv_bytes);
+    //printf("%u PASV %u bytes %u,%u,%u,%u,%u,%u\n",
+    //resp, pasv_bytes, pasv_data[0], pasv_data[1], pasv_data[2],
+    //pasv_data[3], pasv_data[4], pasv_data[5]);
+    if (resp != 227 || pasv_bytes != 6)
+	goto err_disconnect;
+
+    socket->conn = netconn_new(NETCONN_TCP);
+    if (!socket->conn)
+	goto err_disconnect;
+    err = netconn_connect(socket->conn, (struct ip_addr *)&pasv_data[0],
+			  ntohs(*(uint16_t *)&pasv_data[4]));
+    if (err)
+	goto err_disconnect;
+
+    resp = ftp_cmd_response(socket->ctl, "RETR", url->path, NULL, NULL);
+    if (resp != 125 && resp != 150)
+	goto err_disconnect;
+
+    inode->size = -1;
+    return;			/* Sucess! */
+
+err_disconnect:
+    if (ctlsock->conn)
+	netconn_write(ctlsock->conn, "QUIT\r\n", 6, NETCONN_NOCOPY);
+    if (socket->conn)
+	netconn_delete(socket->conn);
+    if (ctlsock->buf)
+	netbuf_delete(ctlsock->buf);
+err_delete:
+    if (ctlsock->conn)
+	netconn_delete(ctlsock->conn);
+err_free:
+    free_socket(socket->ctl);
+}
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index 58866af..71532d6 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -41,6 +41,7 @@ static struct url_scheme {
 } url_schemes[] = {
     { "tftp", tftp_open },
     { "http", http_open },
+    { "ftp",  ftp_open },
     { NULL, NULL },
 };
 
@@ -63,7 +64,7 @@ static struct inode *allocate_socket(struct fs_info *fs)
     return inode;
 }
 
-static void free_socket(struct inode *inode)
+void free_socket(struct inode *inode)
 {
     struct pxe_pvt_inode *socket = PVT(inode);
 
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index db1a566..699cf65 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -137,10 +137,11 @@ struct pxe_pvt_inode {
     char    *tftp_dataptr;     /* Pointer to available data */
     uint8_t  tftp_goteof;      /* 1 if the EOF packet received */
     uint8_t  tftp_unused[3];   /* Currently unused */
+    char    *tftp_pktbuf;      /* Packet buffer */
+    struct inode *ctl;	       /* Control connection (for FTP) */
     void (*fill_buffer)(struct inode *inode);
     void (*close)(struct inode *inode);
-    char    *tftp_pktbuf;      /* Packet buffer */
-} __attribute__ ((packed));
+};
 
 #define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt))
 
@@ -223,6 +224,7 @@ int pxe_call(int, void *);
 extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
 int pxe_getc(struct inode *inode);
 void url_set_ip(struct url_info *);
+void free_socket(struct inode *inode);
 
 /* undiif.c */
 int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw);
@@ -252,6 +254,9 @@ void gpxe_open(struct inode *inode, const char *url);
 /* http.c */
 void http_open(struct url_info *url, struct inode *inode);
 
+/* ftp.c */
+void ftp_open(struct url_info *url, struct inode *inode);
+
 /* tcp.c */
 void tcp_close_file(struct inode *inode);
 void tcp_fill_buffer(struct inode *inode);



More information about the Syslinux-commits mailing list