diff options
Diffstat (limited to 'src/microhttpd/connection.c')
-rw-r--r-- | src/microhttpd/connection.c | 299 |
1 files changed, 109 insertions, 190 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 960c22db..16c5fb93 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -2829,146 +2829,6 @@ enum _MHD_ParseCookie | |||
2829 | /** | 2829 | /** |
2830 | * Parse the cookies string (see RFC 6265). | 2830 | * Parse the cookies string (see RFC 6265). |
2831 | * | 2831 | * |
2832 | * Parsing may fail if the string is not formed strictly as defined by RFC 6265. | ||
2833 | * | ||
2834 | * @param str the string to parse, without leading whitespaces | ||
2835 | * @param str_len the size of the @a str, not including mandatory | ||
2836 | * zero-termination | ||
2837 | * @param connection the connection to add parsed cookies | ||
2838 | * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise | ||
2839 | */ | ||
2840 | static enum _MHD_ParseCookie | ||
2841 | parse_cookies_string_strict (char *str, | ||
2842 | size_t str_len, | ||
2843 | struct MHD_Connection *connection) | ||
2844 | { | ||
2845 | size_t i; | ||
2846 | |||
2847 | i = 0; | ||
2848 | while (i < str_len) | ||
2849 | { | ||
2850 | size_t name_start; | ||
2851 | size_t name_len; | ||
2852 | size_t value_start; | ||
2853 | size_t value_len; | ||
2854 | bool val_quoted; | ||
2855 | /* 'i' must point to the first char of cookie-name */ | ||
2856 | name_start = i; | ||
2857 | /* Find the end of the cookie-name */ | ||
2858 | do | ||
2859 | { | ||
2860 | const char l = str[i]; | ||
2861 | if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) || | ||
2862 | (';' == l) || (0 == l)) | ||
2863 | break; | ||
2864 | } while (str_len > ++i); | ||
2865 | if ((str_len == i) || ('=' != str[i]) || (name_start == i)) | ||
2866 | return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */ | ||
2867 | name_len = i - name_start; | ||
2868 | /* 'i' must point to the '=' char */ | ||
2869 | mhd_assert ('=' == str[i]); | ||
2870 | i++; | ||
2871 | /* 'i' must point to the first char of cookie-value */ | ||
2872 | if (str_len == i) | ||
2873 | { | ||
2874 | value_start = 0; | ||
2875 | value_len = 0; | ||
2876 | #ifdef _DEBUG | ||
2877 | val_quoted = false; /* This assignment used in assert */ | ||
2878 | #endif | ||
2879 | } | ||
2880 | else | ||
2881 | { | ||
2882 | bool valid_cookie; | ||
2883 | val_quoted = ('"' == str[i]); | ||
2884 | if (val_quoted) | ||
2885 | i++; | ||
2886 | value_start = i; | ||
2887 | /* Find the end of the cookie-value */ | ||
2888 | while (str_len > i) | ||
2889 | { | ||
2890 | const char l = str[i]; | ||
2891 | if ((';' == l) || ('"' == l) || (' ' == l) || ('\t' == l) | ||
2892 | || (',' == l) || ('\\' == l) || (0 == l)) | ||
2893 | break; | ||
2894 | i++; | ||
2895 | } | ||
2896 | value_len = i - value_start; | ||
2897 | if (val_quoted) | ||
2898 | { | ||
2899 | if ((str_len == i) || ('"' != str[i])) | ||
2900 | return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */ | ||
2901 | i++; | ||
2902 | } | ||
2903 | if (str_len == i) | ||
2904 | valid_cookie = true; | ||
2905 | else if (';' == str[i]) | ||
2906 | valid_cookie = true; | ||
2907 | else if ((' ' == str[i]) || ('\t' == str[i])) | ||
2908 | { /* Optional whitespace at the end of the string? */ | ||
2909 | while (str_len > ++i) | ||
2910 | { | ||
2911 | if ((' ' != str[i]) && ('\t' != str[i])) | ||
2912 | break; | ||
2913 | } | ||
2914 | if (str_len == i) | ||
2915 | valid_cookie = true; | ||
2916 | else | ||
2917 | valid_cookie = false; | ||
2918 | } | ||
2919 | else | ||
2920 | valid_cookie = false; | ||
2921 | |||
2922 | if (! valid_cookie) | ||
2923 | return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */ | ||
2924 | } | ||
2925 | mhd_assert (0 != name_len); | ||
2926 | str[name_start + name_len] = 0; /* Zero-terminate the name */ | ||
2927 | if (0 != value_len) | ||
2928 | { | ||
2929 | mhd_assert (0 == str[i] || ';' == str[i]); | ||
2930 | mhd_assert (! val_quoted || ';' == str[i]); | ||
2931 | str[value_start + value_len] = 0; /* Zero-terminate the value */ | ||
2932 | if (MHD_NO == | ||
2933 | MHD_set_connection_value_n_nocheck_ (connection, | ||
2934 | MHD_COOKIE_KIND, | ||
2935 | str + name_start, | ||
2936 | name_len, | ||
2937 | str + value_start, | ||
2938 | value_len)) | ||
2939 | return MHD_PARSE_COOKIE_NO_MEMORY; | ||
2940 | } | ||
2941 | else | ||
2942 | { | ||
2943 | if (MHD_NO == | ||
2944 | MHD_set_connection_value_n_nocheck_ (connection, | ||
2945 | MHD_COOKIE_KIND, | ||
2946 | str + name_start, | ||
2947 | name_len, | ||
2948 | "", | ||
2949 | 0)) | ||
2950 | return MHD_PARSE_COOKIE_NO_MEMORY; | ||
2951 | } | ||
2952 | if (str_len > i) | ||
2953 | { | ||
2954 | mhd_assert (0 == str[i] || ';' == str[i]); | ||
2955 | mhd_assert (! val_quoted || ';' == str[i]); | ||
2956 | mhd_assert (';' != str[i] || val_quoted || 0 == value_len); | ||
2957 | i++; | ||
2958 | if (str_len == i) | ||
2959 | return MHD_PARSE_COOKIE_MALFORMED; /* No cookie name after semicolon */ | ||
2960 | if (' ' != str[i]) | ||
2961 | return MHD_PARSE_COOKIE_MALFORMED; /* No space after semicolon */ | ||
2962 | i++; | ||
2963 | } | ||
2964 | } | ||
2965 | return MHD_PARSE_COOKIE_OK; | ||
2966 | } | ||
2967 | |||
2968 | |||
2969 | /** | ||
2970 | * Parse the cookies string (see RFC 6265). | ||
2971 | * | ||
2972 | * Try to parse the cookies string even if it is not strictly formed | 2832 | * Try to parse the cookies string even if it is not strictly formed |
2973 | * as specified by RFC 6265. | 2833 | * as specified by RFC 6265. |
2974 | * | 2834 | * |
@@ -2979,12 +2839,22 @@ parse_cookies_string_strict (char *str, | |||
2979 | * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise | 2839 | * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise |
2980 | */ | 2840 | */ |
2981 | static enum _MHD_ParseCookie | 2841 | static enum _MHD_ParseCookie |
2982 | parse_cookies_string_lenient (char *str, | 2842 | parse_cookies_string (char *str, |
2983 | size_t str_len, | 2843 | const size_t str_len, |
2984 | struct MHD_Connection *connection) | 2844 | struct MHD_Connection *connection) |
2985 | { | 2845 | { |
2986 | size_t i; | 2846 | size_t i; |
2987 | bool non_strict; | 2847 | bool non_strict; |
2848 | /* Skip extra whitespaces and empty cookies */ | ||
2849 | const bool allow_wsp_empty = (0 >= connection->daemon->strict_for_client); | ||
2850 | /* Allow whitespaces around '=' character */ | ||
2851 | const bool wsp_around_eq = (0 > connection->daemon->strict_for_client); | ||
2852 | /* Allow whitespaces in quoted cookie value */ | ||
2853 | const bool wsp_in_quoted = (0 >= connection->daemon->strict_for_client); | ||
2854 | /* Allow tab as space after semicolon between cookies */ | ||
2855 | const bool tab_as_sp = (0 >= connection->daemon->strict_for_client); | ||
2856 | /* Allow no space after semicolon between cookies */ | ||
2857 | const bool allow_no_space = (0 >= connection->daemon->strict_for_client); | ||
2988 | 2858 | ||
2989 | non_strict = false; | 2859 | non_strict = false; |
2990 | i = 0; | 2860 | i = 0; |
@@ -2998,6 +2868,8 @@ parse_cookies_string_lenient (char *str, | |||
2998 | /* Skip any whitespaces and empty cookies */ | 2868 | /* Skip any whitespaces and empty cookies */ |
2999 | while (' ' == str[i] || '\t' == str[i] || ';' == str[i]) | 2869 | while (' ' == str[i] || '\t' == str[i] || ';' == str[i]) |
3000 | { | 2870 | { |
2871 | if (! allow_wsp_empty) | ||
2872 | return MHD_PARSE_COOKIE_MALFORMED; | ||
3001 | non_strict = true; | 2873 | non_strict = true; |
3002 | i++; | 2874 | i++; |
3003 | if (i == str_len) | 2875 | if (i == str_len) |
@@ -3017,6 +2889,8 @@ parse_cookies_string_lenient (char *str, | |||
3017 | /* Skip any whitespaces */ | 2889 | /* Skip any whitespaces */ |
3018 | while (str_len > i && (' ' == str[i] || '\t' == str[i])) | 2890 | while (str_len > i && (' ' == str[i] || '\t' == str[i])) |
3019 | { | 2891 | { |
2892 | if (! wsp_around_eq) | ||
2893 | return MHD_PARSE_COOKIE_MALFORMED; | ||
3020 | non_strict = true; | 2894 | non_strict = true; |
3021 | i++; | 2895 | i++; |
3022 | } | 2896 | } |
@@ -3028,6 +2902,8 @@ parse_cookies_string_lenient (char *str, | |||
3028 | /* Skip any whitespaces */ | 2902 | /* Skip any whitespaces */ |
3029 | while (str_len > i && (' ' == str[i] || '\t' == str[i])) | 2903 | while (str_len > i && (' ' == str[i] || '\t' == str[i])) |
3030 | { | 2904 | { |
2905 | if (! wsp_around_eq) | ||
2906 | return MHD_PARSE_COOKIE_MALFORMED; | ||
3031 | non_strict = true; | 2907 | non_strict = true; |
3032 | i++; | 2908 | i++; |
3033 | } | 2909 | } |
@@ -3058,6 +2934,8 @@ parse_cookies_string_lenient (char *str, | |||
3058 | { | 2934 | { |
3059 | if (! val_quoted) | 2935 | if (! val_quoted) |
3060 | break; | 2936 | break; |
2937 | if (! wsp_in_quoted) | ||
2938 | return MHD_PARSE_COOKIE_MALFORMED; | ||
3061 | non_strict = true; | 2939 | non_strict = true; |
3062 | } | 2940 | } |
3063 | i++; | 2941 | i++; |
@@ -3070,10 +2948,19 @@ parse_cookies_string_lenient (char *str, | |||
3070 | i++; | 2948 | i++; |
3071 | } | 2949 | } |
3072 | /* Skip any whitespaces */ | 2950 | /* Skip any whitespaces */ |
3073 | while (str_len > i && (' ' == str[i] || '\t' == str[i])) | 2951 | if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i]))) |
3074 | { | 2952 | { |
3075 | non_strict = true; | 2953 | do |
3076 | i++; | 2954 | { |
2955 | i++; | ||
2956 | } while (str_len > i && (' ' == str[i] || '\t' == str[i])); | ||
2957 | /* Whitespace at the end? */ | ||
2958 | if (str_len > i) | ||
2959 | { | ||
2960 | if (! allow_wsp_empty) | ||
2961 | return MHD_PARSE_COOKIE_MALFORMED; | ||
2962 | non_strict = true; | ||
2963 | } | ||
3077 | } | 2964 | } |
3078 | if (str_len == i) | 2965 | if (str_len == i) |
3079 | valid_cookie = true; | 2966 | valid_cookie = true; |
@@ -3118,11 +3005,29 @@ parse_cookies_string_lenient (char *str, | |||
3118 | mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len); | 3005 | mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len); |
3119 | i++; | 3006 | i++; |
3120 | if (str_len == i) | 3007 | if (str_len == i) |
3121 | non_strict = true; /* No cookie name after semicolon */ | 3008 | { /* No next cookie after semicolon */ |
3009 | if (! allow_wsp_empty) | ||
3010 | return MHD_PARSE_COOKIE_MALFORMED; | ||
3011 | non_strict = true; | ||
3012 | } | ||
3122 | else if (' ' != str[i]) | 3013 | else if (' ' != str[i]) |
3123 | non_strict = true; /* No space after semicolon */ | 3014 | {/* No space after semicolon */ |
3015 | if (('\t' == str[i]) && tab_as_sp) | ||
3016 | i++; | ||
3017 | else if (! allow_no_space) | ||
3018 | return MHD_PARSE_COOKIE_MALFORMED; | ||
3019 | non_strict = true; | ||
3020 | } | ||
3124 | else | 3021 | else |
3022 | { | ||
3125 | i++; | 3023 | i++; |
3024 | if (str_len == i) | ||
3025 | { | ||
3026 | if (! allow_wsp_empty) | ||
3027 | return MHD_PARSE_COOKIE_MALFORMED; | ||
3028 | non_strict = true; | ||
3029 | } | ||
3030 | } | ||
3126 | } | 3031 | } |
3127 | } | 3032 | } |
3128 | return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; | 3033 | return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; |
@@ -3141,8 +3046,10 @@ parse_cookie_header (struct MHD_Connection *connection) | |||
3141 | const char *hdr; | 3046 | const char *hdr; |
3142 | size_t hdr_len; | 3047 | size_t hdr_len; |
3143 | char *cpy; | 3048 | char *cpy; |
3144 | bool strict_parsing; | ||
3145 | size_t i; | 3049 | size_t i; |
3050 | enum _MHD_ParseCookie parse_res; | ||
3051 | const struct MHD_HTTP_Req_Header *const saved_tail = | ||
3052 | connection->rq.headers_received_tail; | ||
3146 | 3053 | ||
3147 | if (MHD_NO == | 3054 | if (MHD_NO == |
3148 | MHD_lookup_connection_value_n (connection, | 3055 | MHD_lookup_connection_value_n (connection, |
@@ -3159,25 +3066,61 @@ parse_cookie_header (struct MHD_Connection *connection) | |||
3159 | cpy = MHD_connection_alloc_memory_ (connection, | 3066 | cpy = MHD_connection_alloc_memory_ (connection, |
3160 | hdr_len + 1); | 3067 | hdr_len + 1); |
3161 | if (NULL == cpy) | 3068 | if (NULL == cpy) |
3162 | return MHD_PARSE_COOKIE_NO_MEMORY; | 3069 | parse_res = MHD_PARSE_COOKIE_NO_MEMORY; |
3163 | 3070 | else | |
3164 | memcpy (cpy, | 3071 | { |
3165 | hdr, | 3072 | memcpy (cpy, |
3166 | hdr_len); | 3073 | hdr, |
3167 | cpy[hdr_len] = '\0'; | 3074 | hdr_len); |
3075 | cpy[hdr_len] = '\0'; | ||
3076 | |||
3077 | i = 0; | ||
3078 | /* Skip all initial whitespaces */ | ||
3079 | while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i])) | ||
3080 | i++; | ||
3168 | 3081 | ||
3169 | /* TODO: add individual configuration */ | 3082 | parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection); |
3170 | strict_parsing = (0 < connection->daemon->strict_for_client); | 3083 | } |
3171 | i = 0; | ||
3172 | /* Skip all initial whitespaces */ | ||
3173 | while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i])) | ||
3174 | i++; | ||
3175 | 3084 | ||
3176 | /* 'i' points to the first non-whitespace char or to the end of the string */ | 3085 | switch (parse_res) |
3177 | if (strict_parsing) | 3086 | { |
3178 | return parse_cookies_string_strict (cpy + i, hdr_len - i, connection); | 3087 | case MHD_PARSE_COOKIE_OK: |
3088 | break; | ||
3089 | case MHD_PARSE_COOKIE_OK_LAX: | ||
3090 | #ifdef HAVE_MESSAGES | ||
3091 | if (saved_tail != connection->rq.headers_received_tail) | ||
3092 | MHD_DLOG (connection->daemon, | ||
3093 | _ ("The Cookie header has been parsed, but it is not fully " | ||
3094 | "compliant with the standard.\n")); | ||
3095 | #endif /* HAVE_MESSAGES */ | ||
3096 | break; | ||
3097 | case MHD_PARSE_COOKIE_MALFORMED: | ||
3098 | #ifdef HAVE_MESSAGES | ||
3099 | if (saved_tail != connection->rq.headers_received_tail) | ||
3100 | MHD_DLOG (connection->daemon, | ||
3101 | _ ("The Cookie header has been only partially parsed as it " | ||
3102 | "contains malformed data.\n")); | ||
3103 | else | ||
3104 | MHD_DLOG (connection->daemon, | ||
3105 | _ ("The Cookie header has malformed data.\n")); | ||
3106 | #endif /* HAVE_MESSAGES */ | ||
3107 | break; | ||
3108 | case MHD_PARSE_COOKIE_NO_MEMORY: | ||
3109 | #ifdef HAVE_MESSAGES | ||
3110 | MHD_DLOG (connection->daemon, | ||
3111 | _ ("Not enough memory in the connection pool to " | ||
3112 | "parse client cookies!\n")); | ||
3113 | #endif /* HAVE_MESSAGES */ | ||
3114 | break; | ||
3115 | default: | ||
3116 | mhd_assert (0); | ||
3117 | break; | ||
3118 | } | ||
3119 | #ifndef HAVE_MESSAGES | ||
3120 | (void) saved_tail; /* Mute compiler warning */ | ||
3121 | #endif /* ! HAVE_MESSAGES */ | ||
3179 | 3122 | ||
3180 | return parse_cookies_string_lenient (cpy + i, hdr_len - i, connection); | 3123 | return parse_res; |
3181 | } | 3124 | } |
3182 | 3125 | ||
3183 | 3126 | ||
@@ -3946,37 +3889,13 @@ parse_connection_headers (struct MHD_Connection *connection) | |||
3946 | size_t val_len; | 3889 | size_t val_len; |
3947 | 3890 | ||
3948 | #ifdef COOKIE_SUPPORT | 3891 | #ifdef COOKIE_SUPPORT |
3949 | enum _MHD_ParseCookie cookie_res; | 3892 | if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection)) |
3950 | |||
3951 | cookie_res = parse_cookie_header (connection); | ||
3952 | if (MHD_PARSE_COOKIE_NO_MEMORY == cookie_res) | ||
3953 | { | 3893 | { |
3954 | #ifdef HAVE_MESSAGES | ||
3955 | MHD_DLOG (connection->daemon, | ||
3956 | _ ("Not enough memory in pool to parse cookies!\n")); | ||
3957 | #endif | ||
3958 | transmit_error_response_static (connection, | 3894 | transmit_error_response_static (connection, |
3959 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, | 3895 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, |
3960 | REQUEST_TOO_BIG); | 3896 | REQUEST_TOO_BIG); |
3961 | return; | 3897 | return; |
3962 | } | 3898 | } |
3963 | else if (MHD_PARSE_COOKIE_OK_LAX == cookie_res) | ||
3964 | { | ||
3965 | #ifdef HAVE_MESSAGES | ||
3966 | MHD_DLOG (connection->daemon, | ||
3967 | _ ("The Cookie header has been parsed, but is not fully " | ||
3968 | "compliant with the standard.\n")); | ||
3969 | #endif | ||
3970 | (void) 0; /* Mute compiler warning */ | ||
3971 | } | ||
3972 | else if (MHD_PARSE_COOKIE_MALFORMED == cookie_res) | ||
3973 | { | ||
3974 | #ifdef HAVE_MESSAGES | ||
3975 | MHD_DLOG (connection->daemon, | ||
3976 | _ ("The Cookie header has malformed data.\n")); | ||
3977 | #endif | ||
3978 | (void) 0; /* Mute compiler warning */ | ||
3979 | } | ||
3980 | #endif /* COOKIE_SUPPORT */ | 3899 | #endif /* COOKIE_SUPPORT */ |
3981 | if ( (1 <= connection->daemon->strict_for_client) && | 3900 | if ( (1 <= connection->daemon->strict_for_client) && |
3982 | (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) && | 3901 | (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) && |