@@ -117,6 +117,7 @@ static zend_bool check_has_header(const char *headers, const char *header) {
117
117
typedef struct _php_stream_http_response_header_info {
118
118
php_stream_filter * transfer_encoding ;
119
119
size_t file_size ;
120
+ bool error ;
120
121
bool follow_location ;
121
122
char location [HTTP_HEADER_BLOCK_SIZE ];
122
123
} php_stream_http_response_header_info ;
@@ -126,6 +127,7 @@ static void php_stream_http_response_header_info_init(
126
127
{
127
128
header_info -> transfer_encoding = NULL ;
128
129
header_info -> file_size = 0 ;
130
+ header_info -> error = false;
129
131
header_info -> follow_location = 1 ;
130
132
header_info -> location [0 ] = '\0' ;
131
133
}
@@ -163,10 +165,11 @@ static bool php_stream_http_response_header_trim(char *http_header_line,
163
165
/* Process folding headers of the current line and if there are none, parse last full response
164
166
* header line. It returns NULL if the last header is finished, otherwise it returns updated
165
167
* last header line. */
166
- static zend_string * php_stream_http_response_headers_parse (php_stream * stream ,
167
- php_stream_context * context , int options , zend_string * last_header_line_str ,
168
- char * header_line , size_t * header_line_length , int response_code ,
169
- zval * response_header , php_stream_http_response_header_info * header_info )
168
+ static zend_string * php_stream_http_response_headers_parse (php_stream_wrapper * wrapper ,
169
+ php_stream * stream , php_stream_context * context , int options ,
170
+ zend_string * last_header_line_str , char * header_line , size_t * header_line_length ,
171
+ int response_code , zval * response_header ,
172
+ php_stream_http_response_header_info * header_info )
170
173
{
171
174
char * last_header_line = ZSTR_VAL (last_header_line_str );
172
175
size_t last_header_line_length = ZSTR_LEN (last_header_line_str );
@@ -208,6 +211,19 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
208
211
/* Find header separator position. */
209
212
char * last_header_value = memchr (last_header_line , ':' , last_header_line_length );
210
213
if (last_header_value ) {
214
+ /* Verify there is no space in header name */
215
+ char * last_header_name = last_header_line + 1 ;
216
+ while (last_header_name < last_header_value ) {
217
+ if (* last_header_name == ' ' || * last_header_name == '\t' ) {
218
+ header_info -> error = true;
219
+ php_stream_wrapper_log_error (wrapper , options ,
220
+ "HTTP invalid response format (space in header name)!" );
221
+ zend_string_efree (last_header_line_str );
222
+ return NULL ;
223
+ }
224
+ ++ last_header_name ;
225
+ }
226
+
211
227
last_header_value ++ ; /* Skip ':'. */
212
228
213
229
/* Strip leading whitespace. */
@@ -216,9 +232,12 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
216
232
last_header_value ++ ;
217
233
}
218
234
} else {
219
- /* There is no colon. Set the value to the end of the header line, which is effectively
220
- * an empty string. */
221
- last_header_value = last_header_line_end ;
235
+ /* There is no colon which means invalid response so error. */
236
+ header_info -> error = true;
237
+ php_stream_wrapper_log_error (wrapper , options ,
238
+ "HTTP invalid response format (no colon in header line)!" );
239
+ zend_string_efree (last_header_line_str );
240
+ return NULL ;
222
241
}
223
242
224
243
bool store_header = true;
@@ -928,10 +947,16 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
928
947
929
948
if (last_header_line_str != NULL ) {
930
949
/* Parse last header line. */
931
- last_header_line_str = php_stream_http_response_headers_parse (stream , context ,
932
- options , last_header_line_str , http_header_line , & http_header_line_length ,
933
- response_code , response_header , & header_info );
934
- if (last_header_line_str != NULL ) {
950
+ last_header_line_str = php_stream_http_response_headers_parse (wrapper , stream ,
951
+ context , options , last_header_line_str , http_header_line ,
952
+ & http_header_line_length , response_code , response_header , & header_info );
953
+ if (EXPECTED (last_header_line_str == NULL )) {
954
+ if (UNEXPECTED (header_info .error )) {
955
+ php_stream_close (stream );
956
+ stream = NULL ;
957
+ goto out ;
958
+ }
959
+ } else {
935
960
/* Folding header present so continue. */
936
961
continue ;
937
962
}
@@ -961,8 +986,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
961
986
962
987
/* If the stream was closed early, we still want to process the last line to keep BC. */
963
988
if (last_header_line_str != NULL ) {
964
- php_stream_http_response_headers_parse (stream , context , options , last_header_line_str ,
965
- NULL , NULL , response_code , response_header , & header_info );
989
+ php_stream_http_response_headers_parse (wrapper , stream , context , options ,
990
+ last_header_line_str , NULL , NULL , response_code , response_header , & header_info );
966
991
}
967
992
968
993
if (!reqok || (header_info .location [0 ] != '\0' && header_info .follow_location )) {
0 commit comments