1 #include "first.h" 2 3 #include "keyvalue.h" 4 #include "plugin_config.h" /* struct cond_match_t */ 5 #include "burl.h" 6 #include "log.h" 7 8 #include <stdlib.h> 9 #include <string.h> 10 11 #ifdef HAVE_PCRE_H 12 #include <pcre.h> 13 #endif 14 15 typedef struct pcre_keyvalue { 16 #ifdef HAVE_PCRE_H 17 pcre *key; 18 pcre_extra *key_extra; 19 #endif 20 buffer value; 21 } pcre_keyvalue; 22 23 pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void) { 24 pcre_keyvalue_buffer *kvb; 25 26 kvb = calloc(1, sizeof(*kvb)); 27 force_assert(NULL != kvb); 28 29 return kvb; 30 } 31 32 int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, const buffer *key, const buffer *value) { 33 #ifdef HAVE_PCRE_H 34 const char *errptr; 35 int erroff; 36 pcre_keyvalue *kv; 37 38 if (0 == (kvb->used & 3)) { /*(allocate in groups of 4)*/ 39 kvb->kv = realloc(kvb->kv, (kvb->used + 4) * sizeof(*kvb->kv)); 40 force_assert(NULL != kvb->kv); 41 } 42 43 kv = kvb->kv + kvb->used++; 44 kv->key_extra = NULL; 45 46 /* copy persistent config data, and elide free() in free_data below */ 47 memcpy(&kv->value, value, sizeof(buffer)); 48 /*buffer_copy_buffer(&kv->value, value);*/ 49 50 if (NULL == (kv->key = pcre_compile(key->ptr, 51 0, &errptr, &erroff, NULL))) { 52 53 log_error(errh, __FILE__, __LINE__, 54 "rexexp compilation error at %s", errptr); 55 return 0; 56 } 57 58 if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) && 59 errptr != NULL) { 60 return 0; 61 } 62 #else 63 static int logged_message = 0; 64 if (logged_message) return 1; 65 logged_message = 1; 66 log_error(errh, __FILE__, __LINE__, 67 "pcre support is missing, please install libpcre and the headers"); 68 UNUSED(kvb); 69 UNUSED(key); 70 UNUSED(value); 71 #endif 72 73 return 1; 74 } 75 76 void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) { 77 #ifdef HAVE_PCRE_H 78 for (uint32_t i = 0; i < kvb->used; ++i) { 79 pcre_keyvalue * const kv = kvb->kv+i; 80 if (kv->key) pcre_free(kv->key); 81 if (kv->key_extra) pcre_free(kv->key_extra); 82 /*free (kv->value.ptr);*//*(see pcre_keyvalue_buffer_append)*/ 83 } 84 85 if (kvb->kv) free(kvb->kv); 86 #endif 87 free(kvb); 88 } 89 90 #ifdef HAVE_PCRE_H 91 static void pcre_keyvalue_buffer_append_match(buffer *b, const char **list, int n, unsigned int num, int flags) { 92 if (num < (unsigned int)n) { /* n is always > 0 */ 93 burl_append(b, list[num], strlen(list[num]), flags); 94 } 95 } 96 97 static void pcre_keyvalue_buffer_append_ctxmatch(buffer *b, pcre_keyvalue_ctx *ctx, unsigned int num, int flags) { 98 const struct cond_match_t * const cache = ctx->cache; 99 if (!cache) return; /* no enclosing match context */ 100 if ((int)num < ctx->cond_match_count) { 101 const int off = cache->matches[(num <<= 1)]; /*(num *= 2)*/ 102 const int len = cache->matches[num+1] - off; 103 burl_append(b, cache->comp_value->ptr + off, (size_t)len, flags); 104 } 105 } 106 107 static int pcre_keyvalue_buffer_subst_ext(buffer *b, const char *pattern, const char **list, int n, pcre_keyvalue_ctx *ctx) { 108 const unsigned char *p = (unsigned char *)pattern+2;/* +2 past ${} or %{} */ 109 int flags = 0; 110 while (!light_isdigit(*p) && *p != '}' && *p != '\0') { 111 if (0) { 112 } 113 else if (p[0] == 'e' && p[1] == 's' && p[2] == 'c') { 114 p+=3; 115 if (p[0] == ':') { 116 flags |= BURL_ENCODE_ALL; 117 p+=1; 118 } 119 else if (0 == strncmp((const char *)p, "ape:", 4)) { 120 flags |= BURL_ENCODE_ALL; 121 p+=4; 122 } 123 else if (0 == strncmp((const char *)p, "nde:", 4)) { 124 flags |= BURL_ENCODE_NDE; 125 p+=4; 126 } 127 else if (0 == strncmp((const char *)p, "psnde:", 6)) { 128 flags |= BURL_ENCODE_PSNDE; 129 p+=6; 130 } 131 else { /* skip unrecognized esc... */ 132 p = (const unsigned char *)strchr((const char *)p, ':'); 133 if (NULL == p) return -1; 134 ++p; 135 } 136 } 137 else if (p[0] == 'n' && p[1] == 'o') { 138 p+=2; 139 if (0 == strncmp((const char *)p, "esc:", 4)) { 140 flags |= BURL_ENCODE_NONE; 141 p+=4; 142 } 143 else if (0 == strncmp((const char *)p, "escape:", 7)) { 144 flags |= BURL_ENCODE_NONE; 145 p+=7; 146 } 147 else { /* skip unrecognized no... */ 148 p = (const unsigned char *)strchr((const char *)p, ':'); 149 if (NULL == p) return -1; 150 ++p; 151 } 152 } 153 else if (p[0] == 't' && p[1] == 'o') { 154 p+=2; 155 if (0 == strncmp((const char *)p, "lower:", 6)) { 156 flags |= BURL_TOLOWER; 157 p+=6; 158 } 159 else if (0 == strncmp((const char *)p, "upper:", 6)) { 160 flags |= BURL_TOLOWER; 161 p+=6; 162 } 163 else { /* skip unrecognized to... */ 164 p = (const unsigned char *)strchr((const char *)p, ':'); 165 if (NULL == p) return -1; 166 ++p; 167 } 168 } 169 else if (p[0] == 'u' && p[1] == 'r' && p[2] == 'l' && p[3] == '.') { 170 p+=4; 171 if (0 == strncmp((const char *)p, "scheme}", 7)) { 172 burl_append(b, CONST_BUF_LEN(ctx->burl->scheme), flags); 173 p+=6; 174 } 175 else if (0 == strncmp((const char *)p, "authority}", 10)) { 176 burl_append(b, CONST_BUF_LEN(ctx->burl->authority), flags); 177 p+=9; 178 } 179 else if (0 == strncmp((const char *)p, "port}", 5)) { 180 buffer_append_int(b, (int)ctx->burl->port); 181 p+=4; 182 } 183 else if (0 == strncmp((const char *)p, "path}", 5)) { 184 const buffer * const target = ctx->burl->path; 185 const uint32_t len = buffer_string_length(target); 186 const char * const ptr = target->ptr; 187 const char * const qmark = memchr(ptr, '?', len); 188 burl_append(b, ptr, qmark ? (uint32_t)(qmark-ptr) : len, flags); 189 p+=4; 190 } 191 else if (0 == strncmp((const char *)p, "query}", 6)) { 192 burl_append(b, CONST_BUF_LEN(ctx->burl->query), flags); 193 p+=5; 194 } 195 else { /* skip unrecognized url.* */ 196 p = (const unsigned char *)strchr((const char *)p, '}'); 197 if (NULL == p) return -1; 198 } 199 break; 200 } 201 else if (p[0] == 'q' && p[1] == 's' && p[2] == 'a' && p[3] == '}') { 202 const buffer *qs = ctx->burl->query; 203 if (!buffer_is_empty(qs)) { 204 if (NULL != strchr(b->ptr, '?')) { 205 if (!buffer_string_is_empty(qs)) 206 buffer_append_string_len(b, CONST_STR_LEN("&")); 207 } 208 else { 209 buffer_append_string_len(b, CONST_STR_LEN("?")); 210 } 211 burl_append(b, CONST_BUF_LEN(qs), flags); 212 } 213 p+=3; 214 break; 215 } 216 else if (p[0] == 'e' && p[1] == 'n' && p[2] == 'c' 217 && 0 == strncmp((const char *)p+3, "b64u:", 5)) { 218 flags |= BURL_ENCODE_B64U; 219 p+=8; 220 } 221 else if (p[0] == 'd' && p[1] == 'e' && p[2] == 'c' 222 && 0 == strncmp((const char *)p+3, "b64u:", 5)) { 223 flags |= BURL_DECODE_B64U; 224 p+=8; 225 } 226 else ++p; /* skip unrecognized char */ 227 } 228 if (*p == '\0') return -1; 229 if (*p != '}') { /* light_isdigit(*p) */ 230 unsigned int num = *p - '0'; 231 ++p; 232 if (light_isdigit(*p)) num = num * 10 + (*p++ - '0'); 233 if (*p != '}') { 234 p = (const unsigned char *)strchr((const char *)p, '}'); 235 if (NULL == p) return -1; 236 } 237 if (0 == flags) flags = BURL_ENCODE_PSNDE; /* default */ 238 pattern[0] == '$' /*(else '%')*/ 239 ? pcre_keyvalue_buffer_append_match(b, list, n, num, flags) 240 : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, flags); 241 } 242 return (int)(p + 1 - (unsigned char *)pattern - 2); 243 } 244 245 static void pcre_keyvalue_buffer_subst(buffer *b, const buffer *patternb, const char **list, int n, pcre_keyvalue_ctx *ctx) { 246 const char *pattern = patternb->ptr; 247 const size_t pattern_len = buffer_string_length(patternb); 248 size_t start = 0; 249 250 /* search for $... or %... pattern substitutions */ 251 252 buffer_clear(b); 253 254 for (size_t k = 0; k + 1 < pattern_len; ++k) { 255 if (pattern[k] == '$' || pattern[k] == '%') { 256 257 buffer_append_string_len(b, pattern + start, k - start); 258 259 if (pattern[k + 1] == '{') { 260 int num = pcre_keyvalue_buffer_subst_ext(b, pattern+k, list, n, ctx); 261 if (num < 0) return; /* error; truncate result */ 262 k += (size_t)num; 263 } else if (light_isdigit(((unsigned char *)pattern)[k + 1])) { 264 unsigned int num = (unsigned int)pattern[k + 1] - '0'; 265 pattern[k] == '$' /*(else '%')*/ 266 ? pcre_keyvalue_buffer_append_match(b, list, n, num, 0) 267 : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, 0); 268 } else { 269 /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */ 270 buffer_append_string_len(b, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2); 271 } 272 273 k++; 274 start = k + 1; 275 } 276 } 277 278 buffer_append_string_len(b, pattern + start, pattern_len - start); 279 } 280 281 handler_t pcre_keyvalue_buffer_process(const pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, const buffer *input, buffer *result) { 282 for (int i = 0, used = (int)kvb->used; i < used; ++i) { 283 const pcre_keyvalue * const kv = kvb->kv+i; 284 #define N 20 285 int ovec[N * 3]; 286 #undef N 287 int n = pcre_exec(kv->key, kv->key_extra, CONST_BUF_LEN(input), 288 0, 0, ovec, sizeof(ovec)/sizeof(int)); 289 if (n < 0) { 290 if (n != PCRE_ERROR_NOMATCH) { 291 return HANDLER_ERROR; 292 } 293 } 294 else if (buffer_string_is_empty(&kv->value)) { 295 /* short-circuit if blank replacement pattern 296 * (do not attempt to match against remaining kvb rules) */ 297 ctx->m = i; 298 return HANDLER_GO_ON; 299 } 300 else { /* it matched */ 301 const char **list; 302 ctx->m = i; 303 pcre_get_substring_list(input->ptr, ovec, n, &list); 304 pcre_keyvalue_buffer_subst(result, &kv->value, list, n, ctx); 305 pcre_free(list); 306 return HANDLER_FINISHED; 307 } 308 } 309 310 return HANDLER_GO_ON; 311 } 312 #else 313 handler_t pcre_keyvalue_buffer_process(const pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, const buffer *input, buffer *result) { 314 UNUSED(kvb); 315 UNUSED(ctx); 316 UNUSED(input); 317 UNUSED(result); 318 return HANDLER_GO_ON; 319 } 320 #endif 321 322 323 /* modified from burl_normalize_basic() to handle %% extra encoding layer */ 324 325 /* c (char) and n (nibble) MUST be unsigned integer types */ 326 #define li_cton(c,n) \ 327 (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0)) 328 329 static void pcre_keyvalue_burl_percent_toupper (buffer *b) 330 { 331 const unsigned char * const s = (unsigned char *)b->ptr; 332 const int used = (int)buffer_string_length(b); 333 unsigned int n1, n2; 334 for (int i = 0; i < used; ++i) { 335 if (s[i]=='%' && li_cton(s[i+1],n1) && li_cton(s[i+2],n2)) { 336 if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */ 337 if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ 338 i+=2; 339 } 340 } 341 } 342 343 static void pcre_keyvalue_burl_percent_percent_toupper (buffer *b) 344 { 345 const unsigned char * const s = (unsigned char *)b->ptr; 346 const int used = (int)buffer_string_length(b); 347 unsigned int n1, n2; 348 for (int i = 0; i < used; ++i) { 349 if (s[i] == '%' && s[i+1]=='%' 350 && li_cton(s[i+2],n1) && li_cton(s[i+3],n2)) { 351 if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ 352 if (s[i+3] >= 'a') b->ptr[i+3] &= 0xdf; /* uppercase hex */ 353 i+=3; 354 } 355 } 356 } 357 358 static const char hex_chars_uc[] = "0123456789ABCDEF"; 359 360 static void pcre_keyvalue_burl_percent_high_UTF8 (buffer *b, buffer *t) 361 { 362 const unsigned char * const s = (unsigned char *)b->ptr; 363 unsigned char *p; 364 const int used = (int)buffer_string_length(b); 365 unsigned int count = 0, j = 0; 366 for (int i = 0; i < used; ++i) { 367 if (s[i] > 0x7F) ++count; 368 } 369 if (0 == count) return; 370 371 p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*2)); 372 for (int i = 0; i < used; ++i, ++j) { 373 if (s[i] <= 0x7F) 374 p[j] = s[i]; 375 else { 376 p[j] = '%'; 377 p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; 378 p[++j] = hex_chars_uc[s[i] & 0xF]; 379 } 380 } 381 buffer_commit(t, j); 382 buffer_copy_buffer(b, t); 383 } 384 385 static void pcre_keyvalue_burl_percent_percent_high_UTF8 (buffer *b, buffer *t) 386 { 387 const unsigned char * const s = (unsigned char *)b->ptr; 388 unsigned char *p; 389 const int used = (int)buffer_string_length(b); 390 unsigned int count = 0, j = 0; 391 for (int i = 0; i < used; ++i) { 392 if (s[i] > 0x7F) ++count; 393 } 394 if (0 == count) return; 395 396 p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*3)); 397 for (int i = 0; i < used; ++i, ++j) { 398 if (s[i] <= 0x7F) 399 p[j] = s[i]; 400 else { 401 p[j] = '%'; 402 p[++j] = '%'; 403 p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; 404 p[++j] = hex_chars_uc[s[i] & 0xF]; 405 } 406 } 407 buffer_commit(t, j); 408 buffer_copy_buffer(b, t); 409 } 410 411 /* Basic normalization of regex and regex replacement to mirror some of 412 * the normalizations performed on request URI (for better compatibility). 413 * Note: not currently attempting to replace unnecessary percent-encoding 414 * (would need to know if regex was intended to match url-path or 415 * query-string or both, and then would have to regex-escape if those 416 * chars where special regex chars such as . * + ? ( ) [ ] | and more) 417 * Not attempting to percent-encode chars which should be encoded, again 418 * since regex might target url-path, query-string, or both, and we would 419 * have to avoid percent-encoding special regex chars. 420 * Also not attempting to detect unnecessarily regex-escape in, e.g. %\x\x 421 * Preserve improper %-encoded sequences which are not %XX (using hex chars) 422 * Intentionally not performing path simplification (e.g. ./ ../) 423 * If regex-specific normalizations begin to be made to k here, 424 * must revisit callers, e.g. one configfile.c use on non-regex string. 425 * "%%" (percent_percent) is used in regex replacement strings since 426 * otherwise "%n" is used to indicate regex backreference where n is number. 427 */ 428 429 void pcre_keyvalue_burl_normalize_key (buffer *k, buffer *t) 430 { 431 pcre_keyvalue_burl_percent_toupper(k); 432 pcre_keyvalue_burl_percent_high_UTF8(k, t); 433 } 434 435 void pcre_keyvalue_burl_normalize_value (buffer *v, buffer *t) 436 { 437 pcre_keyvalue_burl_percent_percent_toupper(v); 438 pcre_keyvalue_burl_percent_percent_high_UTF8(v, t); 439 } 440