[syslinux:lwip] core: pxe Add native support for http urls

syslinux-bot for Eric W. Biederman ebiederm at xmission.com
Fri Apr 22 20:06:03 PDT 2011


Commit-ID:  4992782a6f87f02f47306a06249d94c93154b4cd
Gitweb:     http://syslinux.zytor.com/commit/4992782a6f87f02f47306a06249d94c93154b4cd
Author:     Eric W. Biederman <ebiederm at xmission.com>
AuthorDate: Mon, 11 Apr 2011 04:22:57 -0700
Committer:  Eric W. Biederman <ebiederm at xmission.com>
CommitDate: Tue, 12 Apr 2011 14:41:13 -0700

core: pxe Add native support for http urls

Signed-off-by: Eric W. Biederman <ebiederm at xmission.com>


---
 core/fs/pxe/http.c |  375 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 core/fs/pxe/pxe.c  |    1 +
 core/fs/pxe/pxe.h  |    3 +
 3 files changed, 379 insertions(+), 0 deletions(-)

diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c
new file mode 100644
index 0000000..6af55d5
--- /dev/null
+++ b/core/fs/pxe/http.c
@@ -0,0 +1,375 @@
+#include <ctype.h>
+#include "pxe.h"
+#include "../../../version.h"
+#include <lwip/api.h>
+
+static void http_close_file(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    if (socket->buf) {
+	netbuf_delete(socket->buf);
+        socket->buf = NULL;
+    }
+    if (socket->conn) {
+	netconn_delete(socket->conn);
+	socket->conn = NULL;
+    }
+}
+
+static void http_fill_buffer(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    void *data;
+    u16_t len;
+    err_t err;
+
+    /* Clean up or advance an inuse netbuf */
+    if (socket->buf) {
+	if (netbuf_next(socket->buf) < 0) {
+	    netbuf_delete(socket->buf);
+	    socket->buf = NULL;
+	}
+    }
+    /* If needed get a new netbuf */
+    if (!socket->buf) {
+	socket->buf = netconn_recv(socket->conn);
+	if (!socket->buf) {
+	    socket->tftp_goteof = 1;
+	    if (inode->size == -1)
+		inode->size = socket->tftp_filepos;
+	    http_close_file(inode);
+	    return;
+	}
+    }
+    /* Report the current fragment of the netbuf */
+    err = netbuf_data(socket->buf, &data, &len);
+    if (err) {
+	printf("netbuf_data err: %d\n", err);
+	kaboom();
+    }
+    socket->tftp_dataptr = data;
+    socket->tftp_filepos += len;
+    socket->tftp_bytesleft = len;
+    return;
+}
+
+static bool is_tspecial(int ch)
+{
+    bool tspecial = false;
+    switch(ch) {
+    case '(':  case ')':  case '<':  case '>':  case '@':
+    case ',':  case ';':  case ':':  case '\\': case '"':
+    case '/':  case '[':  case ']':  case '?':  case '=':
+    case '{':  case '}':  case ' ':  case '\t':
+	tspecial = true;
+    	break;
+    }
+    return tspecial;
+}
+
+static bool is_ctl(int ch)
+{
+    return ch < 0x20;
+}
+
+static bool is_token(int ch)
+{
+    /* Can by antying except a ctl character or a tspecial */
+    return !is_ctl(ch) && !is_tspecial(ch);
+}
+
+static bool append_ch(char *str, size_t size, size_t *pos, int ch)
+{
+    bool success = true;
+    if ((*pos + 1) >= size) {
+	*pos = 0;
+	success = false;    
+    } else {
+	str[*pos] = ch;
+	str[*pos + 1] = '\0';
+	*pos += 1;
+    }
+    return success;
+}
+
+void http_open(struct inode *inode, const char *url)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    char header_buf[512];
+    int header_len;
+    const char *host, *path, *next;
+    size_t host_len;
+    uint16_t port;
+    char field_name[20];
+    char field_value[256];
+    size_t field_name_len, field_value_len;
+    err_t err;
+    enum state {
+	st_httpver,
+	st_stcode,
+	st_skipline,
+	st_fieldfirst,
+	st_fieldname,
+	st_fieldvalue,
+	st_skip_fieldname,
+	st_skip_fieldvalue,
+	st_eoh,
+    } state;
+    struct ip_addr addr;
+    char location[256];
+    uint32_t content_length; /* same as inode->size */
+    size_t response_size;
+    int status;
+    int pos;
+    int redirect_count;
+
+    socket->fill_buffer = http_fill_buffer;
+    socket->close = http_close_file;
+    redirect_count = 0;
+
+restart:
+    /* Reset all of the variables */
+    inode->size = content_length = -1;
+    location[0] = '\0';
+    field_name[0] = '\0';
+    field_value[0] = '\0';
+    field_name_len = 0;
+    field_value_len = 0;
+    
+    /* Skip http:// */
+    host =  url + 7;
+
+    /* Find the end of the hostname */
+    next = host;
+    while (*next && *next != '/' && *next != ':')
+	next++;
+    host_len = next - host;
+
+    /* Obvious url formatting errors */
+    if (!*next || (!host_len && *next == ':'))
+	goto fail;
+
+    /* Compute the dest port */
+    port = 80;
+    if (*next == ':') {
+	port = 0;
+	for (next++; (*next >= '0' && *next <= '9'); next++)
+	    port = (port * 10) * (*next - '0');
+    }
+
+    /* Ensure I have properly parsed the port */
+    if (*next != '/')
+	goto fail;
+
+    path = next;
+
+    /* Resolve the hostname */
+    if (!host_len) {
+	addr.addr = IPInfo.serverip;
+    } else {
+	if (parse_dotquad(host, &addr.addr) != (host + host_len)) {
+	    addr.addr = dns_resolv(host);
+	    if (!addr.addr)
+		goto fail;
+	}
+    }
+
+    /* Start the http connection */
+    socket->conn = netconn_new(NETCONN_TCP);
+    if (!socket->conn) {
+	printf("netconn_new failed\n");
+        return;
+    }
+
+    err = netconn_connect(socket->conn, &addr, port);
+    if (err) {
+	printf("netconn_connect error %d\n", err);
+	goto fail;
+    }
+
+    header_len = snprintf(header_buf, sizeof header_buf,
+				"GET %s HTTP/1.0\r\n"
+	    			"Host: %*.*s\r\n"
+	    			"User-Agent: PXELINUX/%s\r\n"
+	    			"Connection: close\r\n"
+				"\r\n",
+	    			path, host_len, host_len, host, VERSION_STR);
+
+    /* If we tried to overflow our buffer abort */
+    if (header_len > sizeof header_buf)
+	goto fail;
+
+    err = netconn_write(socket->conn, header_buf, header_len, NETCONN_NOCOPY);
+    if (err) {
+	printf("netconn_write error %d\n", err);
+	goto fail;
+    }
+
+    /* Parse the HTTP header */
+    state = st_httpver;
+    pos = 0;
+    status = 0;
+    response_size = 0;
+
+    while (state != st_eoh) {
+	int ch = pxe_getc(inode);
+	/* Eof before I finish paring the header */
+	if (ch == -1)
+	    goto fail;
+#if 0
+        printf("%c", ch);
+#endif
+	response_size++;
+	if (ch == '\r' || ch == '\0')
+	    continue;
+	switch (state) {
+	case st_httpver:
+	    if (ch == ' ') {
+		state = st_stcode;
+		pos = 0;
+	    }
+	    break;
+
+	case st_stcode:
+	    if (ch < '0' || ch > '9')
+	       goto fail;
+	    status = (status*10) + (ch - '0');
+	    if (++pos == 3)
+		state = st_skipline;
+	    break;
+
+	case st_skipline:
+	    if (ch == '\n')
+		state = st_fieldfirst;
+	    break;
+
+	case st_fieldfirst:
+	    if (ch == '\n')
+		state = st_eoh;
+	    else if (isspace(ch)) {
+		/* A continuation line */
+		state = st_fieldvalue;
+		goto fieldvalue;
+	    }
+	    else if (is_token(ch)) {
+		/* Process the previous field before starting on the next one */
+		if (strcasecmp(field_name, "Content-Length") == 0) {
+		    next = field_value;
+		    /* Skip leading whitespace */
+		    while (isspace(*next))
+			next++;
+		    content_length = 0;
+		    for (;(*next >= '0' && *next <= '9'); next++) {
+			if ((content_length * 10) < content_length)
+			    break;
+			content_length = (content_length * 10) + (*next - '0');
+		    }
+		    /* In the case of overflow or other error ignore
+		     * Content-Length.
+		     */
+		    if (*next)
+			content_length = -1;
+		}
+		else if (strcasecmp(field_name, "Location") == 0) {
+		    next = field_value;
+		    /* Skip leading whitespace */
+		    while (isspace(*next))
+			next++;
+		    strcpy(location, next);
+		}
+		/* Start the field name and field value afress */
+		field_name_len = 1;
+		field_name[0] = ch;
+		field_name[1] = '\0';
+		field_value_len = 0;
+		field_value[0] = '\0';
+		state = st_fieldname;
+	    }
+	    else /* Bogus try to recover */
+		state = st_skipline;
+	    break;
+
+	case st_fieldname:
+	    if (ch == ':' ) {
+		state = st_fieldvalue;
+	    }
+	    else if (is_token(ch)) {
+		if (!append_ch(field_name, sizeof field_name, &field_name_len, ch))
+		    state = st_skip_fieldname;
+	    }
+	    /* Bogus cases try to recover */
+	    else if (ch == '\n')
+		state = st_fieldfirst;
+	    else 
+		state = st_skipline;
+	    break;
+
+	 case st_fieldvalue:
+	    if (ch == '\n')
+		state = st_fieldfirst;
+	    else {
+	    fieldvalue:
+		if (!append_ch(field_value, sizeof field_value, &field_value_len, ch))
+		    state = st_skip_fieldvalue;
+	    }
+	    break;
+
+	/* For valid fields whose names are longer than I choose to support. */
+	case st_skip_fieldname:
+	    if (ch == ':')
+		state = st_skip_fieldvalue;
+	    else if (is_token(ch))
+		state = st_skip_fieldname;
+	    /* Bogus cases try to recover */
+	    else if (ch == '\n')
+		state = st_fieldfirst;
+	    else
+		state = st_skipline;
+	    break;
+
+	/* For valid fields whose bodies are longer than I choose to support. */
+	case st_skip_fieldvalue:
+	    if (ch == '\n')
+		state = st_fieldfirst;
+	    break;
+	
+	case st_eoh:
+	   break; /* Should never happen */
+	}
+    }
+
+    if (state != st_eoh)
+	status = 0;
+
+    switch (status) {
+    case 200:
+	/*
+	 * All OK, need to mark header data consumed and set up a file
+	 * structure...
+	 */
+	/* Treat the remainder of the bytes as data */
+	socket->tftp_filepos -= response_size;
+	break;
+    case 301:
+    case 302:
+    case 303:
+    case 307:
+	/* A redirect */
+	if (!location[0])
+	    goto fail;
+	redirect_count++;
+	if (redirect_count > 5)
+	    goto fail;
+	url = location;
+	goto restart;
+	break;
+    default:
+	goto fail;
+	break;
+    }
+    return;
+fail:
+    inode->size = 0;
+    http_close_file(inode);
+    return;
+}
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index c173cd0..96c6e7b 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -35,6 +35,7 @@ static struct url_open {
     const char *scheme;
     void (*open)(struct inode *inode, const char *url);
 } url_table[] = {
+    { "http", http_open },
     { NULL, NULL },
 };
 
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index 59661bc..f41bf82 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -270,4 +270,7 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port,
 void gpxe_open(struct inode *inode, const char *url);
 #define GPXE 1
 
+/* http.c */
+void http_open(struct inode *inode, const char *url);
+
 #endif /* pxe.h */



More information about the Syslinux-commits mailing list