flow-tools 0.66用パッチ

Linux (Debian SargeとかCentOS)にてflow-tools 0.66(NetFlowコレクタ)のコンパイルが通らなかったので,いくつか修正してパッチファイル作った.

直したのは,ANSI C非互換のキャストの使い方をしていた部分と,ブロックの最後にラベルがあるとエラーになる問題.
前者は例えば以下のようなもの.

  /* だめな例 */
  void *foo = bar;
  (char *)foo += 2; /* 左辺値はキャストしたらだめー */

  /* 修正後 */
  void *foo = bar;
  foo = (char *)foo + 2;

後者は例えば以下のようなもの.

  /* だめな例 */
  for (;;) {
    do_something1();
    if (cond) goto skip;
    do_something2();
  skip: /* これだめ */
  }

  /* 修正後 */
  for (;;) {
    do_something1();
    if (cond) goto skip;
    do_something2();
  skip:
    ; /* とにかく何か文があればいいらしい */
  }

パッチ (flow-tools-0.66-patch)

flow-tools-0.66ディレクトリ以下で patch -p0 < flow-tools-0.66-patch てな具合にするとよし.

diff -ru -x '*.Po' flow-tools-0.66.orig/lib/ftchash.c flow-tools-0.66/lib/ftchash.c
--- flow-tools-0.66.orig/lib/ftchash.c	2003-02-13 11:38:41.000000000 +0900
+++ flow-tools-0.66/lib/ftchash.c	2008-05-12 16:32:07.000000000 +0900
@@ -326,7 +326,7 @@
       (char*)ftch->traverse_chunk->base+ftch->traverse_chunk->next) {
 
       ret = ftch->traverse_rec;
-      (char*)ftch->traverse_rec += ftch->d_size;
+      ftch->traverse_rec = (char*)ftch->traverse_rec + ftch->d_size;
       return ret;
 
     } else {
diff -ru -x '*.Po' flow-tools-0.66.orig/lib/ftio.c flow-tools-0.66/lib/ftio.c
--- flow-tools-0.66.orig/lib/ftio.c	2003-02-24 09:51:47.000000000 +0900
+++ flow-tools-0.66/lib/ftio.c	2008-05-12 16:29:11.000000000 +0900
@@ -2267,7 +2267,7 @@
         break;
 
       nleft -= nread;
-      (char*)ptr += nread;
+      ptr = (char*)ptr + nread;
   }
   return (nbytes - nleft);
 } /* readn */
@@ -2292,7 +2292,7 @@
       return(nwritten); /* error */
 
     nleft -= nwritten;
-    (char*)ptr += nwritten;
+    ptr = (char*)ptr + nwritten;
   }
   return(nbytes - nleft);
 } /* writen */
diff -ru -x '*.Po' flow-tools-0.66.orig/lib/fttlv.c flow-tools-0.66/lib/fttlv.c
--- flow-tools-0.66.orig/lib/fttlv.c	2003-02-13 11:38:43.000000000 +0900
+++ flow-tools-0.66/lib/fttlv.c	2008-05-12 16:31:31.000000000 +0900
@@ -68,10 +68,10 @@
   }
 
   bcopy(&t, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&len, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&v, buf, 4);
 
@@ -107,10 +107,10 @@
   }
 
   bcopy(&t, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&len, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&v, buf, 2);
 
@@ -145,10 +145,10 @@
   }
 
   bcopy(&t, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&len, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&v, buf, 1);
 
@@ -183,10 +183,10 @@
   }
 
   bcopy(&t, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&len, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(v, buf, len);
 
@@ -230,16 +230,16 @@
     return -1;
 
   bcopy(&t, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&len, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&ip, buf, 4);
-  (char*)buf += 4;
+  buf = (char*)buf + 4;
 
   bcopy(&ifIndex, buf, 2);
-  (char*)buf += 2;
+  buf = (char*)buf + 2;
 
   bcopy(name, buf, n);
 
@@ -287,19 +287,19 @@
   }
 
   bcopy(&t, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&len, buf, 2);
-  (char*)buf+= 2;
+  buf = (char*)buf + 2;
 
   bcopy(&ip, buf, 4);
-  (char*)buf += 4;
+  buf = (char*)buf + 4;
 
   bcopy(&entries, buf, 2);
-  (char*)buf += 2;
+  buf = (char*)buf + 2;
 
   bcopy(ifIndex_list, buf, esize);
-  (char*)buf += esize;
+  buf = (char*)buf + esize;
 
   bcopy(name, buf, n);
 
