Skip to content

Commit ba36953

Browse files
committedAug 25, 2015
[settings] Re-add "uristring" setting type
Commit 09b057c ("[settings] Remove "uristring" setting type") removed support for URI-encoded settings via the "uristring" setting type, on the basis that such encoding was no longer necessary to avoid problems with the command line parser. Other valid use cases for the "uristring" setting type do exist: for example, a password containing a '/' character expanded via chain http://username:${password:uristring}@server.name/boot.php Restore the existence of the "uristring" setting, avoiding the potentially large stack allocations that were used in the old code prior to commit 09b057c ("[settings] Remove "uristring" setting type"). Requested-by: Robin Smidsrød <robin@smidsrod.no> Signed-off-by: Michael Brown <mcb30@ipxe.org>
1 parent 4e03af8 commit ba36953

File tree

5 files changed

+126
-30
lines changed

5 files changed

+126
-30
lines changed
 

‎src/core/settings.c

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,15 +1666,43 @@ const struct setting_type setting_type_string __setting_type = {
16661666
.format = format_string_setting,
16671667
};
16681668

1669-
/** A URI-encoded string setting type
1669+
/**
1670+
* Parse URI-encoded string setting value
16701671
*
1671-
* This setting type is obsolete; the name ":uristring" is retained to
1672-
* avoid breaking existing scripts.
1672+
* @v type Setting type
1673+
* @v value Formatted setting value
1674+
* @v buf Buffer to contain raw value
1675+
* @v len Length of buffer
1676+
* @ret len Length of raw value, or negative error
16731677
*/
1678+
static int parse_uristring_setting ( const struct setting_type *type __unused,
1679+
const char *value, void *buf, size_t len ){
1680+
1681+
return uri_decode ( value, buf, len );
1682+
}
1683+
1684+
/**
1685+
* Format URI-encoded string setting value
1686+
*
1687+
* @v type Setting type
1688+
* @v raw Raw setting value
1689+
* @v raw_len Length of raw setting value
1690+
* @v buf Buffer to contain formatted value
1691+
* @v len Length of buffer
1692+
* @ret len Length of formatted value, or negative error
1693+
*/
1694+
static int format_uristring_setting ( const struct setting_type *type __unused,
1695+
const void *raw, size_t raw_len,
1696+
char *buf, size_t len ) {
1697+
1698+
return uri_encode ( 0, raw, raw_len, buf, len );
1699+
}
1700+
1701+
/** A URI-encoded string setting type */
16741702
const struct setting_type setting_type_uristring __setting_type = {
16751703
.name = "uristring",
1676-
.parse = parse_string_setting,
1677-
.format = format_string_setting,
1704+
.parse = parse_uristring_setting,
1705+
.format = format_uristring_setting,
16781706
};
16791707

