[syslinux:lwip] pxe: use the unified URL parsing framework

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


Commit-ID:  0eefacc5fd49dae600ab86b14557578efd6a2508
Gitweb:     http://syslinux.zytor.com/commit/0eefacc5fd49dae600ab86b14557578efd6a2508
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Sat, 23 Apr 2011 16:04:00 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Sat, 23 Apr 2011 16:08:44 -0700

pxe: use the unified URL parsing framework

Use the unified URL parsing framework for TFTP and HTTP.  This should
also make it easier to add new protocols (e.g. FTP) in the near
future.

Note that HTTP redirects are still handled wrong: they really should
be sent all the way back to the top of URL parsing; there are sites in
the field which redirect to FTP URLs, for example.

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


---
 core/fs/pxe/dhcp_option.c |    9 +--
 core/fs/pxe/dnsresolv.c   |   71 +++++--------
 core/fs/pxe/http.c        |   98 +++++++------------
 core/fs/pxe/pxe.c         |  240 ++++++++-------------------------------------
 core/fs/pxe/pxe.h         |   39 +-------
 core/fs/pxe/tftp.c        |   24 +++--
 core/fs/pxe/tftp.h        |   48 +++++++++
 core/fs/pxe/url.h         |   13 ++-
 core/fs/pxe/urlparse.c    |  146 +++++++++++++++------------
 9 files changed, 265 insertions(+), 423 deletions(-)

diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c
index 50f2de0..47031fa 100644
--- a/core/fs/pxe/dhcp_option.c
+++ b/core/fs/pxe/dhcp_option.c
@@ -48,13 +48,8 @@ static void dns_servers(const void *data, int opt_len)
 
 static void local_domain(const void *data, int opt_len)
 {
-    char buffer[256];
-    char *ld = LocalDomain;
-
-    memcpy(buffer, data, opt_len);
-    buffer[opt_len] = 0;
-
-    dns_mangle(&ld, buffer);
+    memcpy(LocalDomain, data, opt_len);
+    LocalDomain[opt_len] = 0;
 }
 
 static void vendor_encaps(const void *data, int opt_len)
diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c
index b827edb..a4bbf1f 100644
--- a/core/fs/pxe/dnsresolv.c
+++ b/core/fs/pxe/dnsresolv.c
@@ -50,42 +50,35 @@ struct dnsrr {
 
 uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
 
-
 /*
- * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
- * number of dots encountered. On return, *dst is updated.
+ * parse the ip_str and return the ip address with *res.
+ * return true if the whole string was consumed and the result
+ * was valid.
+ *
  */
-int dns_mangle(char **dst, const char *p)
+static bool parse_dotquad(const char *ip_str, uint32_t *res)
 {
-    char *q = *dst;
-    char *count_ptr;
-    char c;
-    int dots = 0;
-
-    count_ptr = q;
-    *q++ = 0;
-
-    while (1) {
-        c = *p++;
-        if (c == 0 || c == ':' || c == '/')
-            break;
-        if (c == '.') {
-            dots++;
-            count_ptr = q;
-            *q++ = 0;
-            continue;
+    const char *p = ip_str;
+    uint8_t part = 0;
+    uint32_t ip = 0;
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        while (is_digit(*p)) {
+            part = part * 10 + *p - '0';
+            p++;
         }
+        if (i != 3 && *p != '.')
+            return false;
 
-        *count_ptr += 1;
-        *q++ = c;
+        ip = (ip << 8) | part;
+        part = 0;
+        p++;
     }
+    p--;
 
-    if (*count_ptr)
-        *q++ = 0;
-
-    /* update the strings */
-    *dst = q;
-    return dots;
+    *res = htonl(ip);
+    return *p == '\0';
 }
 
 /*
@@ -98,32 +91,22 @@ uint32_t dns_resolv(const char *name)
 {
     err_t err;
     struct ip_addr ip;
-    char dns_name[PKTBUF_SIZE];
-    const char *src;
-    char *dst;
+
+    /* If it is a valid dot quad, just return that value */
+    if (parse_dotquad(name, &ip.addr))
+	return ip.addr;
 
     /* Make sure we have at least one valid DNS server */
     if (!dns_getserver(0).addr)
 	return 0;
 
-    /* Copy the name to look up to ensure it is null terminated */
-    for (dst = dns_name, src = name; *src; src++, dst++) {
-	int ch = *src;
-	if (ch == '\0' || ch == ':' || ch == '/') {
-	    *dst = '\0';
-	    break;
-	}
-	*dst = ch;
-    }
-
-    err = netconn_gethostbyname(dns_name, &ip);
+    err = netconn_gethostbyname(name, &ip);
     if (err)
 	return 0;
 
     return ip.addr;
 }
 
-
 /*
  * the one should be called from ASM file
  */
diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c
index 6af55d5..2f5a645 100644
--- a/core/fs/pxe/http.c
+++ b/core/fs/pxe/http.c
@@ -1,7 +1,10 @@
 #include <ctype.h>
+#include <lwip/api.h>
 #include "pxe.h"
 #include "../../../version.h"
-#include <lwip/api.h>
+#include "url.h"
+
+#define HTTP_PORT	80
 
 static void http_close_file(struct inode *inode)
 {
@@ -92,16 +95,14 @@ static bool append_ch(char *str, size_t size, size_t *pos, int ch)
     return success;
 }
 
-void http_open(struct inode *inode, const char *url)
+void http_open(struct url_info *url, struct inode *inode)
 {
     struct pxe_pvt_inode *socket = PVT(inode);
-    char header_buf[512];
+    char header_buf[4096];
     int header_len;
-    const char *host, *path, *next;
-    size_t host_len;
-    uint16_t port;
+    const char *next;
     char field_name[20];
-    char field_value[256];
+    char field_value[1024];
     size_t field_name_len, field_value_len;
     err_t err;
     enum state {
@@ -116,7 +117,7 @@ void http_open(struct inode *inode, const char *url)
 	st_eoh,
     } state;
     struct ip_addr addr;
-    char location[256];
+    char location[1024], new_url[1024];
     uint32_t content_length; /* same as inode->size */
     size_t response_size;
     int status;
@@ -130,49 +131,6 @@ void http_open(struct inode *inode, const char *url)
 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);
@@ -181,23 +139,31 @@ restart:
         return;
     }
 