diff -ru -x '*.Po' flow-tools-0.66.orig/src/flow-cat.c flow-tools-0.66/src/flow-cat.c
--- flow-tools-0.66.orig/src/flow-cat.c	2003-04-03 03:03:01.000000000 +0900
+++ flow-tools-0.66/src/flow-cat.c	2008-05-12 16:33:59.000000000 +0900
@@ -551,7 +551,7 @@
         break;
 
 next_file:
-
+      ;
     }  /* FOREACH filename in dir */
 
   } /* foreach dir bundle */
diff -ru -x '*.Po' flow-tools-0.66.orig/src/flow-dscan.c flow-tools-0.66/src/flow-dscan.c
--- flow-tools-0.66.orig/src/flow-dscan.c	2003-04-03 03:03:01.000000000 +0900
+++ flow-tools-0.66/src/flow-dscan.c	2008-05-12 16:34:28.000000000 +0900
@@ -560,7 +560,7 @@
       ager(&ds, total_flows32);
 
 skip2:
-
+    ;
 
   } /* while rec */
 
@@ -805,6 +805,7 @@
   fterr_info("ager: reset hash run");
 
 skip3:
+  ;
 
 } /* ager */
 
diff -ru -x '*.Po' flow-tools-0.66.orig/src/flow-fanout.c flow-tools-0.66/src/flow-fanout.c
--- flow-tools-0.66.orig/src/flow-fanout.c	2003-04-04 11:24:40.000000000 +0900
+++ flow-tools-0.66/src/flow-fanout.c	2008-05-12 16:35:39.000000000 +0900
@@ -809,7 +809,7 @@
       } /* fte.buf_size */
     
 skip1:
-
+      ;
     } /* if FD_ISSET */
 
     if (sig_quit_flag) {
diff -ru -x '*.Po' flow-tools-0.66.orig/src/flow-receive.c flow-tools-0.66/src/flow-receive.c
--- flow-tools-0.66.orig/src/flow-receive.c	2003-04-03 03:03:02.000000000 +0900
+++ flow-tools-0.66/src/flow-receive.c	2008-05-12 16:34:46.000000000 +0900
@@ -753,7 +753,7 @@
       } /* for */
 
 skip1:
-
+      ;
     } /* if FD_ISSET */
 
   } /* while 1 */

こういうのCodeReposに上げてもいいのかなあ…….

リストのリストから,ハッシュのリストを作る

「怠慢はプログラマの美徳」というけれど - kなんとかの日記 を見て,

例えば、スクリプト言語で次のような Hash や Dict を書いたとする。

data = [
  {'name'=>'Foo', 'age'=>20, 'email'=>'foo@mail.com'},
  {'name'=>'Bar', 'age'=>21, 'email'=>'bar@mail.net'},
  {'name'=>'Baz', 'age'=>22, 'email'=>'baz@mail.org'},
]

もしこれをみて何も感じないのであれば、スクリプト言語屋といえど Java 屋を笑うことはできない。本質的でない記述に嫌悪感を感じるセンスがあれば、同じキーが何度も現れていることを「めんどくさい」と感じるはずだ。そして、こんなふうに書けないだろうかと一度は思うはずだ。

data = %h{
  ['name', 'age', 'email'],
  ['Foo',   20,   'foo@mail.com'],
  ['Bar',   21,   'bar@mail.net'],
  ['Baz',   22,   'baz@mail.org'],
}
「怠慢はプログラマの美徳」というけれど - kなんとかの日記

何だかむずむずしてきたので,リストのリストからハッシュのリストを生成するコード片を書いてみた.

#!/usr/local/bin/ruby
require 'pp'

class Array
  def to_AoH
    lambda {|keys, columns|
      columns.map! {|column| Hash[*keys.zip(column).flatten] }
    }.call(self.shift, self)
  end
end

table = [
  [:name, :age, :email],
  ['Foo',   20, 'foo@mail.com'],
  ['Bar',   21, 'bar@mail.net'],
  ['Baz',   22, 'baz@mail.org'],
]

pp table.to_AoH

実行結果はこんな感じ.

% ./table2arrayOfHash.rb
[{:email=>"foo@mail.com", :name=>"Foo", :age=>20},
 {:email=>"bar@mail.net", :name=>"Bar", :age=>21},
 {:email=>"baz@mail.org", :name=>"Baz", :age=>22}]

でも,こっちの方が好みかなぁ.

#!/usr/local/bin/ruby
require 'pp'

