1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * Uniform Resource Identifiers
29  *
30  */
31 
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <libgen.h>
36 #include <ctype.h>
37 #include <ipxe/vsprintf.h>
38 #include <ipxe/params.h>
39 #include <ipxe/tcpip.h>
40 #include <ipxe/uri.h>
41 
42 /**
43  * Decode URI field
44  *
45  * @v encoded		Encoded field
46  * @v buf		Data buffer
47  * @v len		Length
48  * @ret len		Length of data
49  *
50  * URI decoding can never increase the length of a string; we can
51  * therefore safely decode in place.
52  */
uri_decode(const char * encoded,void * buf,size_t len)53 size_t uri_decode ( const char *encoded, void *buf, size_t len ) {
54 	uint8_t *out = buf;
55 	unsigned int count = 0;
56 	char hexbuf[3];
57 	char *hexbuf_end;
58 	char c;
59 	char decoded;
60 	unsigned int skip;
61 
62 	/* Copy string, decoding escaped characters as necessary */
63 	while ( ( c = *(encoded++) ) ) {
64 		if ( c == '%' ) {
65 			snprintf ( hexbuf, sizeof ( hexbuf ), "%s", encoded );
66 			decoded = strtoul ( hexbuf, &hexbuf_end, 16 );
67 			skip = ( hexbuf_end - hexbuf );
68 			encoded += skip;
69 			if ( skip )
70 				c = decoded;
71 		}
72 		if ( count < len )
73 			out[count] = c;
74 		count++;
75 	}
76 	return count;
77 }
78 
79 /**
80  * Decode URI field in-place
81  *
82  * @v uri		URI
83  * @v field		URI field index
84  */
uri_decode_inplace(struct uri * uri,unsigned int field)85 static void uri_decode_inplace ( struct uri *uri, unsigned int field ) {
86 	const char *encoded = uri_field ( uri, field );
87 	char *decoded = ( ( char * ) encoded );
88 	size_t len;
89 
90 	/* Do nothing if field is not present */
91 	if ( ! encoded )
92 		return;
93 
94 	/* Decode field in place */
95 	len = uri_decode ( encoded, decoded, strlen ( encoded ) );
96 
97 	/* Terminate decoded string */
98 	decoded[len] = '\0';
99 }
100 
101 /**
102  * Check if character should be escaped within a URI field
103  *
104  * @v c			Character
105  * @v field		URI field index
106  * @ret escaped		Character should be escaped
107  */
uri_character_escaped(char c,unsigned int field)108 static int uri_character_escaped ( char c, unsigned int field ) {
109 
110 	/* Non-printing characters and whitespace should always be
111 	 * escaped, since they cannot sensibly be displayed as part of
112 	 * a coherent URL string.  (This test also catches control
113 	 * characters such as CR and LF, which could affect the
114 	 * operation of line-based protocols such as HTTP.)
115 	 *
116 	 * We should also escape characters which would alter the
117 	 * interpretation of the URL if not escaped, i.e. characters
118 	 * which have significance to the URL parser.  We should not
119 	 * blindly escape all such characters, because this would lead
120 	 * to some very strange-looking URLs (e.g. if we were to
121 	 * always escape '/' as "%2F" even within the URI path).
122 	 *
123 	 * We do not need to be perfect.  Our primary role is as a
124 	 * consumer of URIs rather than a producer; the main situation
125 	 * in which we produce a URI string is for display to a human
126 	 * user, who can probably tolerate some variance from the
127 	 * formal specification.  The only situation in which we
128 	 * currently produce a URI string to be consumed by a computer
129 	 * is when constructing an HTTP request URI, which contains
130 	 * only the path and query fields.
131 	 *
132 	 * We can therefore sacrifice some correctness for the sake of
133 	 * code size.  For example, colons within the URI host should
134 	 * be escaped unless they form part of an IPv6 literal
135 	 * address; doing this correctly would require the URI
136 	 * formatter to be aware of whether or not the URI host
137 	 * contained an IPv4 address, an IPv6 address, or a host name.
138 	 * We choose to simplify and never escape colons within the
139 	 * URI host field: in the event of a pathological hostname
140 	 * containing colons, this could potentially produce a URI
141 	 * string which could not be reparsed.
142 	 *
143 	 * After excluding non-printing characters, whitespace, and
144 	 * '%', the full set of characters with significance to the
145 	 * URL parser is "/#:@?".  We choose for each URI field which
146 	 * of these require escaping in our use cases.
147 	 *
148 	 * For the scheme field (equivalently, if field is zero), we
149 	 * escape anything that has significance not just for our URI
150 	 * parser but for any other URI parsers (e.g. HTTP query
151 	 * string parsers, which care about '=' and '&').
152 	 */
153 	static const char *escaped[URI_FIELDS] = {
154 		/* Scheme or default: escape everything */
155 		[URI_SCHEME]	= "/#:@?=&",
156 		/* Opaque part: escape characters which would affect
157 		 * the reparsing of the URI, allowing everything else
158 		 * (e.g. ':', which will appear in iSCSI URIs).
159 		 */
160 		[URI_OPAQUE]	= "#",
161 		/* User name: escape everything */
162 		[URI_USER]	= "/#:@?",
163 		/* Password: escape everything */
164 		[URI_PASSWORD]	= "/#:@?",
165 		/* Host name: escape everything except ':', which may
166 		 * appear as part of an IPv6 literal address.
167 		 */
168 		[URI_HOST]	= "/#@?",
169 		/* Port number: escape everything */
170 		[URI_PORT]	= "/#:@?",
171 		/* Path: escape everything except '/', which usually
172 		 * appears within paths.
173 		 */
174 		[URI_PATH]	= "#:@?",
175 		/* Query: escape everything except '/', which
176 		 * sometimes appears within queries.
177 		 */
178 		[URI_QUERY]	= "#:@?",
179 		/* Fragment: escape everything */
180 		[URI_FRAGMENT]	= "/#:@?",
181 	};
182 
183 	return ( /* Always escape non-printing characters and whitespace */
184 		 ( ! isprint ( c ) ) || ( c == ' ' ) ||
185 		 /* Always escape '%' */
186 		 ( c == '%' ) ||
187 		 /* Escape field-specific characters */
188 		 strchr ( escaped[field], c ) );
189 }
190 
191 /**
192  * Encode URI field
193  *
194  * @v field		URI field index
195  * @v raw		Raw data
196  * @v raw_len		Length of raw data
197  * @v buf		Buffer
198  * @v len		Length of buffer
199  * @ret len		Length of encoded string (excluding NUL)
200  */
uri_encode(unsigned int field,const void * raw,size_t raw_len,char * buf,ssize_t len)201 size_t uri_encode ( unsigned int field, const void *raw, size_t raw_len,
202 		    char *buf, ssize_t len ) {
203 	const uint8_t *raw_bytes = ( ( const uint8_t * ) raw );
204 	ssize_t remaining = len;
205 	size_t used;
206 	char c;
207 
208 	/* Ensure encoded string is NUL-terminated even if empty */
209 	if ( len > 0 )
210 		buf[0] = '\0';
211 
212 	/* Copy string, escaping as necessary */
213 	while ( raw_len-- ) {
214 		c = *(raw_bytes++);
215 		if ( uri_character_escaped ( c, field ) ) {
216 			used = ssnprintf ( buf, remaining, "%%%02X", c );
217 		} else {
218 			used = ssnprintf ( buf, remaining, "%c", c );
219 		}
220 		buf += used;
221 		remaining -= used;
222 	}
223 
224 	return ( len - remaining );
225 }
226 
227 /**
228  * Encode URI field string
229  *
230  * @v field		URI field index
231  * @v string		String
232  * @v buf		Buffer
233  * @v len		Length of buffer
234  * @ret len		Length of encoded string (excluding NUL)
235  */
uri_encode_string(unsigned int field,const char * string,char * buf,ssize_t len)236 size_t uri_encode_string ( unsigned int field, const char *string,
237 			   char *buf, ssize_t len ) {
238 
239 	return uri_encode ( field, string, strlen ( string ), buf, len );
240 }
241 
242 /**
243  * Dump URI for debugging
244  *
245  * @v uri		URI
246  */
uri_dump(const struct uri * uri)247 static void uri_dump ( const struct uri *uri ) {
248 
249 	if ( ! uri )
250 		return;
251 	if ( uri->scheme )
252 		DBGC ( uri, " scheme \"%s\"", uri->scheme );
253 	if ( uri->opaque )
254 		DBGC ( uri, " opaque \"%s\"", uri->opaque );
255 	if ( uri->user )
256 		DBGC ( uri, " user \"%s\"", uri->user );
257 	if ( uri->password )
258 		DBGC ( uri, " password \"%s\"", uri->password );
259 	if ( uri->host )
260 		DBGC ( uri, " host \"%s\"", uri->host );
261 	if ( uri->port )
262 		DBGC ( uri, " port \"%s\"", uri->port );
263 	if ( uri->path )
264 		DBGC ( uri, " path \"%s\"", uri->path );
265 	if ( uri->query )
266 		DBGC ( uri, " query \"%s\"", uri->query );
267 	if ( uri->fragment )
268 		DBGC ( uri, " fragment \"%s\"", uri->fragment );
269 	if ( uri->params )
270 		DBGC ( uri, " params \"%s\"", uri->params->name );
271 }
272 
273 /**
274  * Free URI
275  *
276  * @v refcnt		Reference count
277  */
uri_free(struct refcnt * refcnt)278 static void uri_free ( struct refcnt *refcnt ) {
279 	struct uri *uri = container_of ( refcnt, struct uri, refcnt );
280 
281 	params_put ( uri->params );
282 	free ( uri );
283 }
284 
285 /**
286  * Parse URI
287  *
288  * @v uri_string	URI as a string
289  * @ret uri		URI
290  *
291  * Splits a URI into its component parts.  The return URI structure is
292  * dynamically allocated and must eventually be freed by calling
293  * uri_put().
294  */
parse_uri(const char * uri_string)295 struct uri * parse_uri ( const char *uri_string ) {
296 	struct uri *uri;
297 	struct parameters *params;
298 	char *raw;
299 	char *tmp;
300 	char *path;
301 	char *authority;
302 	size_t raw_len;
303 	unsigned int field;
304 
305 	/* Allocate space for URI struct and a copy of the string */
306 	raw_len = ( strlen ( uri_string ) + 1 /* NUL */ );
307 	uri = zalloc ( sizeof ( *uri ) + raw_len );
308 	if ( ! uri )
309 		return NULL;
310 	ref_init ( &uri->refcnt, uri_free );
311 	raw = ( ( ( void * ) uri ) + sizeof ( *uri ) );
312 
313 	/* Copy in the raw string */
314 	memcpy ( raw, uri_string, raw_len );
315 
316 	/* Identify the parameter list, if present */
317 	if ( ( tmp = strstr ( raw, "##params" ) ) ) {
318 		*tmp = '\0';
319 		tmp += 8 /* "##params" */;
320 		params = find_parameters ( *tmp ? ( tmp + 1 ) : NULL );
321 		if ( params ) {
322 			uri->params = claim_parameters ( params );
323 		} else {
324 			/* Ignore non-existent submission blocks */
325 		}
326 	}
327 
328 	/* Chop off the fragment, if it exists */
329 	if ( ( tmp = strchr ( raw, '#' ) ) ) {
330 		*(tmp++) = '\0';
331 		uri->fragment = tmp;
332 	}
333 
334 	/* Identify absolute/relative URI */
335 	if ( ( tmp = strchr ( raw, ':' ) ) ) {
336 		/* Absolute URI: identify hierarchical/opaque */
337 		uri->scheme = raw;
338 		*(tmp++) = '\0';
339 		if ( *tmp == '/' ) {
340 			/* Absolute URI with hierarchical part */
341 			path = tmp;
342 		} else {
343 			/* Absolute URI with opaque part */
344 			uri->opaque = tmp;
345 			path = NULL;
346 		}
347 	} else {
348 		/* Relative URI */
349 		path = raw;
350 	}
351 
352 	/* If we don't have a path (i.e. we have an absolute URI with
353 	 * an opaque portion, we're already finished processing
354 	 */
355 	if ( ! path )
356 		goto done;
357 
358 	/* Chop off the query, if it exists */
359 	if ( ( tmp = strchr ( path, '?' ) ) ) {
360 		*(tmp++) = '\0';
361 		uri->query = tmp;
362 	}
363 
364 	/* If we have no path remaining, then we're already finished
365 	 * processing.
366 	 */
367 	if ( ! path[0] )
368 		goto done;
369 
370 	/* Identify net/absolute/relative path */
371 	if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) {
372 		/* Net path.  If this is terminated by the first '/'
373 		 * of an absolute path, then we have no space for a
374 		 * terminator after the authority field, so shuffle
375 		 * the authority down by one byte, overwriting one of
376 		 * the two slashes.
377 		 */
378 		authority = ( path + 2 );
379 		if ( ( tmp = strchr ( authority, '/' ) ) ) {
380 			/* Shuffle down */
381 			uri->path = tmp;
382 			memmove ( ( authority - 1 ), authority,
383 				  ( tmp - authority ) );
384 			authority--;
385 			*(--tmp) = '\0';
386 		}
387 	} else {
388 		/* Absolute/relative path */
389 		uri->path = path;
390 		authority = NULL;
391 	}
392 
393 	/* If we don't have an authority (i.e. we have a non-net
394 	 * path), we're already finished processing
395 	 */
396 	if ( ! authority )
397 		goto done;
398 
399 	/* Split authority into user[:password] and host[:port] portions */
400 	if ( ( tmp = strchr ( authority, '@' ) ) ) {
401 		/* Has user[:password] */
402 		*(tmp++) = '\0';
403 		uri->host = tmp;
404 		uri->user = authority;
405 		if ( ( tmp = strchr ( authority, ':' ) ) ) {
406 			/* Has password */
407 			*(tmp++) = '\0';
408 			uri->password = tmp;
409 		}
410 	} else {
411 		/* No user:password */
412 		uri->host = authority;
413 	}
414 
415 	/* Split host into host[:port] */
416 	if ( ( uri->host[ strlen ( uri->host ) - 1 ] != ']' ) &&
417 	     ( tmp = strrchr ( uri->host, ':' ) ) ) {
418 		*(tmp++) = '\0';
419 		uri->port = tmp;
420 	}
421 
422  done:
423 	/* Decode fields in-place */
424 	for ( field = 0 ; field < URI_FIELDS ; field++ )
425 		uri_decode_inplace ( uri, field );
426 
427 	DBGC ( uri, "URI parsed \"%s\" to", uri_string );
428 	uri_dump ( uri );
429 	DBGC ( uri, "\n" );
430 
431 	return uri;
432 }
433 
434 /**
435  * Get port from URI
436  *
437  * @v uri		URI, or NULL
438  * @v default_port	Default port to use if none specified in URI
439  * @ret port		Port
440  */
uri_port(const struct uri * uri,unsigned int default_port)441 unsigned int uri_port ( const struct uri *uri, unsigned int default_port ) {
442 
443 	if ( ( ! uri ) || ( ! uri->port ) )
444 		return default_port;
445 
446 	return ( strtoul ( uri->port, NULL, 0 ) );
447 }
448 
449 /**
450  * Format URI
451  *
452  * @v uri		URI
453  * @v buf		Buffer to fill with URI string
454  * @v size		Size of buffer
455  * @ret len		Length of URI string
456  */
format_uri(const struct uri * uri,char * buf,size_t len)457 size_t format_uri ( const struct uri *uri, char *buf, size_t len ) {
458 	static const char prefixes[URI_FIELDS] = {
459 		[URI_PASSWORD] = ':',
460 		[URI_PORT] = ':',
461 		[URI_QUERY] = '?',
462 		[URI_FRAGMENT] = '#',
463 	};
464 	char prefix;
465 	size_t used = 0;
466 	unsigned int field;
467 
468 	/* Ensure buffer is NUL-terminated */
469 	if ( len )
470 		buf[0] = '\0';
471 
472 	/* Special-case NULL URI */
473 	if ( ! uri )
474 		return 0;
475 
476 	/* Generate fields */
477 	for ( field = 0 ; field < URI_FIELDS ; field++ ) {
478 
479 		/* Skip non-existent fields */
480 		if ( ! uri_field ( uri, field ) )
481 			continue;
482 
483 		/* Prefix this field, if applicable */
484 		prefix = prefixes[field];
485 		if ( ( field == URI_HOST ) && ( uri->user != NULL ) )
486 			prefix = '@';
487 		if ( prefix ) {
488 			used += ssnprintf ( ( buf + used ), ( len - used ),
489 					    "%c", prefix );
490 		}
491 
492 		/* Encode this field */
493 		used += uri_encode_string ( field, uri_field ( uri, field ),
494 					    ( buf + used ), ( len - used ) );
495 
496 		/* Suffix this field, if applicable */
497 		if ( field == URI_SCHEME ) {
498 			used += ssnprintf ( ( buf + used ), ( len - used ),
499 					    ":%s", ( uri->host ? "//" : "" ) );
500 		}
501 	}
502 
503 	if ( len ) {
504 		DBGC ( uri, "URI formatted" );
505 		uri_dump ( uri );
506 		DBGC ( uri, " to \"%s%s\"\n", buf,
507 		       ( ( used > len ) ? "<TRUNCATED>" : "" ) );
508 	}
509 
510 	return used;
511 }
512 
513 /**
514  * Format URI
515  *
516  * @v uri		URI
517  * @ret string		URI string, or NULL on failure
518  *
519  * The caller is responsible for eventually freeing the allocated
520  * memory.
521  */
format_uri_alloc(const struct uri * uri)522 char * format_uri_alloc ( const struct uri *uri ) {
523 	size_t len;
524 	char *string;
525 
526 	len = ( format_uri ( uri, NULL, 0 ) + 1 /* NUL */ );
527 	string = malloc ( len );
528 	if ( string )
529 		format_uri ( uri, string, len );
530 	return string;
531 }
532 
533 /**
534  * Copy URI fields
535  *
536  * @v src		Source URI
537  * @v dest		Destination URI, or NULL to calculate length
538  * @ret len		Length of raw URI
539  */
uri_copy_fields(const struct uri * src,struct uri * dest)540 static size_t uri_copy_fields ( const struct uri *src, struct uri *dest ) {
541 	size_t len = sizeof ( *dest );
542 	char *out = ( ( void * ) dest + len );
543 	unsigned int field;
544 	size_t field_len;
545 
546 	/* Copy existent fields */
547 	for ( field = 0 ; field < URI_FIELDS ; field++ ) {
548 
549 		/* Skip non-existent fields */
550 		if ( ! uri_field ( src, field ) )
551 			continue;
552 
553 		/* Calculate field length */
554 		field_len = ( strlen ( uri_field ( src, field ) )
555 			      + 1 /* NUL */ );
556 		len += field_len;
557 
558 		/* Copy field, if applicable */
559 		if ( dest ) {
560 			memcpy ( out, uri_field ( src, field ), field_len );
561 			uri_field ( dest, field ) = out;
562 			out += field_len;
563 		}
564 	}
565 	return len;
566 }
567 
568 /**
569  * Duplicate URI
570  *
571  * @v uri		URI
572  * @ret uri		Duplicate URI
573  *
574  * Creates a modifiable copy of a URI.
575  */
uri_dup(const struct uri * uri)576 struct uri * uri_dup ( const struct uri *uri ) {
577 	struct uri *dup;
578 	size_t len;
579 
580 	/* Allocate new URI */
581 	len = uri_copy_fields ( uri, NULL );
582 	dup = zalloc ( len );
583 	if ( ! dup )
584 		return NULL;
585 	ref_init ( &dup->refcnt, uri_free );
586 
587 	/* Copy fields */
588 	uri_copy_fields ( uri, dup );
589 
590 	/* Copy parameters */
591 	dup->params = params_get ( uri->params );
592 
593 	DBGC ( uri, "URI duplicated" );
594 	uri_dump ( uri );
595 	DBGC ( uri, "\n" );
596 
597 	return dup;
598 }
599 
600 /**
601  * Resolve base+relative path
602  *
603  * @v base_uri		Base path
604  * @v relative_uri	Relative path
605  * @ret resolved_uri	Resolved path, or NULL on failure
606  *
607  * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative
608  * path (e.g. "initrd.gz") and produces a new path
609  * (e.g. "/var/lib/tftpboot/initrd.gz").  Note that any non-directory
610  * portion of the base path will automatically be stripped; this
611  * matches the semantics used when resolving the path component of
612  * URIs.
613  */
resolve_path(const char * base_path,const char * relative_path)614 char * resolve_path ( const char *base_path,
615 		      const char *relative_path ) {
616 	char *base_copy;
617 	char *base_tmp;
618 	char *resolved;
619 
620 	/* If relative path is absolute, just re-use it */
621 	if ( relative_path[0] == '/' )
622 		return strdup ( relative_path );
623 
624 	/* Create modifiable copy of path for dirname() */
625 	base_copy = strdup ( base_path );
626 	if ( ! base_copy )
627 		return NULL;
628 
629 	/* Strip filename portion of base path */
630 	base_tmp = dirname ( base_copy );
631 
632 	/* Process "./" and "../" elements */
633 	while ( *relative_path == '.' ) {
634 		relative_path++;
635 		if ( *relative_path == 0 ) {
636 			/* Do nothing */
637 		} else if ( *relative_path == '/' ) {
638 			relative_path++;
639 		} else if ( *relative_path == '.' ) {
640 			relative_path++;
641 			if ( *relative_path == 0 ) {
642 				base_tmp = dirname ( base_tmp );
643 			} else if ( *relative_path == '/' ) {
644 				base_tmp = dirname ( base_tmp );
645 				relative_path++;
646 			} else {
647 				relative_path -= 2;
648 				break;
649 			}
650 		} else {
651 			relative_path--;
652 			break;
653 		}
654 	}
655 
656 	/* Create and return new path */
657 	if ( asprintf ( &resolved, "%s%s%s", base_tmp,
658 			( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ?
659 			  "" : "/" ), relative_path ) < 0 )
660 		resolved = NULL;
661 	free ( base_copy );
662 	return resolved;
663 }
664 
665 /**
666  * Resolve base+relative URI
667  *
668  * @v base_uri		Base URI, or NULL
669  * @v relative_uri	Relative URI
670  * @ret resolved_uri	Resolved URI, or NULL on failure
671  *
672  * Takes a base URI (e.g. "http://ipxe.org/kernels/vmlinuz" and a
673  * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI
674  * (e.g. "http://ipxe.org/initrds/initrd.gz").
675  */
resolve_uri(const struct uri * base_uri,struct uri * relative_uri)676 struct uri * resolve_uri ( const struct uri *base_uri,
677 			   struct uri *relative_uri ) {
678 	struct uri tmp_uri;
679 	char *tmp_path = NULL;
680 	struct uri *new_uri;
681 
682 	/* If relative URI is absolute, just re-use it */
683 	if ( uri_is_absolute ( relative_uri ) || ( ! base_uri ) )
684 		return uri_get ( relative_uri );
685 
686 	/* Mangle URI */
687 	memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) );
688 	if ( relative_uri->path ) {
689 		tmp_path = resolve_path ( ( base_uri->path ?
690 					    base_uri->path : "/" ),
691 					  relative_uri->path );
692 		tmp_uri.path = tmp_path;
693 		tmp_uri.query = relative_uri->query;
694 		tmp_uri.fragment = relative_uri->fragment;
695 		tmp_uri.params = relative_uri->params;
696 	} else if ( relative_uri->query ) {
697 		tmp_uri.query = relative_uri->query;
698 		tmp_uri.fragment = relative_uri->fragment;
699 		tmp_uri.params = relative_uri->params;
700 	} else if ( relative_uri->fragment ) {
701 		tmp_uri.fragment = relative_uri->fragment;
702 		tmp_uri.params = relative_uri->params;
703 	} else if ( relative_uri->params ) {
704 		tmp_uri.params = relative_uri->params;
705 	}
706 
707 	/* Create demangled URI */
708 	new_uri = uri_dup ( &tmp_uri );
709 	free ( tmp_path );
710 	return new_uri;
711 }
712 
713 /**
714  * Construct TFTP URI from server address and filename
715  *
716  * @v sa_server		Server address
717  * @v filename		Filename
718  * @ret uri		URI, or NULL on failure
719  */
tftp_uri(struct sockaddr * sa_server,const char * filename)720 static struct uri * tftp_uri ( struct sockaddr *sa_server,
721 			       const char *filename ) {
722 	struct sockaddr_tcpip *st_server =
723 		( ( struct sockaddr_tcpip * ) sa_server );
724 	char buf[ 6 /* "65535" + NUL */ ];
725 	char *path;
726 	struct uri tmp;
727 	struct uri *uri = NULL;
728 
729 	/* Initialise TFTP URI */
730 	memset ( &tmp, 0, sizeof ( tmp ) );
731 	tmp.scheme = "tftp";
732 
733 	/* Construct TFTP server address */
734 	tmp.host = sock_ntoa ( sa_server );
735 	if ( ! tmp.host )
736 		goto err_host;
737 
738 	/* Construct TFTP server port, if applicable */
739 	if ( st_server->st_port ) {
740 		snprintf ( buf, sizeof ( buf ), "%d",
741 			   ntohs ( st_server->st_port ) );
742 		tmp.port = buf;
743 	}
744 
745 	/* Construct TFTP path */
746 	if ( asprintf ( &path, "/%s", filename ) < 0 )
747 		goto err_path;
748 	tmp.path = path;
749 
750 	/* Demangle URI */
751 	uri = uri_dup ( &tmp );
752 	if ( ! uri )
753 		goto err_uri;
754 
755  err_uri:
756 	free ( path );
757  err_path:
758  err_host:
759 	return uri;
760 }
761 
762 /**
763  * Construct URI from server address and filename
764  *
765  * @v sa_server		Server address
766  * @v filename		Filename
767  * @ret uri		URI, or NULL on failure
768  *
769  * PXE TFTP filenames specified via the DHCP next-server field often
770  * contain characters such as ':' or '#' which would confuse the
771  * generic URI parser.  We provide a mechanism for directly
772  * constructing a TFTP URI from the next-server and filename.
773  */
pxe_uri(struct sockaddr * sa_server,const char * filename)774 struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) {
775 	struct uri *uri;
776 
777 	/* Fail if filename is empty */
778 	if ( ! ( filename && filename[0] ) )
779 		return NULL;
780 
781 	/* If filename is a hierarchical absolute URI, then use that
782 	 * URI.  (We accept only hierarchical absolute URIs, since PXE
783 	 * filenames sometimes start with DOS drive letters such as
784 	 * "C:\", which get misinterpreted as opaque absolute URIs.)
785 	 */
786 	uri = parse_uri ( filename );
787 	if ( uri && uri_is_absolute ( uri ) && ( ! uri->opaque ) )
788 		return uri;
789 	uri_put ( uri );
790 
791 	/* Otherwise, construct a TFTP URI directly */
792 	return tftp_uri ( sa_server, filename );
793 }
794