Hatena::Groupcprogramming

Going My C Way このページをアンテナに追加 RSSフィード

2010年11月03日(水)

bind() をラップするプログラム

| 14:41 |  bind() をラップするプログラム - Going My C Way を含むブックマーク はてなブックマーク -  bind() をラップするプログラム - Going My C Way  bind() をラップするプログラム - Going My C Way のブックマークコメント

プログラムが bind する IP アドレスを変更します。

例えば、

次の ruby スクリプトを実行すると INANY_ADDR で listen します。

(server.rb)

#!/usr/bin/env ruby
require 'socket'

s = TCPServer.new(nil, 50080)
$<.gets

実行。

$ ruby server.rb
$ netstat -n
稼働中のインターネット接続 (サーバと確立)
Proto 受信-Q 送信-Q 内部アドレス    外部アドレス  状態      PID/Program name
               
tcp        0      0 0.0.0.0:50080   0.0.0.0:*     LISTEN    3042/ruby    

これを、

$ bindwrap.sh 127.0.0.1 ruby server.rb

とすることで、127.0.0.1 だけを listen するように変更します。

$ netstat -n
稼働中のインターネット接続 (サーバと確立)
Proto 受信-Q 送信-Q 内部アドレス    外部アドレス  状態      PID/Program name
               
tcp        0      0 127.0.0.1:50080 0.0.0.0:*     LISTEN    3080/ruby   

インストール

ソース bindwrap.c。

/*
 * bindwrap.c
 *
 * [how to make]
 *   $ gcc -shared -fPIC -o bindwrap.so bindwrap.c -ldl
 *
 * [how to run]
 *   $ LD_PRELOAD=${path}/bindwrap.so BIND_ADDR=${addr} ${program}
 *
 *   (e.g.)
 *     $ LD_PRELOAD=./bindwrap.so BIND_ADDR=127.0.0.1 myprogram
 */
#define _GNU_SOURCE
#include <dlfcn.h>

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define is_INADDR_ANY(s_)   (\
    (((struct sockaddr    *)(s_))->sa_family       == AF_INET)      &&\
    (((struct sockaddr_in *)(s_))->sin_addr.s_addr == INADDR_ANY)   )

static int (*bind0)(int, const struct sockaddr *, socklen_t);
static const char *addrstr;

static __attribute__((constructor)) void init_bind0(void)
{
    addrstr = getenv("BIND_ADDR");

    bind0 = dlsym(RTLD_NEXT, "bind");
}

int bind(int sock, const struct sockaddr *myaddr, socklen_t addrlen)
{
    if (addrstr && is_INADDR_ANY(myaddr)) {
        struct sockaddr_in saddr;

        memcpy(&saddr, myaddr, sizeof(saddr));
        inet_aton(addrstr, &saddr.sin_addr);

        return bind0(sock, (struct sockaddr *)&saddr, sizeof(saddr));
    }

    return bind0(sock, myaddr, addrlen);
}

/* vim:set ts=4 sw=4: */

ビルドします。

$ gcc -shared -fPIC -o bindwrap.so bindwrap.c -ldl

ライブラリを適当なディレクトリに配置します。(以下の例では、$HOME/lib/lib)

$ mkdir -p ~/lib/lib/
$ install -c -m 755 bindwrap.so ~/lib/lib/

スクリプト bindwrap.sh

#!/bin/bash

USAGE="usage: $0 bind_addr prog_name"
bind_addr=${1:?$USAGE}
prog_name=${2:?$USAGE} ; shift 2

LD_PRELOAD=${LD_PRELOAD:-$HOME/lib/lib/bindwrap.so} BIND_ADDR=$bind_addr $prog_name $*

スクリプトを適当なPATHの通ったディレクトリに配置します。(以下の例では、$HOME/bin)

$ mkdir -p ~/bin/
$ install -c -m 755 bindwrap.sh ~/bin/

エイリアスしてもよいでしょう。(以下の例では、bw にエイリアス)

alias bw=bindwrap.sh

実行です。

$ bw 127.0.0.1 ruby server.rb

参考書籍

Binary Hacks ―ハッカー秘伝のテクニック100選

Binary Hacks ―ハッカー秘伝のテクニック100選