class Array
  def to_AoH(*keys)
    map {|column| Hash[*keys.zip(column).flatten] }
  end
end

aoh = [
  ['Foo', 20, 'foo@mail.com'],
  ['Bar', 21, 'bar@mail.net'],
  ['Baz', 22, 'baz@mail.org'],
].to_AoH(:name, :age, :email)

pp aoh

いや,やはりキーはまず値より先に現れるべきか,と思い直してさらに修正.

#!/usr/local/bin/ruby
require 'pp'

class Array
  def to_AoH(*lists)
    lists.map {|values| Hash[*self.zip(values).flatten]}
  end
end

aoh = [:name, :age, :email].to_AoH(
  ['Foo', 20, 'foo@mail.com'],
  ['Bar', 21, 'bar@mail.net'],
  ['Baz', 22, 'baz@mail.org']
)

pp aoh

各リストをそれぞれメソッドの引数に積んでいるので,最後の要素の後ろにカンマ(',')を書けないところが若干気持ち悪いけど,こんなところかなぁ.

テイルズ・エール・ハウス


ピンクの象さんの看板でお馴染みの,テイルズ・エール・ハウスに行ってきました.

このお店は,やたらとクオリティの高いベルギービールが飲める,とても危険なお店です.

店内の様子はこんな感じ


お店に着くや否や,最初からロシュフォール10をオーダするだめ人間なわたし


こちらはマスターに教えていただいた「セント・ベルナンデュス ベリオール」というベルギービール.通称「赤ボウズ」と言うらしい.これがまたアホみたいに美味く,当方のメートルも上がる一方.


マルール12.濃厚な味わいと香り.ロシュフォールよりこちらの方が好きかも.


こちらは「青ボウズ」.腰が抜けるほどうまい.



ごちそうさま!

SMTP-AUTH用の証明書を作る

酔っぱらった日は証明書のメンテナンスに限る.

ということで,デフォルトで入ってたSnakeOil証明書に甘んじていた我が家のPostfixを,もう少しましな感じにしようかなと決意する.結局,第四種オレオレ証明書だけどね.

作業手順

/etc/ssl/demoCAができてる事が前提.無い人は/usr/lib/ssl/misc/CA.pl -newcaで.

秘密鍵作る
# cd /etc/ssl
# openssl genrsa -out demoCA/private/`hostname`.key 2048
Generating RSA private key, 2048 bit long modulus
............................................+++
...............................................+++
e is 65537 (0x10001)

作った秘密鍵の中身を確認したければ以下のような感じで.

# openssl rsa -in demoCA/private/`hostname`.key -text
CSRつくる
# openssl req -new -key demoCA/private/`hostname`.key -out csr.pem

OUとかコモンネーム(CN)などを聞かれるので適切に答えていくと,CSRファイルができる.
作ったCSRの中身を確認するには,以下のような感じで.

# openssl req -in csr.pem -text
署名するぜ(by CA)

ここでCAの立場になる.
先ほど作ったCSRをもとに,証明書を発行する.

# openssl ca -in csr.pem -out demoCA/certs/`hostname`.pem -days 3650
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
            de:ad:be:ef:ab:ad:ca:fe
        Validity
            Not Before: Apr 25 00:00:00 2008 GMT
            Not After : Apr 25 00:00:00 2018 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Foreign
            organizationName          = My company
            organizationalUnitName    = My Unit
            commonName                = mail.example.com
            emailAddress              = webmaster@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                DE:AD:BE:EF:AB:AD:CA:FE:DE:AD:BE:EF:AB:AD:CA:FE:DE:AD:BE:EF
            X509v3 Authority Key Identifier:
                keyid:DE:AD:BE:EF:AB:AD:CA:FE:DE:AD:BE:EF:AB:AD:CA:FE:DE:AD:BE:EF

Certificate is to be certified until Apr 25 00:00:00 2018 GMT (3650 days)
Sign the certificate? [y/n]:

証明書の中身を確認し,問題がなければ y と入力.

Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

これで完了

Postix SMTP-AUTH用設定

main.cfで

smtpd_tls_cert_file=/etc/ssl/demoCA/certs/EXAMPLE.pem
smtpd_tls_key_file=/etc/ssl/demoCA/private/EXAMPLE.key

再読み込み

# /etc/init.d/postfix reload
Reloading Postfix configuration...done.

これでSnakeOilから脱却.やったね.

VMware上のUbuntuでMozilla Trunkビルド環境を作る

