src/core/ngx_json_unescape.c - nginx-1.31.7 nginx/ @ 939334eff

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. /*
  5. * JSON string unescape.  Decodes escape sequences defined by
  6. * RFC 8259, Section 7, including \uXXXX and UTF-16 surrogate pairs.
  7. */


  8. #include <ngx_config.h>
  9. #include <ngx_core.h>
  10. #include <ngx_json_unescape.h>


  11. static ngx_inline u_char *ngx_json_utf8_encode(u_char *dst,
  12.     uint32_t codepoint);


  13. ngx_int_t
  14. ‌ngx_json_unescape_string(ngx_str_t *str)
  15. {
  16.     size_t       size;
  17.     u_char      *d, *s, *start, ch;
  18.     uint32_t     codepoint, high_surrogate;
  19.     ngx_int_t    n;
  20.     ngx_uint_t   hex_left;

  21. ‌    enum {
  22.         sw_usual = 0,
  23.         sw_quoted,
  24.         sw_hex,
  25.         sw_surrogate_start,   /* expect '\' of \uDCxx after high surrogate */
  26.         sw_surrogate_u        /* expect 'u' */
  27.     } state;

  28.     /*
  29.      * RFC 8259, Section 7:
  30.      *
  31.      * string = quotation-mark *char quotation-mark
  32.      *
  33.      * char = unescaped /
  34.      *        escape (
  35.      *            %x22 /          ; "    quotation mark  U+0022
  36.      *            %x5C /          ; \    reverse solidus U+005C
  37.      *            %x2F /          ; /    solidus         U+002F
  38.      *            %x62 /          ; b    backspace       U+0008
  39.      *            %x66 /          ; f    form feed       U+000C
  40.      *            %x6E /          ; n    line feed       U+000A
  41.      *            %x72 /          ; r    carriage return U+000D
  42.      *            %x74 /          ; t    tab             U+0009
  43.      *            %x75 4HEXDIG )  ; uXXXX                U+XXXX
  44.      *
  45.      * Non-BMP code points are encoded as a UTF-16 surrogate pair:
  46.      *   \uD800..\uDBFF  high surrogate
  47.      *   \uDC00..\uDFFF  low  surrogate
  48.      * The pair decodes to U+10000 + (high - 0xD800) * 0x400
  49.      *                              + (low  - 0xDC00).
  50.      * The input is an unquoted JSON string body (the bytes between the
  51.      * surrounding double quotes); it is decoded in place.
  52.      */

  53.     if (str->len == 0) {
  54.         return NGX_OK;
  55.     }

  56.     if (str->data == NULL) {
  57.         return NGX_ERROR;
  58.     }

  59.     start = str->data;
  60.     size = str->len;

  61.     d = s = start;

  62.     state = sw_usual;
  63.     codepoint = 0;
  64.     high_surrogate = 0;
  65.     hex_left = 0;

  66.     while (size--) {

  67.         ch = *s++;

  68.         switch (state) {
  69.         case sw_usual:

  70.             if (ch == '"') {
  71.                 return NGX_ERROR;
  72.             }

  73.             if (ch == '\\') {
  74.                 state = sw_quoted;
  75.                 break;
  76.             }

  77.             if (ch < 0x20) {
  78.                 /* RFC 8259: control characters must be escaped */
  79.                 return NGX_ERROR;
  80.             }

  81.             *d++ = ch;
  82.             break;

  83.         case sw_quoted:

  84.             switch (ch) {

  85.             case 'u':
  86.                 codepoint = 0;
  87.                 hex_left = 4;
  88.                 state = sw_hex;
  89.                 break;

  90.             case '"':
  91.             case '/':
  92.             case '\\':
  93.                 *d++ = ch;
  94.                 state = sw_usual;
  95.                 break;

  96.             case 'b':
  97.                 *d++ = '\b';
  98.                 state = sw_usual;
  99.                 break;

  100.             case 'f':
  101.                 *d++ = '\f';
  102.                 state = sw_usual;
  103.                 break;

  104.             case 'n':
  105.                 *d++ = '\n';
  106.                 state = sw_usual;
  107.                 break;

  108.             case 'r':
  109.                 *d++ = '\r';
  110.                 state = sw_usual;
  111.                 break;

  112.             case 't':
  113.                 *d++ = '\t';
  114.                 state = sw_usual;
  115.                 break;

  116.             default:
  117.                 return NGX_ERROR;
  118.             }

  119.             break;

  120.         case sw_hex:

  121.             n = ngx_json_hex_digit(ch);
  122.             if (n == NGX_ERROR) {
  123.                 return NGX_ERROR;
  124.             }

  125.             codepoint = (codepoint << 4) | (uint32_t) n;

  126.             if (--hex_left > 0) {
  127.                 break;
  128.             }

  129.             if (high_surrogate) {
  130.                 if (codepoint < 0xDC00 || codepoint > 0xDFFF) {
  131.                     return NGX_ERROR;
  132.                 }

  133.                 codepoint = 0x10000
  134.                             + ((high_surrogate - 0xD800) << 10)
  135.                             + (codepoint - 0xDC00);
  136.                 d = ngx_json_utf8_encode(d, codepoint);
  137.                 high_surrogate = 0;
  138.                 state = sw_usual;
  139.                 break;
  140.             }

  141.             if (codepoint >= 0xD800 && codepoint <= 0xDBFF) {
  142.                 /* high surrogate - wait for the low surrogate */
  143.                 high_surrogate = codepoint;
  144.                 state = sw_surrogate_start;
  145.                 break;
  146.             }

  147.             if (codepoint >= 0xDC00 && codepoint <= 0xDFFF) {
  148.                 /* lone low surrogate */
  149.                 return NGX_ERROR;
  150.             }

  151.             d = ngx_json_utf8_encode(d, codepoint);
  152.             state = sw_usual;
  153.             break;

  154.         case sw_surrogate_start:
  155.             /*
  156.              * Expect '\' to begin the low-surrogate escape.
  157.              * Lone surrogates are not valid Unicode scalar values
  158.              * (RFC 8259, Section 8.2).
  159.              */
  160.             if (ch == '\\') {
  161.                 state = sw_surrogate_u;
  162.                 break;
  163.             }

  164.             /* lone high surrogate */
  165.             return NGX_ERROR;

  166.         case sw_surrogate_u:
  167.             if (ch == 'u') {
  168.                 codepoint = 0;
  169.                 hex_left = 4;
  170.                 state = sw_hex;
  171.                 break;
  172.             }

  173.             /* lone high surrogate */
  174.             return NGX_ERROR;
  175.         }
  176.     }

  177.     if (state != sw_usual) {
  178.         /* truncated escape sequence or unpaired high surrogate */
  179.         return NGX_ERROR;
  180.     }

  181.     str->data = start;
  182.     str->len = d - start;

  183.     return NGX_OK;
  184. }


  185. static ngx_inline u_char *
  186. ‌ngx_json_utf8_encode(u_char *dst, uint32_t codepoint)
  187. {
  188.     if (codepoint <= 0x7F) {
  189.         *dst++ = (u_char) codepoint;

  190.     } else if (codepoint <= 0x7FF) {
  191.         *dst++ = (u_char) (0xC0 | (codepoint >> 6));
  192.         *dst++ = (u_char) (0x80 | (codepoint & 0x3F));

  193.     } else if (codepoint <= 0xFFFF) {
  194.         *dst++ = (u_char) (0xE0 | (codepoint >> 12));
  195.         *dst++ = (u_char) (0x80 | ((codepoint >> 6) & 0x3F));
  196.         *dst++ = (u_char) (0x80 | (codepoint & 0x3F));

  197.     } else {
  198.         *dst++ = (u_char) (0xF0 | (codepoint >> 18));
  199.         *dst++ = (u_char) (0x80 | ((codepoint >> 12) & 0x3F));
  200.         *dst++ = (u_char) (0x80 | ((codepoint >> 6) & 0x3F));
  201.         *dst++ = (u_char) (0x80 | (codepoint & 0x3F));
  202.     }

  203.     return dst;
  204. }


  205. ngx_int_t
  206. ‌ngx_json_hex_digit(u_char ch)
  207. {
  208.     if (ch >= '0' && ch <= '9') {
  209.         return ch - '0';
  210.     }

  211.     ch = (u_char) (ch | 0x20);

  212.     if (ch >= 'a' && ch <= 'f') {
  213.         return ch - 'a' + 10;
  214.     }

  215.     return NGX_ERROR;
  216. }