zoukankan      html  css  js  c++  java
  • 解决boa网页操作出现502 Bad Gateway The CGI was not CGI/1.1 compliant的一种可能

    最近在把一套网页操作的接口从原来Android5.0上移植到Android7.0上。

    客户端连接验证的时候主页显示异常

    502 Bad Gateway The CGI was not CGI/1.1 compliant

    从板子的串口上看到log显示为

    [02/Jan/1970:17:13:49 +0000] cgi_header: unable to find LFLF

    这部分log为boa里面打印出来的,跟踪源码在cgi_header.c关键函数如下

                                                                                  
      45 /* TODO:                                                                      
      46  We still need to cycle through the data before the end of the headers,       
      47  line-by-line, and check for any problems with the CGI                        
      48  outputting overriding http responses, etc...                                 
      49  */                                                                           
      50                                                                               
      51 int process_cgi_header(request * req)                                         
      52 {                                                                             
      53     char *buf;                                                                
      54     char *c;                                                                  
      55                                                                               
      56     if (req->cgi_status != CGI_DONE)                                          
      57         req->cgi_status = CGI_BUFFER;                                         
      58                                                                               
      59     buf = req->header_line;                                                   
      60                                                                               
      61     c = strstr(buf, "
    
    ");                                                
      62     if (c == NULL) {                                                          
      63         c = strstr(buf, "
    
    ");                                              
      64         if (c == NULL) {                                                      
      65             log_error_time();                                                 
      66             fputs("cgi_header: unable to find LFLF
    ", stderr);               
      67 #ifdef FASCIST_LOGGING                                                        
      68             log_error_time();                                                 
      69             fprintf(stderr, ""%s"
    ", buf);                                 
      70 #endif                                                                        
      71             send_r_bad_gateway(req);                                          
      72             return 0;                                                         
      73         }                                                                     
      74     }                                                                         
      75     if (req->simple) {                                                        
      76         if (*(c + 1) == '
    ')                                                 
      77             req->header_line = c + 2;                                         
      78         else                                                                  
      79             req->header_line = c + 1;                                         
      80         return 1;                                                             
      81     }            

    该函数

    process_cgi_header
    是boa父进程fork出子进去去执行对应的cgi程序后,通过管道来获取cgi执行的输出内容进行解析,从而对客户端进行相应。
    在src/pipe.c调用如下:
                                                                                   
      27 /*                                                                            
      28  * Name: read_from_pipe                                                       
      29  * Description: Reads data from a pipe                                        
      30  *                                                                            
      31  * Return values:                                                             
      32  *  -1: request blocked, move to blocked queue                                
      33  *   0: EOF or error, close it down                                           
      34  *   1: successful read, recycle in ready queue                               
      35  */                                                                           
      36                                                                               
      37 int read_from_pipe(request * req)                                             
      38 {                                                                             
      39     int bytes_read, bytes_to_read =                                           
      40         BUFFER_SIZE - (req->header_end - req->buffer);                        
      41                                                                               
      42     if (bytes_to_read == 0) {   /* buffer full */                             
      43         if (req->cgi_status == CGI_PARSE) { /* got+parsed header */           
      44             req->cgi_status = CGI_BUFFER;                                     
      45             *req->header_end = ''; /* points to end of read data */         
      46             /* Could the above statement overwrite data???                    
      47                No, because req->header_end points to where new data           
      48                should begin, not where old data is.                           
      49              */                                                               
      50             return process_cgi_header(req); /* cgi_status will change */      
      51         }                                                                     
      52         req->status = PIPE_WRITE;                                             
      53         return 1;                                                             
      54     }                                                                         
      55                                                                                 
      56     bytes_read = read(req->data_fd, req->header_end, bytes_to_read);          
      57 #ifdef FASCIST_LOGGING                                                        
      58     if (bytes_read > 0) {                                                     
      59         *(req->header_end + bytes_read) = '';                               
      60         fprintf(stderr, "pipe.c - read %d bytes: "%s"
    ",                   
      61                 bytes_read, req->header_end);                                 
      62     } else                                                                    
      63         fprintf(stderr, "pipe.c - read %d bytes
    ", bytes_read);              
      64     fprintf(stderr, "status, cgi_status: %d, %d
    ", req->status,              
      65             req->cgi_status);                                                 
      66 #endif                                                                        
      67                                                                               
      68     if (bytes_read == -1) {                                                   
      69         if (errno == EINTR)                                                   
      70             return 1;                                                         
      71         else if (errno == EWOULDBLOCK || errno == EAGAIN)                     
      72             return -1;          /* request blocked at the pipe level, but keep going */
      73         else {                                                                
      74         req->status = DEAD;                                                   
      75             log_error_doc(req);                                               
      76             perror("pipe read");                                              
      77             return 0;                                                         
      78         }                                                                     
      79     } else if (bytes_read == 0) { /* eof, write rest of buffer */             
      80         req->status = PIPE_WRITE;                                             
      81         if (req->cgi_status == CGI_PARSE) { /* hasn't processed header yet */ 
      82             req->cgi_status = CGI_DONE;                                       
      83             *req->header_end = ''; /* points to end of read data */         
      84             return process_cgi_header(req); /* cgi_status will change */      
      85         }                                                                     
      86         req->cgi_status = CGI_DONE;                                           
      87         return 1;                                                             
      88     }                                                                         
      89     req->header_end += bytes_read;                                            
      90     return 1;                                                                 
      91 }                                                                             
      92                                                                                        

    这部分代码中有一个宏

    FASCIST_LOGGING

    加上这个宏重新编译验证可以看到更信息的log

    [02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Host: 192.168.49.1"
    [02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Upgrade-Insecure-Requests: 1"
    [02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
    [02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1"
    [02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Accept-Language: zh-cn"
    [02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Accept-Encoding: gzip, deflate"
    [02/Jan/1970:17:13:49 +0000] external/boa/src/request.c:662 - Parsing "Connection: keep-alive"
    external/boa/src/alias.c:150 - comparing "/cgi-bin/home.cgi" (request) to "/cgi-bin/" (alias): Got it!
    [02/Jan/1970:17:13:49 +0000] external/boa/src/alias.c:414 - pathname in init_script_alias is: "/data/boa/www/cgi-bin/home.cgi" ("home.cgi")
    external/boa/src/cgi.c - environment variable for cgi: "PATH=/bin:/usr/bin:/usr/local/bin"
    external/boa/src/cgi.c - environment variable for cgi: "SERVER_SOFTWARE=Boa/0.94.13"
    external/boa/src/cgi.c - environment variable for cgi: "SERVER_NAME=www.lollipop.com"
    external/boa/src/cgi.c - environment variable for cgi: "GATEWAY_INTERFACE=CGI/1.1"
    external/boa/src/cgi.c - environment variable for cgi: "SERVER_PORT=80"
    external/boa/src/cgi.c - environment variable for cgi: "SERVER_ADMIN="
    external/boa/src/cgi.c - environment variable for cgi: "HTTP_HOST=192.168.49.1"
    external/boa/src/cgi.c - environment variable for cgi: "HTTP_UPGRADE_INSECURE_REQUESTS=1"
    external/boa/src/cgi.c - environment variable for cgi: "HTTP_USER_AGENT=Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1"
    external/boa/src/cgi.c - environment variable for cgi: "HTTP_ACCEPT_LANGUAGE=zh-cn"
    external/boa/src/cgi.c - environment variable for cgi: "HTTP_ACCEPT_ENCODING=gzip, deflate"
    external/boa/src/cgi.c - environment variable for cgi: "REQUEST_METHOD=GET"
    external/boa/src/cgi.c - environment variable for cgi: "SERVER_ADDR=192.168.49.1"
    external/boa/src/cgi.c - environment variable for cgi: "SERVER_PROTOCOL=HTTP/1.1"
    external/boa/src/cgi.c - environment variable for cgi: "REQUEST_URI=/cgi-bin/home.cgi"
    external/boa/src/cgi.c - environment variable for cgi: "SCRIPT_NAME=/cgi-bin/home.cgi"
    external/boa/src/cgi.c - environment variable for cgi: "REMOTE_ADDR=192.168.49.77"
    external/boa/src/cgi.c - environment variable for cgi: "REMOTE_PORT=54608"
    pipe.c - read -1 bytes
    status, cgi_status: 7, 1
    pipe.c - read 0 bytes
    status, cgi_status: 7, 1
    [02/Jan/1970:17:13:49 +0000] cgi_header: unable to find LFLF
    [02/Jan/1970:17:13:49 +0000] ""
    [02/Jan/1970:17:13:49 +0000] external/boa/src/buffer.c:200 - Wrote "HTTP/1.0 502 Bad Gateway
    Date: Fri, 02 Jan 1970 17:13:49 GMT
    Server: Boa/0.94.13
    Connection: close
    Content-Type: text/html; charset=ISO-8859-1
    
    <HTML><HEAD><TITLE>502 Bad Gateway</TITLE></HEAD>
    <BODY><H1>502 Bad Gateway</H1>
    The CGI was not CGI/1.1 compliant.
    </BODY></HTML>
    " (281 bytes)

    从log上看出读取pipe的时候第一次读取失败返回来-1,第二次读取到0 说明管道里没有读取到东西,可能是cgi没有往管道里写东西。

    查看boa是如何执行对应的cgi并从cgi获取输出信息的

    在cgi.c里面有如下代码

                                                                                  
     346 /*                                                                            
     347  * Name: init_cgi                                                             
     348  *                                                                            
     349  * Description: Called for GET/POST requests that refer to ScriptAlias        
     350  * directories or application/x-httpd-cgi files.  Ties stdout to socket,      
     351  * stdin to data if POST, and execs CGI.                                      
     352  * stderr remains tied to our log file; is this good?                         
     353  *                                                                            
     354  * Returns:                                                                   
     355  * 0 - error or NPH, either way the socket is closed                          
     356  * 1 - success                                                                
     357  */                                                                           
     358                                                                               
     359 int init_cgi(request * req)                                                   
     360 {                                                                             
     361     int child_pid;                                                            
     362     int pipes[2];                                                             
     363     int use_pipes = 0;                                                        
     364                                                                               
     365     SQUASH_KA(req);                                                           
     366                                                                               
     367     if (req->is_cgi) {                                                        
     368         if (complete_env(req) == 0) {                                         
     369             return 0;                                                         
     370         }                                                                     
     371     }                                                                         
     372 #ifdef FASCIST_LOGGING                                                        
     373     {                                                                         
     374         int i;                                                                
     375         for (i = 0; i < req->cgi_env_index; ++i)                              
     376             fprintf(stderr, "%s - environment variable for cgi: "%s"
    ",    
     377                     __FILE__, req->cgi_env[i]);                               
     378     }                                                                         
     379 #endif                                                                        
     380            
     381   if (req->is_cgi == CGI || 1) {                                            
     382         use_pipes = 1;                                                        
     383         if (pipe(pipes) == -1) {                                              
     384             log_error_time();                                                 
     385             perror("pipe");                                                   
     386             return 0;                                                         
     387         }                                                                     
     388                                                                               
     389         /* set the read end of the socket to non-blocking */                  
     390         if (set_nonblock_fd(pipes[0]) == -1) {                                
     391             log_error_time();                                                 
     392             perror("cgi-fcntl");                                              
     393             close(pipes[0]);                                                  
     394             close(pipes[1]);                                                  
     395             return 0;                                                         
     396         }                                                                     
     397     }                                                                         
     398                                                                               
     399     child_pid = fork();                                                       
     400     switch(child_pid) {                                                       
     401     case -1:                                                                  
     402         /* fork unsuccessful */                                               
     403         log_error_time();                                                     
     404         perror("fork");                                                       
     405                                                                               
     406         if (use_pipes) {                                                      
     407             close(pipes[0]);                                                  
     408             close(pipes[1]);                                                  
     409         }                                                                     
     410         send_r_error(req);                                                    
     411         /* FIXME: There is aproblem here. send_r_error would work             
     412            for NPH and CGI, but not for GUNZIP.  Fix that. */                 
     413         /* i'd like to send_r_error, but.... */                               
     414         return 0;                                                             
     415         break;                                                                        
     416     case 0:                                                                   
     417         /* child */                                                           
     418         if (req->is_cgi == CGI || req->is_cgi == NPH) {                       
     419             char *foo = strdup(req->pathname);                                
     420             char *c;                                                          
     421                                                                               
     422             if (!foo) {                                                       
     423                 WARN("unable to strdup pathname for req->pathname");          
     424                 _exit(1);                                                     
     425             }                                                                 
     426             c = strrchr(foo, '/');                                            
     427             if (c) {                                                          
     428                 ++c;                                                          
     429                 *c = '';                                                    
     430             } else {                                                          
     431                 /* we have a serious problem */                               
     432                 log_error_time();                                             
     433                 perror("chdir");                                              
     434                 if (use_pipes)                                                
     435                     close(pipes[1]);                                          
     436                 _exit(1);                                                     
     437             }                                                                 
     438             if (chdir(foo) != 0) {                                            
     439                 log_error_time();                                             
     440                 perror("chdir");                                              
     441                 if (use_pipes)                                                
     442                     close(pipes[1]);                                          
     443                 _exit(1);                                                     
     444             }                                                                 
     445         }                                                                     
     446         if (use_pipes) {                                                      
     447             close(pipes[0]);                                                  
     448             /* tie cgi's STDOUT to it's write end of pipe */                  
     449             if (dup2(pipes[1], STDOUT_FILENO) == -1) {                        
     450                 log_error_time();                                             
     451                 perror("dup2 - pipes");                                       
     452                 close(pipes[1]);                                              
     453                 _exit(1);                                                           
     454             }                                                                 
     455             close(pipes[1]);                                                  
     456             if (set_block_fd(STDOUT_FILENO) == -1) {                          
     457                 log_error_time();                                             
     458                 perror("cgi-fcntl");                                          
     459                 _exit(1);                                                     
     460             }                                                                 
     461         } else {                                                              
     462             /* tie stdout to socket */                                        
     463             if (dup2(req->fd, STDOUT_FILENO) == -1) {                         
     464                 log_error_time();                                             
     465                 perror("dup2 - fd");                                          
     466                 _exit(1);                                                     
     467             }                                                                 
     468             /* Switch socket flags back to blocking */                        
     469             if (set_block_fd(req->fd) == -1) {                                
     470                 log_error_time();                                             
     471                 perror("cgi-fcntl");                                          
     472                 _exit(1);                                                     
     473             }                                                                 
     474         }                                                                     
     475         /* tie post_data_fd to POST stdin */                                  
     476         if (req->method == M_POST) { /* tie stdin to file */                  
     477             lseek(req->post_data_fd, SEEK_SET, 0);                            
     478             dup2(req->post_data_fd, STDIN_FILENO);                            
     479             close(req->post_data_fd);                                         
     480         }                                                                     
     481         /* Close access log, so CGI program can't scribble                    
     482          * where it shouldn't                                                 
     483          */                                                                   
     484         close_access_log();                                                   
     485                                                                               
     486         /*                                                                    
     487          * tie STDERR to cgi_log_fd                                           
     488          * cgi_log_fd will automatically close, close-on-exec rocks!          
     489          * if we don't tied STDERR (current log_error) to cgi_log_fd,         
     490          *  then we ought to close it.                                        
     491          */                                                                                                         
     492         if (!cgi_log_fd)                                                      
     493             dup2(devnullfd, STDERR_FILENO);                                   
     494         else                                                                  
     495             dup2(cgi_log_fd, STDERR_FILENO);                                  
     496                                                                               
     497         if (req->is_cgi) {                                                    
     498             char *aargv[CGI_ARGC_MAX + 1];                                    
     499             create_argv(req, aargv);                                          
     500             execve(req->pathname, aargv, req->cgi_env);                       
     501         } else {                                                              
     502             if (req->pathname[strlen(req->pathname) - 1] == '/')              
     503                 execl(dirmaker, dirmaker, req->pathname, req->request_uri,    
     504                       NULL);                                                  
     505 #ifdef GUNZIP                                                                 
     506             else                                                              
     507                 execl(GUNZIP, GUNZIP, "--stdout", "--decompress",             
     508                       req->pathname, NULL);                                   
     509 #endif                                                                        
     510         }                                                                     
     511         /* execve failed */                                                   
     512         WARN(req->pathname);                                                  
     513         _exit(1);                                                             
     514         break;                                                                
     515                                                                               
     516     default:                                                                  
     517         /* parent */                                                          
     518         /* if here, fork was successful */                                    
     519         if (verbose_cgi_logs) {                                               
     520             log_error_time();                                                 
     521             fprintf(stderr, "Forked child "%s" pid %d
    ",                   
     522                     req->pathname, child_pid);                                
     523         }                                                                     
     524                                                                               
     525         if (req->method == M_POST) {                                          
     526             close(req->post_data_fd); /* child closed it too */               
     527             req->post_data_fd = 0;                                            
     528         }                                                                                           
     529                                                                               
     530         /* NPH, GUNZIP, etc... all go straight to the fd */                   
     531         if (!use_pipes)                                                       
     532             return 0;                                                         
     533                                                                               
     534         close(pipes[1]);                                                      
     535         req->data_fd = pipes[0];                                              
     536                                                                               
     537         req->status = PIPE_READ;                                              
     538         if (req->is_cgi == CGI) {                                             
     539             req->cgi_status = CGI_PARSE; /* got to parse cgi header */        
     540             /* for cgi_header... I get half the buffer! */                    
     541             req->header_line = req->header_end =                              
     542                 (req->buffer + BUFFER_SIZE / 2);                              
     543         } else {                                                              
     544             req->cgi_status = CGI_BUFFER;                                     
     545             /* I get all the buffer! */                                       
     546             req->header_line = req->header_end = req->buffer;                 
     547         }                                                                     
     548                                                                               
     549         /* reset req->filepos for logging (it's used in pipe.c) */            
     550         /* still don't know why req->filesize might be reset though */        
     551         req->filepos = 0;                                                     
     552         break;                                                                
     553     }                                                                         
     554                                                                               
     555     return 1;                                                                 
     556 }                         

    从这部分代码可以知道boa在fork出子进程去执行对应的cgi的时候将子进程的标准输出dup到了提前创建好的pipe,父进程则从pipe的另一端读取cgi的输出内容(既cgi程序printf输出的内容)。

    现在需要去看下对应的cgi是否有内容输出来

    home.c编译成home.cgi

    main函数如下

                                                                                  
     116 int main(void)                                                                
     117 {                                                                             
     118     char mode[32] = {0};                                                      
     119                                                                               
     120     memset(mode,  0, sizeof(mode));                                           
     121     lollipopConfInit();                                                       
     122     getLollipopConf("function_mode", mode);                                   
     123     lollipopConfDeinit();                                                     
     124                                                                               
     125     126     if (!strcmp(mode, "WFD")) {                                               
     127         return p2p_state();                                                   
     128     } else if (!strcmp(mode, "DLNA") || !strcmp(mode, "MIX")) {               
     129         return ap_state();                                                    
     130     } else {                                                                  
     131         return -1;                                                            
     132     }                                                                         
     133 }                   
                                                                                  
      24 int p2p_state(void)                                                           
      25 {                                                                             
      26     struct list_head *pList;                                                  
      27     struct list_head groupList;                                               
      28                                                                               
      29     printf("Content-type: text/html
    
    ");                                    
      30     printf("<HTML><BODY>
    ");                                                 
      31     printf("<HEAD>
    ");                                                       
      32     printf("<TITLE>Lollipop Home</TITLE>
    ");                                 
      33     printf("<link rel='stylesheet' type='text/css' href='css/main.css' />");  
      34     printf("</HEAD>
    ");                                                      
      35                                                                               
      36     printf("<p>");                                                            
      37     printf("<a href="wifi.cgi"><img border="0" src="/res/icon_wifi_01.png" /></a>
    ");
      38     printf("touch icon to show wifi direct group list");                      
      39     printf("</p>");                                                           
      40     printf("<br />
    ");                                                       
      41     printf("<br />
    ");             
    ...........
                                                                                  
      73 int ap_state(void)                                                            
      74 {                                                                             
      75     struct list_head apList;                                                  
      76     struct list_head *pList;                                                  
      77                                                                               
      78     printf("Content-type:text/html;charset=utf-8
    
    "); //response header     
      79     printf("<HTML><BODY>
    ");                                                 
      80     printf("<HEAD>
    ");                                                       
      81     printf("<TITLE>Lollipop Home</TITLE>
    ");                                 
      82     printf("<link rel='stylesheet' type='text/css' href='/css/main.css' />"); 
      83     printf("</HEAD>
    ");                                                      
      84                                                                               
      85     printf("<div class='content_icon'>
    ");                                   
      86     printf("<p><a href='wifi.cgi' class='icon_wifi'>WiFi AP</a></p>
    ");      
      87     printf("</div>
    
    ");                                                     
      88                                                                               
      89                                                
    .....

    在这里需要获取mode类型,然后输出不同的内容

    检查过p2p_state()及ap_state()函数都是由正常的格式内容打印出来,而且是在原来的平台验证过的.

    但是main里面还有一个else的分支简单粗暴的返回了一个 -1.

    添加一个log信息到else信息里重新验证了一次,果然main函数跑到了else分支,也就是直接返回-1而没有printf任何内容,这样boa进程则无法从home.cgi读取到任何输出内容。

    作为一个cgi程序响应客户端的GET请求,如果不是正确的请求个人认为至少应该回复一组正确的消息,只是在内容上可以显示一些提示信息,而不能像普通的程序一样直接返回一个-1.

    这会导致客户端看到的错误信息与你想表达的意思不一致。

    所以在这只需要添加一个notFound的返回消息

                                                                                  
     141 void not_found(void)                                                          
     142 {                                                                             
     143     printf("Content-type:text/html;charset=utf-8
    
    "); //response header     
     144     printf("<HTML><BODY>
    ");                                                 
     145     printf("<HEAD>
    ");                                                       
     146     printf("<TITLE>Lollipop Home</TITLE>
    ");                                 
     147     printf("<link rel='stylesheet' type='text/css' href='/css/main.css' />"); 
     148     printf("</HEAD>
    ");                                                      
     149                                                                               
     150     printf("<p>Not Support Mode</a></p>
    ");                                         
     151                                                                               
     152     printf("</BODY>
    ");                                                      
     153     printf("</HTML>
    ");                                                      
     154                                                                               
     155 }                                                                             
     156                                                                               
     157 int main(void)                                                                
     158 {                                                                             
     159     char mode[32] = {0};                                                      
     160                                                                               
     161     memset(mode,  0, sizeof(mode));                                           
     162     lollipopConfInit();                                                       
     163     getLollipopConf("function_mode", mode);                                   
     164     lollipopConfDeinit();                                                     
     165     if (!strcmp(mode, "WFD")) {                                               
     166         return p2p_state();                                                   
     167     } else if (!strcmp(mode, "DLNA") || !strcmp(mode, "MIX") || !strcmp(mode, "AIRPLAY")) {
     168         return ap_state();                                                    
     169     } else {                                                                  
     170         not_found();                                                          
     171     }                                                                         
     172     return 0;                                                                 
     173 }                    
    
    





  • 相关阅读:
    在Workload Automation中实现suspend分析
    Linux kernel的中断子系统之(九):tasklet
    Linux kernel的中断子系统之(八):softirq
    Linux kernel的中断子系统之(七):GIC代码分析
    Linux kernel的中断子系统之(六):ARM中断处理过程
    Linux kernel的中断子系统之(五):驱动申请中断API
    Linux kernel的中断子系统之(四):High level irq event handler
    Linux kernel的中断子系统之(三):IRQ number和中断描述符
    Linux kernel的中断子系统之(二):IRQ Domain介绍
    Linux kernel的中断子系统之(一):综述
  • 原文地址:https://www.cnblogs.com/tid-think/p/11652472.html
Copyright © 2011-2022 走看看