VMware上にUbuntu 7.10環境を構築し,Mozilla Trunk(Minefield)のビルド環境を構築するためのメモ.

環境

  • Ubuntu 7.10 (Gutsy Gibbon)
    • Desktop CDを使いGNOME環境を構築する.
    • 事前にisoイメージを適宜入手しておく.

作業手順 (Ubuntuセットアップ)

VMware上にUbuntu用ゲストOSを作成

ゲストOS作成後,vmxファイルをテキストエディタで開き,以下を追加する.これは,VMware上でUbuntuインストーラを起動させた際,解像度がやたら高くなってしまう問題を回避するため.

svga.maxWidth = "1024"
svga.maxHeight = "768"

D: にUbuntu 7.10のisoイメージをマウントし,普通にインストール.再起動する.

まずはアップデート

GUIが好きな人は,タスクトレイ(?)の所にあるアレをクリックしてアップデートしてもよいと思う.

% sudo apt-get update
% sudo apt-get upgrade

一度再起動が必要かも.

Mozillaのビルドに必要なパッケージをapt-get install

がんがんapt-get installする.

% sudo apt-get -y install build-essential
% sudo apt-get -y build-dep firefox-3.0
% sudo apt-get -y install cvs
% sudo apt-get -y install libdbus-glib-1-dev
% sudo apt-get -y install libcurl3
% sudo apt-get -y install libcurl4-openssl-dev

生活環境も整える.

% sudo apt-get -y install vim lv
% sudo apt-get -y install subversion zsh openssh-server
% sudo apt-get -y install ddd global

zshが無いと死ぬ人はchshしておく.

VMware toolsをビルドするのに必要なパッケージもインストール.

% sudo apt-get install libqt3-headers libqt3-mt-dev
% sudo apt-get install bin86 kernel-package

VMware toolsをここでインストールしてもよいし,しなくてもよい.

私は上記を毎回手で打つのも面倒なので,.sshや.zshenv, .zshrc, .screenrc, ... etc ... を展開する作業とあわせてシェルスクリプトにした.このあたりは各自お好みで.

Mozilla Firefox(Minefield)ビルド手順

ビルド環境を整える

${HOME}/.mozconfigを作る

. $topsrcdir/browser/config/mozconfig
export CC=/usr/bin/gcc
export CXX=/usr/bin/g++

mk_add_options MOZ_CO_PROJECT=browser
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/firefox-build

ac_add_options --enable-application=browser
ac_add_options --enable-default-toolkit=cairo-gtk2

# below are not necessary. do as you like:
ac_add_options --disable-ldap
ac_add_options --disable-postscript
ac_add_options --disable-accessibility
ac_add_options --disable-printing
ac_add_options --disable-composer
ac_add_options --disable-mathml
ac_add_options --disable-svg
ac_add_options --disable-svg-foreignobject
ac_add_options --enable-debug
ac_add_options --enable-optimize=-O

ビルドの度に手でコマンドを打つのも面倒なので,シェルスクリプト書く.

  • build-trunk.sh
#!/bin/sh
# build-trunk.sh
# $Id: build-trunk.sh 87 2008-04-07 08:57:04Z genta $

# BRANCH="-r MOZILLA_1_8_BRANCH"
BRANCH=""
DIRNAME="mozilla_trunk"
LOG=${HOME}/log/build.${DIRNAME}.log
export CVSROOT=':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot'

# Output to STDOUT and STDERR gose to file ${LOG}
exec 4>&1 5>&2
exec 3>${LOG} 1>&3 2>&3

echo 'Begin building'
LANG=C date

# rotate dir and mkdir
if [ -d ${DIRNAME} ]; then
        if [ -d ${DIRNAME}.old ]; then
                mv ${DIRNAME}.old ${DIRNAME}.trash
                rm -rf ${DIRNAME}.trash
        fi
        mv ${DIRNAME} ${DIRNAME}.old
fi
mkdir ${DIRNAME}; cd ${DIRNAME}

cvs co ${BRANCH} mozilla/client.mk
cvs co ${BRANCH} mozilla/browser/config/mozconfig
mkdir mozilla/firefox-build
cd mozilla

cp ${HOME}/.mozconfig .

make -f client.mk checkout
make -f client.mk build 2>&1

echo 'Build done'
LANG=C date

echo 'Creating gtags'
LANG=C date
gtags -v
htags -saF
echo 'Done'

gtagsとかしている部分はお好みで.

ビルド中のログを出力するためのディレクトリを掘る.

