iPXE
uri.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * Uniform Resource Identifiers
00029  *
00030  */
00031 
00032 #include <stdint.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <libgen.h>
00036 #include <ctype.h>
00037 #include <ipxe/vsprintf.h>
00038 #include <ipxe/params.h>
00039 #include <ipxe/tcpip.h>
00040 #include <ipxe/uri.h>
00041 
00042 /**
00043  * Decode URI field
00044  *
00045  * @v encoded           Encoded field
00046  * @v buf               Data buffer
00047  * @v len               Length
00048  * @ret len             Length of data
00049  *
00050  * URI decoding can never increase the length of a string; we can
00051  * therefore safely decode in place.
00052  */
00053 size_t uri_decode ( const char *encoded, void *buf, size_t len ) {
00054         uint8_t *out = buf;
00055         unsigned int count = 0;
00056         char hexbuf[3];
00057         char *hexbuf_end;
00058         char c;
00059         char decoded;
00060         unsigned int skip;
00061 
00062         /* Copy string, decoding escaped characters as necessary */
00063         while ( ( c = *(encoded++) ) ) {
00064                 if ( c == '%' ) {
00065                         snprintf ( hexbuf, sizeof ( hexbuf ), "%s", encoded );
00066                         decoded = strtoul ( hexbuf, &hexbuf_end, 16 );
00067                         skip = ( hexbuf_end - hexbuf );
00068                         encoded += skip;
00069                         if ( skip )
00070                                 c = decoded;
00071                 }
00072                 if ( count < len )
00073                         out[count] = c;
00074                 count++;
00075         }
00076         return count;
00077 }
00078 
00079 /**
00080  * Decode URI field in-place
00081  *
00082  * @v uri               URI
00083  * @v field             URI field index
00084  */
00085 static void uri_decode_inplace ( struct uri *uri, unsigned int field ) {
00086         const char *encoded = uri_field ( uri, field );
00087         char *decoded = ( ( char * ) encoded );
00088         size_t len;
00089 
00090         /* Do nothing if field is not present */
00091         if ( ! encoded )
00092                 return;
00093 
00094         /* Decode field in place */
00095         len = uri_decode ( encoded, decoded, strlen ( encoded ) );
00096 
00097         /* Terminate decoded string */
00098         decoded[len] = '\0';
00099 }
00100 
00101 /**
00102  * Check if character should be escaped within a URI field
00103  *
00104  * @v c                 Character
00105  * @v field             URI field index
00106  * @ret escaped         Character should be escaped
00107  */
00108 static int uri_character_escaped ( char c, unsigned int field ) {
00109 
00110         /* Non-printing characters and whitespace should always be
00111          * escaped, since they cannot sensibly be displayed as part of
00112          * a coherent URL string.  (This test also catches control
00113          * characters such as CR and LF, which could affect the
00114          * operation of line-based protocols such as HTTP.)
00115          *
00116          * We should also escape characters which would alter the
00117          * interpretation of the URL if not escaped, i.e. characters
00118          * which have significance to the URL parser.  We should not
00119          * blindly escape all such characters, because this would lead
00120          * to some very strange-looking URLs (e.g. if we were to
00121          * always escape '/' as "%2F" even within the URI path).
00122          *
00123          * We do not need to be perfect.  Our primary role is as a
00124          * consumer of URIs rather than a producer; the main situation
00125          * in which we produce a URI string is for display to a human
00126          * user, who can probably tolerate some variance from the
00127          * formal specification.  The only situation in which we
00128          * currently produce a URI string to be consumed by a computer
00129          * is when constructing an HTTP request URI, which contains
00130          * only the path and query fields.
00131          *
00132          * We can therefore sacrifice some correctness for the sake of
00133          * code size.  For example, colons within the URI host should
00134          * be escaped unless they form part of an IPv6 literal
00135          * address; doing this correctly would require the URI
00136          * formatter to be aware of whether or not the URI host
00137          * contained an IPv4 address, an IPv6 address, or a host name.
00138          * We choose to simplify and never escape colons within the
00139          * URI host field: in the event of a pathological hostname
00140          * containing colons, this could potentially produce a URI
00141          * string which could not be reparsed.
00142          *
00143          * After excluding non-printing characters, whitespace, and
00144          * '%', the full set of characters with significance to the
00145          * URL parser is "/#:@?".  We choose for each URI field which
00146          * of these require escaping in our use cases.
00147          *
00148          * For the scheme field (equivalently, if field is zero), we
00149          * escape anything that has significance not just for our URI
00150          * parser but for any other URI parsers (e.g. HTTP query
00151          * string parsers, which care about '=' and '&').
00152          */
00153         static const char *escaped[URI_FIELDS] = {
00154                 /* Scheme or default: escape everything */
00155                 [URI_SCHEME]    = "/#:@?=&",
00156                 /* Opaque part: escape characters which would affect
00157                  * the reparsing of the URI, allowing everything else
00158                  * (e.g. ':', which will appear in iSCSI URIs).
00159                  */
00160                 [URI_OPAQUE]    = "#",
00161                 /* User name: escape everything */
00162                 [URI_USER]      = "/#:@?",
00163                 /* Password: escape everything */
00164                 [URI_PASSWORD]  = "/#:@?",
00165                 /* Host name: escape everything except ':', which may
00166                  * appear as part of an IPv6 literal address.
00167                  */
00168                 [URI_HOST]      = "/#@?",
00169                 /* Port number: escape everything */
00170                 [URI_PORT]      = "/#:@?",
00171                 /* Path: escape everything except '/', which usually
00172                  * appears within paths.
00173                  */
00174                 [URI_PATH]      = "#:@?",
00175                 /* Query: escape everything except '/', which
00176                  * sometimes appears within queries.
00177                  */
00178                 [URI_QUERY]     = "#:@?",
00179                 /* Fragment: escape everything */
00180                 [URI_FRAGMENT]  = "/#:@?",
00181         };
00182 
00183         return ( /* Always escape non-printing characters and whitespace */
00184                  ( ! isprint ( c ) ) || ( c == ' ' ) ||
00185                  /* Always escape '%' */
00186                  ( c == '%' ) ||
00187                  /* Escape field-specific characters */
00188                  strchr ( escaped[field], c ) );
00189 }
00190 
00191 /**
00192  * Encode URI field
00193  *
00194  * @v field             URI field index
00195  * @v raw               Raw data
00196  * @v raw_len           Length of raw data
00197  * @v buf               Buffer
00198  * @v len               Length of buffer
00199  * @ret len             Length of encoded string (excluding NUL)
00200  */
00201 size_t uri_encode ( unsigned int field, const void *raw, size_t raw_len,
00202                     char *buf, ssize_t len ) {
00203         const uint8_t *raw_bytes = ( ( const uint8_t * ) raw );
00204         ssize_t remaining = len;
00205         size_t used;
00206         char c;
00207 
00208         /* Ensure encoded string is NUL-terminated even if empty */
00209         if ( len > 0 )
00210                 buf[0] = '\0';
00211 
00212         /* Copy string, escaping as necessary */
00213         while ( raw_len-- ) {
00214                 c = *(raw_bytes++);
00215                 if ( uri_character_escaped ( c, field ) ) {
00216                         used = ssnprintf ( buf, remaining, "%%%02X", c );
00217                 } else {
00218                         used = ssnprintf ( buf, remaining, "%c", c );
00219                 }
00220                 buf += used;
00221                 remaining -= used;
00222         }
00223 
00224         return ( len - remaining );
00225 }
00226 
00227 /**
00228  * Encode URI field string
00229  *
00230  * @v field             URI field index
00231  * @v string            String
00232  * @v buf               Buffer
00233  * @v len               Length of buffer
00234  * @ret len             Length of encoded string (excluding NUL)
00235  */
00236 size_t uri_encode_string ( unsigned int field, const char *string,
00237                            char *buf, ssize_t len ) {
00238 
00239         return uri_encode ( field, string, strlen ( string ), buf, len );
00240 }
00241 
00242 /**
00243  * Dump URI for debugging
00244  *
00245  * @v uri               URI
00246  */
00247 static void uri_dump ( const struct uri *uri ) {
00248 
00249         if ( ! uri )
00250                 return;
00251         if ( uri->scheme )
00252                 DBGC ( uri, " scheme \"%s\"", uri->scheme );
00253         if ( uri->opaque )
00254                 DBGC ( uri, " opaque \"%s\"", uri->opaque );
00255         if ( uri->user )
00256                 DBGC ( uri, " user \"%s\"", uri->user );
00257         if ( uri->password )
00258                 DBGC ( uri, " password \"%s\"", uri->password );
00259         if ( uri->host )
00260                 DBGC ( uri, " host \"%s\"", uri->host );
00261         if ( uri->port )
00262                 DBGC ( uri, " port \"%s\"", uri->port );
00263         if ( uri->path )
00264                 DBGC ( uri, " path \"%s\"", uri->path );
00265         if ( uri->query )
00266                 DBGC ( uri, " query \"%s\"", uri->query );
00267         if ( uri->fragment )
00268                 DBGC ( uri, " fragment \"%s\"", uri->fragment );
00269         if ( uri->params )
00270                 DBGC ( uri, " params \"%s\"", uri->params->name );
00271 }
00272 
00273 /**
00274  * Free URI
00275  *
00276  * @v refcnt            Reference count
00277  */
00278 static void uri_free ( struct refcnt *refcnt ) {
00279         struct uri *uri = container_of ( refcnt, struct uri, refcnt );
00280 
00281         params_put ( uri->params );
00282         free ( uri );
00283 }
00284 
00285 /**
00286  * Parse URI
00287  *
00288  * @v uri_string        URI as a string
00289  * @ret uri             URI
00290  *
00291  * Splits a URI into its component parts.  The return URI structure is
00292  * dynamically allocated and must eventually be freed by calling
00293  * uri_put().
00294  */
00295 struct uri * parse_uri ( const char *uri_string ) {
00296         struct uri *uri;
00297         struct parameters *params;
00298         char *raw;
00299         char *tmp;
00300         char *path;
00301         char *authority;
00302         size_t raw_len;
00303         unsigned int field;
00304 
00305         /* Allocate space for URI struct and a copy of the string */
00306         raw_len = ( strlen ( uri_string ) + 1 /* NUL */ );
00307         uri = zalloc ( sizeof ( *uri ) + raw_len );
00308         if ( ! uri )
00309                 return NULL;
00310         ref_init ( &uri->refcnt, uri_free );
00311         raw = ( ( ( void * ) uri ) + sizeof ( *uri ) );
00312 
00313         /* Copy in the raw string */
00314         memcpy ( raw, uri_string, raw_len );
00315 
00316         /* Identify the parameter list, if present */
00317         if ( ( tmp = strstr ( raw, "##params" ) ) ) {
00318                 *tmp = '\0';
00319                 tmp += 8 /* "##params" */;
00320                 params = find_parameters ( *tmp ? ( tmp + 1 ) : NULL );
00321                 if ( params ) {
00322                         uri->params = claim_parameters ( params );
00323                 } else {
00324                         /* Ignore non-existent submission blocks */
00325                 }
00326         }
00327 
00328         /* Chop off the fragment, if it exists */
00329         if ( ( tmp = strchr ( raw, '#' ) ) ) {
00330                 *(tmp++) = '\0';
00331                 uri->fragment = tmp;
00332         }
00333 
00334         /* Identify absolute/relative URI */
00335         if ( ( tmp = strchr ( raw, ':' ) ) ) {
00336                 /* Absolute URI: identify hierarchical/opaque */
00337                 uri->scheme = raw;
00338                 *(tmp++) = '\0';
00339                 if ( *tmp == '/' ) {
00340                         /* Absolute URI with hierarchical part */
00341                         path = tmp;
00342                 } else {
00343                         /* Absolute URI with opaque part */
00344                         uri->opaque = tmp;
00345                         path = NULL;
00346                 }
00347         } else {
00348                 /* Relative URI */
00349                 path = raw;
00350         }
00351 
00352         /* If we don't have a path (i.e. we have an absolute URI with
00353          * an opaque portion, we're already finished processing
00354          */
00355         if ( ! path )
00356                 goto done;
00357 
00358         /* Chop off the query, if it exists */
00359         if ( ( tmp = strchr ( path, '?' ) ) ) {
00360                 *(tmp++) = '\0';
00361                 uri->query = tmp;
00362         }
00363 
00364         /* If we have no path remaining, then we're already finished
00365          * processing.
00366          */
00367         if ( ! path[0] )
00368                 goto done;
00369 
00370         /* Identify net/absolute/relative path */
00371         if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) {
00372                 /* Net path.  If this is terminated by the first '/'
00373                  * of an absolute path, then we have no space for a
00374                  * terminator after the authority field, so shuffle
00375                  * the authority down by one byte, overwriting one of
00376                  * the two slashes.
00377                  */
00378                 authority = ( path + 2 );
00379                 if ( ( tmp = strchr ( authority, '/' ) ) ) {
00380                         /* Shuffle down */
00381                         uri->path = tmp;
00382                         memmove ( ( authority - 1 ), authority,
00383                                   ( tmp - authority ) );
00384                         authority--;
00385                         *(--tmp) = '\0';
00386                 }
00387         } else {
00388                 /* Absolute/relative path */
00389                 uri->path = path;
00390                 authority = NULL;
00391         }
00392 
00393         /* If we don't have an authority (i.e. we have a non-net
00394          * path), we're already finished processing
00395          */
00396         if ( ! authority )
00397                 goto done;
00398 
00399         /* Split authority into user[:password] and host[:port] portions */
00400         if ( ( tmp = strchr ( authority, '@' ) ) ) {
00401                 /* Has user[:password] */
00402                 *(tmp++) = '\0';
00403                 uri->host = tmp;
00404                 uri->user = authority;
00405                 if ( ( tmp = strchr ( authority, ':' ) ) ) {
00406                         /* Has password */
00407                         *(tmp++) = '\0';
00408                         uri->password = tmp;
00409                 }
00410         } else {
00411                 /* No user:password */
00412                 uri->host = authority;
00413         }
00414 
00415         /* Split host into host[:port] */
00416         if ( ( uri->host[ strlen ( uri->host ) - 1 ] != ']' ) &&
00417              ( tmp = strrchr ( uri->host, ':' ) ) ) {
00418                 *(tmp++) = '\0';
00419                 uri->port = tmp;
00420         }
00421 
00422  done:
00423         /* Decode fields in-place */
00424         for ( field = 0 ; field < URI_FIELDS ; field++ )
00425                 uri_decode_inplace ( uri, field );
00426 
00427         DBGC ( uri, "URI parsed \"%s\" to", uri_string );
00428         uri_dump ( uri );
00429         DBGC ( uri, "\n" );
00430 
00431         return uri;
00432 }
00433 
00434 /**
00435  * Get port from URI
00436  *
00437  * @v uri               URI, or NULL
00438  * @v default_port      Default port to use if none specified in URI
00439  * @ret port            Port
00440  */
00441 unsigned int uri_port ( const struct uri *uri, unsigned int default_port ) {
00442 
00443         if ( ( ! uri ) || ( ! uri->port ) )
00444                 return default_port;
00445 
00446         return ( strtoul ( uri->port, NULL, 0 ) );
00447 }
00448 
00449 /**
00450  * Format URI
00451  *
00452  * @v uri               URI
00453  * @v buf               Buffer to fill with URI string
00454  * @v size              Size of buffer
00455  * @ret len             Length of URI string
00456  */
00457 size_t format_uri ( const struct uri *uri, char *buf, size_t len ) {
00458         static const char prefixes[URI_FIELDS] = {
00459                 [URI_PASSWORD] = ':',
00460                 [URI_PORT] = ':',
00461                 [URI_QUERY] = '?',
00462                 [URI_FRAGMENT] = '#',
00463         };
00464         char prefix;
00465         size_t used = 0;
00466         unsigned int field;
00467 
00468         /* Ensure buffer is NUL-terminated */
00469         if ( len )
00470                 buf[0] = '\0';
00471 
00472         /* Special-case NULL URI */
00473         if ( ! uri )
00474                 return 0;
00475 
00476         /* Generate fields */
00477         for ( field = 0 ; field < URI_FIELDS ; field++ ) {
00478 
00479                 /* Skip non-existent fields */
00480                 if ( ! uri_field ( uri, field ) )
00481                         continue;
00482 
00483                 /* Prefix this field, if applicable */
00484                 prefix = prefixes[field];
00485                 if ( ( field == URI_HOST ) && ( uri->user != NULL ) )
00486                         prefix = '@';
00487                 if ( prefix ) {
00488                         used += ssnprintf ( ( buf + used ), ( len - used ),
00489                                             "%c", prefix );
00490                 }
00491 
00492                 /* Encode this field */
00493                 used += uri_encode_string ( field, uri_field ( uri, field ),
00494                                             ( buf + used ), ( len - used ) );
00495 
00496                 /* Suffix this field, if applicable */
00497                 if ( field == URI_SCHEME ) {
00498                         used += ssnprintf ( ( buf + used ), ( len - used ),
00499                                             ":%s", ( uri->host ? "//" : "" ) );
00500                 }
00501         }
00502 
00503         if ( len ) {
00504                 DBGC ( uri, "URI formatted" );
00505                 uri_dump ( uri );
00506                 DBGC ( uri, " to \"%s%s\"\n", buf,
00507                        ( ( used > len ) ? "<TRUNCATED>" : "" ) );
00508         }
00509 
00510         return used;
00511 }
00512 
00513 /**
00514  * Format URI
00515  *
00516  * @v uri               URI
00517  * @ret string          URI string, or NULL on failure
00518  *
00519  * The caller is responsible for eventually freeing the allocated
00520  * memory.
00521  */
00522 char * format_uri_alloc ( const struct uri *uri ) {
00523         size_t len;
00524         char *string;
00525 
00526         len = ( format_uri ( uri, NULL, 0 ) + 1 /* NUL */ );
00527         string = malloc ( len );
00528         if ( string )
00529                 format_uri ( uri, string, len );
00530         return string;
00531 }
00532 
00533 /**
00534  * Copy URI fields
00535  *
00536  * @v src               Source URI
00537  * @v dest              Destination URI, or NULL to calculate length
00538  * @ret len             Length of raw URI
00539  */
00540 static size_t uri_copy_fields ( const struct uri *src, struct uri *dest ) {
00541         size_t len = sizeof ( *dest );
00542         char *out = ( ( void * ) dest + len );
00543         unsigned int field;
00544         size_t field_len;
00545 
00546         /* Copy existent fields */
00547         for ( field = 0 ; field < URI_FIELDS ; field++ ) {
00548 
00549                 /* Skip non-existent fields */
00550                 if ( ! uri_field ( src, field ) )
00551                         continue;
00552 
00553                 /* Calculate field length */
00554                 field_len = ( strlen ( uri_field ( src, field ) )
00555                               + 1 /* NUL */ );
00556                 len += field_len;
00557 
00558                 /* Copy field, if applicable */
00559                 if ( dest ) {
00560                         memcpy ( out, uri_field ( src, field ), field_len );
00561                         uri_field ( dest, field ) = out;
00562                         out += field_len;
00563                 }
00564         }
00565         return len;
00566 }
00567 
00568 /**
00569  * Duplicate URI
00570  *
00571  * @v uri               URI
00572  * @ret uri             Duplicate URI
00573  *
00574  * Creates a modifiable copy of a URI.
00575  */
00576 struct uri * uri_dup ( const struct uri *uri ) {
00577         struct uri *dup;
00578         size_t len;
00579 
00580         /* Allocate new URI */
00581         len = uri_copy_fields ( uri, NULL );
00582         dup = zalloc ( len );
00583         if ( ! dup )
00584                 return NULL;
00585         ref_init ( &dup->refcnt, uri_free );
00586 
00587         /* Copy fields */
00588         uri_copy_fields ( uri, dup );
00589 
00590         /* Copy parameters */
00591         dup->params = params_get ( uri->params );
00592 
00593         DBGC ( uri, "URI duplicated" );
00594         uri_dump ( uri );
00595         DBGC ( uri, "\n" );
00596 
00597         return dup;
00598 }
00599 
00600 /**
00601  * Resolve base+relative path
00602  *
00603  * @v base_uri          Base path
00604  * @v relative_uri      Relative path
00605  * @ret resolved_uri    Resolved path, or NULL on failure
00606  *
00607  * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative
00608  * path (e.g. "initrd.gz") and produces a new path
00609  * (e.g. "/var/lib/tftpboot/initrd.gz").  Note that any non-directory
00610  * portion of the base path will automatically be stripped; this
00611  * matches the semantics used when resolving the path component of
00612  * URIs.
00613  */
00614 char * resolve_path ( const char *base_path,
00615                       const char *relative_path ) {
00616         char *base_copy;
00617         char *base_tmp;
00618         char *resolved;
00619 
00620         /* If relative path is absolute, just re-use it */
00621         if ( relative_path[0] == '/' )
00622                 return strdup ( relative_path );
00623 
00624         /* Create modifiable copy of path for dirname() */
00625         base_copy = strdup ( base_path );
00626         if ( ! base_copy )
00627                 return NULL;
00628 
00629         /* Strip filename portion of base path */
00630         base_tmp = dirname ( base_copy );
00631 
00632         /* Process "./" and "../" elements */
00633         while ( *relative_path == '.' ) {
00634                 relative_path++;
00635                 if ( *relative_path == 0 ) {
00636                         /* Do nothing */
00637                 } else if ( *relative_path == '/' ) {
00638                         relative_path++;
00639                 } else if ( *relative_path == '.' ) {
00640                         relative_path++;
00641                         if ( *relative_path == 0 ) {
00642                                 base_tmp = dirname ( base_tmp );
00643                         } else if ( *relative_path == '/' ) {
00644                                 base_tmp = dirname ( base_tmp );
00645                                 relative_path++;
00646                         } else {
00647                                 relative_path -= 2;
00648                                 break;
00649                         }
00650                 } else {
00651                         relative_path--;
00652                         break;
00653                 }
00654         }
00655 
00656         /* Create and return new path */
00657         if ( asprintf ( &resolved, "%s%s%s", base_tmp,
00658                         ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ?
00659                           "" : "/" ), relative_path ) < 0 )
00660                 resolved = NULL;
00661         free ( base_copy );
00662         return resolved;
00663 }
00664 
00665 /**
00666  * Resolve base+relative URI
00667  *
00668  * @v base_uri          Base URI, or NULL
00669  * @v relative_uri      Relative URI
00670  * @ret resolved_uri    Resolved URI, or NULL on failure
00671  *
00672  * Takes a base URI (e.g. "http://ipxe.org/kernels/vmlinuz" and a
00673  * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI
00674  * (e.g. "http://ipxe.org/initrds/initrd.gz").
00675  */
00676 struct uri * resolve_uri ( const struct uri *base_uri,
00677                            struct uri *relative_uri ) {
00678         struct uri tmp_uri;
00679         char *tmp_path = NULL;
00680         struct uri *new_uri;
00681 
00682         /* If relative URI is absolute, just re-use it */
00683         if ( uri_is_absolute ( relative_uri ) || ( ! base_uri ) )
00684                 return uri_get ( relative_uri );
00685 
00686         /* Mangle URI */
00687         memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) );
00688         if ( relative_uri->path ) {
00689                 tmp_path = resolve_path ( ( base_uri->path ?
00690                                             base_uri->path : "/" ),
00691                                           relative_uri->path );
00692                 tmp_uri.path = tmp_path;
00693                 tmp_uri.query = relative_uri->query;
00694                 tmp_uri.fragment = relative_uri->fragment;
00695                 tmp_uri.params = relative_uri->params;
00696         } else if ( relative_uri->query ) {
00697                 tmp_uri.query = relative_uri->query;
00698                 tmp_uri.fragment = relative_uri->fragment;
00699                 tmp_uri.params = relative_uri->params;
00700         } else if ( relative_uri->fragment ) {
00701                 tmp_uri.fragment = relative_uri->fragment;
00702                 tmp_uri.params = relative_uri->params;
00703         } else if ( relative_uri->params ) {
00704                 tmp_uri.params = relative_uri->params;
00705         }
00706 
00707         /* Create demangled URI */
00708         new_uri = uri_dup ( &tmp_uri );
00709         free ( tmp_path );
00710         return new_uri;
00711 }
00712 
00713 /**
00714  * Construct TFTP URI from server address and filename
00715  *
00716  * @v sa_server         Server address
00717  * @v filename          Filename
00718  * @ret uri             URI, or NULL on failure
00719  */
00720 static struct uri * tftp_uri ( struct sockaddr *sa_server,
00721                                const char *filename ) {
00722         struct sockaddr_tcpip *st_server =
00723                 ( ( struct sockaddr_tcpip * ) sa_server );
00724         char buf[ 6 /* "65535" + NUL */ ];
00725         char *path;
00726         struct uri tmp;
00727         struct uri *uri = NULL;
00728 
00729         /* Initialise TFTP URI */
00730         memset ( &tmp, 0, sizeof ( tmp ) );
00731         tmp.scheme = "tftp";
00732 
00733         /* Construct TFTP server address */
00734         tmp.host = sock_ntoa ( sa_server );
00735         if ( ! tmp.host )
00736                 goto err_host;
00737 
00738         /* Construct TFTP server port, if applicable */
00739         if ( st_server->st_port ) {
00740                 snprintf ( buf, sizeof ( buf ), "%d",
00741                            ntohs ( st_server->st_port ) );
00742                 tmp.port = buf;
00743         }
00744 
00745         /* Construct TFTP path */
00746         if ( asprintf ( &path, "/%s", filename ) < 0 )
00747                 goto err_path;
00748         tmp.path = path;
00749 
00750         /* Demangle URI */
00751         uri = uri_dup ( &tmp );
00752         if ( ! uri )
00753                 goto err_uri;
00754 
00755  err_uri:
00756         free ( path );
00757  err_path:
00758  err_host:
00759         return uri;
00760 }
00761 
00762 /**
00763  * Construct URI from server address and filename
00764  *
00765  * @v sa_server         Server address
00766  * @v filename          Filename
00767  * @ret uri             URI, or NULL on failure
00768  *
00769  * PXE TFTP filenames specified via the DHCP next-server field often
00770  * contain characters such as ':' or '#' which would confuse the
00771  * generic URI parser.  We provide a mechanism for directly
00772  * constructing a TFTP URI from the next-server and filename.
00773  */
00774 struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) {
00775         struct uri *uri;
00776 
00777         /* Fail if filename is empty */
00778         if ( ! ( filename && filename[0] ) )
00779                 return NULL;
00780 
00781         /* If filename is a hierarchical absolute URI, then use that
00782          * URI.  (We accept only hierarchical absolute URIs, since PXE
00783          * filenames sometimes start with DOS drive letters such as
00784          * "C:\", which get misinterpreted as opaque absolute URIs.)
00785          */
00786         uri = parse_uri ( filename );
00787         if ( uri && uri_is_absolute ( uri ) && ( ! uri->opaque ) )
00788                 return uri;
00789         uri_put ( uri );
00790 
00791         /* Otherwise, construct a TFTP URI directly */
00792         return tftp_uri ( sa_server, filename );
00793 }