Browse Source

[uri] Refactor URI parsing and formatting

Add support for parsing of URIs containing literal IPv6 addresses
(e.g. "http://[fe80::69ff:fe50:5845%25net0]/boot.ipxe").

Duplicate URIs by directly copying the relevant fields, rather than by
formatting and reparsing a URI string.  This relaxes the requirements
on the URI formatting code and allows it to focus on generating
human-readable URIs (e.g. by not escaping ':' characters within
literal IPv6 addresses).  As a side-effect, this allows relative URIs
containing parameter lists (e.g. "../boot.php##params") to function
as expected.

Add validity check for FTP paths to ensure that only printable
characters are accepted (since FTP is a human-readable line-based
protocol with no support for character escaping).

Construct TFTP next-server+filename URIs directly, rather than parsing
a constructed "tftp://..." string,

Add self-tests for URI functions.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 10 years ago
parent
commit
7667536527
12 changed files with 1312 additions and 437 deletions
  1. 1
    2
      src/arch/i386/image/multiboot.c
  2. 330
    180
      src/core/uri.c
  3. 31
    31
      src/crypto/ocsp.c
  4. 41
    37
      src/include/ipxe/uri.h
  5. 29
    2
      src/net/tcp/ftp.c
  6. 34
    28
      src/net/tcp/httpcore.c
  7. 1
    1
      src/net/udp/slam.c
  8. 1
    13
      src/net/udp/tftp.c
  9. 1
    0
      src/tests/tests.c
  10. 832
    120
      src/tests/uri_test.c
  11. 2
    11
      src/usr/autoboot.c
  12. 9
    12
      src/usr/imgmgmt.c

+ 1
- 2
src/arch/i386/image/multiboot.c View File

