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