【问题标题】:Apache module in C: how to write data to client socket?C中的Apache模块:如何将数据写入客户端套接字?
【发布时间】:2010-10-23 06:52:52
【问题描述】:

我创建了一个简单的 mod_perl 模块,它将一个以 0 结尾的字符串写入连接到端口 843 的Flash-clients。它工作正常,但在我的 CentOS 5 Linux 机器上每个 httpd-child 使用 20m。

所以我试图用 C 重写我的模块,但我不确定如何通过协议处理程序接收的 conn_rec 结构访问客户端套接字。

我在邮件列表中询问并尝试添加 #define CORE_PRIVATE 并使用 ap_get_module_config(conn->conn_config, &core_module) 但这会破坏我的网络服务器:字符串既可以提供给端口 843(可以),也可以提供给端口 80(不可以)。

这里有人给点建议吗?

这是我的 SocketPolicy.pm(工作正常,但需要大量内存):

package SocketPolicy;

# Listen 843
# <VirtualHost _default_:843>
#       PerlModule                   SocketPolicy
#       PerlProcessConnectionHandler SocketPolicy
# </VirtualHost>

use strict;
use warnings FATAL => 'all';
use APR::Const(-compile => 'SO_NONBLOCK');
use APR::Socket();
use Apache2::ServerRec();
use Apache2::Connection();
use Apache2::Const(-compile => qw(OK DECLINED));

use constant POLICY =>
qq{<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">

<cross-domain-policy>
<allow-access-from domain="*" to-ports="8080"/>
</cross-domain-policy>
\0};

sub handler {
        my $conn   = shift;
        my $socket = $conn->client_socket();
        my $offset = 0;

        # set the socket to the blocking mode
        $socket->opt_set(APR::Const::SO_NONBLOCK => 0);

        do {
                my $nbytes = $socket->send(substr(POLICY, $offset), length(POLICY) - $offset);
                # client connection closed or interrupted
                return Apache2::Const::DECLINED unless $nbytes;
                $offset += $nbytes;
        } while ($offset < length(POLICY));

        my $slog = $conn->base_server()->log();
        $slog->warn('served socket policy to: ', $conn->remote_ip());
        return Apache2::Const::OK;
}

1;

这是我损坏的 mod_socket_policy.c(劫持 80 端口):

/*
LoadModule socket_policy_module modules/mod_socket_policy.so
Listen 843
<VirtualHost _default_:843>
        SetHandler socket_policy
</VirtualHost>
*/

#include <httpd.h>
#include <http_protocol.h>
#include <http_connection.h>
#include <http_config.h>
#include <http_log.h>
#define CORE_PRIVATE
#include <http_core.h>

#define POLICY "<?xml version=\"1.0\"?>\n" \
               "<!DOCTYPE cross-domain-policy SYSTEM\n" \
               "\"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">\n" \
               "<cross-domain-policy>\n" \
               "<allow-access-from domain=\"*\" to-ports=\"8080\"/>\n" \
               "</cross-domain-policy>\0"

static int socket_policy_handler(conn_rec *conn) {
        apr_socket_t *socket = ap_get_module_config(conn->conn_config, &core_module);
        apr_size_t len = strlen(POLICY);

        apr_socket_send(socket, POLICY, &len);

        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, conn->base_server,
                "served socket policy to %s", conn->remote_ip);

        return OK;
}

static void register_hooks(apr_pool_t *pool) {
        ap_hook_process_connection(socket_policy_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA socket_policy_module = {
        STANDARD20_MODULE_STUFF,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        register_hooks
};

另外,我查看了mod_perl source,他们似乎使用相同的方法来访问客户端套接字:

static MP_INLINE
apr_socket_t *mpxs_Apache2__Connection_client_socket(pTHX_ conn_rec *c,
                                                    apr_socket_t *s)
{
    apr_socket_t *socket =
        ap_get_module_config(c->conn_config, &core_module);

    if (s) {
        ap_set_module_config(c->conn_config, &core_module, s);
    }

    return socket;
}

那么为什么我的 Perl 模块可以工作,而 C 模块却不行呢?

【问题讨论】:

    标签: c apache sockets mod-perl apache2-module


    【解决方案1】:

    对于内容处理程序,约定是 在 httpd.conf 中使用 SetHandler XXX 和 然后在运行时检查该字符串与

    if (!r->handler || (strcmp(r->handler, "XXX") != 0))
       return DECLINED;
    

    但是对于协议处理程序,没有这样的约定。 您必须为 httpd.conf 引入一些关键字 并检查它。例如mod_echo(Apache 包含的源代码)引入了ProtocolEcho On。或者就我而言,您可以只检查端口:

       if (conn->base_server->port != 843)
               return DECLINED;
    

    下面也是用于相同目的的工作模块,但使用bucket brigades,这比直接写入客户端套接字要好:

    /*
    LoadModule socket_policy_module modules/mod_socket_policy.so
    Listen 843
    <VirtualHost _default_:843>
    </VirtualHost>
    */
    #include <httpd.h>
    #include <http_protocol.h>
    #include <http_connection.h>
    #include <http_config.h>
    #include <http_log.h>
    
    #define POLICY "<?xml version=\"1.0\"?>\n" \
                   "<!DOCTYPE cross-domain-policy SYSTEM\n" \
                   "\"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">\n" \
                   "<cross-domain-policy>\n" \
                   "<allow-access-from domain=\"*\" to-ports=\"8080\"/>\n" \
                   "</cross-domain-policy>\n"
    
    static int socket_policy_handler(conn_rec *conn) {
            apr_bucket_brigade *bb;
            apr_bucket *b;
            apr_status_t rv;
    
            if (conn->base_server->port != 843)
                    return DECLINED;
    
            bb = apr_brigade_create(conn->pool, conn->bucket_alloc);
            /* this will send the terminating 0 as well */
            b = apr_bucket_immortal_create(POLICY, sizeof(POLICY), bb->bucket_alloc);
            APR_BRIGADE_INSERT_TAIL(bb, b);
            b = apr_bucket_eos_create(bb->bucket_alloc);
            APR_BRIGADE_INSERT_TAIL(bb, b);
            rv = ap_pass_brigade(conn->output_filters, bb);
            if (rv != APR_SUCCESS) {
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, conn->base_server, "output error");
                    return DECLINED;
            }
    
            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, conn->base_server,
                "served socket policy to %s", conn->remote_ip);
            return OK;
    }
    
    static void register_hooks(apr_pool_t *pool) {
            ap_hook_process_connection(socket_policy_handler, NULL, NULL, APR_HOOK_MIDDLE);
    }
    
    module AP_MODULE_DECLARE_DATA socket_policy_module = {
            STANDARD20_MODULE_STUFF,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            register_hooks
    };
    

    【讨论】:

    • 如果 SELinux 处于活动状态,那么您需要此命令,否则 Apache 将无法绑定到新端口:semanage port -a -t http_port_t -p tcp 843
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-19
    • 2013-08-27
    • 2021-11-14
    • 2019-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多