[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