-    err = netconn_connect(socket->conn, &addr, port);
+    addr.addr = url->ip;
+    if (!url->port)
+	url->port = HTTP_PORT;
+    err = netconn_connect(socket->conn, &addr, url->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 */
+    strcpy(header_buf, "GET /");
+    header_len = 5;
+    header_len += url_escape_unsafe(header_buf+5, url->path,
+				    sizeof header_buf - 5);
     if (header_len > sizeof header_buf)
-	goto fail;
+	goto fail;		/* Buffer overflow */
+    header_len += snprintf(header_buf + header_len,
+			   sizeof header_buf - header_len,
+			   " HTTP/1.0\r\n"
+			   "Host: %s\r\n"
+			   "User-Agent: PXELINUX/%s\r\n"
+			   "Connection: close\r\n"
+			   "\r\n",
+			   url->host, VERSION_STR);
+    if (header_len > sizeof header_buf)
+	goto fail;		/* Buffer overflow */
 
     err = netconn_write(socket->conn, header_buf, header_len, NETCONN_NOCOPY);
     if (err) {
@@ -210,6 +176,8 @@ restart:
     pos = 0;
     status = 0;
     response_size = 0;
+    field_value_len = 0;
+    field_name_len = 0;
 
     while (state != st_eoh) {
 	int ch = pxe_getc(inode);
@@ -360,7 +328,11 @@ restart:
 	redirect_count++;
 	if (redirect_count > 5)
 	    goto fail;
-	url = location;
+	strlcpy(new_url, location, sizeof new_url);
+	parse_url(url, new_url);
+	url_set_ip(url);
+	http_close_file(inode);
+	/* XXX: This needs to go all the way back to scheme selection */
 	goto restart;
 	break;
     default:
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index b4ff1f7..58866af 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -5,11 +5,13 @@
 #include <fs.h>
 #include <minmax.h>
 #include <sys/cpu.h>
-#include "pxe.h"
-#include "thread.h"
 #include <lwip/api.h>
 #include <lwip/dns.h>
 #include <lwip/tcpip.h>
+#include "pxe.h"
+#include "thread.h"
+#include "url.h"
+#include "tftp.h"
 
 static uint16_t real_base_mem;	   /* Amount of DOS memory after freeing */
 
@@ -33,10 +35,11 @@ bool have_uuid = false;
 /* Common receive buffer */
 __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
 
-static struct url_open {
-    const char *scheme;
-    void (*open)(struct inode *inode, const char *url);
-} url_table[] = {
+static struct url_scheme {
+    const char *name;
+    void (*open)(struct url_info *url, struct inode *inode);
+} url_schemes[] = {
+    { "tftp", tftp_open },
     { "http", http_open },
     { NULL, NULL },
 };
@@ -125,18 +128,6 @@ static void uchexbytes(char *dst, const void *src, int count)
 }
 
 /*
- * Parse a single hexadecimal byte, which must be complete (two
- * digits).  This is used in URL parsing.
- */
-static int hexbyte(const char *p)
-{
-    if (!is_hex(p[0]) || !is_hex(p[1]))
-	return -1;
-    else
-	return (hexval(p[0]) << 4) + hexval(p[1]);
-}
-
-/*
  * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
  * We used to refuse class E, but class E addresses are likely to become
  * assignable unicast addresses in the near future.
@@ -188,36 +179,6 @@ static int gendotquad(char *dst, uint32_t ip)
 }
 
 /*
- * parse the ip_str and return the ip address with *res.
- * return the the string address after the ip string
- *
- */
-const char *parse_dotquad(const char *ip_str, uint32_t *res)
-{
-    const char *p = ip_str;
-    uint8_t part = 0;
-    uint32_t ip = 0;
-    int i;
-
-    for (i = 0; i < 4; i++) {
-        while (is_digit(*p)) {
-            part = part * 10 + *p - '0';
-            p++;
-        }
-        if (i != 3 && *p != '.')
-            return NULL;
-
-        ip = (ip << 8) | part;
-        part = 0;
-        p++;
-    }
-    p--;
-
-    *res = htonl(ip);
-    return p;
-}
-
-/*
  * the ASM pxenv function wrapper, return 1 if error, or 0
  *
  */
@@ -270,59 +231,13 @@ static int pxe_get_cached_info(int type)
     return get_cached_info.BufferSize;
 }
 
-
-/*
- * Return the type of pathname passed.
- */
-enum pxe_path_type {
-    PXE_RELATIVE,		/* No :: or URL */
-    PXE_HOMESERVER,		/* Starting with :: */
-    PXE_TFTP,			/* host:: */
-    PXE_URL_TFTP,		/* tftp:// */
-    PXE_URL,			/* Absolute URL syntax */
-};
-
-static enum pxe_path_type pxe_path_type(const char *str)
-{
-    const char *p;
-    
-    p = str;
-
-    while (1) {
-	switch (*p) {
-	case ':':
-	    if (p[1] == ':') {
-		if (p == str)
-		    return PXE_HOMESERVER;
-		else
-		    return PXE_TFTP;
-	    } else if (p > str && p[1] == '/' && p[2] == '/') {
-		if (!strncasecmp(str, "tftp://", 7))
-		    return PXE_URL_TFTP;
-		else
-		    return PXE_URL;
-	    }
-
-	    /* else fall through */
-	case '/': case '!': case '@': case '#': case '%':
-	case '^': case '&': case '*': case '(': case ')':
-	case '[': case ']': case '{': case '}': case '\\':
-	case '|': case '=': case '`': case '~': case '\'':
-	case '\"': case ';': case '>': case '<': case '?':
-	case '\0':
-	    /* Any of these characters terminate the colon search */
-	    return PXE_RELATIVE;
-	default:
-	    break;
-	}
-	p++;
-    }
-}
-
 /*
  * mangle a filename pointed to by _src_ into a buffer pointed
  * to by _dst_; ends on encountering any whitespace.
  *
+ * This deliberately does not attempt to do any conversion of
+ * pathname separators.
+ *
  */
 static void pxe_mangle_name(char *dst, const char *src)
 {
@@ -425,6 +340,17 @@ static uint32_t pxe_getfssec(struct file *file, char *buf,
     return bytes_read;
 }
 
+/*
+ * Assign an IP address to a URL
+ */
+void url_set_ip(struct url_info *url)
+{
+    url->ip = 0;
+    if (url->host)
+	url->ip = dns_resolv(url->host);
+    if (!url->ip)
+	url->ip = IPInfo.serverip;
+}
 
 /**
  * Open the specified connection
@@ -452,119 +378,37 @@ static void __pxe_searchdir(const char *filename, struct file *file)
 {
     struct fs_info *fs = file->fs;
     struct inode *inode;
-    const char *np;
-    char *buf;
-    uint32_t ip = 0;
-    enum pxe_path_type path_type;
     char fullpath[2*FILENAME_MAX];
-    uint16_t server_port = TFTP_PORT;  /* TFTP server port */
+    struct url_info url;
+    const struct url_scheme *us;
 
     inode = file->inode = NULL;
 
-    path_type = pxe_path_type(filename);
-    if (path_type == PXE_RELATIVE) {
+    strlcpy(fullpath, filename, sizeof fullpath);
+    parse_url(&url, fullpath);
+    if (url.type == URL_SUFFIX) {
 	snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
-	path_type = pxe_path_type(filename = fullpath);
-    }
-
-    switch (path_type) {
-    case PXE_RELATIVE:		/* Really shouldn't happen... */
-    case PXE_URL:
-	ip = IPInfo.serverip;	/* Default server */
-	break;
-
-    case PXE_HOMESERVER:
-	filename = filename+2;
-	ip = IPInfo.serverip;
-	break;
-
-    case PXE_TFTP:
-	np = strchr(filename, ':');
-	if (parse_dotquad(filename, &ip) != np)
-	    ip = dns_resolv(filename);
-	filename = np+2;
-	break;
-
-    case PXE_URL_TFTP:
-	np = filename + 7;
-	while (*np && *np != '/' && *np != ':')
-	    np++;
-	if (np > filename + 7) {
-	    if (parse_dotquad(filename + 7, &ip) != np)
-		ip = dns_resolv(filename + 7);
-	}
-	if (*np == ':') {
-	    np++;
-	    server_port = 0;
-	    while (*np >= '0' && *np <= '9')
-		server_port = server_port * 10 + *np++ - '0';
-	    server_port = server_port ? htons(server_port) : TFTP_PORT;
-	}
-	if (*np == '/')
-	    np++;		/* Do *NOT* eat more than one slash here... */
-	/*
-	 * The ; is because of a quirk in the TFTP URI spec (RFC
-	 * 3617); it is to be followed by TFTP modes, which we just ignore.
-	 */
-	filename = buf = fullpath;
-	while (*np && *np != ';') {
-	    int v;
-	    if (*np == '%' && (v = hexbyte(np+1)) > 0) {
-		*buf++ = v;
-		np += 3;
-	    } else {
-		*buf++ = *np++;
-	    }
-	}
-	*buf = '\0';
-	break;
+	parse_url(&url, fullpath);
     }
 
     inode = allocate_socket(fs);
     if (!inode)
 	return;			/* Allocation failure */
 
-    if (path_type == PXE_URL) {
-	struct url_open *entry;
-	np = strchr(filename, ':');
-	
-	for (entry = url_table; entry->scheme; entry++) {
-	    int scheme_len = strlen(entry->scheme);
-	    if (scheme_len != (np - filename))
-	        continue;
-	    if (memcmp(entry->scheme, filename, scheme_len) != 0)
-	        continue;
-	    entry->open(inode, filename);
-	    goto done;
-	}
-    }
+    url_set_ip(&url);
 
-#if GPXE
-    if (path_type == PXE_URL) {
-	if (has_gpxe) {
-	    gpxe_open(inode, filename);
-	    goto done;
-	} else {
-	    static bool already = false;
-	    if (!already) {
-		printf("URL syntax, but gPXE extensions not detected, "
-		       "trying plain TFTP...\n");
-		already = true;
-	    }
+    for (us = url_schemes; us->name; us++) {
+	if (!strcmp(us->name, url.scheme)) {
+	    us->open(&url, inode);
+	    break;
 	}
     }
-#endif /* GPXE */
 
-    if (!ip)
-	    goto done;		/* No server */
-
-    tftp_open(inode, ip, server_port, filename);
-done:
-    if (!inode->size) {
+    if (inode->size)
+	file->inode = inode;
+    else
         free_socket(inode);
-	return;
-    }
-    file->inode = inode;
+
     return;
 }
 
@@ -612,10 +456,8 @@ static void get_prefix(void)
 static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
 			   size_t bufsize)
 {
-    enum pxe_path_type path_type = pxe_path_type(src);
-
     return snprintf(dst, bufsize, "%s%s",
-		    path_type == PXE_RELATIVE ? fs->cwd_name : "", src);
+		    url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
 }
 
 /*
@@ -624,9 +466,7 @@ static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
 static int pxe_chdir(struct fs_info *fs, const char *src)
 {
     /* The cwd for PXE is just a text prefix */
-    enum pxe_path_type path_type = pxe_path_type(src);
-
-    if (path_type == PXE_RELATIVE)
+    if (url_type(src) == URL_SUFFIX)
 	strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
     else
 	strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index 0aa3c5a..bb833d6 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -1,7 +1,7 @@
 /* -----------------------------------------------------------------------
  *
  *   Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
- *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -26,7 +26,6 @@
 /*
  * Some basic defines...
  */
-#define TFTP_PORT        htons(69)              /* Default TFTP port */
 #define TFTP_BLOCKSIZE_LG2 9
 #define TFTP_BLOCKSIZE  (1 << TFTP_BLOCKSIZE_LG2)
 #define PKTBUF_SIZE     2048			/*  */
@@ -45,43 +44,16 @@ static inline int hexval(char c)
     return (c >= 'A') ? (c & ~0x20) - 'A' + 10 : (c - '0');
 }
 
-/*
- * TFTP operation codes
- */
-#define TFTP_RRQ	 htons(1)		// Read rest
-#define TFTP_WRQ	 htons(2)		// Write rest
-#define TFTP_DATA	 htons(3)		// Data packet
-#define TFTP_ACK	 htons(4)		// ACK packet
-#define TFTP_ERROR	 htons(5)		// ERROR packet
-#define TFTP_OACK	 htons(6)		// OACK packet
-
-/*
- * TFTP error codes
- */
-#define TFTP_EUNDEF	 htons(0)		// Unspecified error
-#define TFTP_ENOTFOUND	 htons(1)		// File not found
-#define TFTP_EACCESS	 htons(2)		// Access violation
-#define TFTP_ENOSPACE	 htons(3)		// Disk full
-#define TFTP_EBADOP	 htons(4)		// Invalid TFTP operation
-#define TFTP_EBADID	 htons(5)		// Unknown transfer
-#define TFTP_EEXISTS	 htons(6)		// File exists
-#define TFTP_ENOUSER	 htons(7)		// No such user
-#define TFTP_EOPTNEG	 htons(8)		// Option negotiation failure
-
-
 #define BOOTP_OPTION_MAGIC  htonl(0x63825363)
 #define MAC_MAX 32
 
 /* Defines for DNS */
-#define DNS_PORT	htons(53)		/* Default DNS port */
 #define DNS_MAX_PACKET	512			/* Defined by protocol */
 #define DNS_MAX_SERVERS 4			/* Max no of DNS servers */
 
-
 /*
  * structures 
  */
-
 struct pxenv_t {
     uint8_t    signature[6];	/* PXENV+ */
     uint16_t   version;
@@ -247,11 +219,12 @@ void pxe_cleanup_isr(void);
 bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old);
 
 /* pxe.c */
+struct url_info;
 bool ip_ok(uint32_t);
 int pxe_call(int, void *);
 extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
-const char *parse_dotquad(const char *ip_str, uint32_t *res);
 int pxe_getc(struct inode *inode);
+void url_set_ip(struct url_info *);
 
 /* undiif.c */
 int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw);
@@ -261,7 +234,6 @@ void undiif_input(t_PXENV_UNDI_ISR *isr);
 void parse_dhcp(int);
 
 /* dnsresolv.c */
-int dns_mangle(char **, const char *);
 uint32_t dns_resolv(const char *);
 
 /* idle.c */
@@ -273,14 +245,13 @@ uint16_t get_port(void);
 void free_port(uint16_t port);
 
 /* tftp.c */
-void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port,
-	       const char *filename);
+void tftp_open(struct url_info *url, struct inode *inode);
 
 /* gpxeurl.c */
 void gpxe_open(struct inode *inode, const char *url);
 #define GPXE 1
 
 /* http.c */
-void http_open(struct inode *inode, const char *url);
+void http_open(struct url_info *url, struct inode *inode);
 
 #endif /* pxe.h */
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
index 06566d3..c5a757c 100644
--- a/core/fs/pxe/tftp.c
+++ b/core/fs/pxe/tftp.c
@@ -1,6 +1,8 @@
 #include <minmax.h>
+#include <lwip/api.h>
 #include "pxe.h"
-#include "lwip/api.h"
+#include "url.h"
+#include "tftp.h"
 
 const uint8_t TimeoutTable[] = {
     2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
@@ -207,8 +209,7 @@ static void tftp_get_packet(struct inode *inode)
  * @out: the lenght of this file, stores in file->file_len
  *
  */
-void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port,
-	       const char *filename)
+void tftp_open(struct url_info *url, struct inode *inode)
 {
     struct pxe_pvt_inode *socket = PVT(inode);
     char *buf;
@@ -232,6 +233,16 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port,
     uint32_t opdata, *opdata_ptr;
     struct ip_addr addr;
 
+    if (url->type != URL_OLD_TFTP) {
+	/*
+	 * The TFTP URL specification allows the TFTP to end with a
+	 * ;mode= which we just ignore.
+	 */
+	url_unescape(url->path, ';');
+    }
+    if (!url->port)
+	url->port = TFTP_PORT;
+
     socket->fill_buffer = tftp_get_packet;
     socket->close = tftp_close_file;
 
@@ -250,7 +261,7 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port,
     *(uint16_t *)buf = TFTP_RRQ;  /* TFTP opcode */
     buf += 2;
 
-    buf = stpcpy(buf, filename);
+    buf = stpcpy(buf, url->path);
 
     buf++;			/* Point *past* the final NULL */
     memcpy(buf, rrq_tail, sizeof rrq_tail);
@@ -267,11 +278,10 @@ sendreq:
 	return;			/* No file available... */
     oldtime = jiffies();
 
-    socket->tftp_remoteip = ip;
     nbuf = netbuf_new();
     netbuf_ref(nbuf, rrq_packet_buf, rrq_len);
-    addr.addr = ip;
-    netconn_sendto(socket->conn, nbuf, &addr, ntohs(server_port));
+    addr.addr =  socket->tftp_remoteip = url->ip;
+    netconn_sendto(socket->conn, nbuf, &addr, url->port);
     netbuf_delete(nbuf);
 
     /* If the WRITE call fails, we let the timeout take care of it... */
diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h
new file mode 100644
index 0000000..b8b7f10
--- /dev/null
+++ b/core/fs/pxe/tftp.h
@@ -0,0 +1,48 @@
+/* -----------------------------------------------------------------------
+ *
+ *   Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * tftp.h
+ */
+#ifndef PXE_TFTP_H
+#define PXE_TFTP_H
+
+/*
+ * TFTP default port number
+ */
+#define TFTP_PORT	 69
+
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ	 htons(1)		// Read rest
+#define TFTP_WRQ	 htons(2)		// Write rest
+#define TFTP_DATA	 htons(3)		// Data packet
+#define TFTP_ACK	 htons(4)		// ACK packet
+#define TFTP_ERROR	 htons(5)		// ERROR packet
+#define TFTP_OACK	 htons(6)		// OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF	 htons(0)		// Unspecified error
+#define TFTP_ENOTFOUND	 htons(1)		// File not found
+#define TFTP_EACCESS	 htons(2)		// Access violation
+#define TFTP_ENOSPACE	 htons(3)		// Disk full
+#define TFTP_EBADOP	 htons(4)		// Invalid TFTP operation
+#define TFTP_EBADID	 htons(5)		// Unknown transfer
+#define TFTP_EEXISTS	 htons(6)		// File exists
+#define TFTP_ENOUSER	 htons(7)		// No such user
+#define TFTP_EOPTNEG	 htons(8)		// Option negotiation failure
+
+#endif /* PXE_TFTP_H */
diff --git a/core/fs/pxe/url.h b/core/fs/pxe/url.h
index baea2b7..53984f3 100644
--- a/core/fs/pxe/url.h
+++ b/core/fs/pxe/url.h
@@ -5,10 +5,13 @@
 #ifndef CORE_PXE_URL_H
 #define CORE_PXE_URL_H
 
+#include <stddef.h>
+#include <stdint.h>
+
 enum url_type {
-    URL_NORMAL,
-    URL_OLD_TFTP,
-    URL_PREFIX
+    URL_NORMAL,			/* It is a full URL */
+    URL_OLD_TFTP,		/* It's a ::-style TFTP path */
+    URL_SUFFIX			/* Prepend the pathname prefix */
 };
 
 struct url_info {
@@ -16,13 +19,15 @@ struct url_info {
     char *user;
     char *passwd;
     char *host;
+    uint32_t ip;		/* Placeholder field not set by parse_url() */
     unsigned int port;
     char *path;			/* Includes query */
     enum url_type type;
 };
 
+enum url_type url_type(const char *url);
 void parse_url(struct url_info *ui, char *url);
-char *url_escape_unsafe(const char *input);
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize);
 char *url_unescape(char *buffer, char terminator);
 
 #endif /* CORE_PXE_URL_H */
diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c
index 459dca4..bc55197 100644
--- a/core/fs/pxe/urlparse.c
+++ b/core/fs/pxe/urlparse.c
@@ -35,6 +35,26 @@
 #include "url.h"
 
 /*
+ * Return the type of a URL without modifying the string
+ */
+enum url_type url_type(const char *url)
+{
+    const char *q;
+
+    q = strchr(url, ':');
+    if (!q)
+	return URL_SUFFIX;
+
+    if (q[1] == '/' && q[2] == '/')
+	return URL_NORMAL;
+
+    if (q[1] == ':')
+	return URL_OLD_TFTP;
+
+    return URL_SUFFIX;
+}
+
+/*
  * Decompose a URL into its components.  This is done in-place;
  * this routine does not allocate any additional storage.  Freeing the
  * original buffer frees all storage used.
@@ -43,93 +63,91 @@ void parse_url(struct url_info *ui, char *url)
 {
     char *p = url;
     char *q, *r, *s;
+    int c;
 
     memset(ui, 0, sizeof *ui);
 
-    q = strstr(p, "://");
-    if (!q) {
-	q = strstr(p, "::");
+    q = strchr(p, ':');
+    if (q && (q[1] == '/' && q[2] == '/')) {
+	ui->type = URL_NORMAL;
+	
+	ui->scheme = p;
+	*q = '\0';
+	p = q+3;
+	
+	q = strchr(p, '/');
 	if (q) {
 	    *q = '\0';
-	    ui->scheme = "tftp";
-	    ui->host = p;
-	    ui->path = q+2;
-	    ui->type = URL_OLD_TFTP;
-	    return;
+	    ui->path = q+1;
+	    q = strchr(q+1, '#');
+	    if (q)
+		*q = '\0';
 	} else {
-	    ui->path = p;
-	    ui->type = URL_PREFIX;
-	    return;
+	    ui->path = "";
 	}
-    }
-
-    ui->type = URL_NORMAL;
-
-    ui->scheme = p;
-    *q = '\0';
-    p = q+3;
-
-    q = strchr(p, '/');
-    if (q) {
+	
+	r = strchr(p, '@');
+	if (r) {
+	    ui->user = p;
+	    *r = '\0';
+	    s = strchr(p, ':');
+	    if (s) {
+		*s = '\0';
+		ui->passwd = s+1;
+	    }
+	    p = r+1;
+	}
+	
+	ui->host = p;
+	r = strchr(p, ':');
+	if (r) {
+	    *r++ = '\0';
+	    ui->port = 0;
+	    while ((c = *r++)) {
+		c -= '0';
+		if (c > 9)
+		    break;
+		ui->port = ui->port * 10 + c;
+	    }
+	}
+    } else if (q && q[1] == ':') {
 	*q = '\0';
-	ui->path = q+1;
-	q = strchr(q+1, '#');
-	if (q)
-	    *q = '\0';
+	ui->scheme = "tftp";
+	ui->host = p;
+	ui->path = q+2;
+	ui->type = URL_OLD_TFTP;
     } else {
-	ui->path = "";
-    }
-
-    r = strchr(p, '@');
-    if (r) {
-	ui->user = p;
-	*r = '\0';
-	s = strchr(p, ':');
-	if (s) {
-	    *s = '\0';
-	    ui->passwd = s+1;
-	}
-	p = r+1;
-    }
-
-    ui->host = p;
-    r = strchr(p, ':');
-    if (r) {
-	*r = '\0';
-	ui->port = atoi(r+1);
+	ui->path = p;
+	ui->type = URL_SUFFIX;
     }
 }
 
 /*
- * Escapes unsafe characters in a URL.  Returns a malloc'd buffer.
+ * Escapes unsafe characters in a URL.
+ * This does *not* escape things like query characters!
+ * Returns the number of characters in the total output.
  */
-char *url_escape_unsafe(const char *input)
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize)
 {
-    const char *p = input;
+    static const char uchexchar[] = "0123456789ABCDEF";
+    const char *p;
     unsigned char c;
-    char *out, *q;
+    char *q;
     size_t n = 0;
 
-    while ((c = *p++)) {
-	if (c < ' ' || c > '~') {
-	    n += 3;		/* Need escaping */
-	} else {
-	    n++;
-	}
-    }
-
-    q = out = malloc(n+1);
-    while ((c = *p++)) {
-	if (c < ' ' || c > '~') {
-	    q += snprintf(q, 3, "%%%02X", c);
+    q = output;
+    for (p = input; (c = *p); p++) {
+	if (c <= ' ' || c > '~') {
+	    if (++n < bufsize) *q++ = '%';
+	    if (++n < bufsize) *q++ = uchexchar[c >> 4];
+	    if (++n < bufsize) *q++ = uchexchar[c & 15];
 	} else {
-	    *q++ = c;
+	    if (++n < bufsize) *q++ = c;
 	}
     }
 
     *q = '\0';
-
-    return out;
+    return n;
 }
 
 static int hexdigit(char c)



More information about the Syslinux-commits mailing list