diff options
Diffstat (limited to 'core/fs/pxe')
-rw-r--r-- | core/fs/pxe/dhcp_option.c | 9 | ||||
-rw-r--r-- | core/fs/pxe/dnsresolv.c | 71 | ||||
-rw-r--r-- | core/fs/pxe/http.c | 98 | ||||
-rw-r--r-- | core/fs/pxe/pxe.c | 240 | ||||
-rw-r--r-- | core/fs/pxe/pxe.h | 39 | ||||
-rw-r--r-- | core/fs/pxe/tftp.c | 24 | ||||
-rw-r--r-- | core/fs/pxe/tftp.h | 48 | ||||
-rw-r--r-- | core/fs/pxe/url.h | 13 | ||||
-rw-r--r-- | 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 50f2de04..47031faf 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 b827edbb..a4bbf1ff 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 6af55d59..2f5a645a 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 b4ff1f7b..58866afc 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 0aa3c5a7..bb833d60 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 06566d3f..c5a757c8 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 00000000..b8b7f10e --- /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 baea2b70..53984f3a 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 459dca42..bc55197b 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) |