Fossil SCM
Keep [square brackets] for IPv6 addresses at all times in url_parse_local(). From libfossil.
Commit
af496edce570b1c43b8107a054050054cf0ee50b10491412be1096b6436d8d2e
Parent
c6fbe0c05a2964d…
1 file changed
+52
-19
+52
-19
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -156,10 +156,12 @@ | ||
| 156 | 156 | ){ |
| 157 | 157 | int iStart; |
| 158 | 158 | char *zLogin; |
| 159 | 159 | char *zExe; |
| 160 | 160 | char cQuerySep = '?'; |
| 161 | + const char *zLb; /* "[" before an IPv6 address in a URL, else "" */ | |
| 162 | + const char *zRb; /* "]" after an IPv6 address in a URL, else "" */ | |
| 161 | 163 | |
| 162 | 164 | if( zUrl[4]=='s' ){ |
| 163 | 165 | pUrlData->isHttps = 1; |
| 164 | 166 | pUrlData->protocol = "https"; |
| 165 | 167 | pUrlData->dfltPort = 443; |
| @@ -198,43 +200,61 @@ | ||
| 198 | 200 | if( urlFlags & URL_OMIT_USER ){ |
| 199 | 201 | zLogin = mprintf(""); |
| 200 | 202 | }else{ |
| 201 | 203 | zLogin = mprintf("%t@", pUrlData->user); |
| 202 | 204 | } |
| 203 | - for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!='?' && c!='#' && c!=':'; | |
| 204 | - j++){} | |
| 205 | - pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); | |
| 206 | - i = j; | |
| 205 | + iStart = i+1; | |
| 207 | 206 | }else{ |
| 207 | + zLogin = mprintf(""); | |
| 208 | + } | |
| 209 | + { | |
| 210 | + /* An IPv6 address is written within square brackets because it | |
| 211 | + ** contains ':'. The brackets are not part of pUrlData->name. */ | |
| 208 | 212 | int inSquare = 0; |
| 209 | 213 | int n; |
| 210 | 214 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='?' && c!='#' |
| 211 | 215 | && (inSquare || c!=':'); i++){ |
| 212 | 216 | if( c=='[' ) inSquare = 1; |
| 213 | 217 | if( c==']' ) inSquare = 0; |
| 214 | 218 | } |
| 215 | - pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); | |
| 216 | - n = strlen(pUrlData->name); | |
| 217 | - if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){ | |
| 218 | - pUrlData->name++; | |
| 219 | - pUrlData->name[n-2] = 0; | |
| 219 | + n = i-iStart; | |
| 220 | + if( n>2 && zUrl[iStart]=='[' && zUrl[i-1]==']' ){ | |
| 221 | + pUrlData->name = mprintf("%.*s", n-2, &zUrl[iStart+1]); | |
| 222 | + }else{ | |
| 223 | + pUrlData->name = mprintf("%.*s", n, &zUrl[iStart]); | |
| 220 | 224 | } |
| 221 | - zLogin = mprintf(""); | |
| 222 | 225 | } |
| 223 | 226 | fossil_strtolwr(pUrlData->name); |
| 227 | + if( strchr(pUrlData->name, ':')!=0 ){ | |
| 228 | + zLb = "["; | |
| 229 | + zRb = "]"; | |
| 230 | + }else{ | |
| 231 | + zLb = zRb = ""; | |
| 232 | + } | |
| 224 | 233 | if( c==':' ){ |
| 225 | 234 | pUrlData->port = 0; |
| 226 | 235 | i++; |
| 227 | 236 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 228 | 237 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 229 | 238 | i++; |
| 230 | 239 | } |
| 231 | 240 | if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number"); |
| 232 | - pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); | |
| 241 | + /* The HTTP Host: header needs the brackets around an IPv6 | |
| 242 | + ** address. For ssh:, the hostname is part of the name of the | |
| 243 | + ** use-path-for-ssh setting, which is stored without them. */ | |
| 244 | + pUrlData->hostname = mprintf("%s%s%s:%d", | |
| 245 | + pUrlData->isSsh ? "" : zLb, | |
| 246 | + pUrlData->name, | |
| 247 | + pUrlData->isSsh ? "" : zRb, | |
| 248 | + pUrlData->port); | |
| 233 | 249 | }else{ |
| 234 | 250 | pUrlData->port = pUrlData->dfltPort; |
| 235 | - pUrlData->hostname = pUrlData->name; | |
| 251 | + if( zLb[0] && !pUrlData->isSsh ){ | |
| 252 | + pUrlData->hostname = mprintf("[%s]", pUrlData->name); | |
| 253 | + }else{ | |
| 254 | + pUrlData->hostname = pUrlData->name; | |
| 255 | + } | |
| 236 | 256 | } |
| 237 | 257 | dehttpize(pUrlData->name); |
| 238 | 258 | pUrlData->path = fossil_strdup(&zUrl[i]); |
| 239 | 259 | for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} |
| 240 | 260 | if( pUrlData->path[i] ){ |
| @@ -269,18 +289,19 @@ | ||
| 269 | 289 | } |
| 270 | 290 | |
| 271 | 291 | dehttpize(pUrlData->path); |
| 272 | 292 | if( pUrlData->dfltPort==pUrlData->port ){ |
| 273 | 293 | pUrlData->canonical = mprintf( |
| 274 | - "%s://%s%T%T%z", | |
| 275 | - pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe | |
| 294 | + "%s://%s%s%T%s%T%z", | |
| 295 | + pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb, | |
| 296 | + pUrlData->path, zExe | |
| 276 | 297 | ); |
| 277 | 298 | }else{ |
| 278 | 299 | pUrlData->canonical = mprintf( |
| 279 | - "%s://%s%T:%d%T%z", | |
| 280 | - pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, | |
| 281 | - pUrlData->path, zExe | |
| 300 | + "%s://%s%s%T%s:%d%T%z", | |
| 301 | + pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb, | |
| 302 | + pUrlData->port, pUrlData->path, zExe | |
| 282 | 303 | ); |
| 283 | 304 | } |
| 284 | 305 | if( pUrlData->isSsh && pUrlData->path[1] ){ |
| 285 | 306 | char *zOld = pUrlData->path; |
| 286 | 307 | pUrlData->path = fossil_strdup(zOld+1); |
| @@ -334,10 +355,22 @@ | ||
| 334 | 355 | pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; |
| 335 | 356 | } |
| 336 | 357 | } |
| 337 | 358 | } |
| 338 | 359 | } |
| 360 | + | |
| 361 | +/* | |
| 362 | +** Append host name zName to the URL being built in x, within square | |
| 363 | +** brackets if it is an IPv6 address, which contains ':'. | |
| 364 | +*/ | |
| 365 | +static void url_append_host(Blob *x, const char *zName){ | |
| 366 | + if( strchr(zName, ':')!=0 ){ | |
| 367 | + blob_appendf(x, "[%T]", zName); | |
| 368 | + }else{ | |
| 369 | + blob_appendf(x, "%T", zName); | |
| 370 | + } | |
| 371 | +} | |
| 339 | 372 | |
| 340 | 373 | /* |
| 341 | 374 | ** Construct the complete URL for a UrlData object, including the |
| 342 | 375 | ** login name and password, into memory obtained from fossil_malloc() |
| 343 | 376 | ** and return a pointer to that URL text. |
| @@ -353,11 +386,11 @@ | ||
| 353 | 386 | if( p->passwd && p->passwd[0] ){ |
| 354 | 387 | blob_appendf(&x, ":%t", p->passwd); |
| 355 | 388 | } |
| 356 | 389 | blob_appendf(&x, "@"); |
| 357 | 390 | } |
| 358 | - blob_appendf(&x, "%T", p->name); | |
| 391 | + url_append_host(&x, p->name); | |
| 359 | 392 | if( p->dfltPort!=p->port ){ |
| 360 | 393 | blob_appendf(&x, ":%d", p->port); |
| 361 | 394 | } |
| 362 | 395 | blob_appendf(&x, "%T", p->path); |
| 363 | 396 | (void)blob_str(&x); |
| @@ -373,11 +406,11 @@ | ||
| 373 | 406 | Blob x = BLOB_INITIALIZER; |
| 374 | 407 | if( p->isFile || p->user==0 || p->user[0]==0 ){ |
| 375 | 408 | return fossil_strdup(p->canonical); |
| 376 | 409 | } |
| 377 | 410 | blob_appendf(&x, "%s://", p->protocol); |
| 378 | - blob_appendf(&x, "%T", p->name); | |
| 411 | + url_append_host(&x, p->name); | |
| 379 | 412 | if( p->dfltPort!=p->port ){ |
| 380 | 413 | blob_appendf(&x, ":%d", p->port); |
| 381 | 414 | } |
| 382 | 415 | blob_appendf(&x, "%T", p->path); |
| 383 | 416 | (void)blob_str(&x); |
| 384 | 417 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -156,10 +156,12 @@ | |
| 156 | ){ |
| 157 | int iStart; |
| 158 | char *zLogin; |
| 159 | char *zExe; |
| 160 | char cQuerySep = '?'; |
| 161 | |
| 162 | if( zUrl[4]=='s' ){ |
| 163 | pUrlData->isHttps = 1; |
| 164 | pUrlData->protocol = "https"; |
| 165 | pUrlData->dfltPort = 443; |
| @@ -198,43 +200,61 @@ | |
| 198 | if( urlFlags & URL_OMIT_USER ){ |
| 199 | zLogin = mprintf(""); |
| 200 | }else{ |
| 201 | zLogin = mprintf("%t@", pUrlData->user); |
| 202 | } |
| 203 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!='?' && c!='#' && c!=':'; |
| 204 | j++){} |
| 205 | pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 206 | i = j; |
| 207 | }else{ |
| 208 | int inSquare = 0; |
| 209 | int n; |
| 210 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='?' && c!='#' |
| 211 | && (inSquare || c!=':'); i++){ |
| 212 | if( c=='[' ) inSquare = 1; |
| 213 | if( c==']' ) inSquare = 0; |
| 214 | } |
| 215 | pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 216 | n = strlen(pUrlData->name); |
| 217 | if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){ |
| 218 | pUrlData->name++; |
| 219 | pUrlData->name[n-2] = 0; |
| 220 | } |
| 221 | zLogin = mprintf(""); |
| 222 | } |
| 223 | fossil_strtolwr(pUrlData->name); |
| 224 | if( c==':' ){ |
| 225 | pUrlData->port = 0; |
| 226 | i++; |
| 227 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 228 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 229 | i++; |
| 230 | } |
| 231 | if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number"); |
| 232 | pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 233 | }else{ |
| 234 | pUrlData->port = pUrlData->dfltPort; |
| 235 | pUrlData->hostname = pUrlData->name; |
| 236 | } |
| 237 | dehttpize(pUrlData->name); |
| 238 | pUrlData->path = fossil_strdup(&zUrl[i]); |
| 239 | for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} |
| 240 | if( pUrlData->path[i] ){ |
| @@ -269,18 +289,19 @@ | |
| 269 | } |
| 270 | |
| 271 | dehttpize(pUrlData->path); |
| 272 | if( pUrlData->dfltPort==pUrlData->port ){ |
| 273 | pUrlData->canonical = mprintf( |
| 274 | "%s://%s%T%T%z", |
| 275 | pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe |
| 276 | ); |
| 277 | }else{ |
| 278 | pUrlData->canonical = mprintf( |
| 279 | "%s://%s%T:%d%T%z", |
| 280 | pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, |
| 281 | pUrlData->path, zExe |
| 282 | ); |
| 283 | } |
| 284 | if( pUrlData->isSsh && pUrlData->path[1] ){ |
| 285 | char *zOld = pUrlData->path; |
| 286 | pUrlData->path = fossil_strdup(zOld+1); |
| @@ -334,10 +355,22 @@ | |
| 334 | pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; |
| 335 | } |
| 336 | } |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | /* |
| 341 | ** Construct the complete URL for a UrlData object, including the |
| 342 | ** login name and password, into memory obtained from fossil_malloc() |
| 343 | ** and return a pointer to that URL text. |
| @@ -353,11 +386,11 @@ | |
| 353 | if( p->passwd && p->passwd[0] ){ |
| 354 | blob_appendf(&x, ":%t", p->passwd); |
| 355 | } |
| 356 | blob_appendf(&x, "@"); |
| 357 | } |
| 358 | blob_appendf(&x, "%T", p->name); |
| 359 | if( p->dfltPort!=p->port ){ |
| 360 | blob_appendf(&x, ":%d", p->port); |
| 361 | } |
| 362 | blob_appendf(&x, "%T", p->path); |
| 363 | (void)blob_str(&x); |
| @@ -373,11 +406,11 @@ | |
| 373 | Blob x = BLOB_INITIALIZER; |
| 374 | if( p->isFile || p->user==0 || p->user[0]==0 ){ |
| 375 | return fossil_strdup(p->canonical); |
| 376 | } |
| 377 | blob_appendf(&x, "%s://", p->protocol); |
| 378 | blob_appendf(&x, "%T", p->name); |
| 379 | if( p->dfltPort!=p->port ){ |
| 380 | blob_appendf(&x, ":%d", p->port); |
| 381 | } |
| 382 | blob_appendf(&x, "%T", p->path); |
| 383 | (void)blob_str(&x); |
| 384 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -156,10 +156,12 @@ | |
| 156 | ){ |
| 157 | int iStart; |
| 158 | char *zLogin; |
| 159 | char *zExe; |
| 160 | char cQuerySep = '?'; |
| 161 | const char *zLb; /* "[" before an IPv6 address in a URL, else "" */ |
| 162 | const char *zRb; /* "]" after an IPv6 address in a URL, else "" */ |
| 163 | |
| 164 | if( zUrl[4]=='s' ){ |
| 165 | pUrlData->isHttps = 1; |
| 166 | pUrlData->protocol = "https"; |
| 167 | pUrlData->dfltPort = 443; |
| @@ -198,43 +200,61 @@ | |
| 200 | if( urlFlags & URL_OMIT_USER ){ |
| 201 | zLogin = mprintf(""); |
| 202 | }else{ |
| 203 | zLogin = mprintf("%t@", pUrlData->user); |
| 204 | } |
| 205 | iStart = i+1; |
| 206 | }else{ |
| 207 | zLogin = mprintf(""); |
| 208 | } |
| 209 | { |
| 210 | /* An IPv6 address is written within square brackets because it |
| 211 | ** contains ':'. The brackets are not part of pUrlData->name. */ |
| 212 | int inSquare = 0; |
| 213 | int n; |
| 214 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='?' && c!='#' |
| 215 | && (inSquare || c!=':'); i++){ |
| 216 | if( c=='[' ) inSquare = 1; |
| 217 | if( c==']' ) inSquare = 0; |
| 218 | } |
| 219 | n = i-iStart; |
| 220 | if( n>2 && zUrl[iStart]=='[' && zUrl[i-1]==']' ){ |
| 221 | pUrlData->name = mprintf("%.*s", n-2, &zUrl[iStart+1]); |
| 222 | }else{ |
| 223 | pUrlData->name = mprintf("%.*s", n, &zUrl[iStart]); |
| 224 | } |
| 225 | } |
| 226 | fossil_strtolwr(pUrlData->name); |
| 227 | if( strchr(pUrlData->name, ':')!=0 ){ |
| 228 | zLb = "["; |
| 229 | zRb = "]"; |
| 230 | }else{ |
| 231 | zLb = zRb = ""; |
| 232 | } |
| 233 | if( c==':' ){ |
| 234 | pUrlData->port = 0; |
| 235 | i++; |
| 236 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 237 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 238 | i++; |
| 239 | } |
| 240 | if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number"); |
| 241 | /* The HTTP Host: header needs the brackets around an IPv6 |
| 242 | ** address. For ssh:, the hostname is part of the name of the |
| 243 | ** use-path-for-ssh setting, which is stored without them. */ |
| 244 | pUrlData->hostname = mprintf("%s%s%s:%d", |
| 245 | pUrlData->isSsh ? "" : zLb, |
| 246 | pUrlData->name, |
| 247 | pUrlData->isSsh ? "" : zRb, |
| 248 | pUrlData->port); |
| 249 | }else{ |
| 250 | pUrlData->port = pUrlData->dfltPort; |
| 251 | if( zLb[0] && !pUrlData->isSsh ){ |
| 252 | pUrlData->hostname = mprintf("[%s]", pUrlData->name); |
| 253 | }else{ |
| 254 | pUrlData->hostname = pUrlData->name; |
| 255 | } |
| 256 | } |
| 257 | dehttpize(pUrlData->name); |
| 258 | pUrlData->path = fossil_strdup(&zUrl[i]); |
| 259 | for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} |
| 260 | if( pUrlData->path[i] ){ |
| @@ -269,18 +289,19 @@ | |
| 289 | } |
| 290 | |
| 291 | dehttpize(pUrlData->path); |
| 292 | if( pUrlData->dfltPort==pUrlData->port ){ |
| 293 | pUrlData->canonical = mprintf( |
| 294 | "%s://%s%s%T%s%T%z", |
| 295 | pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb, |
| 296 | pUrlData->path, zExe |
| 297 | ); |
| 298 | }else{ |
| 299 | pUrlData->canonical = mprintf( |
| 300 | "%s://%s%s%T%s:%d%T%z", |
| 301 | pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb, |
| 302 | pUrlData->port, pUrlData->path, zExe |
| 303 | ); |
| 304 | } |
| 305 | if( pUrlData->isSsh && pUrlData->path[1] ){ |
| 306 | char *zOld = pUrlData->path; |
| 307 | pUrlData->path = fossil_strdup(zOld+1); |
| @@ -334,10 +355,22 @@ | |
| 355 | pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | /* |
| 362 | ** Append host name zName to the URL being built in x, within square |
| 363 | ** brackets if it is an IPv6 address, which contains ':'. |
| 364 | */ |
| 365 | static void url_append_host(Blob *x, const char *zName){ |
| 366 | if( strchr(zName, ':')!=0 ){ |
| 367 | blob_appendf(x, "[%T]", zName); |
| 368 | }else{ |
| 369 | blob_appendf(x, "%T", zName); |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | /* |
| 374 | ** Construct the complete URL for a UrlData object, including the |
| 375 | ** login name and password, into memory obtained from fossil_malloc() |
| 376 | ** and return a pointer to that URL text. |
| @@ -353,11 +386,11 @@ | |
| 386 | if( p->passwd && p->passwd[0] ){ |
| 387 | blob_appendf(&x, ":%t", p->passwd); |
| 388 | } |
| 389 | blob_appendf(&x, "@"); |
| 390 | } |
| 391 | url_append_host(&x, p->name); |
| 392 | if( p->dfltPort!=p->port ){ |
| 393 | blob_appendf(&x, ":%d", p->port); |
| 394 | } |
| 395 | blob_appendf(&x, "%T", p->path); |
| 396 | (void)blob_str(&x); |
| @@ -373,11 +406,11 @@ | |
| 406 | Blob x = BLOB_INITIALIZER; |
| 407 | if( p->isFile || p->user==0 || p->user[0]==0 ){ |
| 408 | return fossil_strdup(p->canonical); |
| 409 | } |
| 410 | blob_appendf(&x, "%s://", p->protocol); |
| 411 | url_append_host(&x, p->name); |
| 412 | if( p->dfltPort!=p->port ){ |
| 413 | blob_appendf(&x, ":%d", p->port); |
| 414 | } |
| 415 | blob_appendf(&x, "%T", p->path); |
| 416 | (void)blob_str(&x); |
| 417 |