@@ -152,8 +152,7 @@ static physaddr_t multiboot_add_cmdline ( struct image *image ) {
152 152
 	size_t len;
153 153
 
154 154
 	/* Copy image URI to base memory buffer as start of command line */
155
-	len = ( unparse_uri ( buf, remaining, image->uri,
156
-			      URI_ALL ) + 1 /* NUL */ );
155
+	len = ( format_uri ( image->uri, buf, remaining ) + 1 /* NUL */ );
157 156
 	if ( len > remaining )
158 157
 		len = remaining;
159 158
 	mb_cmdline_offset += len;

+ 330
- 180
src/core/uri.c View File

@@ -34,34 +34,184 @@ FILE_LICENCE ( GPL2_OR_LATER );
34 34
 #include <ipxe/params.h>
35 35
 #include <ipxe/uri.h>
36 36
 
37
+/**
38
+ * Decode URI field (in place)
39
+ *
40
+ * @v string		String
41
+ *
42
+ * URI decoding can never increase the length of a string; we can
43
+ * therefore safely decode in place.
44
+ */
45
+static void uri_decode ( char *string ) {
46
+	char *dest = string;
47
+	char hexbuf[3];
48
+	char *hexbuf_end;
49
+	char c;
50
+	char decoded;
51
+	unsigned int skip;
52
+
53
+	/* Copy string, decoding escaped characters as necessary */
54
+	do {
55
+		c = *(string++);
56
+		if ( c == '%' ) {
57
+			snprintf ( hexbuf, sizeof ( hexbuf ), "%s", string );
58
+			decoded = strtoul ( hexbuf, &hexbuf_end, 16 );
59
+			skip = ( hexbuf_end - hexbuf );
60
+			string += skip;
61
+			if ( skip )
62
+				c = decoded;
63
+		}
64
+		*(dest++) = c;
65
+	} while ( c );
66
+}
67
+
68
+/**
69
+ * Check if character should be escaped within a URI field
70
+ *
71
+ * @v c			Character
72
+ * @v field		URI field index
73
+ * @ret escaped		Character should be escaped
74
+ */
75
+static int uri_character_escaped ( char c, unsigned int field ) {
76
+
77
+	/* Non-printing characters and whitespace should always be
78
+	 * escaped, since they cannot sensibly be displayed as part of
79
+	 * a coherent URL string.  (This test also catches control
80
+	 * characters such as CR and LF, which could affect the
81
+	 * operation of line-based protocols such as HTTP.)
82
+	 *
83
+	 * We should also escape characters which would alter the
84
+	 * interpretation of the URL if not escaped, i.e. characters
85
+	 * which have significance to the URL parser.  We should not
86
+	 * blindly escape all such characters, because this would lead
87
+	 * to some very strange-looking URLs (e.g. if we were to
88
+	 * always escape '/' as "%2F" even within the URI path).
89
+	 *
90
+	 * We do not need to be perfect.  Our primary role is as a
91
+	 * consumer of URIs rather than a producer; the main situation
92
+	 * in which we produce a URI string is for display to a human
93
+	 * user, who can probably tolerate some variance from the
94
+	 * formal specification.  The only situation in which we
95
+	 * currently produce a URI string to be consumed by a computer
96
+	 * is when constructing an HTTP request URI, which contains
97
+	 * only the path and query fields.
98
+	 *
99
+	 * We can therefore sacrifice some correctness for the sake of
100
+	 * code size.  For example, colons within the URI host should
101
+	 * be escaped unless they form part of an IPv6 literal
102
+	 * address; doing this correctly would require the URI
103
+	 * formatter to be aware of whether or not the URI host
104
+	 * contained an IPv4 address, an IPv6 address, or a host name.
105
+	 * We choose to simplify and never escape colons within the
106
+	 * URI host field: in the event of a pathological hostname
107
+	 * containing colons, this could potentially produce a URI
108
+	 * string which could not be reparsed.
109
+	 *
110
+	 * After excluding non-printing characters, whitespace, and
111
+	 * '%', the full set of characters with significance to the
112
+	 * URL parser is "/#:@?".  We choose for each URI field which
113
+	 * of these require escaping in our use cases.
114
+	 */
115
+	static const char *escaped[URI_FIELDS] = {
116
+		/* Scheme: escape everything */
117
+		[URI_SCHEME]	= "/#:@?",
118
+		/* Opaque part: escape characters which would affect
119
+		 * the reparsing of the URI, allowing everything else
120
+		 * (e.g. ':', which will appear in iSCSI URIs).
121
+		 */
122
+		[URI_OPAQUE]	= "/#",
123
+		/* User name: escape everything */
124
+		[URI_USER]	= "/#:@?",
125
+		/* Password: escape everything */
126
+		[URI_PASSWORD]	= "/#:@?",
127
+		/* Host name: escape everything except ':', which may
128
+		 * appear as part of an IPv6 literal address.
129
+		 */
130
+		[URI_HOST]	= "/#@?",
131
+		/* Port number: escape everything */
132
+		[URI_PORT]	= "/#:@?",
133
+		/* Path: escape everything except '/', which usually
134
+		 * appears within paths.
135
+		 */
136
+		[URI_PATH]	= "#:@?",
137
+		/* Query: escape everything except '/', which
138
+		 * sometimes appears within queries.
139
+		 */
140
+		[URI_QUERY]	= "#:@?",
141
+		/* Fragment: escape everything */
142
+		[URI_FRAGMENT]	= "/#:@?",
143
+	};
144
+
145
+	return ( /* Always escape non-printing characters and whitespace */
146
+		 ( ! isprint ( c ) ) || ( c == ' ' ) ||
147
+		 /* Always escape '%' */
148
+		 ( c == '%' ) ||
149
+		 /* Escape field-specific characters */
150
+		 strchr ( escaped[field], c ) );
151
+}
152
+
153
+/**
154
+ * Encode URI field
155
+ *
156
+ * @v uri		URI
157
+ * @v field		URI field index
158
+ * @v buf		Buffer to contain encoded string
159
+ * @v len		Length of buffer
160
+ * @ret len		Length of encoded string (excluding NUL)
161
+ */
162
+size_t uri_encode ( const char *string, unsigned int field,
163
+		    char *buf, ssize_t len ) {
164
+	ssize_t remaining = len;
165
+	size_t used;
166
+	char c;
167
+
168
+	/* Ensure encoded string is NUL-terminated even if empty */
169
+	if ( len > 0 )
170
+		buf[0] = '\0';
171
+
172
+	/* Copy string, escaping as necessary */
173
+	while ( ( c = *(string++) ) ) {
174
+		if ( uri_character_escaped ( c, field ) ) {
175
+			used = ssnprintf ( buf, remaining, "%%%02X", c );
176
+		} else {
177
+			used = ssnprintf ( buf, remaining, "%c", c );
178
+		}
179
+		buf += used;
180
+		remaining -= used;
181
+	}
182
+
183
+	return ( len - remaining );
184
+}
185
+
37 186
 /**
38 187
  * Dump URI for debugging
39 188
  *
40 189
  * @v uri		URI
41 190
  */
42
-static void dump_uri ( struct uri *uri ) {
191
+static void uri_dump ( const struct uri *uri ) {
192
+
43 193
 	if ( ! uri )
44 194
 		return;
45 195
 	if ( uri->scheme )
46
-		DBG ( " scheme \"%s\"", uri->scheme );
196
+		DBGC ( uri, " scheme \"%s\"", uri->scheme );
47 197
 	if ( uri->opaque )
48
-		DBG ( " opaque \"%s\"", uri->opaque );
198
+		DBGC ( uri, " opaque \"%s\"", uri->opaque );
49 199
 	if ( uri->user )
50
-		DBG ( " user \"%s\"", uri->user );
200
+		DBGC ( uri, " user \"%s\"", uri->user );
51 201
 	if ( uri->password )
52
-		DBG ( " password \"%s\"", uri->password );
202
+		DBGC ( uri, " password \"%s\"", uri->password );
53 203
 	if ( uri->host )
54
-		DBG ( " host \"%s\"", uri->host );
204
+		DBGC ( uri, " host \"%s\"", uri->host );
55 205
 	if ( uri->port )
56
-		DBG ( " port \"%s\"", uri->port );
206
+		DBGC ( uri, " port \"%s\"", uri->port );
57 207
 	if ( uri->path )
58
-		DBG ( " path \"%s\"", uri->path );
208
+		DBGC ( uri, " path \"%s\"", uri->path );
59 209
 	if ( uri->query )
60
-		DBG ( " query \"%s\"", uri->query );
210
+		DBGC ( uri, " query \"%s\"", uri->query );
61 211
 	if ( uri->fragment )
62
-		DBG ( " fragment \"%s\"", uri->fragment );
212
+		DBGC ( uri, " fragment \"%s\"", uri->fragment );
63 213
 	if ( uri->params )
64
-		DBG ( " params \"%s\"", uri->params->name );
214
+		DBGC ( uri, " params \"%s\"", uri->params->name );
65 215
 }
66 216
 
67 217
 /**
@@ -69,7 +219,7 @@ static void dump_uri ( struct uri *uri ) {
69 219
  *
70 220
  * @v refcnt		Reference count
71 221
  */
72
-static void free_uri ( struct refcnt *refcnt ) {
222
+static void uri_free ( struct refcnt *refcnt ) {
73 223
 	struct uri *uri = container_of ( refcnt, struct uri, refcnt );
74 224
 
75 225
 	params_put ( uri->params );
@@ -93,16 +243,16 @@ struct uri * parse_uri ( const char *uri_string ) {
93 243
 	char *tmp;
94 244
 	char *path;
95 245
 	char *authority;
96
-	int i;
97 246
 	size_t raw_len;
247
+	unsigned int field;
98 248
 
99 249
 	/* Allocate space for URI struct and a copy of the string */
100 250
 	raw_len = ( strlen ( uri_string ) + 1 /* NUL */ );
101 251
 	uri = zalloc ( sizeof ( *uri ) + raw_len );
102 252
 	if ( ! uri )
103 253
 		return NULL;
104
-	ref_init ( &uri->refcnt, free_uri );
105
-	raw = ( ( ( char * ) uri ) + sizeof ( *uri ) );
254
+	ref_init ( &uri->refcnt, uri_free );
255
+	raw = ( ( ( void * ) uri ) + sizeof ( *uri ) );
106 256
 
107 257
 	/* Copy in the raw string */
108 258
 	memcpy ( raw, uri_string, raw_len );
@@ -125,12 +275,8 @@ struct uri * parse_uri ( const char *uri_string ) {
125 275
 		uri->fragment = tmp;
126 276
 	}
127 277
 
128
-	/* Identify absolute/relative URI.  We ignore schemes that are
129
-	 * apparently only a single character long, since otherwise we
130
-	 * misinterpret a DOS-style path name ("C:\path\to\file") as a
131
-	 * URI with scheme="C",opaque="\path\to\file".
132
-	 */
133
-	if ( ( tmp = strchr ( raw, ':' ) ) && ( tmp > ( raw + 1 ) ) ) {
278
+	/* Identify absolute/relative URI */
279
+	if ( ( tmp = strchr ( raw, ':' ) ) ) {
134 280
 		/* Absolute URI: identify hierarchical/opaque */
135 281
 		uri->scheme = raw;
136 282
 		*(tmp++) = '\0';
@@ -159,6 +305,12 @@ struct uri * parse_uri ( const char *uri_string ) {
159 305
 		uri->query = tmp;
160 306
 	}
161 307
 
308
+	/* If we have no path remaining, then we're already finished
309
+	 * processing.
310
+	 */
311
+	if ( ! path[0] )
312
+		goto done;
313
+
162 314
 	/* Identify net/absolute/relative path */
163 315
 	if ( strncmp ( path, "//", 2 ) == 0 ) {
164 316
 		/* Net path.  If this is terminated by the first '/'
@@ -205,23 +357,22 @@ struct uri * parse_uri ( const char *uri_string ) {
205 357
 	}
206 358
 
207 359
 	/* Split host into host[:port] */
208
-	if ( ( tmp = strchr ( uri->host, ':' ) ) ) {
360
+	if ( ( uri->host[ strlen ( uri->host ) - 1 ] != ']' ) &&
361
+	     ( tmp = strrchr ( uri->host, ':' ) ) ) {
209 362
 		*(tmp++) = '\0';
210 363
 		uri->port = tmp;
211 364
 	}
212 365
 
213
-	/* Decode fields that should be decoded */
214
-	for ( i = URI_FIRST_FIELD; i <= URI_LAST_FIELD; i++ ) {
215
-		const char *field = uri_get_field ( uri, i );
216
-		if ( field && ( URI_ENCODED & ( 1 << i ) ) )
217
-			uri_decode ( field, ( char * ) field,
218
-				     strlen ( field ) + 1 /* NUL */ );
366
+	/* Decode fields in-place */
367
+	for ( field = 0 ; field < URI_FIELDS ; field++ ) {
368
+		if ( uri_field ( uri, field ) )
369
+			uri_decode ( ( char * ) uri_field ( uri, field ) );
219 370
 	}
220 371
 
221 372
  done:
222
-	DBG ( "URI \"%s\" split into", uri_string );
223
-	dump_uri ( uri );
224
-	DBG ( "\n" );
373
+	DBGC ( uri, "URI parsed \"%s\" to", uri_string );
374
+	uri_dump ( uri );
375
+	DBGC ( uri, "\n" );
225 376
 
226 377
 	return uri;
227 378
 }
@@ -233,82 +384,137 @@ struct uri * parse_uri ( const char *uri_string ) {
233 384
  * @v default_port	Default port to use if none specified in URI
234 385
  * @ret port		Port
235 386
  */
236
-unsigned int uri_port ( struct uri *uri, unsigned int default_port ) {
387
+unsigned int uri_port ( const struct uri *uri, unsigned int default_port ) {
388
+
237 389
 	if ( ( ! uri ) || ( ! uri->port ) )
238 390
 		return default_port;
391
+
239 392
 	return ( strtoul ( uri->port, NULL, 0 ) );
240 393
 }
241 394
 
242 395
 /**
243
- * Unparse URI
396
+ * Format URI
244 397
  *
398
+ * @v uri		URI
245 399
  * @v buf		Buffer to fill with URI string
246 400
  * @v size		Size of buffer
247
- * @v uri		URI to write into buffer, or NULL
248
- * @v fields		Bitmask of fields to include in URI string, or URI_ALL
249 401
  * @ret len		Length of URI string
250 402
  */
251
-int unparse_uri ( char *buf, size_t size, struct uri *uri,
252
-		  unsigned int fields ) {
253
-	/* List of characters that typically go before certain fields */
254
-	static char separators[] = { /* scheme */ 0, /* opaque */ ':',
255
-				     /* user */ 0, /* password */ ':',
256
-				     /* host */ '@', /* port */ ':',
257
-				     /* path */ 0, /* query */ '?',
258
-				     /* fragment */ '#' };
259
-	int used = 0;
260
-	int i;
261
-
262
-	DBG ( "URI unparsing" );
263
-	dump_uri ( uri );
264
-	DBG ( "\n" );
403
+size_t format_uri ( const struct uri *uri, char *buf, size_t len ) {
404
+	static const char prefixes[URI_FIELDS] = {
405
+		[URI_OPAQUE] = ':',
406
+		[URI_PASSWORD] = ':',
407
+		[URI_PORT] = ':',
408
+		[URI_PATH] = '/',
409
+		[URI_QUERY] = '?',
410
+		[URI_FRAGMENT] = '#',
411
+	};
412
+	char prefix;
413
+	size_t used = 0;
414
+	unsigned int field;
265 415
 
266 416
 	/* Ensure buffer is NUL-terminated */
267
-	if ( size )
417
+	if ( len )
268 418
 		buf[0] = '\0';
269 419
 
270 420
 	/* Special-case NULL URI */
271 421
 	if ( ! uri )
272 422
 		return 0;
273 423
 
274
-	/* Iterate through requested fields */
275
-	for ( i = URI_FIRST_FIELD; i <= URI_LAST_FIELD; i++ ) {
276
-		const char *field = uri_get_field ( uri, i );
277
-		char sep = separators[i];
278
-
279
-		/* Ensure `fields' only contains bits for fields that exist */
280
-		if ( ! field )
281
-			fields &= ~( 1 << i );
282
-
283
-		/* Store this field if we were asked to */
284
-		if ( fields & ( 1 << i ) ) {
285
-			/* Print :// if we're non-opaque and had a scheme */
286
-			if ( ( fields & URI_SCHEME_BIT ) &&
287
-			     ( i > URI_OPAQUE ) ) {
288
-				used += ssnprintf ( buf + used, size - used,
289
-						    "://" );
290
-				/* Only print :// once */
291
-				fields &= ~URI_SCHEME_BIT;
292
-			}
424
+	/* Generate fields */
425
+	for ( field = 0 ; field < URI_FIELDS ; field++ ) {
426
+
427
+		/* Skip non-existent fields */
428
+		if ( ! uri_field ( uri, field ) )
429
+			continue;
430
+
431
+		/* Prefix this field, if applicable */
432
+		prefix = prefixes[field];
433
+		if ( ( field == URI_HOST ) && ( uri->user != NULL ) )
434
+			prefix = '@';
435
+		if ( ( field == URI_PATH ) && ( uri->path[0] == '/' ) )
436
+			prefix = '\0';
437
+		if ( prefix ) {
438
+			used += ssnprintf ( ( buf + used ), ( len - used ),
439
+					    "%c", prefix );
440
+		}
441
+
442
+		/* Encode this field */
443
+		used += uri_encode ( uri_field ( uri, field ), field,
444
+				     ( buf + used ), ( len - used ) );
293 445
 
294
-			/* Only print separator if an earlier field exists */
295
-			if ( sep && ( fields & ( ( 1 << i ) - 1 ) ) )
296
-				used += ssnprintf ( buf + used, size - used,
297
-						    "%c", sep );
298
-
299
-			/* Print contents of field, possibly encoded */
300
-			if ( URI_ENCODED & ( 1 << i ) )
301
-				used += uri_encode ( field, buf + used,
302
-						     size - used, i );
303
-			else
304
-				used += ssnprintf ( buf + used, size - used,
305
-						    "%s", field );
446
+		/* Suffix this field, if applicable */
447
+		if ( ( field == URI_SCHEME ) && ( ! uri->opaque ) ) {
448
+			used += ssnprintf ( ( buf + used ), ( len - used ),
449
+					    "://" );
306 450
 		}
307 451
 	}
308 452
 
453
+	if ( len ) {
454
+		DBGC ( uri, "URI formatted" );
455
+		uri_dump ( uri );
456
+		DBGC ( uri, " to \"%s%s\"\n", buf,
457
+		       ( ( used > len ) ? "<TRUNCATED>" : "" ) );
458
+	}
459
+
309 460
 	return used;
310 461
 }
311 462
 
463
+/**
464
+ * Format URI
465
+ *
466
+ * @v uri		URI
467
+ * @ret string		URI string, or NULL on failure
468
+ *
469
+ * The caller is responsible for eventually freeing the allocated
470
+ * memory.
471
+ */
472
+char * format_uri_alloc ( const struct uri *uri ) {
473
+	size_t len;
474
+	char *string;
475
+
476
+	len = ( format_uri ( uri, NULL, 0 ) + 1 /* NUL */ );
477
+	string = malloc ( len );
478
+	if ( string )
479
+		format_uri ( uri, string, len );
480
+	return string;
481
+}
482
+
483
+/**
484
+ * Copy URI fields
485
+ *
486
+ * @v src		Source URI
487
+ * @v dest		Destination URI, or NULL to calculate length
488
+ * @ret len		Length of raw URI
489
+ */
490
+static size_t uri_copy_fields ( const struct uri *src, struct uri *dest ) {
491
+	size_t len = sizeof ( *dest );
492
+	char *out = ( ( void * ) dest + len );
493
+	unsigned int field;
494
+	size_t field_len;
495
+
496
+	/* Copy existent fields */
497
+	for ( field = 0 ; field < URI_FIELDS ; field++ ) {
498
+
499
+		/* Skip non-existent fields */
500
+		if ( ! uri_field ( src, field ) )
501
+			continue;
502
+
503
+		/* Calculate field length */
504
+		field_len = ( strlen ( uri_field ( src, field ) )
505
+			      + 1 /* NUL */ );
506
+		len += field_len;
507
+
508
+		/* Copy field, if applicable */
509
+		if ( dest ) {
510
+			memcpy ( out, uri_field ( src, field ), field_len );
511
+			uri_field ( dest, field ) = out;
512
+			out += field_len;
513
+		}
514
+	}
515
+	return len;
516
+}
517
+
312 518
 /**
313 519
  * Duplicate URI
314 520
  *
@@ -317,12 +523,28 @@ int unparse_uri ( char *buf, size_t size, struct uri *uri,
317 523
  *
318 524
  * Creates a modifiable copy of a URI.
319 525
  */
320
-struct uri * uri_dup ( struct uri *uri ) {
321
-	size_t len = ( unparse_uri ( NULL, 0, uri, URI_ALL ) + 1 );
322
-	char buf[len];
526
+struct uri * uri_dup ( const struct uri *uri ) {
527
+	struct uri *dup;
528
+	size_t len;
529
+
530
+	/* Allocate new URI */
531
+	len = uri_copy_fields ( uri, NULL );
532
+	dup = zalloc ( len );
533
+	if ( ! dup )
534
+		return NULL;
535
+	ref_init ( &dup->refcnt, uri_free );
536
+
537
+	/* Copy fields */
538
+	uri_copy_fields ( uri, dup );
539
+
540
+	/* Copy parameters */
541
+	dup->params = params_get ( uri->params );
323 542
 
324
-	unparse_uri ( buf, len, uri, URI_ALL );
325
-	return parse_uri ( buf );
543
+	DBGC ( uri, "URI duplicated" );
544
+	uri_dump ( uri );
545
+	DBGC ( uri, "\n" );
546
+
547
+	return dup;
326 548
 }
327 549
 
328 550
 /**
@@ -398,7 +620,7 @@ char * resolve_path ( const char *base_path,
398 620
  * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI
399 621
  * (e.g. "http://ipxe.org/initrds/initrd.gz").
400 622
  */
401
-struct uri * resolve_uri ( struct uri *base_uri,
623
+struct uri * resolve_uri ( const struct uri *base_uri,
402 624
 			   struct uri *relative_uri ) {
403 625
 	struct uri tmp_uri;
404 626
 	char *tmp_path = NULL;
@@ -417,11 +639,16 @@ struct uri * resolve_uri ( struct uri *base_uri,
417 639
 		tmp_uri.path = tmp_path;
418 640
 		tmp_uri.query = relative_uri->query;
419 641
 		tmp_uri.fragment = relative_uri->fragment;
642
+		tmp_uri.params = relative_uri->params;
420 643
 	} else if ( relative_uri->query ) {
421 644
 		tmp_uri.query = relative_uri->query;
422 645
 		tmp_uri.fragment = relative_uri->fragment;
646
+		tmp_uri.params = relative_uri->params;
423 647
 	} else if ( relative_uri->fragment ) {
424 648
 		tmp_uri.fragment = relative_uri->fragment;
649
+		tmp_uri.params = relative_uri->params;
650
+	} else if ( relative_uri->params ) {
651
+		tmp_uri.params = relative_uri->params;
425 652
 	}
426 653
 
427 654
 	/* Create demangled URI */
@@ -431,100 +658,23 @@ struct uri * resolve_uri ( struct uri *base_uri,
431 658
 }
432 659
 
433 660
 /**
434
- * Test for unreserved URI characters
435
- *
436
- * @v c			Character to test
437
- * @v field		Field of URI in which character lies
438
- * @ret is_unreserved	Character is an unreserved character
439
- */
440
-static int is_unreserved_uri_char ( int c, int field ) {
441
-	/* According to RFC3986, the unreserved character set is
442
-	 *
443
-	 * A-Z a-z 0-9 - _ . ~
444
-	 *
445
-	 * but we also pass & ; = in queries, / in paths,
446
-	 * and everything in opaques
447
-	 */
448
-	int ok = ( isupper ( c ) || islower ( c ) || isdigit ( c ) ||
449
-		    ( c == '-' ) || ( c == '_' ) ||
450
-		    ( c == '.' ) || ( c == '~' ) );
451
-
452
-	if ( field == URI_QUERY )
453
-		ok = ok || ( c == ';' ) || ( c == '&' ) || ( c == '=' );
454
-
455
-	if ( field == URI_PATH )
456
-		ok = ok || ( c == '/' );
457
-
458
-	if ( field == URI_OPAQUE )
459
-		ok = 1;
460
-
461
-	return ok;
462
-}
463
-
464
-/**
465
- * URI-encode string
466
- *
467
- * @v raw_string	String to be URI-encoded
468
- * @v buf		Buffer to contain encoded string
469
- * @v len		Length of buffer
470
- * @v field		Field of URI in which string lies
471
- * @ret len		Length of encoded string (excluding NUL)
472
- */
473
-size_t uri_encode ( const char *raw_string, char *buf, ssize_t len,
474
-		    int field ) {
475
-	ssize_t remaining = len;
476
-	size_t used;
477
-	unsigned char c;
478
-
479
-	if ( len > 0 )
480
-		buf[0] = '\0';
481
-
482
-	while ( ( c = *(raw_string++) ) ) {
483
-		if ( is_unreserved_uri_char ( c, field ) ) {
484
-			used = ssnprintf ( buf, remaining, "%c", c );
485
-		} else {
486
-			used = ssnprintf ( buf, remaining, "%%%02X", c );
487
-		}
488
-		buf += used;
489
-		remaining -= used;
490
-	}
491
-
492
-	return ( len - remaining );
493
-}
494
-
495
-/**
496
- * Decode URI-encoded string
661
+ * Construct TFTP URI from next-server and filename
497 662
  *
498
- * @v encoded_string	URI-encoded string
499
- * @v buf		Buffer to contain decoded string
500
- * @v len		Length of buffer
501
- * @ret len		Length of decoded string (excluding NUL)
663
+ * @v next_server	Next-server address
664
+ * @v filename		Filename
665
+ * @ret uri		URI, or NULL on failure
502 666
  *
503
- * This function may be used in-place, with @a buf the same as
504
- * @a encoded_string.
667
+ * TFTP filenames specified via the DHCP next-server field often
668
+ * contain characters such as ':' or '#' which would confuse the
669
+ * generic URI parser.  We provide a mechanism for directly
670
+ * constructing a TFTP URI from the next-server and filename.
505 671
  */
506
-size_t uri_decode ( const char *encoded_string, char *buf, ssize_t len ) {
507
-	ssize_t remaining;
508
-	char hexbuf[3];
509
-	char *hexbuf_end;
510
-	unsigned char c;
511
-
512
-	for ( remaining = len; *encoded_string; remaining-- ) {
513
-		if ( *encoded_string == '%' ) {
514
-			encoded_string++;
515
-			snprintf ( hexbuf, sizeof ( hexbuf ), "%s",
516
-				   encoded_string );
517
-			c = strtoul ( hexbuf, &hexbuf_end, 16 );
518
-			encoded_string += ( hexbuf_end - hexbuf );
519
-		} else {
520
-			c = *(encoded_string++);
521
-		}
522
-		if ( remaining > 1 )
523
-			*buf++ = c;
524
-	}
525
-
526
-	if ( len )
527
-		*buf = 0;
528
-
529
-	return ( len - remaining );
672
+struct uri * tftp_uri ( struct in_addr next_server, const char *filename ) {
673
+	struct uri uri;
674
+
675
+	memset ( &uri, 0, sizeof ( uri ) );
676
+	uri.scheme = "tftp";
677
+	uri.host = inet_ntoa ( next_server );
678
+	uri.path = filename;
679
+	return uri_dup ( &uri );
530 680
 }

+ 31
- 31
src/crypto/ocsp.c View File

@@ -206,11 +206,12 @@ static int ocsp_request ( struct ocsp_check *ocsp ) {
206 206
  * @ret rc		Return status code
207 207
  */
208 208
 static int ocsp_uri_string ( struct ocsp_check *ocsp ) {
209
+	struct uri path_uri;
209 210
 	char *base_uri_string;
210
-	char *base64_request;
211
-	size_t base64_request_len;
212
-	size_t uri_string_len;
213
-	size_t prefix_len;
211
+	char *path_base64_string;
212
+	char *path_uri_string;
213
+	size_t path_len;
214
+	int len;
214 215
 	int rc;
215 216
 
216 217
 	/* Sanity check */
@@ -222,44 +223,43 @@ static int ocsp_uri_string ( struct ocsp_check *ocsp ) {
222 223
 		goto err_no_uri;
223 224
 	}
224 225
 
225
-	/* Base64-encode the request */
226
-	base64_request_len = ( base64_encoded_len ( ocsp->request.builder.len )
227
-			       + 1 /* NUL */ );
228
-	base64_request = malloc ( base64_request_len );
229
-	if ( ! base64_request ) {
226
+	/* Base64-encode the request as the URI path */
227
+	path_len = ( base64_encoded_len ( ocsp->request.builder.len )
228
+		     + 1 /* NUL */ );
229
+	path_base64_string = malloc ( path_len );
230
+	if ( ! path_base64_string ) {
230 231
 		rc = -ENOMEM;
231
-		goto err_alloc_base64;
232
+		goto err_path_base64;
232 233
 	}
233 234
 	base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len,
234
-			base64_request );
235
-
236
-	/* Allocate URI string */
237
-	uri_string_len = ( strlen ( base_uri_string ) + 1 /* "/" */ +
238
-			   uri_encode ( base64_request, NULL, 0, URI_FRAGMENT )
239
-			   + 1 /* NUL */ );
240
-	ocsp->uri_string = malloc ( uri_string_len );
241
-	if ( ! ocsp->uri_string ) {
235
+			path_base64_string );
236
+
237
+	/* URI-encode the Base64-encoded request */
238
+	memset ( &path_uri, 0, sizeof ( path_uri ) );
239
+	path_uri.path = path_base64_string;
240
+	path_uri_string = format_uri_alloc ( &path_uri );
241
+	if ( ! path_uri_string ) {
242 242
 		rc = -ENOMEM;
243
-		goto err_alloc_uri;
243
+		goto err_path_uri;
244 244
 	}
245 245
 
246 246
 	/* Construct URI string */
247
-	prefix_len = snprintf ( ocsp->uri_string, uri_string_len,
248
-				"%s/", base_uri_string );
249
-	uri_encode ( base64_request, ( ocsp->uri_string + prefix_len ),
250
-		     ( uri_string_len - prefix_len ), URI_FRAGMENT );
247
+	if ( ( len = asprintf ( &ocsp->uri_string, "%s/%s", base_uri_string,
248
+				path_uri_string ) ) < 0 ) {
249
+		rc = len;
250
+		goto err_ocsp_uri;
251
+	}
251 252
 	DBGC2 ( ocsp, "OCSP %p \"%s\" URI is %s\n",
252 253
 		ocsp, ocsp->cert->subject.name, ocsp->uri_string );
253 254
 
254
-	/* Free base64-encoded request */
255
-	free ( base64_request );
256
-	base64_request = NULL;
257
-
258
-	return 0;
255
+	/* Success */
256
+	rc = 0;
259 257
 
260
- err_alloc_uri:
261
-	free ( base64_request );
262
- err_alloc_base64:
258
+ err_ocsp_uri:
259
+	free ( path_uri_string );
260
+ err_path_uri:
261
+	free ( path_base64_string );
262
+ err_path_base64:
263 263
  err_no_uri:
264 264
 	return rc;
265 265
 }

+ 41
- 37
src/include/ipxe/uri.h View File

@@ -12,6 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
12 12
 #include <stddef.h>
13 13
 #include <stdlib.h>
14 14
 #include <ipxe/refcnt.h>
15
+#include <ipxe/in.h>
15 16
 
16 17
 struct parameters;
17 18
 
@@ -71,37 +72,38 @@ struct uri {
71 72
 	struct parameters *params;
72 73
 } __attribute__ (( packed ));
73 74
 
74
-/** A field in a URI
75
+/**
76
+ * Access URI field
75 77
  *
76
- * The order of the indices in this enumeration must match the order
77
- * of the fields in the URI structure.
78
+ * @v uri		URI
79
+ * @v field		URI field index
80
+ * @ret field		URI field (as an lvalue)
78 81
  */
79
-enum {
80
-	URI_SCHEME = 0,		URI_SCHEME_BIT = ( 1 << URI_SCHEME ),
81
-	URI_OPAQUE = 1,		URI_OPAQUE_BIT = ( 1 << URI_OPAQUE ),
82
-	URI_USER = 2,		URI_USER_BIT = ( 1 << URI_USER ),
83
-	URI_PASSWORD = 3,	URI_PASSWORD_BIT = ( 1 << URI_PASSWORD ),
84
-	URI_HOST = 4,		URI_HOST_BIT = ( 1 << URI_HOST ),
85
-	URI_PORT = 5,		URI_PORT_BIT = ( 1 << URI_PORT ),
86
-	URI_PATH = 6,		URI_PATH_BIT = ( 1 << URI_PATH ),
87
-	URI_QUERY = 7,		URI_QUERY_BIT = ( 1 << URI_QUERY ),
88
-	URI_FRAGMENT = 8,	URI_FRAGMENT_BIT = ( 1 << URI_FRAGMENT ),
89
-
90
-	URI_FIRST_FIELD = URI_SCHEME,
91
-	URI_LAST_FIELD = URI_FRAGMENT,
92
-};
93
-
94
-/** Extract field from URI */
95
-#define uri_get_field( uri, field )	(&uri->scheme)[field]
82
+#define uri_field( uri, field ) (&uri->scheme)[field]
96 83
 
97
-/** All URI fields */
98
-#define URI_ALL		( URI_SCHEME_BIT | URI_OPAQUE_BIT | URI_USER_BIT | \
99
-			  URI_PASSWORD_BIT | URI_HOST_BIT | URI_PORT_BIT | \
100
-			  URI_PATH_BIT | URI_QUERY_BIT | URI_FRAGMENT_BIT )
101
-
102
-/** URI fields that should be decoded on storage */
103
-#define URI_ENCODED	( URI_USER_BIT | URI_PASSWORD_BIT | URI_HOST_BIT | \
104
-			  URI_PATH_BIT | URI_QUERY_BIT | URI_FRAGMENT_BIT )
84
+/**
85
+ * Calculate index of a URI field
86
+ *
87
+ * @v name		URI field name
88
+ * @ret field		URI field index
89
+ */
90
+#define URI_FIELD( name )						\
91
+	( ( offsetof ( struct uri, name ) -				\
92
+	    offsetof ( struct uri, scheme ) ) / sizeof ( void * ) )
93
+
94
+/** URI fields */
95
+enum uri_fields {
96
+	URI_SCHEME = URI_FIELD ( scheme ),
97
+	URI_OPAQUE = URI_FIELD ( opaque ),
98
+	URI_USER = URI_FIELD ( user ),
99
+	URI_PASSWORD = URI_FIELD ( password ),
100
+	URI_HOST = URI_FIELD ( host ),
101
+	URI_PORT = URI_FIELD ( port ),
102
+	URI_PATH = URI_FIELD ( path ),
103
+	URI_QUERY = URI_FIELD ( query ),
104
+	URI_FRAGMENT = URI_FIELD ( fragment ),
105
+	URI_FIELDS
106
+};
105 107
 
106 108
 /**
107 109
  * URI is an absolute URI
@@ -125,8 +127,8 @@ static inline int uri_is_absolute ( const struct uri *uri ) {
125 127
  */
126 128
 static inline int uri_has_opaque ( const struct uri *uri ) {
127 129
 	return ( uri->opaque && ( uri->opaque[0] != '\0' ) );
128
-
129 130
 }
131
+
130 132
 /**
131 133
  * URI has a path
132 134
  *
@@ -189,18 +191,20 @@ uri_put ( struct uri *uri ) {
189 191
 
190 192
 extern struct uri *cwuri;
191 193
 
194
+extern size_t uri_encode ( const char *string, unsigned int field,
195
+			   char *buf, ssize_t len );
192 196
 extern struct uri * parse_uri ( const char *uri_string );
193
-extern unsigned int uri_port ( struct uri *uri, unsigned int default_port );
194
-extern int unparse_uri ( char *buf, size_t size, struct uri *uri,
195
-			 unsigned int fields );
196
-extern struct uri * uri_dup ( struct uri *uri );
197
+extern size_t format_uri ( const struct uri *uri, char *buf, size_t len );
198
+extern char * format_uri_alloc ( const struct uri *uri );
199
+extern unsigned int uri_port ( const struct uri *uri,
200
+			       unsigned int default_port );
201
+extern struct uri * uri_dup ( const struct uri *uri );
197 202
 extern char * resolve_path ( const char *base_path,
198 203
 			     const char *relative_path );
199
-extern struct uri * resolve_uri ( struct uri *base_uri,
204
+extern struct uri * resolve_uri ( const struct uri *base_uri,
200 205
 				  struct uri *relative_uri );
206
+extern struct uri * tftp_uri ( struct in_addr next_server,
207
+			       const char *filename );
201 208
 extern void churi ( struct uri *uri );
202
-extern size_t uri_encode ( const char *raw_string, char *buf, ssize_t len,
203
-			   int field );
204
-extern size_t uri_decode ( const char *encoded_string, char *buf, ssize_t len );
205 209
 
206 210
 #endif /* _IPXE_URI_H */

+ 29
- 2
src/net/tcp/ftp.c View File

@@ -23,6 +23,7 @@
23 23
 #include <string.h>
24 24
 #include <assert.h>
25 25
 #include <errno.h>
26
+#include <ctype.h>
26 27
 #include <byteswap.h>
27 28
 #include <ipxe/socket.h>
28 29
 #include <ipxe/tcpip.h>
@@ -459,6 +460,25 @@ static struct interface_descriptor ftp_xfer_desc =
459 460
  *
460 461
  */
461 462
 
463
+/**
464
+ * Check validity of FTP control channel string
465
+ *
466
+ * @v string		String
467
+ * @ret rc		Return status code
468
+ */
469
+static int ftp_check_string ( const char *string ) {
470
+	char c;
471
+
472
+	/* The FTP control channel is line-based.  Check for invalid
473
+	 * non-printable characters (e.g. newlines).
474
+	 */
475
+	while ( ( c = *(string++) ) ) {
476
+		if ( ! isprint ( c ) )
477
+			return -EINVAL;
478
+	}
479
+	return 0;
480
+}
481
+
462 482
 /**
463 483
  * Initiate an FTP connection
464 484
  *
@@ -472,10 +492,17 @@ static int ftp_open ( struct interface *xfer, struct uri *uri ) {
472 492
 	int rc;
473 493
 
474 494
 	/* Sanity checks */
475
-	if ( ! uri->path )
476
-		return -EINVAL;
477 495
 	if ( ! uri->host )
478 496
 		return -EINVAL;
497
+	if ( ! uri->path )
498
+		return -EINVAL;
499
+	if ( ( rc = ftp_check_string ( uri->path ) ) != 0 )
500
+		return rc;
501
+	if ( uri->user && ( ( rc = ftp_check_string ( uri->user ) ) != 0 ) )
502
+		return rc;
503
+	if ( uri->password &&
504
+	     ( ( rc = ftp_check_string ( uri->password ) ) != 0 ) )
505
+		return rc;
479 506
 
480 507
 	/* Allocate and populate structure */
481 508
 	ftp = zalloc ( sizeof ( *ftp ) );

+ 34
- 28
src/net/tcp/httpcore.c View File

@@ -958,8 +958,8 @@ static void http_socket_close ( struct http_request *http, int rc ) {
958 958
  */
959 959
 static char * http_basic_auth ( struct http_request *http ) {
960 960
 	const char *user = http->uri->user;
961
-	const char *password =
962
-		( http->uri->password ? http->uri->password : "" );
961
+	const char *password = ( http->uri->password ?
962
+				 http->uri->password : "" );
963 963
 	size_t user_pw_len =
964 964
 		( strlen ( user ) + 1 /* ":" */ + strlen ( password ) );
965 965
 	char user_pw[ user_pw_len + 1 /* NUL */ ];
@@ -1000,8 +1000,8 @@ static char * http_basic_auth ( struct http_request *http ) {
1000 1000
 static char * http_digest_auth ( struct http_request *http,
1001 1001
 				 const char *method, const char *uri ) {
1002 1002
 	const char *user = http->uri->user;
1003
-	const char *password =
1004
-		( http->uri->password ? http->uri->password : "" );
1003
+	const char *password = ( http->uri->password ?
1004
+				 http->uri->password : "" );
1005 1005
 	const char *realm = http->auth_realm;
1006 1006
 	const char *nonce = http->auth_nonce;
1007 1007
 	const char *opaque = http->auth_opaque;
@@ -1088,7 +1088,7 @@ static size_t http_post_params ( struct http_request *http,
1088 1088
 		}
1089 1089
 
1090 1090
 		/* URI-encode the key */
1091
-		frag_len = uri_encode ( param->key, buf, remaining, 0 );
1091
+		frag_len = uri_encode ( param->key, 0, buf, remaining );
1092 1092
 		buf += frag_len;
1093 1093
 		len += frag_len;
1094 1094
 		remaining -= frag_len;
@@ -1101,7 +1101,7 @@ static size_t http_post_params ( struct http_request *http,
1101 1101
 		remaining--;
1102 1102
 
1103 1103
 		/* URI-encode the value */
1104
-		frag_len = uri_encode ( param->value, buf, remaining, 0 );
1104
+		frag_len = uri_encode ( param->value, 0, buf, remaining );
1105 1105
 		buf += frag_len;
1106 1106
 		len += frag_len;
1107 1107
 		remaining -= frag_len;
@@ -1149,9 +1149,11 @@ static struct io_buffer * http_post ( struct http_request *http ) {
1149 1149
  */
1150 1150
 static void http_step ( struct http_request *http ) {
1151 1151
 	struct io_buffer *post;
1152
-	size_t uri_len;
1152
+	struct uri host_uri;
1153
+	struct uri path_uri;
1154
+	char *host_uri_string;
1155
+	char *path_uri_string;
1153 1156
 	char *method;
1154
-	char *uri;
1155 1157
 	char *range;
1156 1158
 	char *auth;
1157 1159
 	char *content;
@@ -1176,19 +1178,24 @@ static void http_step ( struct http_request *http ) {
1176 1178
 	method = ( ( http->flags & HTTP_HEAD_ONLY ) ? "HEAD" :
1177 1179
 		   ( http->uri->params ? "POST" : "GET" ) );
1178 1180
 
1179
-	/* Construct path?query request */
1180
-	uri_len = ( unparse_uri ( NULL, 0, http->uri,
1181
-				  URI_PATH_BIT | URI_QUERY_BIT )
1182
-		    + 1 /* possible "/" */ + 1 /* NUL */ );
1183
-	uri = malloc ( uri_len );
1184
-	if ( ! uri ) {
1181
+	/* Construct host URI */
1182
+	memset ( &host_uri, 0, sizeof ( host_uri ) );
1183
+	host_uri.host = http->uri->host;
1184
+	host_uri.port = http->uri->port;
1185
+	host_uri_string = format_uri_alloc ( &host_uri );
1186
+	if ( ! host_uri_string ) {
1185 1187
 		rc = -ENOMEM;
1186
-		goto err_uri;
1188
+		goto err_host_uri;
1187 1189
 	}
1188
-	unparse_uri ( uri, uri_len, http->uri, URI_PATH_BIT | URI_QUERY_BIT );
1189
-	if ( ! uri[0] ) {
1190
-		uri[0] = '/';
1191
-		uri[1] = '\0';
1190
+
1191
+	/* Construct path URI */
1192
+	memset ( &path_uri, 0, sizeof ( path_uri ) );
1193
+	path_uri.path = ( http->uri->path ? http->uri->path : "/" );
1194
+	path_uri.query = http->uri->query;
1195
+	path_uri_string = format_uri_alloc ( &path_uri );
1196
+	if ( ! path_uri_string ) {
1197
+		rc = -ENOMEM;
1198
+		goto err_path_uri;
1192 1199
 	}
1193 1200
 
1194 1201
 	/* Calculate range request parameters if applicable */
@@ -1213,7 +1220,7 @@ static void http_step ( struct http_request *http ) {
1213 1220
 			goto err_auth;
1214 1221
 		}
1215 1222
 	} else if ( http->flags & HTTP_DIGEST_AUTH ) {
1216
-		auth = http_digest_auth ( http, method, uri );
1223
+		auth = http_digest_auth ( http, method, path_uri_string );
1217 1224
 		if ( ! auth ) {
1218 1225
 			rc = -ENOMEM;
1219 1226
 			goto err_auth;
@@ -1248,14 +1255,11 @@ static void http_step ( struct http_request *http ) {
1248 1255
 	if ( ( rc = xfer_printf ( &http->socket,
1249 1256
 				  "%s %s HTTP/1.1\r\n"
1250 1257
 				  "User-Agent: iPXE/%s\r\n"
1251
-				  "Host: %s%s%s\r\n"
1258
+				  "Host: %s\r\n"
1252 1259
 				  "%s%s%s%s"
1253 1260
 				  "\r\n",
1254
-				  method, uri, product_version, http->uri->host,
1255
-				  ( http->uri->port ?
1256
-				    ":" : "" ),
1257
-				  ( http->uri->port ?
1258
-				    http->uri->port : "" ),
1261
+				  method, path_uri_string, product_version,
1262
+				  host_uri_string,
1259 1263
 				  ( ( http->flags & HTTP_CLIENT_KEEPALIVE ) ?
1260 1264
 				    "Connection: keep-alive\r\n" : "" ),
1261 1265
 				  ( range ? range : "" ),
@@ -1281,8 +1285,10 @@ static void http_step ( struct http_request *http ) {
1281 1285
  err_auth:
1282 1286
 	free ( range );
1283 1287
  err_range:
1284
-	free ( uri );
1285
- err_uri:
1288
+	free ( path_uri_string );
1289
+ err_path_uri:
1290
+	free ( host_uri_string );
1291
+ err_host_uri:
1286 1292
 	if ( rc != 0 )
1287 1293
 		http_close ( http, rc );
1288 1294
 }

+ 1
- 1
src/net/udp/slam.c View File

@@ -723,7 +723,7 @@ static int slam_open ( struct interface *xfer, struct uri *uri ) {
723 723
 
724 724
 	/* Open multicast socket */
725 725
 	memcpy ( &multicast, &default_multicast, sizeof ( multicast ) );
726
-	if ( uri->path && 
726
+	if ( uri->path &&
727 727
 	     ( ( rc = slam_parse_multicast_address ( slam, uri->path,
728 728
 						     &multicast ) ) != 0 ) ) {
729 729
 		goto err;

+ 1
- 13
src/net/udp/tftp.c View File

@@ -323,24 +323,12 @@ void tftp_set_mtftp_port ( unsigned int port ) {
323 323
  * @ret rc		Return status code
324 324
  */
325 325
 static int tftp_send_rrq ( struct tftp_request *tftp ) {
326
+	const char *path = tftp->uri->path;
326 327
 	struct tftp_rrq *rrq;
327
-	const char *path;
328 328
 	size_t len;
329 329
 	struct io_buffer *iobuf;
330 330
 	size_t blksize;
331 331
 
332
-	/* Strip initial '/' if present.  If we were opened via the
333
-	 * URI interface, then there will be an initial '/', since a
334
-	 * full tftp:// URI provides no way to specify a non-absolute
335
-	 * path.  However, many TFTP servers (particularly Windows
336
-	 * TFTP servers) complain about having an initial '/', and it
337
-	 * violates user expectations to have a '/' silently added to
338
-	 * the DHCP-specified filename.
339
-	 */
340
-	path = tftp->uri->path;
341
-	if ( *path == '/' )
342
-		path++;
343
-
344 332
 	DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path );
345 333
 
346 334
 	/* Allocate buffer */

+ 1
- 0
src/tests/tests.c View File

@@ -53,3 +53,4 @@ REQUIRE_OBJECT ( pnm_test );
53 53
 REQUIRE_OBJECT ( deflate_test );
54 54
 REQUIRE_OBJECT ( png_test );
55 55
 REQUIRE_OBJECT ( dns_test );
56
+REQUIRE_OBJECT ( uri_test );

+ 832
- 120
src/tests/uri_test.c View File

@@ -1,146 +1,858 @@
1
-#include <stdint.h>
2
-#include <stddef.h>
3
-#include <stdio.h>
1
+/*
2
+ * Copyright (C) 2014 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
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+/** @file
23
+ *
24
+ * URI self-tests
25
+ *
26
+ */
27
+
28
+/* Forcibly enable assertions */
29
+#undef NDEBUG
30
+
4 31
 #include <string.h>
5
-#include <errno.h>
32
+#include <byteswap.h>
6 33
 #include <ipxe/uri.h>
34
+#include <ipxe/params.h>
35
+#include <ipxe/test.h>
7 36
 
8
-#define URI_MAX_LEN 1024
9
-
37
+/** A URI parsing/formatting test */
10 38
 struct uri_test {
11
-	const char *base_uri_string;
12
-	const char *relative_uri_string;
13
-	const char *resolved_uri_string;
14
-};
15
-
16
-static struct uri_test uri_tests[] = {
17
-	{ "http://www.fensystems.co.uk", "",
18
-	  "http://www.fensystems.co.uk/" },
19
-	{ "http://ipxe.org/wiki/page1", "page2",
20
-	  "http://ipxe.org/wiki/page2" },
21
-	{ "http://ipxe.org/wiki/page1", "../page3",
22
-	  "http://ipxe.org/page3" },
23
-	{ "tftp://192.168.0.1/", "/tftpboot/vmlinuz",
24
-	  "tftp://192.168.0.1/tftpboot/vmlinuz" },
25
-	{ "ftp://the%41nswer%3d:%34ty%32wo@ether%62oot.org:8080/p%41th/foo",
26
-	  "to?%41=b#%43d",
27
-	  "ftp://theAnswer%3d:4ty2wo@ipxe.org:8080/path/to?a=b#cd" },
28
-#if 0
29
-	"http://www.ipxe.org/wiki",
30
-	"mailto:bob@nowhere.com",
31
-	"ftp://joe:secret@insecure.org:8081/hidden/path/to?what=is#this",
32
-#endif
33
-};
34
-
35
-static int test_parse_unparse ( const char *uri_string ) {
36
-	char buf[URI_MAX_LEN];
37
-	struct uri *uri = NULL;
38
-	int rc;
39
-
40
-	/* Parse and unparse URI */
41
-	uri = parse_uri ( uri_string );
42
-	if ( ! uri ) {
43
-		rc = -ENOMEM;
44
-		goto done;
45
-	}
46
-	unparse_uri ( buf, sizeof ( buf ), uri, URI_ALL );
47
-
48
-	/* Compare result */
49
-	if ( strcmp ( buf, uri_string ) != 0 ) {
50
-		printf ( "Unparse of \"%s\" produced \"%s\"\n",
51
-			 uri_string, buf );
52
-		rc = -EINVAL;
53
-		goto done;
39
+	/** URI string */
40
+	const char *string;
41
+	/** URI */
42
+	struct uri uri;
43
+};
44
+
45
+/** A URI port number test */
46
+struct uri_port_test {
47
+	/** URI string */
48
+	const char *string;
49
+	/** Default port number */
50
+	unsigned int default_port;
51
+	/** Expected port number */
52
+	unsigned int port;
53
+};
54
+
55
+/** A URI or path resolution test */
56
+struct uri_resolve_test {
57
+	/** Base path or URI */
58
+	const char *base;
59
+	/** Relative path or URI */
60
+	const char *relative;
61
+	/** Expected resolved path or URI */
62
+	const char *resolved;
63
+};
64
+
65
+/** A TFTP URI test */
66
+struct uri_tftp_test {
67
+	/** Next-server address */
68
+	struct in_addr next_server;
69
+	/** Filename */
70
+	const char *filename;
71
+	/** URI */
72
+	struct uri uri;
73
+	/** URI string (for display only; cannot be reparsed) */
74
+	const char *string;
75
+};
76
+
77
+/** A current working URI test */
78
+struct uri_churi_test {
79
+	/** Relative URI */
80
+	const char *relative;
81
+	/** Expected new working URI */
82
+	const char *expected;
83
+};
84
+
85
+/** A form parameter URI test list */
86
+struct uri_params_test_list {
87
+	/** Key */
88
+	const char *key;
89
+	/** Value */
90
+	const char *value;
91
+};
92
+
93
+/** A form parameter URI test */
94
+struct uri_params_test {
95
+	/** URI string */
96
+	const char *string;
97
+	/** URI */
98
+	struct uri uri;
99
+	/** Parameter list name */
100
+	const char *name;
101
+	/** Parameter list */
102
+	struct uri_params_test_list *list;
103
+};
104
+
105
+/**
106
+ * Compare two URI component strings
107
+ *
108
+ * @v first		First string, or NULL
109
+ * @v second		Second string, or NULL
110
+ * @v difference	Difference
111
+ */
112
+static int uristrcmp ( const char *first, const char *second ) {
113
+
114
+	/* Compare strings, allowing for either to be NULL */
115
+	if ( first == second ) {
116
+		return 0;
117
+	} else if ( ( first == NULL ) || ( second == NULL ) ) {
118
+		return -1;
119
+	} else {
120
+		return strcmp ( first, second );
54 121
 	}
122
+}
55 123
 
56
-	rc = 0;
124
+/**
125
+ * Report URI equality test result
126
+ *
127
+ * @v uri		URI
128
+ * @v expected		Expected URI
129
+ * @v file		Test code file
130
+ * @v line		Test code line
131
+ */
132
+static void uri_okx ( struct uri *uri, struct uri *expected, const char *file,
133
+		      unsigned int line ) {
57 134
 
58
- done:
135
+	okx ( uristrcmp ( uri->scheme, expected->scheme ) == 0, file, line );
136
+	okx ( uristrcmp ( uri->opaque, expected->opaque ) == 0, file, line );
137
+	okx ( uristrcmp ( uri->user, expected->user ) == 0, file, line );
138
+	okx ( uristrcmp ( uri->password, expected->password ) == 0, file, line);
139
+	okx ( uristrcmp ( uri->host, expected->host ) == 0, file, line );
140
+	okx ( uristrcmp ( uri->port, expected->port ) == 0, file, line );
141
+	okx ( uristrcmp ( uri->path, expected->path ) == 0, file, line );
142
+	okx ( uristrcmp ( uri->query, expected->query ) == 0, file, line );
143
+	okx ( uristrcmp ( uri->fragment, expected->fragment ) == 0, file, line);
144
+	okx ( uri->params == expected->params, file, line );
145
+}
146
+#define uri_ok( uri, expected ) uri_okx ( uri, expected, __FILE__, __LINE__ )
147
+
148
+/**
149
+ * Report URI parsing test result
150
+ *
151
+ * @v test		URI test
152
+ * @v file		Test code file
153
+ * @v line		Test code line
154
+ */
155
+static void uri_parse_okx ( struct uri_test *test, const char *file,
156
+			    unsigned int line ) {
157
+	struct uri *uri;
158
+
159
+	/* Parse URI */
160
+	uri = parse_uri ( test->string );
161
+	okx ( uri != NULL, file, line );
162
+	if ( uri )
163
+		uri_okx ( uri, &test->uri, file, line );
59 164
 	uri_put ( uri );
60
-	if ( rc ) {
61
-		printf ( "URI parse-unparse of \"%s\" failed: %s\n",
62
-			 uri_string, strerror ( rc ) );
165
+}
166
+#define uri_parse_ok( test ) uri_parse_okx ( test, __FILE__, __LINE__ )
167
+
168
+/**
169
+ * Report URI formatting test result
170
+ *
171
+ * @v test		URI test
172
+ * @v file		Test code file
173
+ * @v line		Test code line
174
+ */
175
+static void uri_format_okx ( struct uri_test *test, const char *file,
176
+			     unsigned int line ) {
177
+	char buf[ strlen ( test->string ) + 1 /* NUL */ ];
178
+	char *tmp;
179
+	size_t len;
180
+
181
+	/* Format into fixed-size buffer */
182
+	len = format_uri ( &test->uri, buf, sizeof ( buf ) );
183
+	okx ( len == ( sizeof ( buf ) - 1 /* NUL */ ), file, line );
184
+	okx ( strcmp ( buf, test->string ) == 0, file, line );
185
+
186
+	/* Format into temporarily allocated buffer */
187
+	tmp = format_uri_alloc ( &test->uri );
188
+	okx ( tmp != NULL, file, line );
189
+	if ( tmp )
190
+		okx ( strcmp ( tmp, test->string ) == 0, file, line );
191
+	free ( tmp );
192
+}
193
+#define uri_format_ok( test ) uri_format_okx ( test, __FILE__, __LINE__ )
194
+
195
+/**
196
+ * Report URI duplication test result
197
+ *
198
+ * @v test		URI
199
+ * @v file		Test code file
200
+ * @v line		Test code line
201
+ */
202
+static void uri_dup_okx ( struct uri *uri, const char *file,
203
+			  unsigned int line ) {
204
+	struct uri *dup;
205
+
206
+	dup = uri_dup ( uri );
207
+	okx ( dup != NULL, file, line );
208
+	if ( dup )
209
+		uri_okx ( dup, uri, file, line );
210
+	uri_put ( dup );
211
+}
212
+#define uri_dup_ok( test ) uri_dup_okx ( test, __FILE__, __LINE__ )
213
+
214
+/**
215
+ * Report URI combined parsing and formatting test result
216
+ *
217
+ * @v test		URI test
218
+ * @v file		Test code file
219
+ * @v line		Test code line
220
+ */
221
+static void uri_parse_format_dup_okx ( struct uri_test *test, const char *file,
222
+				       unsigned int line ) {
223
+
224
+	uri_parse_okx ( test, file, line );
225
+	uri_format_okx ( test, file, line );
226
+	uri_dup_okx ( &test->uri, file, line );
227
+}
228
+#define uri_parse_format_dup_ok( test ) \
229
+	uri_parse_format_dup_okx ( test, __FILE__, __LINE__ )
230
+
231
+/**
232
+ * Report URI port number test result
233
+ *
234
+ * @v test		URI port number test
235
+ * @v file		Test code file
236
+ * @v line		Test code line
237
+ */
238
+static void uri_port_okx ( struct uri_port_test *test, const char *file,
239
+			   unsigned int line ) {
240
+	struct uri *uri;
241
+	unsigned int port;
242
+
243
+	/* Parse URI */
244
+	uri = parse_uri ( test->string );
245
+	okx ( uri != NULL, file, line );
246
+	if ( uri ) {
247
+		port = uri_port ( uri, test->default_port );
248
+		okx ( port == test->port, file, line );
63 249
 	}
64
-	return rc;
250
+	uri_put ( uri );
65 251
 }
252
+#define uri_port_ok( test ) uri_port_okx ( test, __FILE__, __LINE__ )
66 253
 
67
-static int test_resolve ( const char *base_uri_string,
68
-			  const char *relative_uri_string,
69
-			  const char *resolved_uri_string ) {
70
-	struct uri *base_uri = NULL;
71
-	struct uri *relative_uri = NULL;
72
-	struct uri *resolved_uri = NULL;
73
-	char buf[URI_MAX_LEN];
74
-	int rc;
254
+/**
255
+ * Report URI resolution test result
256
+ *
257
+ * @v test		Path resolution test
258
+ * @v file		Test code file
259
+ * @v line		Test code line
260
+ */
261
+static void uri_resolve_okx ( struct uri_resolve_test *test,
262
+			      const char *file, unsigned int line ) {
263
+	struct uri *base;
264
+	struct uri *relative;
265
+	struct uri *resolved = NULL;
266
+	char *formatted;
75 267
 
76 268
 	/* Parse URIs */
77
-	base_uri = parse_uri ( base_uri_string );
78
-	if ( ! base_uri ) {
79
-		rc = -ENOMEM;
80
-		goto done;
81
-	}
82
-	relative_uri = parse_uri ( relative_uri_string );
83
-	if ( ! relative_uri ) {
84
-		rc = -ENOMEM;
85
-		goto done;
269
+	base = parse_uri ( test->base );
270
+	okx ( base != NULL, file, line );
271
+	relative = parse_uri ( test->relative );
272
+	okx ( relative != NULL, file, line );
273
+
274
+	/* Resolve URI  */
275
+	if ( base && relative ) {
276
+		resolved = resolve_uri ( base, relative );
277
+		okx ( resolved != NULL, file, line );
86 278
 	}
87 279
 
88
-	/* Resolve URI */
89
-	resolved_uri = resolve_uri ( base_uri, relative_uri );
90
-	if ( ! resolved_uri ) {
91
-		rc = -ENOMEM;
92
-		goto done;
280
+	/* Format resolved URI */
281
+	formatted = format_uri_alloc ( resolved );
282
+	okx ( formatted != NULL, file, line );
283
+
284
+	/* Check resolved URI */
285
+	if ( formatted )
286
+		okx ( strcmp ( formatted, test->resolved ) == 0, file, line );
287
+
288
+	free ( formatted );
289
+	uri_put ( resolved );
290
+	uri_put ( relative );
291
+	uri_put ( base );
292
+}
293
+#define uri_resolve_ok( test ) uri_resolve_okx ( test, __FILE__, __LINE__ )
294
+
295
+/**
296
+ * Report path resolution test result
297
+ *
298
+ * @v test		Path resolution test
299
+ * @v file		Test code file
300
+ * @v line		Test code line
301
+ */
302
+static void uri_resolve_path_okx ( struct uri_resolve_test *test,
303
+				   const char *file, unsigned int line ) {
304
+	char *resolved;
305
+
306
+	/* Resolve paths using resolve_path() directly */
307
+	resolved = resolve_path ( test->base, test->relative );
308
+	okx ( resolved != NULL, file, line );
309
+	if ( resolved )
310
+		okx ( strcmp ( resolved, test->resolved ) == 0, file, line );
311
+	free ( resolved );
312
+
313
+	/* Resolve paths as URIs (since all paths are valid URIs) */
314
+	uri_resolve_okx ( test, file, line );
315
+}
316
+#define uri_resolve_path_ok( test ) \
317
+	uri_resolve_path_okx ( test, __FILE__, __LINE__ )
318
+
319
+/**
320
+ * Report URI TFTP test result
321
+ *
322
+ * @v test		URI TFTP test
323
+ * @v file		Test code file
324
+ * @v line		Test code line
325
+ */
326
+static void uri_tftp_okx ( struct uri_tftp_test *test, const char *file,
327
+			   unsigned int line ) {
328
+	char buf[ strlen ( test->string ) + 1 /* NUL */ ];
329
+	struct uri *uri;
330
+	size_t len;
331
+
332
+	/* Construct URI */
333
+	uri = tftp_uri ( test->next_server, test->filename );
334
+	okx ( uri != NULL, file, line );
335
+	if ( uri ) {
336
+		uri_okx ( uri, &test->uri, file, line );
337
+		len = format_uri ( uri, buf, sizeof ( buf ) );
338
+		okx ( len == ( sizeof ( buf ) - 1 /* NUL */ ), file, line );
339
+		okx ( strcmp ( buf, test->string ) == 0, file, line );
93 340
 	}
341
+	uri_put ( uri );
342
+}
343
+#define uri_tftp_ok( test ) uri_tftp_okx ( test, __FILE__, __LINE__ )
344
+
345
+/**
346
+ * Report current working URI test result
347
+ *
348
+ * @v tests		List of current working URI tests
349
+ * @v file		Test code file
350
+ * @v line		Test code line
351
+ */
352
+static void uri_churi_okx ( struct uri_churi_test *test, const char *file,
353
+			    unsigned int line ) {
354
+	struct uri *old_cwuri;
355
+	struct uri *uri;
356
+	char *formatted;
357
+
358
+	/* Preserve original current working URI */
359
+	old_cwuri = uri_get ( cwuri );
94 360
 
95
-	/* Compare result */
96
-	unparse_uri ( buf, sizeof ( buf ), resolved_uri, URI_ALL );
97
-	if ( strcmp ( buf, resolved_uri_string ) != 0 ) {
98
-		printf ( "Resolution of \"%s\"+\"%s\" produced \"%s\"\n",
99
-			 base_uri_string, relative_uri_string, buf );
100
-		rc = -EINVAL;
101
-		goto done;
361
+	/* Perform sequence of current working URI changes */
362
+	do {
363
+		/* Parse relative URI */
364
+		uri = parse_uri ( test->relative );
365
+		okx ( uri != NULL, file, line );
366
+
367
+		/* Move to this URI */
368
+		churi ( uri );
369
+
370
+		/* Format new current working URI */
371
+		formatted = format_uri_alloc ( cwuri );
372
+		okx ( formatted != NULL, file, line );
373
+		if ( formatted ) {
374
+			okx ( strcmp ( formatted, test->expected ) == 0,
375
+			      file, line );
376
+		}
377
+
378
+		/* Free temporary storage */
379
+		free ( formatted );
380
+		uri_put ( uri );
381
+
382
+		/* Move to next current working URI test */
383
+		test++;
384
+
385
+	} while ( test->relative != NULL );
386
+
387
+	/* Restore original current working URI */
388
+	churi ( old_cwuri );
389
+	uri_put ( old_cwuri );
390
+}
391
+#define uri_churi_ok( test ) uri_churi_okx ( test, __FILE__, __LINE__ )
392
+
393
+/**
394
+ * Report form parameter URI test list result
395
+ *
396
+ * @v test		Form parameter URI test
397
+ * @v uri		URI
398
+ * @v file		Test code file
399
+ * @v line		Test code line
400
+ */
401
+static void uri_params_list_okx ( struct uri_params_test *test,
402
+				  struct uri *uri, const char *file,
403
+				  unsigned int line ) {
404
+	struct uri_params_test_list *list;
405
+	struct parameter *param;
406
+
407
+	/* Check URI */
408
+	uri_okx ( uri, &test->uri, file, line );
409
+
410
+	/* Check URI parameters */
411
+	okx ( uri->params != NULL, file, line );
412
+	if ( uri->params ) {
413
+		list = test->list;
414
+		for_each_param ( param, uri->params ) {
415
+			okx ( strcmp ( param->key, list->key ) == 0,
416
+			      file, line );
417
+			okx ( strcmp ( param->value, list->value ) == 0,
418
+			      file, line );
419
+			list++;
420
+		}
421
+		okx ( list->key == NULL, file, line );
102 422
 	}
423
+}
424
+#define uri_params_list_ok( test ) \
425
+	uri_params_list_okx ( test, __FILE__, __LINE__ )
103 426
 
104
-	rc = 0;
427
+/**
428
+ * Report form parameter URI test result
429
+ *
430
+ * @v test		Form parameter URI test
431
+ * @v file		Test code file
432
+ * @v line		Test code line
433
+ */
434
+static void uri_params_okx ( struct uri_params_test *test, const char *file,
435
+			     unsigned int line ) {
436
+	struct uri_params_test_list *list;
437
+	struct parameters *params;
438
+	struct parameter *param;
439
+	struct uri *uri;
440
+	struct uri *dup;
105 441
 
106
- done:
107
-	uri_put ( base_uri );
108
-	uri_put ( relative_uri );
109
-	uri_put ( resolved_uri );
110
-	if ( rc ) {
111
-		printf ( "URI resolution of \"%s\"+\"%s\" failed: %s\n",
112
-			 base_uri_string, relative_uri_string,
113
-			 strerror ( rc ) );
442
+	/* Create parameter list */
443
+	params = create_parameters ( test->name );
444
+	okx ( params != NULL, file, line );
445
+	if ( params ) {
446
+		for ( list = test->list ; list->key ; list++ ) {
447
+			param = add_parameter ( params, list->key, list->value);
448
+			okx ( param != NULL, file, line );
449
+		}
114 450
 	}
115
-	return rc;
451
+
452
+	/* Record parameter list as part of expected URI */
453
+	test->uri.params = params;
454
+
455
+	/* Parse URI */
456
+	uri = parse_uri ( test->string );
457
+	okx ( uri != NULL, file, line );
458
+	if ( uri )
459
+		uri_params_list_okx ( test, uri, file, line );
460
+
461
+	/* Duplicate URI */
462
+	dup = uri_dup ( uri );
463
+	okx ( dup != NULL, file, line );
464
+	if ( dup )
465
+		uri_params_list_okx ( test, dup, file, line );
466
+
467
+	/* Clear parameter list in expected URI */
468
+	test->uri.params = NULL;
469
+
470
+	uri_put ( uri );
471
+	uri_put ( dup );
116 472
 }
473
+#define uri_params_ok( test ) uri_params_okx ( test, __FILE__, __LINE__ )
117 474
 
118
-int uri_test ( void ) {
119
-	unsigned int i;
120
-	struct uri_test *uri_test;
121
-	int rc;
122
-	int overall_rc = 0;
123
-
124
-	for ( i = 0 ; i < ( sizeof ( uri_tests ) /
125
-			    sizeof ( uri_tests[0] ) ) ; i++ ) {
126
-		uri_test = &uri_tests[i];
127
-		rc = test_parse_unparse ( uri_test->base_uri_string );
128
-		if ( rc != 0 )
129
-			overall_rc = rc;
130
-		rc = test_parse_unparse ( uri_test->relative_uri_string );
131
-		if ( rc != 0 )
132
-			overall_rc = rc;
133
-		rc = test_parse_unparse ( uri_test->resolved_uri_string );
134
-		if ( rc != 0 )
135
-			overall_rc = rc;
136
-		rc = test_resolve ( uri_test->base_uri_string,
137
-				    uri_test->relative_uri_string,
138
-				    uri_test->resolved_uri_string );
139
-		if ( rc != 0 )
140
-			overall_rc = rc;
475
+/** Empty URI */
476
+static struct uri_test uri_empty = {
477
+	.string = "",
478
+};
479
+
480
+/** Basic HTTP URI */
481
+static struct uri_test uri_boot_ipxe_org = {
482
+	"http://boot.ipxe.org/demo/boot.php",
483
+	{ .scheme = "http", .host = "boot.ipxe.org", .path = "/demo/boot.php" }
484
+};
485
+
486
+/** Basic opaque URI */
487
+static struct uri_test uri_mailto = {
488
+	"mailto:ipxe-devel@lists.ipxe.org",
489
+	{ .scheme = "mailto", .opaque = "ipxe-devel@lists.ipxe.org" }
490
+};
491
+
492
+/** HTTP URI with all the trimmings */
493
+static struct uri_test uri_http_all = {
494
+	"http://anon:password@example.com:3001/~foo/cgi-bin/foo.pl?a=b&c=d#bit",
495
+	{
496
+		.scheme = "http",
497
+		.user = "anon",
498
+		.password = "password",
499
+		.host = "example.com",
500
+		.port = "3001",
501
+		.path = "/~foo/cgi-bin/foo.pl",
502
+		.query = "a=b&c=d",
503
+		.fragment = "bit",
504
+	},
505
+};
506
+
507
+/** HTTP URI with escaped characters */
508
+static struct uri_test uri_http_escaped = {
509
+	"https://test.ipxe.org/wtf%3F%0A?kind%23of/uri%20is#this%3F",
510
+	{
511
+		.scheme = "https",
512
+		.host = "test.ipxe.org",
513
+		.path = "/wtf?\n",
514
+		.query = "kind#of/uri is",
515
+		.fragment = "this?",
516
+	},
517
+};
518
+
519
+/** HTTP URI with improperly escaped characters */
520
+static struct uri_test uri_http_escaped_improper = {
521
+	/* We accept for parsing improperly escaped characters.
522
+	 * (Formatting the parsed URI would produce the properly
523
+	 * encoded form, and so would not exactly match the original
524
+	 * URI string.)
525
+	 */
526
+	"https://test%2eipxe.org/wt%66%3f\n?kind%23of/uri is#this?",
527
+	{
528
+		.scheme = "https",
529
+		.host = "test.ipxe.org",
530
+		.path = "/wtf?\n",
531
+		.query = "kind#of/uri is",
532
+		.fragment = "this?",
533
+	},
534
+};
535
+
536
+/** IPv6 URI */
537
+static struct uri_test uri_ipv6 = {
538
+	"http://[2001:ba8:0:1d4::6950:5845]/",
539
+	{
540
+		.scheme = "http",
541
+		.host = "[2001:ba8:0:1d4::6950:5845]",
542
+		.path = "/",
543
+	},
544
+};
545
+
546
+/** IPv6 URI with port */
547
+static struct uri_test uri_ipv6_port = {
548
+	"http://[2001:ba8:0:1d4::6950:5845]:8001/boot",
549
+	{
550
+		.scheme = "http",
551
+		.host = "[2001:ba8:0:1d4::6950:5845]",
552
+		.port = "8001",
553
+		.path = "/boot",
554
+	},
555
+};
556
+
557
+/** IPv6 URI with link-local address */
558
+static struct uri_test uri_ipv6_local = {
559
+	"http://[fe80::69ff:fe50:5845%25net0]/ipxe",
560
+	{
561
+		.scheme = "http",
562
+		.host = "[fe80::69ff:fe50:5845%net0]",
563
+		.path = "/ipxe",
564
+	},
565
+};
566
+
567
+/** IPv6 URI with link-local address not conforming to RFC 6874 */
568
+static struct uri_test uri_ipv6_local_non_conforming = {
569
+	/* We accept for parsing a single "%" in "%net0" (rather than
570
+	 * the properly encoded form "%25net0").  (Formatting the
571
+	 * parsed URI would produce the properly encoded form, and so
572
+	 * would not exactly match the original URI string.)
573
+	 */
574
+	"http://[fe80::69ff:fe50:5845%net0]/ipxe",
575
+	{
576
+		.scheme = "http",
577
+		.host = "[fe80::69ff:fe50:5845%net0]",
578
+		.path = "/ipxe",
579
+	},
580
+};
581
+
582
+/** iSCSI URI */
583
+static struct uri_test uri_iscsi = {
584
+	"iscsi:10.253.253.1::::iqn.2010-04.org.ipxe:rabbit",
585
+	{
586
+		.scheme = "iscsi",
587
+		.opaque = "10.253.253.1::::iqn.2010-04.org.ipxe:rabbit",
588
+	},
589
+};
590
+
591
+/** URI with port number */
592
+static struct uri_port_test uri_explicit_port = {
593
+	"http://192.168.0.1:8080/boot.php",
594
+	80,
595
+	8080,
596
+};
597
+
598
+/** URI without port number */
599
+static struct uri_port_test uri_default_port = {
600
+	"http://192.168.0.1/boot.php",
601
+	80,
602
+	80,
603
+};
604
+
605
+/** Simple path resolution test */
606
+static struct uri_resolve_test uri_simple_path = {
607
+	"/etc/passwd",
608
+	"group",
609
+	"/etc/group",
610
+};
611
+
612
+/** Path resolution test with "." and ".." elements */
613
+static struct uri_resolve_test uri_relative_path = {
614
+	"/var/lib/tftpboot/pxe/pxelinux.0",
615
+	"./../ipxe/undionly.kpxe",
616
+	"/var/lib/tftpboot/ipxe/undionly.kpxe",
617
+};
618
+
619
+/** Path resolution test terminating with directory */
620
+static struct uri_resolve_test uri_directory_path = {
621
+	"/test/cgi-bin.pl/boot.ipxe",
622
+	"..",
623
+	"/test/",
624
+};
625
+
626
+/** Path resolution test with excessive ".." elements */
627
+static struct uri_resolve_test uri_excessive_path = {
628
+	"/var/lib/tftpboot/ipxe.pxe",
629
+	"../../../../../../../foo",
630
+	"/foo",
631
+};
632
+
633
+/** Path resolution test with absolute path */
634
+static struct uri_resolve_test uri_absolute_path = {
635
+	"/var/lib/tftpboot",
636
+	"/etc/hostname",
637
+	"/etc/hostname",
638
+};
639
+
640
+/** Relative URI resolution test */
641
+static struct uri_resolve_test uri_relative = {
642
+	"http://boot.ipxe.org/demo/boot.php?vendor=10ec&device=8139",
643
+	"initrd.img",
644
+	"http://boot.ipxe.org/demo/initrd.img",
645
+};
646
+
647
+/** Absolute URI resolution test */
648
+static struct uri_resolve_test uri_absolute = {
649
+	"http://boot.ipxe.org/demo/boot.php",
650
+	"ftp://192.168.0.1/boot.ipxe",
651
+	"ftp://192.168.0.1/boot.ipxe",
652
+};
653
+
654
+/** Absolute path URI resolution test */
655
+static struct uri_resolve_test uri_absolute_uri_path = {
656
+	"http://boot.ipxe.org/demo/boot.php#test",
657
+	"/demo/vmlinuz",
658
+	"http://boot.ipxe.org/demo/vmlinuz",
659
+};
660
+
661
+/** Query URI resolution test */
662
+static struct uri_resolve_test uri_query = {
663
+	"http://10.253.253.1/test.pl?mac=02-00-69-50-58-45",
664
+	"?mac=00-1f-16-bc-fe-2f",
665
+	"http://10.253.253.1/test.pl?mac=00-1f-16-bc-fe-2f",
666
+};
667
+
668
+/** Fragment URI resolution test */
669
+static struct uri_resolve_test uri_fragment = {
670
+	"http://192.168.0.254/test#foo",
671
+	"#bar",
672
+	"http://192.168.0.254/test#bar",
673
+};
674
+
675
+/** TFTP URI with absolute path */
676
+static struct uri_tftp_test uri_tftp_absolute = {
677
+	{ .s_addr = htonl ( 0xc0a80002 ) /* 192.168.0.2 */ },
678
+	"/absolute/path",
679
+	{
680
+		.scheme = "tftp",
681
+		.host = "192.168.0.2",
682
+		.path = "/absolute/path",
683
+	},
684
+	"tftp://192.168.0.2/absolute/path",
685
+};
686
+
687
+/** TFTP URI with relative path */
688
+static struct uri_tftp_test uri_tftp_relative = {
689
+	{ .s_addr = htonl ( 0xc0a80003 ) /* 192.168.0.3 */ },
690
+	"relative/path",
691
+	{
692
+		.scheme = "tftp",
693
+		.host = "192.168.0.3",
694
+		.path = "relative/path",
695
+	},
696
+	"tftp://192.168.0.3/relative/path",
697
+};
698
+
699
+/** TFTP URI with path containing special characters */
700
+static struct uri_tftp_test uri_tftp_icky = {
701
+	{ .s_addr = htonl ( 0x0a000006 ) /* 10.0.0.6 */ },
702
+	"C:\\tftpboot\\icky#path",
703
+	{
704
+		.scheme = "tftp",
705
+		.host = "10.0.0.6",
706
+		.path = "C:\\tftpboot\\icky#path",
707
+	},
708
+	"tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path",
709
+};
710
+
711
+/** Current working URI test */
712
+static struct uri_churi_test uri_churi[] = {
713
+	{
714
+		"http://boot.ipxe.org/demo/boot.php",
715
+		"http://boot.ipxe.org/demo/boot.php",
716
+	},
717
+	{
718
+		"?vendor=10ec&device=8139",
719
+		"http://boot.ipxe.org/demo/boot.php?vendor=10ec&device=8139",
720
+	},
721
+	{
722
+		"fedora/fedora.ipxe",
723
+		"http://boot.ipxe.org/demo/fedora/fedora.ipxe",
724
+	},
725
+	{
726
+		"vmlinuz",
727
+		"http://boot.ipxe.org/demo/fedora/vmlinuz",
728
+	},
729
+	{
730
+		"http://local/boot/initrd.img",
731
+		"http://local/boot/initrd.img",
732
+	},
733
+	{
734
+		"modules/8139too.ko",
735
+		"http://local/boot/modules/8139too.ko",
736
+	},
737
+	{
738
+		NULL,
739
+		NULL,
141 740
 	}
741
+};
142 742
 
143
-	if ( overall_rc )
144
-		printf ( "URI tests failed: %s\n", strerror ( overall_rc ) );
145
-	return overall_rc;
743
+/** Form parameter URI test list */
744
+static struct uri_params_test_list uri_params_list[] = {
745
+	{
746
+		"vendor",
747
+		"10ec",
748
+	},
749
+	{
750
+		"device",
751
+		"8139",
752
+	},
753
+	{
754
+		"uuid",
755
+		"f59fac00-758f-498f-9fe5-87d790045d94",
756
+	},
757
+	{
758
+		NULL,
759
+		NULL,
760
+	}
761
+};
762
+
763
+/** Form parameter URI test */
764
+static struct uri_params_test uri_params = {
765
+	"http://boot.ipxe.org/demo/boot.php##params",
766
+	{
767
+		.scheme = "http",
768
+		.host = "boot.ipxe.org",
769
+		.path = "/demo/boot.php",
770
+	},
771
+	NULL,
772
+	uri_params_list,
773
+};
774
+
775
+/** Named form parameter URI test list */
776
+static struct uri_params_test_list uri_named_params_list[] = {
777
+	{
778
+		"mac",
779
+		"00:1e:65:80:d3:b6",
780
+	},
781
+	{
782
+		"serial",
783
+		"LXTQ20Z1139322762F2000",
784
+	},
785
+	{
786
+		NULL,
787
+		NULL,
788
+	}
789
+};
790
+
791
+/** Named form parameter URI test */
792
+static struct uri_params_test uri_named_params = {
793
+	"http://192.168.100.4:3001/register##params=foo",
794
+	{
795
+		.scheme = "http",
796
+		.host = "192.168.100.4",
797
+		.port = "3001",
798
+		.path = "/register",
799
+	},
800
+	"foo",
801
+	uri_named_params_list,
802
+};
803
+
804
+/**
805
+ * Perform URI self-test
806
+ *
807
+ */
808
+static void uri_test_exec ( void ) {
809
+
810
+	/* URI parsing, formatting, and duplication tests */
811
+	uri_parse_format_dup_ok ( &uri_empty );
812
+	uri_parse_format_dup_ok ( &uri_boot_ipxe_org );
813
+	uri_parse_format_dup_ok ( &uri_mailto );
814
+	uri_parse_format_dup_ok ( &uri_http_all );
815
+	uri_parse_format_dup_ok ( &uri_http_escaped );
816
+	uri_parse_ok ( &uri_http_escaped_improper ); /* Parse only */
817
+	uri_parse_format_dup_ok ( &uri_ipv6 );
818
+	uri_parse_format_dup_ok ( &uri_ipv6_port );
819
+	uri_parse_format_dup_ok ( &uri_ipv6_local );
820
+	uri_parse_ok ( &uri_ipv6_local_non_conforming ); /* Parse only */
821
+	uri_parse_format_dup_ok ( &uri_iscsi );
822
+
823
+	/** URI port number tests */
824
+	uri_port_ok ( &uri_explicit_port );
825
+	uri_port_ok ( &uri_default_port );
826
+
827
+	/** Path resolution tests */
828
+	uri_resolve_path_ok ( &uri_simple_path );
829
+	uri_resolve_path_ok ( &uri_relative_path );
830
+	uri_resolve_path_ok ( &uri_directory_path );
831
+	uri_resolve_path_ok ( &uri_excessive_path );
832
+	uri_resolve_path_ok ( &uri_absolute_path );
833
+
834
+	/** URI resolution tests */
835
+	uri_resolve_ok ( &uri_relative );
836
+	uri_resolve_ok ( &uri_absolute );
837
+	uri_resolve_ok ( &uri_absolute_uri_path );
838
+	uri_resolve_ok ( &uri_query );
839
+	uri_resolve_ok ( &uri_fragment );
840
+
841
+	/* TFTP URI construction tests */
842
+	uri_tftp_ok ( &uri_tftp_absolute );
843
+	uri_tftp_ok ( &uri_tftp_relative );
844
+	uri_tftp_ok ( &uri_tftp_icky );
845
+
846
+	/* Current working URI tests */
847
+	uri_churi_ok ( uri_churi );
848
+
849
+	/* Form parameter URI tests */
850
+	uri_params_ok ( &uri_params );
851
+	uri_params_ok ( &uri_named_params );
146 852
 }
853
+
854
+/** URI self-test */
855
+struct self_test uri_test __self_test = {
856
+	.name = "uri",
857
+	.exec = uri_test_exec,
858
+};

+ 2
- 11
src/usr/autoboot.c View File

@@ -91,8 +91,6 @@ static struct net_device * find_boot_netdev ( void ) {
91 91
  */
92 92
 static struct uri * parse_next_server_and_filename ( struct in_addr next_server,
93 93
 						     const char *filename ) {
94
-	char buf[ 23 /* "tftp://xxx.xxx.xxx.xxx/" */ + strlen ( filename )
95
-		  + 1 /* NUL */ ];
96 94
 	struct uri *uri;
97 95
 
98 96
 	/* Parse filename */
@@ -100,17 +98,10 @@ static struct uri * parse_next_server_and_filename ( struct in_addr next_server,
100 98
 	if ( ! uri )
101 99
 		return NULL;
102 100
 
103
-	/* Construct a tftp:// URI for the filename, if applicable.
104
-	 * We can't just rely on the current working URI, because the
105
-	 * relative URI resolution will remove the distinction between
106
-	 * filenames with and without initial slashes, which is
107
-	 * significant for TFTP.
108
-	 */
101
+	/* Construct a TFTP URI for the filename, if applicable */
109 102
 	if ( next_server.s_addr && filename[0] && ! uri_is_absolute ( uri ) ) {
110 103
 		uri_put ( uri );
111
-		snprintf ( buf, sizeof ( buf ), "tftp://%s/%s",
112
-			   inet_ntoa ( next_server ), filename );
113
-		uri = parse_uri ( buf );
104
+		uri = tftp_uri ( next_server, filename );
114 105
 		if ( ! uri )
115 106
 			return NULL;
116 107
 	}

+ 9
- 12
src/usr/imgmgmt.c View File

@@ -44,9 +44,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
44 44
  * @ret rc		Return status code
45 45
  */
46 46
 int imgdownload ( struct uri *uri, struct image **image ) {
47
-	size_t len = ( unparse_uri ( NULL, 0, uri, URI_ALL ) + 1 );
48
-	char uri_string_redacted[len];
49 47
 	const char *password;
48
+	char *uri_string_redacted;
50 49
 	int rc;
51 50
 
52 51
 	/* Allocate image */
@@ -56,13 +55,16 @@ int imgdownload ( struct uri *uri, struct image **image ) {
56 55
 		goto err_alloc_image;
57 56
 	}
58 57
 
59
-	/* Redact password portion of URI, if necessary */
58
+	/* Construct redacted URI */
60 59
 	password = uri->password;
61 60
 	if ( password )
62 61
 		uri->password = "***";
63
-	unparse_uri ( uri_string_redacted, sizeof ( uri_string_redacted ),
64
-		      uri, URI_ALL );
62
+	uri_string_redacted = format_uri_alloc ( uri );
65 63
 	uri->password = password;
64
+	if ( ! uri_string_redacted ) {
65
+		rc = -ENOMEM;
66
+		goto err_uri;
67
+	}
66 68
 
67 69
 	/* Create downloader */
68 70
 	if ( ( rc = create_downloader ( &monojob, *image, LOCATION_URI,
@@ -81,16 +83,11 @@ int imgdownload ( struct uri *uri, struct image **image ) {
81 83
 		goto err_register_image;
82 84
 	}
83 85
 
84
-	/* Drop local reference to image.  Image is guaranteed to
85
-	 * remain in scope since it is registered.
86
-	 */
87
-	image_put ( *image );
88
-
89
-	return 0;
90
-
91 86
  err_register_image:
92 87
  err_monojob_wait:
93 88
  err_create_downloader:
89
+	free ( uri_string_redacted );
90
+ err_uri:
94 91
 	image_put ( *image );
95 92
  err_alloc_image:
96 93
 	return rc;

Loading…
Cancel
Save