% mkdir ~/log
ビルド
% ./build-trunk.sh

ビルド中のログは~/log/build.mozilla_trunk.logに落ちているので,気になる人は他の端末エミュレータを開いてtail -fしておくとよいと思う.

起動

無事ビルドが終わったら,起動してみる.

% cd mozilla_trunk/mozilla/firefox-build/dist/bin
% ./run-mozilla.sh ./firefox

run-mozilla.shの引数には "./firefox" と,"./" を忘れないように注意.忘れると/usr/bin/firefoxの方が起動したりしてわりと混乱することになる.

デバッガ(ddd)の上でfirefoxを起動するときは以下のようにする.

% ./run-mozilla.sh -g ./firefox-bin

runさせて適当なタイミングでinfo threadsとかして途方に暮れるのもよし.

ssh経由で起動するときは,DISPLAY環境変数を適当に設定しておくとよし.手元にX11が起動していればそれを使ってもいいと思う.

  • SSH端末(putty等)より以下を実行
% env DISPLAY=:0.0 ./run-mozilla.sh ./firefox-bin

これでVMware上でMinefieldが起動する.

GNU Globalで作ったHTMLを閲覧

htagsが~/mozilla_trunk/mozilla/HTML以下にHTMLファイルを作ってくれているので,これをソースコード解析の基点にすることができる.普通にUbuntuの上でWebブラウザを用いて閲覧するか,またはlighttpdとか適当なHTTPdをインストールして他のマシンから閲覧してもよい.

lighttpdのセットアップ (おまけ)

public_htmlを用意

% mkdir ~/public_html
% cd ~/public_html
% ln -s ../mozilla_trunk/mozilla/HTML

lighttpdインストール用スクリプト

#!/bin/sh
# build-lighttpd.sh -- Build lighttpd environment for reading source code

apt-get install lighttpd

cp lighttpd.user /etc/lighttpd
(cd /etc/lighttpd; patch ) < patch.lighttpd.conf
(cd /etc/lighttpd/conf-available; patch -p1 ) < patch.auth.conf

/usr/sbin/lighty-enable-mod auth
/usr/sbin/lighty-enable-mod userdir

/etc/init.d/lighttpd force-reload
yourname:yourBasicAuthPassword

お好みで.

*** lighttpd.conf.orig  2008-03-12 04:23:02.000000000 +0900
--- lighttpd.conf       2008-04-07 12:03:07.000000000 +0900
***************
*** 63,68 ****
--- 63,69 ----

  ## bind to port (default: 80)
  # server.port               = 81
+ server.port = 10080

  ## bind to localhost only (default: all interfaces)
  ## server.bind                = "localhost"

server.portはお好みで.

  • patch.auth.conf
*** conf-available/10-auth.conf.orig    2008-03-12 04:23:02.000000000 +0900
--- conf-available/10-auth.conf 2008-04-07 12:17:00.000000000 +0900
***************
*** 5,13 ****

  server.modules                += ( "mod_auth" )

! # auth.backend                 = "plain"
! # auth.backend.plain.userfile  = "lighttpd.user"
! # auth.backend.plain.groupfile = "lighttpd.group"

  # auth.backend.ldap.hostname   = "localhost"
  # auth.backend.ldap.base-dn    = "dc=my-domain,dc=com"
--- 5,20 ----

  server.modules                += ( "mod_auth" )

! auth.backend                 = "plain"
! auth.backend.plain.userfile  = "/etc/lighttpd/lighttpd.user"
! auth.backend.plain.groupfile = "lighttpd.group"
!
! auth.require = ( "/" =>
!                ( "method" => "digest",
!                  "realm"  => "Permitted user",
!                  "require" => "valid-user"
!                )
!              )

  # auth.backend.ldap.hostname   = "localhost"
  # auth.backend.ldap.base-dn    = "dc=my-domain,dc=com"

BASIC認証の設定ファイル.これまたお好みで.

インストール用スクリプトを実行.

% sudo ./build-lighttpd.sh

他のマシンから http://:10080/~/HTML/ をアクセス.以下はサンプル.

または,mozilla.orgが提供しているLXRも便利かも.

そのほか

インストール手順は,とりあえずshell scriptで書いておいて,SVNレポジトリに放り込んでおくと便利.新しいマシンをセットアップしたら,レポジトリの内容をtarアーカイブにしてSFTPして実行,という具合にすれば楽.

WEBrickをちょこっとだけ触ってみる

デバッグ用にちょっとしたWebサーバを作ってみたくなったので,WEBrickを触ってみた.

