src/core/ngx_json_unescape.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

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 *dst, ngx_str_t *src)
  15. {
  16.     size_t       size;
  17.     u_char      *d, *s, 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).
  52.      */

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

  57.     if (dst->len < src->len) {
  58.         return NGX_ERROR;
  59.     }

  60.     s = src->data;
  61.     d = dst->data;
  62.     size = src->len;


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

  67.     while (size--) {

  68.         ch = *s++;

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

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

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

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

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

  84.         case sw_quoted:

  85.             switch (ch) {

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

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

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

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

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

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

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

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

  120.             break;

  121.         case sw_hex:

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

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

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

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

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

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

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

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

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

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

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

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

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

  182.     dst->len = d - dst->data;

  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. }