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