16801708
/**

‎src/core/uri.c

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,62 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
3939
#include <ipxe/uri.h>
4040

4141
/**
42-
* Decode URI field (in place)
42+
* Decode URI field
4343
*
44-
* @v string String
44+
* @v encoded Encoded field
45+
* @v buf Data buffer
46+
* @v len Length
47+
* @ret len Length of data
4548
*
4649
* URI decoding can never increase the length of a string; we can
4750
* therefore safely decode in place.
4851
*/
49-
static void uri_decode ( char *string ) {
50-
char *dest = string;
52+
size_t uri_decode ( const char *encoded, void *buf, size_t len ) {
53+
uint8_t *out = buf;
54+
unsigned int count = 0;
5155
char hexbuf[3];
5256
char *hexbuf_end;
5357
char c;
5458
char decoded;
5559
unsigned int skip;
5660

5761
/* Copy string, decoding escaped characters as necessary */
58-
do {
59-
c = *(string++);
62+
while ( ( c = *(encoded++) ) ) {
6063
if ( c == '%' ) {
61-
snprintf ( hexbuf, sizeof ( hexbuf ), "%s", string );
64+
snprintf ( hexbuf, sizeof ( hexbuf ), "%s", encoded );
6265
decoded = strtoul ( hexbuf, &hexbuf_end, 16 );
6366
skip = ( hexbuf_end - hexbuf );
64-
string += skip;
67+
encoded += skip;
6568
if ( skip )
6669
c = decoded;
6770
}
68-
*(dest++) = c;
69-
} while ( c );
71+
if ( count < len )
72+
out[count] = c;
73+
count++;
74+
}
75+
return count;
76+
}
77+
78+
/**
79+
* Decode URI field in-place
80+
*
81+
* @v uri URI
82+
* @v field URI field index
83+
*/
84+
static void uri_decode_inplace ( struct uri *uri, unsigned int field ) {
85+
const char *encoded = uri_field ( uri, field );
86+
char *decoded = ( ( char * ) encoded );
87+
size_t len;
88+
89+
/* Do nothing if field is not present */
90+
if ( ! encoded )
91+
return;
92+
93+
/* Decode field in place */
94+
len = uri_decode ( encoded, decoded, strlen ( encoded ) );
95+
96+
/* Terminate decoded string */
97+
decoded[len] = '\0';
7098
}
7199

72100
/**
@@ -115,10 +143,15 @@ static int uri_character_escaped ( char c, unsigned int field ) {
115143
* '%', the full set of characters with significance to the
116144
* URL parser is "/#:@?". We choose for each URI field which
117145
* of these require escaping in our use cases.
146+
*
147+
* For the scheme field (equivalently, if field is zero), we
148+
* escape anything that has significance not just for our URI
149+
* parser but for any other URI parsers (e.g. HTTP query
150+
* string parsers, which care about '=' and '&').
118151
*/
119152
static const char *escaped[URI_FIELDS] = {
120-
/* Scheme: escape everything */
121-
[URI_SCHEME] = "/#:@?",
153+
/* Scheme or default: escape everything */
154+
[URI_SCHEME] = "/#:@?=&",
122155
/* Opaque part: escape characters which would affect
123156
* the reparsing of the URI, allowing everything else
124157
* (e.g. ':', which will appear in iSCSI URIs).
@@ -157,14 +190,16 @@ static int uri_character_escaped ( char c, unsigned int field ) {
157190
/**
158191
* Encode URI field
159192
*
160-
* @v uri URI
161193
* @v field URI field index
162-
* @v buf Buffer to contain encoded string
194+
* @v raw Raw data
195+
* @v raw_len Length of raw data
196+
* @v buf Buffer
163197
* @v len Length of buffer
164198
* @ret len Length of encoded string (excluding NUL)
165199
*/
166-
size_t uri_encode ( const char *string, unsigned int field,
200+
size_t uri_encode ( unsigned int field, const void *raw, size_t raw_len,
167201
char *buf, ssize_t len ) {
202+
const uint8_t *raw_bytes = ( ( const uint8_t * ) raw );
168203
ssize_t remaining = len;
169204
size_t used;
170205
char c;
@@ -174,7 +209,8 @@ size_t uri_encode ( const char *string, unsigned int field,
174209
buf[0] = '\0';
175210

176211
/* Copy string, escaping as necessary */
177-
while ( ( c = *(string++) ) ) {
212+
while ( raw_len-- ) {
213+
c = *(raw_bytes++);
178214
if ( uri_character_escaped ( c, field ) ) {
179215
used = ssnprintf ( buf, remaining, "%%%02X", c );
180216
} else {
@@ -187,6 +223,21 @@ size_t uri_encode ( const char *string, unsigned int field,
187223
return ( len - remaining );
188224
}
189225

226+
/**
227+
* Encode URI field string
228+
*
229+
* @v field URI field index
230+
* @v string String
231+
* @v buf Buffer
232+
* @v len Length of buffer
233+
* @ret len Length of encoded string (excluding NUL)
234+
*/
235+
size_t uri_encode_string ( unsigned int field, const char *string,
236+
char *buf, ssize_t len ) {
237+
238+
return uri_encode ( field, string, strlen ( string ), buf, len );
239+
}
240+
190241
/**
191242
* Dump URI for debugging
192243
*
@@ -368,10 +419,8 @@ struct uri * parse_uri ( const char *uri_string ) {
368419
}
369420

370421
/* Decode fields in-place */
371-
for ( field = 0 ; field < URI_FIELDS ; field++ ) {
372-
if ( uri_field ( uri, field ) )
373-
uri_decode ( ( char * ) uri_field ( uri, field ) );
374-
}
422+
for ( field = 0 ; field < URI_FIELDS ; field++ )
423+
uri_decode_inplace ( uri, field );
375424

376425
done:
377426
DBGC ( uri, "URI parsed \"%s\" to", uri_string );
@@ -444,8 +493,8 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) {
444493
}
445494

446495
/* Encode this field */
447-
used += uri_encode ( uri_field ( uri, field ), field,
448-
( buf + used ), ( len - used ) );
496+
used += uri_encode_string ( field, uri_field ( uri, field ),
497+
( buf + used ), ( len - used ) );
449498

450499
/* Suffix this field, if applicable */
451500
if ( ( field == URI_SCHEME ) && ( ! uri->opaque ) ) {

‎src/include/ipxe/uri.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,11 @@ uri_put ( struct uri *uri ) {
191191

192192
extern struct uri *cwuri;
193193

194-
extern size_t uri_encode ( const char *string, unsigned int field,
194+
extern size_t uri_decode ( const char *encoded, void *buf, size_t len );
195+
extern size_t uri_encode ( unsigned int field, const void *raw, size_t raw_len,
195196
char *buf, ssize_t len );
197+
extern size_t uri_encode_string ( unsigned int field, const char *string,
198+
char *buf, ssize_t len );
196199
extern struct uri * parse_uri ( const char *uri_string );
197200
extern size_t format_uri ( const struct uri *uri, char *buf, size_t len );
198201
extern char * format_uri_alloc ( const struct uri *uri );

‎src/net/tcp/httpcore.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,7 +1826,7 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
18261826
}
18271827

18281828
/* URI-encode the key */
1829-
frag_len = uri_encode ( param->key, 0, buf, remaining );
1829+
frag_len = uri_encode_string ( 0, param->key, buf, remaining );
18301830
buf += frag_len;
18311831
len += frag_len;
18321832
remaining -= frag_len;
@@ -1839,7 +1839,7 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
18391839
remaining--;
18401840

18411841
/* URI-encode the value */
1842-
frag_len = uri_encode ( param->value, 0, buf, remaining );
1842+
frag_len = uri_encode_string ( 0, param->value, buf, remaining);
18431843
buf += frag_len;
18441844
len += frag_len;
18451845
remaining -= frag_len;

‎src/tests/settings_test.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ static struct setting test_string_setting = {
166166
.type = &setting_type_string,
167167
};
168168

169+
/** Test URI-encoded string setting */
170+
static struct setting test_uristring_setting = {
171+
.name = "test_uristring",
172+
.type = &setting_type_uristring,
173+
};
174+
169175
/** Test IPv4 address setting type */
170176
static struct setting test_ipv4_setting = {
171177
.name = "test_ipv4",
@@ -265,6 +271,16 @@ static void settings_test_exec ( void ) {
265271
fetchf_ok ( &test_settings, &test_string_setting,
266272
RAW ( 'w', 'o', 'r', 'l', 'd' ), "world" );
267273

274+
/* "uristring" setting type */
275+
storef_ok ( &test_settings, &test_uristring_setting, "hello%20world",
276+
RAW ( 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l',
277+
'd' ) );
278+
fetchf_ok ( &test_settings, &test_uristring_setting,
279+
RAW ( 1, 2, 3, 4, 5 ), "%01%02%03%04%05" );
280+
fetchf_ok ( &test_settings, &test_uristring_setting,
281+
RAW ( 0, ' ', '%', '/', '#', ':', '@', '?', '=', '&' ),
282+
"%00%20%25%2F%23%3A%40%3F%3D%26" );
283+
268284
/* "ipv4" setting type */
269285
storef_ok ( &test_settings, &test_ipv4_setting, "192.168.0.1",
270286
RAW ( 192, 168, 0, 1 ) );

0 commit comments

Comments
 (0)
Please sign in to comment.