出力されたコンテンツをoutput filterで覗き見る

最近覗き系が多い気がしますが、解決したのでまとめておきます。


まずはソースから、デバッグ用にモジュールを1ついい加減に作ってしまいました。

static int outputfilter(ap_filter_t* f, apr_bucket_brigade* bb)
{
    request_rec* r = f->r;
    apr_bucket* backet;
    apr_size_t length = 0;
    apr_status_t read_status;

    for (backet = APR_BRIGADE_FIRST(bb);
        backet != APR_BRIGADE_SENTINEL(bb);
        backet = APR_BUCKET_NEXT(backet))
    {
        const char *str;

        if (APR_BUCKET_IS_EOS(backet))
        {
            return ap_pass_brigade(f->next, bb);
        }

        read_status = apr_bucket_read(backet, &str, &length, APR_BLOCK_READ);
        if (read_status != APR_SUCCESS)
        {
            ERROR("apr_bucket_read error [\d]", read_status);
            return read_status;
        }

        total_length += length;

        if (length > 0)
        {
            char* temp = (char*)apr_palloc(r->pool, total_length);
            if (content != NULL) memcpy(temp, content, total_length - length);
            memcpy(temp + total_length - length, str, length);
            content = temp;
        }
    }

    FUNC_TRACE("total_length = %d", total_length);
    return ap_pass_brigade(f->next, bb);
}


まず1つ目のポイントがid:hkrnさんから教えていただいた、bucketのイテレートです。
APR_BRIGADE_FOREACHマクロは避けたほうがよいらしい(see http://www.schumann.cx/docs/apr-util/group__APR__Util__Bucket__Brigades.html#a80)
ので、別のマクロを利用して、forループでイテレートします。


2つ目のポイントは、1リクエストに対して、output filterが複数回呼ばれるので、覗き見するコンテンツを格納する変数がグローバルになっている点。
(マルチスレッドモードでApacheを動作させる場合は、この方法は正しくありません。tableなどに格納すべきでしょう。)


あと、id:higepon:20051121:1132566569で問題となっていた、出力が途中で切れしまう問題だが、原因が判明しました。
出力を覗くために、ap_log_errorでエラーログに出力していたのですが、こいつが長いコンテンツを途中でぶった切っていました(泣。

WSASendToを試してみた

任意のIPパケットを送りたいという動機で、WSASendToを使ってみました。

bool RawSocket::Write(byte* buff, dword size, dword* writeSize)
{
    unsigned long length;
    unsigned long flags = 0;
    IP::Header* h = (IP::Header*)buff;
    SOCKADDR_IN addr_in;
    addr_in.sin_addr.s_addr = h->dstip;
    addr_in.sin_family      = AF_INET;              // IPv4
    addr_in.sin_port        = htons(0);             // 0で初期化らしい

    WSABUF wsb;
    wsb.buf = (PTCHAR)buff;
    wsb.len = size;

//    if (SOCKET_ERROR == WSASend(this->socket, &wsb, 1, &length, flags, NULL, NULL))
//    if (SOCKET_ERROR == sendto(this->socket, wsb.buf, sizeof(wsb.buf), 0, (SOCKADDR*)&addr_in, sizeof(addr_in)))
    if (SOCKET_ERROR == WSASendTo(this->socket, &wsb, 1, &length, flags, (struct sockaddr *)&addr_in, sizeof(SOCKADDR_IN), NULL, NULL))
    {
        TRACE("SEND ERROR \n");
        return false;
    }
    *writeSize = length;
    return true;
}

これを利用して先日のsocket.Readで受信した、自分宛のパケットをちょっとだけ改変して、自分自身に送ってみました。

        header->srcip = inet_addr("192.168.10.1");
        header->prot = 7;
        memset(header->data, 0xef, sizeof(header->data));
        if (!socket.Write( (byte*)header, sizeof(IP::Header), &length) )
        {
            printf("Write Error:%s\n", socket.GetErrorMessage() );
        }

結果がこちら

Waiting for Packet...
protocol number 0
source   address 192.168.11.2
dest     address 192.168.11.2
data     45 00 00 28 ffffffb1 fffffff2 40 00 36 07

確かに自分から自分へ送受信できているのですが、ちょっと様子がおかしいです。
srcipの書き換えやらがぜんぜん無視されてます(´ヘ`;)
bufの中身はIPHeaderを渡しているのですが、どうもこれがIPHeaderとして送られているのではなくて、IPHeaderは勝手に作られてしまっているようです。


なんだかWSASendToが目的の関数じゃない予感がしてきました・・・。
というわけで微妙にパケットが送れているソースを置いておきます。
rawsock-20051126.zip

あまりこれにはまって、時間をとられるのもあれだからもう少しがんばってだめならあきらめよう。

追記:2005/12/21
その後の調査によりWindows XP SP2のセキュリティ向上によりRawSocketによる送信は制限されていることが分かりました。
WinPcapによる実装をおすすめします。
RawSocketに関してはid:higepon:20051207:1133963958
WinPcapに関してはid:higepon:20051209:1134143054
辺りに書いてあります。