- 本文地址: https://www.laruence.com/2008/11/07/586.html
- 转载请注明出处
今天研究PHP注册POST/GET大变量的时候,看到了关于这块的一些东西,跟踪了半天,,先记录下来,免得以后再如此麻烦的跟踪
处理器注册:
[mod_php5.c, mod_php5模块初始化] php_init_handler(server_rec *s, pool *p) ->[main/SAPI.c]sapi_startup(&apache_sapi_module) ->[main/SAPI.c] sapi_globals_ctor(&sapi_globals) ->[main/php_content_types.c]php_setup_sapi_content_types(TSRMLS_C) ->[main/php_content_types.c php_post_entries如下]sapi_register_post_entries(php_post_entries TSRMLS_CC) ->[main/SAPI.c]sapi_register_post_entry(p TSRMLS_CC)
如下面的代码,共注册了俩个处理器,分别处理post数据和文件上传。
注1:G(http_globals)[TRACK_VARS_COOKIE]这部分,可以参看我较早前的 在PHP Module中获取$_GET/$_POST/$_COOKIE的方法研究
[main/rfc1867.h] #define MULTIPART_CONTENT_TYPE "multipart/form-data" [main/php_content_types.h] #define DEFAULT_POST_CONTENT_TYPE "application/x-www-form-urlencoded" [main/SAPI.c] struct _sapi_post_entry { char *content_type; uint content_type_len; void (*post_reader)(TSRMLS_D); void (*post_handler)(char *content_type_dup, void *arg TSRMLS_DC); }; [main/php_content_types.c] static sapi_post_entry php_post_entries[] = { { DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1, sapi_read_standard_form_data, php_std_post_handler }, { MULTIPART_CONTENT_TYPE, sizeof(MULTIPART_CONTENT_TYPE)-1, NULL, rfc1867_post_handler }, { NULL, 0, NULL, NULL } };
那么对于rfc1867_post_handler函数来说,我罗列出源码如下, 加了我的注释.
SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) { char *boundary, *s=NULL, *boundary_end = NULL, *start_arr=NULL, *array_index=NULL; char *temp_filename=NULL, *lbuf=NULL, *abuf=NULL; int boundary_len=0, total_bytes=0, cancel_upload=0, is_arr_upload=0, array_len=0; int max_file_size=0, skip_upload=0, anonindex=0, is_anonymous; zval *http_post_files=NULL; HashTable *uploaded_files=NULL; #if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) int str_len = 0, num_vars = 0, num_vars_max = 2*10, *len_list = NULL; char **val_list = NULL; #endif zend_bool magic_quotes_gpc; multipart_buffer *mbuff; zval *array_ptr = (zval *) arg; int fd=-1; zend_llist header; void *event_extra_data = NULL; int llen = 0; //检查是否超出最大上传文件大小 if (SG(request_info).content_length > SG(post_max_size)) { sapi_module.sapi_error(E_WARNING, "POST Content-Length of %ld bytes exceeds the limit of %ld bytes", SG(request_info).content_length, SG(post_max_size)); return; } //取得上传文件的分隔符 boundary = strstr(content_type_dup, "boundary"); if (!boundary || !(boundary=strchr(boundary, '='))) { sapi_module.sapi_error(E_WARNING, "Missing boundary in multipart/form-data POST data"); return; } boundary++; boundary_len = strlen(boundary); if (boundary[0] == '"') { boundary++; boundary_end = strchr(boundary, '"'); if (!boundary_end) { sapi_module.sapi_error(E_WARNING, "Invalid boundary in multipart/form-data POST data"); return; } } else { /* search for the end of the boundary */ boundary_end = strchr(boundary, ','); } if (boundary_end) { boundary_end[0] = ''; boundary_len = boundary_end-boundary; } /* Initialize the buffer */ if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) { sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer"); return; } //初始化$_FILE变量 zend_hash_init(&PG(rfc1867_protected_variables), 5, NULL, NULL, 0); ALLOC_HASHTABLE(uploaded_files); zend_hash_init(uploaded_files, 5, NULL, (dtor_func_t) free_estring, 0); SG(rfc1867_uploaded_files) = uploaded_files; ALLOC_ZVAL(http_post_files); array_init(http_post_files); INIT_PZVAL(http_post_files); PG(http_globals)[TRACK_VARS_FILES] = http_post_files; //TRACK_VARS_FILE正是_FILE在php_core_globals.http_globals中的index (注1) #if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) if (php_mb_encoding_translation(TSRMLS_C)) { val_list = (char **)ecalloc(num_vars_max+2, sizeof(char *)); len_list = (int *)ecalloc(num_vars_max+2, sizeof(int)); } #endif zend_llist_init(&header, sizeof(mime_header_entry), (llist_dtor_func_t) php_free_hdr_entry, 0); if (php_rfc1867_callback != NULL) { multipart_event_start event_start; event_start.content_length = SG(request_info).content_length; if (php_rfc1867_callback(MULTIPART_EVENT_START, &event_start, &event_extra_data TSRMLS_CC) == FAILURE) { goto fileupload_done; } } while (!multipart_buffer_eof(mbuff TSRMLS_CC)) { char buff[FILLUNIT]; char *cd=NULL,*param=NULL,*filename=NULL, *tmp=NULL; size_t blen=0, wlen=0; off_t offset; zend_llist_clean(&header); if (!multipart_buffer_headers(mbuff, &header TSRMLS_CC)) { goto fileupload_done; } if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) { char *pair=NULL; int end=0; while (isspace(*cd)) { ++cd; } while (*cd && (pair = php_ap_getword(&cd, ';'))) { char *key=NULL, *word = pair; while (isspace(*cd)) { ++cd; } if (strchr(pair, '=')) { key = php_ap_getword(&pair, '='); if (!strcasecmp(key, "name")) { if (param) { efree(param); } param = php_ap_getword_conf(&pair TSRMLS_CC); } else if (!strcasecmp(key, "filename")) { if (filename) { efree(filename); } filename = php_ap_getword_conf(&pair TSRMLS_CC); } } if (key) { efree(key); } efree(word); } /* Normal form variable, safe to read all data into memory */ if (!filename && param) { unsigned int value_len; char *value = multipart_buffer_read_body(mbuff, &value_len TSRMLS_CC); unsigned int new_val_len; /* Dummy variable */ if (!value) { value = estrdup(""); } if (sapi_module.input_filter(PARSE_POST, param, &value, value_len, &new_val_len TSRMLS_CC)) { if (php_rfc1867_callback != NULL) { multipart_event_formdata event_formdata; size_t newlength = 0; event_formdata.post_bytes_processed = SG(read_post_bytes); event_formdata.name = param; event_formdata.value = &value; event_formdata.length = new_val_len; event_formdata.newlength = &newlength; if (php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data TSRMLS_CC) == FAILURE) { efree(param); efree(value); continue; } new_val_len = newlength; } #if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) if (php_mb_encoding_translation(TSRMLS_C)) { php_mb_gpc_stack_variable(param, value, &val_list, &len_list, &num_vars, &num_vars_max TSRMLS_CC); } else { safe_php_register_variable(param, value, new_val_len, array_ptr, 0 TSRMLS_CC); } #else safe_php_register_variable(param, value, new_val_len, array_ptr, 0 TSRMLS_CC); #endif } else if (php_rfc1867_callback != NULL) { multipart_event_formdata event_formdata; event_formdata.post_bytes_processed = SG(read_post_bytes); event_formdata.name = param; event_formdata.value = &value; event_formdata.length = value_len; event_formdata.newlength = NULL; php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data TSRMLS_CC); } if (!strcasecmp(param, "MAX_FILE_SIZE")) { max_file_size = atol(value); } efree(param); efree(value); continue; } /* If file_uploads=off, skip the file part */ if (!PG(file_uploads)) { skip_upload = 1; } /* Return with an error if the posted data is garbled */ if (!param && !filename) { sapi_module.sapi_error(E_WARNING, "File Upload Mime headers garbled"); goto fileupload_done; } if (!param) { is_anonymous = 1; param = emalloc(MAX_SIZE_ANONNAME); snprintf(param, MAX_SIZE_ANONNAME, "%u", anonindex++); } else { is_anonymous = 0; } /* New Rule: never repair potential malicious user input */ if (!skip_upload) { char *tmp = param; long c = 0; while (*tmp) { if (*tmp == '[') { c++; } else if (*tmp == ']') { c--; if (tmp[1] && tmp[1] != '[') { skip_upload = 1; break; } } if (c < 0) { skip_upload = 1; break; } tmp++; } } total_bytes = cancel_upload = 0; if (!skip_upload) { /* Handle file */ fd = php_open_temporary_fd_ex(PG(upload_tmp_dir), "php", &temp_filename, 1 TSRMLS_CC); if (fd==-1) { sapi_module.sapi_error(E_WARNING, "File upload error - unable to create a temporary file"); cancel_upload = UPLOAD_ERROR_E; } } if (!skip_upload && php_rfc1867_callback != NULL) { multipart_event_file_start event_file_start; event_file_start.post_bytes_processed = SG(read_post_bytes); event_file_start.name = param; event_file_start.filename = &filename; if (php_rfc1867_callback(MULTIPART_EVENT_FILE_START, &event_file_start, &event_extra_data TSRMLS_CC) == FAILURE) { if (temp_filename) { if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */ close(fd); unlink(temp_filename); } efree(temp_filename); } temp_filename=""; efree(param); efree(filename); continue; } } if (skip_upload) { efree(param); efree(filename); continue; } if(strlen(filename) == 0) { #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "No file uploaded"); #endif cancel_upload = UPLOAD_ERROR_D; } offset = 0; end = 0; while (!cancel_upload && (blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end TSRMLS_CC))) { if (php_rfc1867_callback != NULL) { multipart_event_file_data event_file_data; event_file_data.post_bytes_processed = SG(read_post_bytes); event_file_data.offset = offset; event_file_data.data = buff; event_file_data.length = blen; event_file_data.newlength = &blen; if (php_rfc1867_callback(MULTIPART_EVENT_FILE_DATA, &event_file_data, &event_extra_data TSRMLS_CC) == FAILURE) { cancel_upload = UPLOAD_ERROR_X; continue; } } if (PG(upload_max_filesize) > 0 && total_bytes > PG(upload_max_filesize)) { #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of %ld bytes exceeded - file [%s=%s] not saved", PG(upload_max_filesize), param, filename); #endif cancel_upload = UPLOAD_ERROR_A; } else if (max_file_size && (total_bytes > max_file_size)) { #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "MAX_FILE_SIZE of %ld bytes exceeded - file [%s=%s] not saved", max_file_size, param, filename); #endif cancel_upload = UPLOAD_ERROR_B; } else if (blen > 0) { wlen = write(fd, buff, blen); if (wlen == -1) { /* write failed */ #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno)); #endif cancel_upload = UPLOAD_ERROR_F; } else if (wlen < blen) { #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "Only %d bytes were written, expected to write %d", wlen, blen); #endif cancel_upload = UPLOAD_ERROR_F; } else { total_bytes += wlen; } offset += wlen; } } if (fd!=-1) { /* may not be initialized if file could not be created */ close(fd); } if (!cancel_upload && !end) { #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "Missing mime boundary at the end of the data for file %s", strlen(filename) > 0 ? filename : ""); #endif cancel_upload = UPLOAD_ERROR_C; } #if DEBUG_FILE_UPLOAD if(strlen(filename) > 0 && total_bytes == 0 && !cancel_upload) { sapi_module.sapi_error(E_WARNING, "Uploaded file size 0 - file [%s=%s] not saved", param, filename); cancel_upload = 5; } #endif if (php_rfc1867_callback != NULL) { multipart_event_file_end event_file_end; event_file_end.post_bytes_processed = SG(read_post_bytes); event_file_end.temp_filename = temp_filename; event_file_end.cancel_upload = cancel_upload; if (php_rfc1867_callback(MULTIPART_EVENT_FILE_END, &event_file_end, &event_extra_data TSRMLS_CC) == FAILURE) { cancel_upload = UPLOAD_ERROR_X; } } if (cancel_upload) { if (temp_filename) { if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */ unlink(temp_filename); } efree(temp_filename); } temp_filename=""; } else { zend_hash_add(SG(rfc1867_uploaded_files), temp_filename, strlen(temp_filename) + 1, &temp_filename, sizeof(char *), NULL); } /* is_arr_upload is true when name of file upload field * ends in [.*] * start_arr is set to point to 1st [ */ is_arr_upload = (start_arr = strchr(param,'[')) && (param[strlen(param)-1] == ']'); if (is_arr_upload) { array_len = strlen(start_arr); if (array_index) { efree(array_index); } array_index = estrndup(start_arr+1, array_len-2); } /* Add $foo_name */ if (llen < strlen(param) + MAX_SIZE_OF_INDEX + 1) { llen = strlen(param); lbuf = (char *) safe_erealloc(lbuf, llen, 1, MAX_SIZE_OF_INDEX + 1); llen += MAX_SIZE_OF_INDEX + 1; } if (is_arr_upload) { if (abuf) efree(abuf); abuf = estrndup(param, strlen(param)-array_len); snprintf(lbuf, llen, "%s_name[%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s_name", param); } #if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) if (php_mb_encoding_translation(TSRMLS_C)) { if (num_vars>=num_vars_max){ php_mb_gpc_realloc_buffer(&val_list, &len_list, &num_vars_max, 1 TSRMLS_CC); } val_list[num_vars] = filename; len_list[num_vars] = strlen(filename); num_vars++; if(php_mb_gpc_encoding_detector(val_list, len_list, num_vars, NULL TSRMLS_CC) == SUCCESS) { str_len = strlen(filename); php_mb_gpc_encoding_converter(&filename, &str_len, 1, NULL, NULL TSRMLS_CC); } s = php_mb_strrchr(filename, '\' TSRMLS_CC); if ((tmp = php_mb_strrchr(filename, '/' TSRMLS_CC)) > s) { s = tmp; } num_vars--; goto filedone; } #endif /* The check should technically be needed for win32 systems only where * it is a valid path separator. However, IE in all it's wisdom always sends * the full path of the file on the user's filesystem, which means that unless * the user does basename() they get a bogus file name. Until IE's user base drops * to nill or problem is fixed this code must remain enabled for all systems. */ s = strrchr(filename, '\'); if ((tmp = strrchr(filename, '/')) > s) { s = tmp; } #ifdef PHP_WIN32 if (PG(magic_quotes_gpc)) { s = s ? s : filename; tmp = strrchr(s, '''); s = tmp > s ? tmp : s; tmp = strrchr(s, '"'); s = tmp > s ? tmp : s; } #endif #if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) filedone: #endif if (!is_anonymous) { if (s && s > filename) { safe_php_register_variable(lbuf, s+1, strlen(s+1), NULL, 0 TSRMLS_CC); } else { safe_php_register_variable(lbuf, filename, strlen(filename), NULL, 0 TSRMLS_CC); } } /* Add $foo[name] */ if (is_arr_upload) { snprintf(lbuf, llen, "%s[name][%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s[name]", param); } if (s && s > filename) { register_http_post_files_variable(lbuf, s+1, http_post_files, 0 TSRMLS_CC); } else { register_http_post_files_variable(lbuf, filename, http_post_files, 0 TSRMLS_CC); } efree(filename); s = NULL; /* Possible Content-Type: */ if (cancel_upload || !(cd = php_mime_get_hdr_value(header, "Content-Type"))) { cd = ""; } else { /* fix for Opera 6.01 */ s = strchr(cd, ';'); if (s != NULL) { *s = ''; } } /* Add $foo_type */ if (is_arr_upload) { snprintf(lbuf, llen, "%s_type[%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s_type", param); } if (!is_anonymous) { safe_php_register_variable(lbuf, cd, strlen(cd), NULL, 0 TSRMLS_CC); } /* Add $foo[type] */ if (is_arr_upload) { snprintf(lbuf, llen, "%s[type][%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s[type]", param); } register_http_post_files_variable(lbuf, cd, http_post_files, 0 TSRMLS_CC); /* Restore Content-Type Header */ if (s != NULL) { *s = ';'; } s = ""; /* Initialize variables */ add_protected_variable(param TSRMLS_CC); magic_quotes_gpc = PG(magic_quotes_gpc); PG(magic_quotes_gpc) = 0; /* if param is of form xxx[.*] this will cut it to xxx */ if (!is_anonymous) { safe_php_register_variable(param, temp_filename, strlen(temp_filename), NULL, 1 TSRMLS_CC); } /* Add $foo[tmp_name] */ if (is_arr_upload) { snprintf(lbuf, llen, "%s[tmp_name][%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s[tmp_name]", param); } add_protected_variable(lbuf TSRMLS_CC); register_http_post_files_variable(lbuf, temp_filename, http_post_files, 1 TSRMLS_CC); PG(magic_quotes_gpc) = magic_quotes_gpc; { zval file_size, error_type; error_type.value.lval = cancel_upload; error_type.type = IS_LONG; /* Add $foo[error] */ if (cancel_upload) { file_size.value.lval = 0; file_size.type = IS_LONG; } else { file_size.value.lval = total_bytes; file_size.type = IS_LONG; } if (is_arr_upload) { snprintf(lbuf, llen, "%s[error][%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s[error]", param); } register_http_post_files_variable_ex(lbuf, &error_type, http_post_files, 0 TSRMLS_CC); /* Add $foo_size */ if (is_arr_upload) { snprintf(lbuf, llen, "%s_size[%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s_size", param); } if (!is_anonymous) { safe_php_register_variable_ex(lbuf, &file_size, NULL, 0 TSRMLS_CC); } /* Add $foo[size] */ if (is_arr_upload) { snprintf(lbuf, llen, "%s[size][%s]", abuf, array_index); } else { snprintf(lbuf, llen, "%s[size]", param); } register_http_post_files_variable_ex(lbuf, &file_size, http_post_files, 0 TSRMLS_CC); } efree(param); } } fileupload_done: if (php_rfc1867_callback != NULL) { multipart_event_end event_end; event_end.post_bytes_processed = SG(read_post_bytes); php_rfc1867_callback(MULTIPART_EVENT_END, &event_end, &event_extra_data TSRMLS_CC); } SAFE_RETURN; }
[…] 深入理解PHP原理之文件上传 | 风雪之隅 […]
[…] 可以在mian/rfc1867.c找到, 另外也可以参看我之前的深入理解PHP之文件上传, […]
[…] 可以在mian/rfc1867.c找到, 另外也可以参看我之前的深入理解PHP之文件上传, […]
[…] 关于php_rfc1867_callback是什么,可以看哥学社另一哥鸟哥的分析http://www.laruence.com/2008/11/07/586.html三.总结 在探测临时文件大小的时候,APC和 uploadprogress其实是大同小异的方法,先记录,再取大小百分比。 关于哥学社:哥学社是一个自由博客组织,以提高原创技术博客质量,为成员博客增加更有效评论,由来自腾讯、百度、盛大、人人网、新浪、奇虎、TOM等各大企业的哥们共同起草发起。 关于五四陈: 54chen(陈臻),人人网分布式存储研究人员,业余时间混迹于各技术组织且乐此不疲。目前关注实施PHP培训。对flex等前端技术有一点研究。 个人技术站点:http://www.54chen.com/ 。可以通过电子邮件czhttp@gmail.com 联系到他。 本文提及的文章:http://www.php.net/releases/5_2_0.phphttp://www.ibm.com/developerworks/cn/opensource/os-php-v525/http://www.laruence.com/2008/11/07/586.html […]
[…] 深入理解PHP原理之文件上传 […]
[…] 关于php_rfc1867_callback是什么,可以看哥学社另一哥鸟哥的分析 http://www.laruence.com/2008/11/07/586.html […]
[…] 可以在mian/rfc1867.c找到, 另外也可以参看我之前的深入理解PHP之文件上传, […]
[…] 可以在mian/rfc1867.c找到, 另外也可以参看我之前的深入理解PHP之文件上传, […]
[…] 可以在mian/rfc1867.c找到, 另外也可以参看我之前的深入理解PHP之文件上传, […]
[…] 关于php_rfc1867_callback是什么,可以看哥学社另一哥鸟哥的分析 http://www.laruence.com/2008/11/07/586.html […]
[…] 关于php_rfc1867_callback是什么,可以看哥学社另一哥鸟哥的分析 http://www.laruence.com/2008/11/07/586.html […]
新来这2篇文章很喜欢,尤其是文件上传,曾经研究过文件上传流程,没有你这么细,要检讨啦~~
呵呵,那就好, 只是现在还只是整理出来个大体框架,最好能有问题驱动, 才能找到一些可以被应用的features,
就是你用什么工具跟踪的,我对这个比较感兴趣
grep ,vim
鸟哥同学,能不能介绍下你是用的啥方法跟踪的?或者说在分析调试的时候用的啥方法?
大胆猜测,小心求证
呵呵.
在这个求证的过程中是不是一遍遍的修改编译调试,最后得到结果?
一般不需要,除非是实在通过猜测流程走不下去了才需要这样.