#!/usr/local/bin/ruby
require 'webrick'
require 'pp'

class MyServlet < WEBrick::HTTPServlet::AbstractServlet
  # Process GET Request
  #
  # _req_:: WEBrick::HTTPRequest
  # _res_:: WEBrick::HTTPResponse
  # returns:: WEBrick::HTTPResponse
  def do_GET(req, res)
    res['Content-Type'] = 'text/html; charset=utf-8'
    you = req.query['name'] || 'you'
    greeting = {'morning' => 'Good morning',
                'evening' => 'Good evening' }[req.query['time']] ||
               'Hello'
    res.body = "<html><body><p>#{greeting}, #{you}!</p></body></html>\n"
    return res
  end
end

srv = WEBrick::HTTPServer.new(:BindAddress => '127.0.0.1',
                              :Port => 10080 )
['INT', 'TERM'].each do |signal|
  trap(signal) { srv.shutdown }
end
srv.mount('/app', MyServlet)
srv.start

たったこれだけのコードで,localhost:10080上でWebサーバが起動した.こりゃおもしろい.何より,わかりやすい所がいい.AbstractServletを継承したクラスを作って,扱いたいHTTPリクエストのメソッドを処理する関数(do_GETとかdo_HEADとか)を定義して,WEBrick::HTTPServer#mountするだけ.シンプルで良い.

難点は,セッション管理とか細かい所の面倒を見てくれない所かな.探せばライブラリあるんだろうか.

Smartyをちょこっとだけ触ってみる

ほとんど忘れていたPHP5の復習がてら,Smarty触ってみた.

とりあえずサンプルコードから適当にコピペしてでっちあげる.動く.全く簡単だ(動かす分には).

メモ

  • {$var|escape:"htmlall":char_set}はhtmlentities($string, ENT_QUOTES, $char_set)相当
  • default_modifiersに書くと全項目で適用されるescapeを書けるけど,charsetを忘れずに指定しておく
    • さもないとXSS脆弱性になる可能性あり
      • 文字列の末尾にマルチバイト文字の1byte目っぽいコードを食わせてvalue="foo#" (#が0x81とか0xC0)とかして「"」をマルチバイト文字の一部だとWebブラウザに誤認させてマークアップを崩すアレ
    • escapeしたくない項目は,個別に{$var|smarty:nodefaults}とかすれば良し
    • ググッて見つかるようなSmartyのサンプルでは,なぜかdefault_modifiers=array('escape:"htmlall"')とか,htmlentitiesの第三引数(charset)書いてない場合が多い.何故だ.みんなノーガード戦法か?
  • テンプレート中で,meta〜charsetを何よりも先に書く.またはApacheの方でAddDefaultCharset: UTF-8とかしてHTTPレスポンスヘッダでcharset吐く(UTF-7 XSS対策)
    • PHPに限らないけど
  • 属性値をクォートしない奴はクビ
    • PHPに限らないけど
    • テンプレート例)
    • フォーム入力例)
      • `` style=`background-image: url(javascript:alert(document.URL))`
    • HTML出力例)
      • <input value=`` style=`background-image: url(javascript:alert(document.URL))`>
    • IE6は,バッククォートで属性値をクォートできるという素敵仕様のため,こんな感じでhtmlentitiesに引っかからない文字列だけでXSSできる(クォート忘れてると)
    • その他XSSの例: http://ha.ckers.org/xss

ToDo

  • templateはDocumentRootの外に置くなり403とかになるようにしとけ > 俺
  • Smarty自体もDocumentRootの外に置いてphp.iniでinclude_path通しとけ > 俺

ソース

index.php
<?php
require_once('smarty/Smarty.class.php');
$smarty = new Smarty;
$smarty->default_modifiers = array('escape:"htmlall":"UTF-8"');

$name = isset($_POST['name'])
          ? $_POST['name']
          : 'id:ihag';
$url  = isset($_POST['url'])
          ? $_POST['url']
          : 'http://d.hatena.ne.jp/ihag/';

$smarty->assign('name', $name);
$smarty->assign('url', $url);

$smarty->display('index.tpl');
?>
template/index.tpl
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Index page</title>
</head>

<body>
  <p>ユーザ情報表示:</p>

  <form method="POST" action="/index.php">
    名前: <input type="text" size="60" name="name" value="{$name}"><br>
    URL: <input type="text" size="80" name="url" value="{$url}"><br>
    <input type="submit" value="Submit">
  </form>
</body>
</html>