2004年11月24日
MovableType×IMDb
MovableTypeでIMDbへのリンクを張りたい人って結構いると思うんだけど、 Movable Plugins Directoryをみても、 投票履歴を表示するなにかしかみつからない。 んー。なんだかなあ、と思いつつ、やむをえないので作ることにしました。
というわけで、このへんはこのプラグインで生成されているわけです。 生成されるHTMLもテンプレート化して、Webで設定できるようにしたら公開するつもりでございます。
ちなみに、ドキュメントはこのへん。
2004年12月 1日
ツーカーS
説明書の要らないケータイ、という触れ込みで発売されたツーカーS。 ディスプレイなし、メール機能なし、電話帳なし、着信/発信履歴なし、ないないづくし。 ほかにもボタンを大きめにしたりスピーカを大きくしたり。 中心に松本人志をおいた「日本って、 おじさんとおばさんの国なんだ」という広告がなにかの広告賞をとったことでも有名になりましたが、 まあ要するにターゲットは高齢者なわけです。
確かに昨今のケータイは複雑怪奇、若者でさえすべての機能を使っているとは思えません。 でも、年齢を問わず利用しそうな機能って云うのはあるはずで、 電話帳と着信/発信履歴くらいは最低限必要だろうと思うわけです。 ツーカーSはこの機能までも省いていて、さてシニアに受け入れられるのかなあ、と。
で、ITmediaでシニアに対する調査結果の記事が出ていました。 うーむ、男性の5割半ばと女性の6割弱で「シンプルすぎる」ですか。
いまどき3割強の家庭がADSL/FTTH/CATVなどなどでインターネットに接続している時代なので、 シニアのみなさんのなかにもインターネットを利用している人は多いのでしょう。 機械に対する苦手意識もずいぶん減っているはずです。 シニアの6割弱で「シンプルすぎる」と感じているというのはツーカーにしてみれば予想外だとおもいますが、 最近の傾向を少し読み間違えたというところでしょう。 ツーカーの戦略は一昔前ならどんぴしゃだったかもしれないけど、 昨今のシニアの状況からすれば若干やりすぎたという結果だということだと思います。
まあ、シニアをばかにするな、といったところでしょうか。
2004年12月 3日
Apache AXIS C++
今回の実装合宿はWeb Serviceをつくるのが大きな目的です。 言語はC++ということで事前に合意していたのですが、 C++が使えるWeb Serviceってほんっとないんですよね。 みんなJavaばっかり。世の中どうかしてる。
で、調べていくとフリーで利用できそうなC++のWeb Serviceは、 おおよそ次のどちらかで決まりのようです。
前者はあるところで非常に評価がよかったらしく、また商用サービスでも利用されているようです。 一方、はドキュメントが充実していないことと、 WSDLからコードを生成するツール(WSDL2Ws)はJavaを利用している点でいまいちそう。 さらに、上記の評価では最低レベルです。
そんなわけでgSOAPで始めてみたのですが、 WSDLを食わせてみると出力するコードがとってもいまいち。 あと、Serviceの Deployもなんだかちょっとめんどくさそう。 このままコードを書き進めてもなんだか報われない気がしたので、 昨日の夜から評価の悪いのApache AXIS C++をNetBSDに入れるべく格闘していたわけです。 ドキュメントを端から端まで読んでがんばればできそうですが、 ちょっと癖があったりするので導入の仕方をまとめておきます。
まず、サーバのインストールからです。
1) 必要なパッケージのインストール
NetBSDではパッケージシステムとしてpkgsrcがあります。 Apache AXIS C++をインストールするために必要なものは、 すべてpkgsrcでインストールすることが可能です。
- Apache
- Apache AXIS C++はApacheのモジュールとして動作します。 したがって、www/apacheまたはwww/apache2が必要です。 以下ではwww/apache2を前提としています。
- XMLパーサ
- SOAPやWSDLのXMLを処理するためにXMLパーサが必要です。 textproc/expatかtextproc/xerces-cのどちらかをインストールします。 両方インストールし、必要に応じて切り替えながら使うこともできます。 pkgsrcでApacheをインストールすると、 依存関係からtextproc/expatがインストールされます。 以下では両方をインストールしていることを前提としています。
2) パッケージの取得と展開
準備ができたら、Apache AXIS C++のパッケージを このあたりから取得して展開します。
$ tar zxvf axis-c-src-linux-current-src.tar.gz axis-c-src-1-3-linux axis-c-src-1-3-linux/ChangeLog axis-c-src-1-3-linux/AUTHORS axis-c-src-1-3-linux/COPYING : axis-c-src-1-3-linux/vc/xml/xerces axis-c-src-1-3-linux/vc/xml/xerces/AxisXMLParserXerces.dsp axis-c-src-1-3-linux/vc/xml/xerces/AxisXMLParserXml4c.dsp $
ここではaxis-c-src-1-3-linuxというディレクトリができています。
3) 環境変数を設定
以下のふたつの環境変数を設定します。 これをさぼるとうまくいきません! 環境変数でなんとかしようというのが大嫌いな私は、 これでかなりはまりました。
- AXISCPP_HOME
- 2)で作成されたディレクトリへのフルパス
- AXISCPP_DEPLOY
- Apache AXIS C++をインストールしたいディレクトリ
ここでは前者を/usr/naoto/tmp/axis-c-src-1-3-linux、 後者を/usr/local/axisとします。
$ AXISCPP_HOME=/usr/naoto/tmp/axis-c-src-1-3-linux; export AXISCPP_HOME $ AXISCPP_DEPLOY=/usr/local/axis; export AXISCPP_DEPLOY $
4) configureの変更と実行
ここからはやっつけ仕事です。 ちゃんとパッチ作れよという話はもっともなので、 時間ができたらそのうち…
まず、configureに手を入れて実行します。 netbsdでは配布に含まれたままのconfigureは動作しません。 LDFLAGSを以下のように変更します。
LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib -lgcc_s -lstdc++ -lm"
おわったらconfigureを実行します。 xerces-cをインストールしていない場合や、 expatを利用しない場合は、 適宜オプションを変更してください。
$ ./configure --prefix=$AXISCPP_DEPLOY \
--with-apache2=/usr/pkg \
--with-xercesc=/usr/pkg --with-expat=/usr/pkg
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
:
config.status: creating config.h
config.status: executing depfiles commands
$
5) Makefileの修正
出力されたMakefileを修正します。 なんのためのconfigureじゃ! って、 ええ、わかってます、そのうち何とかしますから… まず、NetBSDにはlibdlが存在しないので、 すべてのMakefileから取り去ります。 zshを使っている場合は以下のような感じでしょうか。
$ cd $AXISCPP_HOME $ for i in **/Makefile;do > sed 's/-ldl//g' < $i > $i- > mv -f $i- $i > done $
Apache AXISの心臓部にあたるライブラリはpthreadを利用しているようですが、 リンクするように指定されていません。 該当するMakefileは、
- $AXISCPP_HOME/src/engine/client/Makefile
- $AXISCPP_HOME/src/engine/server/Makefile
のふたつです。 このなかのLDFLAGSを指定している行は、 以下のようになるはずです。
LDFLAGS = -L/usr/pkg/lib -R/usr/pkg/lib -lgcc_s -lstdc++ -lm -lpthread
さらにもうひとつ。 ApacheとAPR(www/apache2の場合だけ)のヘッダファイルは通常と異なる場所にインストールされています。 したがって、これも教えてあげる必要があります。 変更するMakefileは利用しているApacheによって異なります。 www/apache2の場合は、
- $AXISCPP_HOME/src/server/apache2/Makefile
です。INCLUDESは以下のようになります。
INCLUDES = -I/usr/pkg/include -I../../../include \ -I/usr/pkg/include/httpd -I/usr/pkg/include/apr-0
Makefileの変更は以上です。
6) コンパイルとインストール
普通の手順でコンパイル・インストールします。
$ make : # sudo -s # make install : #
7) Apache AXIS C++の設定
はやる気持ちはさておき、 まずApache AXIS C++の設定をします。 というのも、設定が正しくない場合はことごとくApacheごと落ちるからです。 逆に言えば、Apacheが正常に起動しない場合は設定がおかしいと思って間違いないでしょう。
設定ファイルは$AXISCPP_DELOY/etcにあります。 ここに_linuxがおしりについたファイルがあるので、 これを基に修正していきます。
# cd $AXISXPP_DEPLOY/etc
# for i in *_linux; do
> mv $i ${i%%_linux}
> done
#
サーバ側で修正が必要なのはaxiscpp.confとserver.wsddです。 これらのファイルはインストール先が /usr/local/axiscpp_deploy であることを想定して書かれているので、 インストール先にあわせて修正します。 また、 axiscpp.confのXMLParserには、 利用したいXMLパーサを指定します。 今回はxerces-cを利用するということで、 以下のように設定しました。
LogPath:/usr/local/axis/log/AxisLog WSDDFilePath:/usr/local/axis/etc/server.wsdd ClientLogPath:/usr/local/axis/log/AxisClientLog XMLParser:/usr/local/axis/lib/libaxis_xercesc.so Transport_http:/usr/local/axis/lib/libaxis2_transport.so
8) Apacheモジュールのインストール
上記のインストールでは、Apache AXIS C++のApacheモジュールは、 本来インストールされるるべきところにおかれていません。 以下のようにしてインストール(というか、単なるコピー)します。
# cp /usr/local/axis/lib/libaxiscpp_mod2.so \
/usr/pkg/lib/httpd
#
9) Apacheの設定
Apacheの設定ファイルに以下の行を追加します。
LoadModule axis_module lib/httpd/libaxiscpp_mod2.so <Location /axis> SetHandler axis </Location>
10) Apacheの起動
Apacheを(再)起動します。 ここで、Apache AXIS C++は環境変数AXISCPP_DEPLOYを参照し、 $AXISCPP_DEPLOY/etc/axiscpp.confを読みにいきます。 OSの起動時に/etc/rc.d/apacheで自動的に起動させる場合には、 どこかで環境変数を設定しておかなければなりません! このダサさをなんとかしたいところですが、 とりあえず/etc/rc.d/apacheの先頭で環境変数を設定することにします(涙
おわったらいつもの要領で、
# /etc/rc.d/apache start Starting apache. #
で起動です。 ただしくhttpdがいることを確認してください。
11) 動作確認
まず、サービスが正しくdeployされているかを確認します。 http://localhost/axisにアクセスして、 サービスの一覧が見えたらOKです。
つぎに、baseという試験用のサービスで確認します。
$ /usr/local/axis/bin/base Using service at http://localhost/axis/base invoking echoString... successful : invoking echoDecimal... successful $
ここでも環境変数AXISCPP_DEPLOYが必要なことに注意してください。 ここまででサーバのインストールはおしまいです。
つぎに、SWDLからskeltonを生成するツール、 WSDL2Wsです。
1) 必要なパッケージのインストール
WSDL2WsはJavaで記述されているので、 Javaの実行環境が必要です。
- Java(tm) 2 Runtime Environment
- Javaの実行環境には、 lang/sun-jre{13,14,15}のみっつのバージョンが用意されています。 どれでも問題ないとは思いますが、以下では1.5.0を利用しています。 なお、linux emulationにはsuse91_linuxをインストールしました。
Javaの実行ファイルはコンパイルされた状態で配布されていますが、 コンパイルするためにはJava(tm) 2 SDKが必要です。 どうせsun-jreもインストールしなければならないので、 ついでにsun-jdkもインストールしておくとよいでしょう。
2) jarファイルのインストール
$AXISCPP_HOME/lib/axisjavaにいくつかのjarファイルがあります。 これらを適当な場所にコピーします。 ここでは$AXISCPP_DEPLOY/javaにコピーします。
# mkdir $AXISCPP_DEPLOY/java # cp $AXISCPP_HOME/lib/axisjava/* $AXISCPP_DEPLOY/java #
さらに、$AXISCPP_HOME/lib/axisにwsdl2ws.jarがあるので、 同じようにコピーします。
# cp $AXISCPP_HOME/lib/axis/wsdl2ws.jar \
$AXISCPP_DEPLOY/java
#
3) 環境変数の設定
環境変数CLASSPATHを設定します。 上記でコピーしたjarファイルのフルパスを":"で接続してください。 このとき、wsdl2ws.jarが最初になるようにする必要があります。
4) WSDL2Wsの実行
これから生成するサービスのWSDLを用意し、 以下のように実行します。
$ sun15-java org.apache.axis.wsdl.wsdl2ws.WSDL2Ws \
TEST.wsdl -lc++ -sserver
:
Code generation completed.
$
これでめでたくskeltonが生成されているはずです。
5) コンパイル
skeltonの中身を埋めたら、コンパイルしましょう。
$ g++ -shared -I$AXISCPP_HOME/include -olibTEST.so *.cpp $
6) サービスのdeploy
コンパイルでできたライブラリを$AXISCPP_DEPLOY/libにコピーし、 service.wsddに記述を追加してdeployします。 設定が終わったらapacheを再起動する必要があることに注意してください。
2004年12月 8日
Thunderbird 1.0
Thunderbirdの初の正式リリース版1.0がリリースされました。 まあ、このこと自体はいろんなところで取り上げられてるので…。 Microsoftに対する反撃なんていってるニュースまであって、 ばっかじゃねーのと思うんですけど、それもさておき。
さて、ThunderbirdはよくできたMUAだというのは云うまでもないと思うんですけど、 使ってるといくつか苦しいことがあります。 その最たるものが、新着メールのチェック。
利用してるのが100% IMAPで、 しかもSIEVEを利用してサーバサイドリファイルしているのですが、 どうもINBOX以外の新着メールをうまくチェックする方法がないっぽい。 いや、Thunderbirdをあげっぱなしにしておけば、 それぞれのフォルダに到着した件数がちゃんと見えるんですけど、 やっぱり起動した直後は購読しているすべてのフォルダをチェックしてほしかったりするんですよね。 Wanderlustでは云うまでもなくできるし、 Outlookでもチェック対象のフォルダを設定できるので、 ぜひとも対応してほしいところです。 あるいはやり方を知らないだけなのかしらん?
2004年12月10日
Apache AXIS C++ 1.4 Alpha
気がつけばApache AXIS C++の1.4 Alphaが出てる。 まめに開発してるなあ。 1.4の目玉は以下のものらしい。
- SSLのサポート
- Cookieによるセッションのサポート
- SOAPヘッダによるセッションのサポート
下のふたつはクライアント側だけのようです。 サーバ側がすでにサポートしているのかどうかはなぞ。 たぶんされてないんだろうなあ。
ちなみに、NetBSDでの導入の仕方は1.3のときと変わりありません。
2005年1月16日
プログラミングRuby―達人プログラマーガイド
Perl捨ててRubyに移行したいなあと思いつつ早幾月。 てか、2年くらい経ってる気もしますが、 必要に駆られてRubyを覚え始めました(てか、一瞬で覚えました)。 で、前に覚えようと思ったときに買った本がこれです。
この本、とてもわかりやすい。 筆者が勘違いしてないのでさくさく読めるし、 rubyの本質が結構わかる仕上がりです。 ほら、rubyってperlと比較した本があったりして 「なに考えてんの?」と云いたくなるような本もたくさん出てるみたいですけど、 そんな中で圧倒的にお勧めです。 おかげで約1日でほぼ使えるようになりました。
| プログラミングRuby―達人プログラマーガイド | |
![]() | デビット トーマス アンドリュー ハント David Thomas Andrew Hunt おすすめ平均 ![]() いい本。でも、いきなりこれから入ると難しいかも。 良書。Ruby関連の書籍では最高の一冊。 もっともお奨めのRuby本Amazonで詳しく見る by G-Tools |
2005年1月18日
PostgreSQLでエラーになったステートメントを見たい
DBを叩くアプリケーションを書いていたりすると、 エラーが発生したときに、 どういうステートメントでこけているかを知りたくなります。 アプリケーション側でクエリを出力するようにしてもいいんだけど、 エラーが起きたときにDB側でステートメントをログに出力するほうが楽です。
PostgreSQLの場合は、postgres.confに以下のように書きます (ほかのDBはわからないけど、同じような感じでしょう)。
syslog = 2 syslog_facility = 'LOCAL0' log_min_error_statement = error
2005年1月19日
rubyのソースを読む
そんなわけでRubyし続けてます。 書いても書いてもなかなか行数の増えない不思議な言語ですが、 そのうちruby自体のコードも読みたくなったりしました。 てか、コード読みたくなった言語って初めてだ。
で、実は去年くらいから「Rubyソースコード完全解説」という本が出ているのも知っていたのです。 いまどきこんな本はあまり売れないだろうに、やるなあ、 と思っていたわけですが、ちょうどいいのでこれで読むことに。 実は、著者のサイト『Rubyソースコード完全解説』サポートページ でHTML版が公開されていたりするので、 これで読み始めるというのもいいかもしれません。 でも、いい本だからHTML読みながら本も買おうね。
| Rubyソースコード完全解説 | |
![]() | 青木 峰郎 おすすめ平均 ![]() C言語でオブジェクト指向しようAmazonで詳しく見る by G-Tools |
2005年2月28日
VMware nisetools
まさにWindowsを捨てようかと検討しているときにこういう話もなんですが、 WindowsでVMwareを使うときの話です。 って、たぶんLinuxでもいいと思うんですけど。
VMwareでは、GuestOS側でVMware toolsを動かすといろいろ便利になります。 例えば、時間がHostOSと同期するとか、その手のやつです。 しかし、GuestOSとしてNetBSDを使うと、 VMware toolsがないので、以下の選択肢のいずれかをとらないといけません。
- Linux版(あるいはFreeBSD版)のVMware toolsを気合で動かす
- ntpdなんかで気合で時間を同期
- 時間の同期はあきらめる(某北陸方面の人はこれですな)
これではあまりにかわいそう。というか、時間は何とかなっても、 XとHostOSの間はシームレスにポインタ移動してほしいし、 できればカット・バッファも共有してほしいですよね。
そこで、VMware nisetoolsを書いてみました。 基本的にはこのへんを実装しただけなんですが、 時間の同期と、なんとなくシームレスなポインタと、 ASCIIだけ対応したカット・バッファの共有ができます。
とはいえ、本人はすでにVMwareでXを使ってないので、 後半の機能は使ってないんですけどね…
2005年4月 8日
救急医療とインターネット
奈良先端科学技術大学院大学と生駒市消防局が、 救急車からの情報を救急救命センタへ配送するシステムを共同開発することが発表されました。 まあ、これのシステム開発の一部に携わっているわけですが。
実はこれ、いろいろな政治的理由があって、 いわゆる救急救命センタの先生方からはあまりヒアリングしてなかったりしました。 結局、見栄えがするのは動画像でしょうということで、 最初に持ってこられた内容が「現場と車両から動画を送りたい!」だったのです。 だけど、生駒なんて田舎で車載ルータからインターネットに接続なんて、 ほとんどPHSくらいしかないわけで、動画像配信なんて絶望的なわけで、 こんな状態で28kbpsの映像を見せてがっかりされてもやっぱり困るわけで。
ところが今日、記者発表の当日だというのに、 それが終わったあとで救急救命センタの先生方とお会いすることになったのです。
こちらは医療のど素人ですが、 先生方もまた動画像やインターネットの専門家ではありません。 でも、やろうとしていることの可能性を感じていただくことができたようです。 現状である技術の限界、そのなかでできることとできないこと、 将来的な技術の進歩に関する展望。 いろいろ聞いていただき、 救急医療現場で何が必要とされているかについて話してくださいました。
こういう経験をするたびに、 インターネットも社会的なインフラになったなあ、と思うとともに、 領域を超えたコラボレーションの面白さと難しさを感じるのです。 とりあえず、現場の先生方と話せてよかった。 だいぶがんばる元気がでました。
2005年4月25日
sendmailの罠
昨日から、同じメールがずーっと再送されるという現象に悩まされていました。 管理下にあるサーバの構成は以下の通り。
外部 -(smtp)-> postfix -(smtp)-> clamsmtp -(smtp)-> postfix
-(pipe)-> bogofilter -(pipe)-> postfix -(lmtp)-> cyrus-imapd
ここの、bogofilterからpostfixへのpipeで、Broken Pipeと怒られている。 bogofilterは実際にはrubyで書かれた簡単なスクリプトで、 そこからbogofilterを通したメールをPostfix sendmailに突っ込んでるわけです。
調査の結果、メールが途中でちょんぎれてることもあり、 これは「.」問題かと。 なんとsendmail(もちろん互換性問題でPostfix sendmailも含む)は、 標準入力からメールを取得しても「.」で終わりだとおもうのね。 これを回避するためには-oiオプションが必要なんだと。 世界中で同じようなスクリプトが書かれてること間違いなしだな、これ…
2005年7月18日
dump(8)
dump(8)でバックアップをとるときは、 誰しも簡単なスクリプトを書いて使ってたりするものです。 けど、こういうのって残しておかないとなくなっちゃうんだよね…
というわけで、はたと思い立ってここに残すことにする。 まあ、この手のスクリプトにありがちなように、 たいしたものではないんですが。
#!/bin/sh
DUMP=/sbin/dump
DATE=/bin/date
DEVICE=/dev/nrst0
TARGET=/home
DUMPDATE=`${DATE} +%Y%m%d`
MONTHDAY=`${DATE} +%e`
WEEKDAY=`${DATE} +%w`
# week S M Tu W Th F S
# (0th) (2)(4)(6)(8)(4)(6)(8)
# 1st 0 4 6 8 4 6 8
# 2nd 2 4 6 8 4 6 8
# 3rd 2 4 6 8 4 6 8
# 4th 2 4 6 8 4 6 8
# 5th 2 4 ...
case ${WEEKDAY} in
0) # Sunday
if [ ${MONTHDAY} -le 7 ]; then
LEVEL=0
else
LEVEL=2
fi
;;
1) # Monday
LEVEL=4
;;
2) # Tuesday
LEVEL=6
;;
3) # Wednesday
LEVEL=8
;;
4) # Thursday
LEVEL=4
;;
5) # Friday
LEVEL=6
;;
6) # Saturday
LEVEL=8
;;
esac
${DUMP} -u -c -d 327670 -s 1000000 \
-${LEVEL} -L ${DUMPDATE}L${LEVEL} -f ${DEVICE} ${TARGET} 2>&1
2005年7月28日
OID assignments
このページ、よく忘れちゃうので。 OID調べるときにとても便利です。
2005年8月 4日
PL/Ruby
このへん。 PostgreSQLでもruby、何でもありますなあ。
インストールした後はお約束ですが、
CREATE FUNCTION plruby_call_handler() RETURNS language_handler
AS '/usr/pkg/lib/ruby/site_ruby/1.8/powerpc-darwin/plruby.bundle', 'plruby_call_handler'
LANGUAGE c;
CREATE TRUSTED PROCEDURAL LANGUAGE plruby HANDLER plruby_call_handler;
です。
2005年10月14日
jsreport
ってないのかしらと思ったら、オプションで対応。
2005年10月15日
Buffaloとフレッツ・スクウェア
Buffaloの箱、たとえばAirStationとフレッツ・スクウェアの関係。 ながらく、さっぱりつながらない原因がわからなかったんだけど、 箱の設定に問題があることがわかりました。 アタックブロック設定でIP Spoofingのブロックが有効になってるとだめなんだって。 こんなに小さく書かれたって、気づくわけないつーの。
これ、だれも困ってないのかなあ。
2005年10月17日
ブログの女王
ブログの女王、眞鍋かをりですが、 (ただのブログなのに)LBSいれないととてもじゃないけどもたない、 なんて話を聞きました。 で、見てみると…
………トラックバック3,000超えてたりするじゃん? おっかしーよ、これ。 さすが女王と云うだけありますな…
2005年10月20日
Perl5が11歳?
ほんと早いもんだ… 11年前というと、ちょうど初めて駱駝本を開いたときで、 そのときはperl4だったんだけど、すぐにperl5の駱駝に買い換えた記憶が。 でも、さっぱりわかんなかったんだよなあ。
いまでも、perl5をperl4のように使ってるひとは多いと思う。 おれはどうかっつーと…ええ、perlはもう使ってません。
2005年11月18日
OpenVPN
最近、いろいろtipsをMTに書いてないと云って怒られ気味。 で、なんとOpenVPNについても書いてないことが発覚したので、メモ代わりに。
OpenVPNは、 オープンソースのVPNの実装です。 プロトコルが独自仕様なところがいたいですが、 動作プラットフォームは各種UNIXからMacOSX、Windowsまでをサポートしており、 L2 VPNおよびL3 VPNが可能です。 L2 VPNといえば昨年S**tE***rが一世風靡しましたが、 あれを購入するくらいならOpenVPNのほうがよっぽどいいでしょう。
で、実はOpenVPNは難しいことはなにもありません。 証明書発行してサーバとクライアント双方に仕込めばおしまい。 でもこれじゃつまんないのでいくつかtipsを…
CRLをどうするかApacheでHTTPSすると困るのがCRLですが、OpenVPNの場合はどうでしょう? というわけでドキュメントを見てみると次のような記述があります。
When the crl-verify option is used in OpenVPN, the CRL file will be re-read any time a new client connects or an existing client renegotiates the SSL/TLS connection (by default once per hour). This means that you can update the CRL file while the OpenVPN server daemon is running, and have the new CRL take effect immediately for newly connecting clients.
ううむ、偉すぎる。 アクセス頻度が違うのでApacheがこれをやるとえぐいんですけどね。 というわけで、cronかなにかで更新するようにすればおしまい。
CNでフィルタしたい!そもそも、基本的に証明書ベースの認証は、 証明書を持ってればだれでもおっけーになりがちです。 もちろん、EKUつかってごにょごにょしたりできますが、 現実的にはこのような方法を使っているケースは稀でしょう。 というわけで、CNベースでサービスをフィルタしたいという要望が出てきます。
OpenVPNの場合は、設定ファイルにtls-verifyというのがあります。 以下は、openvpn(8)から抜粋。
Execute shell command cmd to verify the X509 name of a pending TLS connection that has otherwise passed all other tests of certification. cmd should return 0 to allow the TLS handshake to proceed, or 1 to fail.
これをつかって、たとえば以下のようなやっつけスクリプトを書いておきます。 ここではverify-cnとしましょう。
#!/bin/sh
RUBY=/usr/pkg/bin/ruby18
DENY=1
if [ ! -x "${RUBY}" ]; then
exit ${DENY}
fi
exec ${RUBY} -S -x $0 "$@"
#! ruby
ALLOW, DENY = 0, 1
class VerifyCN
def initialize(allowlist)
read_allowlist(allowlist)
end
public ;#
def verify(depth, x509_name_oneline)
return ALLOW if depth.to_i != 0
return DENY \
unless x509_name_oneline =~ /\/CN=([^\/]+)/
return DENY \
if @allowlist_esc.find{|cn_esc| cn_esc == $1 }.nil?
return ALLOW
end
private ;#
def read_allowlist(allowlist)
@allowlist = IO.readlines(allowlist)
@allowlist_esc = @allowlist.collect{|cn| cn.chomp.gsub(/ /, "_") }
end
end
allowlist = ARGV.shift
verifier = VerifyCN.new(allowlist) \
rescue exit(DENY)
exit(verifier.verify(*ARGV)) \
rescue exit(DENY)
で、設定ファイルには、
tls-verify "/path/to/verify-cn /path/to/allowlist"
ここで指定しているallowlistというファイルには、 接続を許可するクライアント証明書のCNを、 1行にひとつずつ記述しておきます。
という具合で、結構融通が利くOpenVPN、 L2モードでもクライアント間通信を制限することもできるし、 TCPでもUDPでも使えます。 TCPの場合はTCP over TCP問題がありますが、 HTTP Proxyを通したりもできるらしい(未検証)。 というわけで、みなさんもいかがですか?
2005年11月19日
GNU id-utils for Windowsはいずこに?
もはや開発は完全に止まっているのでほとんど知られていないかもしれませんが、 GNU id-utilsというソフトウェアがあります。 ソース内のtokenを拾い出してデータベース化し、 それを検索できるというもの。 似たようなものにGNU globalとかありますけど、 やっぱりid-utilsのほうが断然よい(慣れてるからかしら)。
で、id-utilsをEmacs(当時はmule 2.1だったけどね)から使いたいがために ElScreenを書いて、 その上にElScreen-GFをかぶせるようにしたのでした。
ところでいまどきの多くの学生は、 嘆かわしいことにコードの読み方をほとんど知りません。 そこで、MeadowでElScreen-GF使えよ…と云いかけたんですけど、 これまたEmacsの使い方もよく知らないらしいというのは置いておくとして、 id-utilsのWin32版はあるのか?という問題にぶち当たってしまいました。
そんなわけで軽く捜索してみたんですが、さっぱりない。 GNUWin32で2004年の最初くらいまでは公開されてたみたいなんですが、 さっぱり見あたらないよー。 どなたか、ここにあるぜ節穴め!というかた、教えてください…
GNU GLOBAL
id-utilsのところで「GNU GLOBALよりid-utilsのほうが断然よい」と書きましたが、 理由を思い出した。 GNU GLOBALの出力フォーマットがさっぱりいけてなくて、 機械的に処理できないんだ。 しかもフォーマットを指定することもできないぽい。 なんでgrep互換出力サポートしないかねえ…
2005年12月17日
ID Utils 4.0
やっぱりメンテナンス再開されてるのね。
2006年2月16日
ID Utils 4.0を使ってみた。
ID Utils 4.0は基本的に3.2から大きな(機能面での)変更点はありません。 とはいえ全くないかというと、perl対応が追加されてたりします。
個人的には、言語ごとのスキャナはplug-in形式にして本体から追い出すべきだと思ってるんですが、 ちょっとこれをまじめに書き直すのはパワーがいりそうなのでしばらくパス。 でもscanners.cがふくれていくのはいまいちうれしくないなあ。
で、めっきりperlでコード書かなくなったのでわかんないんですが、 手元にあったpmで試してみた。 まあ、ふつうに使えますなという感じですが、 大規模プロジェクトでどれだけうれしいのかはよくわかんない。 んー、rubyのスキャナ欲しいなあ。
2006年2月19日
weakシンボル
いやなブログのこのへんに、 weakシンボルが説明されていた。 使い方によっては便利なんだろうけど、やっぱり移植性が下がるのがちといまいちか。
それにしても、gccの独自拡張は知らないことが多いなあ…
2006年3月28日
spamフィルタ偉大
だいぶ前からspamフィルタを入れて自動振り分けしてますが、 思い立ってどれくらいspamが来てるのか調べてみた。 ちょこちょこフィルタを抜けてくるspamも増えてきたので、 どうなってんだろうかってことで。
で、今月の今日までの総数(自動振り分けの対象になった数):3846
Received見てるのでそれなりの精度ですが、昔に比べたらだいぶ増えたなあ。 まあ、もっとえぐいひとはたくさんいると思うんですけど、 やはりspamフィルタ偉大、という話でした。
2006年4月 2日
virusフィルタ偉大
ついでに昨年度のvirusの数も数えてみました。 完全に正確ではないんですが、サーバ側で検出して捨てた数が(フィッシングも含めて)3,000くらい。 これ、他のサーバでも一度検出して捨てられてるので、実際には倍くらいあるかもしれない。 とすると、月刊500通?まじすか。
virusフィルタ偉大
ついでに昨年度のvirusの数も数えてみました。 完全に正確ではないんですが、サーバ側で検出して捨てた数が(フィッシングも含めて)3,000くらい。 これ、他のサーバでも一度検出して捨てられてるので、実際には倍くらいあるかもしれない。 とすると、月刊500通?まじすか。
2006年4月15日
参照されていないレコードを探す
今回はSQLの話。 一対一の関係づけを考えます。 一般的には一対多の場合と同じようにどちらかに外部参照キーをつけることになると思います。 このとき、迷子というか相方のいないレコードを探すにはどうしたらいいでしょうか。
例えば、次のようなふたつのtableがあるとします。
CREATE TABLE computers (
id SERIAL NOT NULL,
name VARCHAR(8),
PRIMARY KEY (id)
);
CREATE TABLE keyboards (
id SERIAL NOT NULL,
computer_id INTEGER,
keymap VARCHAR (8),
PRIMARY KEY (id),
FOREIGN KEY computer_id
REFERENCES computers (id) ON DELETE SET NULL
);
テーブルの内容には特に深い意味はありません。 で、KVMとか使ってないとしてコンピュータとキーボードは一対一の関係だとします。 Railsを使ってるとmodelは以下のようになるでしょう。
class Computer < ActiveRecord::Base has_one :keyboard end class Keyboard < ActiveRecord::Base belongs_to :computer end
さて、迷子のキーボードを探すのは簡単です。
SELECT * FROM keyboards WHERE computer_id IS NULL;
問題は迷子のコンピュータを探す場合です。 一般的にどうするかはよくわかりませんが、例えば次のようにすれば実現できます。
SELECT computers.* FROM computers LEFT OUTER JOIN keyboards ON computers.id=keyboards.computer_id WHERE keyboards.computer_id IS NULL;
LEFT OUTER JOINして、さらに相方がいないやつを探しています。 Railsのmodelでは、以下のようにしておくといいでしょう。
class Computer < ActiveRecord::Base
has_one :keyboard
class << self
def orphans(order = nil)
options = {
:select => 'computers.*',
:joins => 'LEFT OUTER JOIN keyboards ON computers.id=keyboards.computer_id',
:conditions => 'keyboards.computer_id IS NULL',
}
options[:order] = order if order
find(:all, options)
end
end
end
class Keyboard < ActiveRecord::Base
belongs_to :computer
class << self
def orphans(order = nil)
options = {
:conditions => 'computer_id IS NULL',
}
options[:order] = order if order
find(:all, options)
end
end
end
2006年6月29日
OpenSSLでPKCS#12に変換
これも何回やっても忘れるのでメモ…
openssl pkcs12 -export \ -in hoge_crt.pem -inkey hoge_key.pem -CApath /etc/opessl/certs \ -out hoge.p12
2006年7月28日
teTeX3とdvipdfmx
MacOSXでpkgsrcを使ってるのは物好きらしいですが、その物好きなわけです。 理由はいろいろあるんだけど、まあそこは今回の主題ではないので触れないことで。
で、いつだったかteTeXを3系統に更新しました。 バージョン2ではコンパイルのときにちょっくら細工をしないとだめだったんですが、 そういうこともなくなり快適…かとおもいきや、思わぬ落とし穴が。 dvipdfmxがまともに動かないのです。
これ、ちゃんと調べてませんが、おそらくpkgsrcがどうこうというよりは、 teTeX3ではちゃんと環境にあった設定をしろよって方針に変わったんですかね。 結論から書くと、設定がことごとくされてなかったと云うことです。 まず、
** WARNING ** Could not locate a virtual/physical font for TFM "rml". ** WARNING ** >> There are no valid font mapping entry for this font. ** WARNING ** >> Font file name "rml" was assumed but failed to locate that font. ** ERROR ** Cannot proceed without .vf or "physical" font for PDF output...
って怒られます。dvipdfmxではフォントはcid-x.map (pkgsrcでは/usr/pkg/share/texmf-local/dvipdfm/config/cid-x.map) に書かれてるんですけど、見事に空っぽ。 ってことで、お好みに応じてなんですけど、次のような感じに。
%% This file 'cid-x.map' is read by dvipdfmx for default %% dvipdfmx.cfg setting. Please append fontmap entries here. rml H Ryumin-Light gbm H GothicBBB-Medium rmlv V Ryumin-Light gbmv V GothicBBB-Medium
さて、これで万事解決かというとそういうわけにもいかず。
** WARNING ** No image converter available for converting file "xxxxx.eps" to PDF format. ** WARNING ** >> Please check if you have 'D' option in config file. ** WARNING ** pdf: image inclusion failed for "xxxxx.eps". ** WARNING ** Failed to read image file: xxxxx.eps ** WARNING ** Interpreting special command PSfile (ps:) failed. ** WARNING ** >> at page="4" position="(141.304, 552.345)" (in PDF) ** WARNING ** >> xxx "PSfile="xxxxx.eps" llx=0 lly=0 urx=1039 ury=612 rwi=103"
まーじで。teTeX2ではこんなことはなかったんだがなーとおもいつつ、 云われるがままにDオプションを確認するとdvipdfmx.cfg (/usr/pkg/share/texmf-local/dvipdfm/config/dvipdfmx.cfg) を確認すると(ちょっと長いので途中折り返してます)、
%% *Examples* for GhostScript (PS-to-PDF)%% %% The following example decode all DCT (i.e., JPEG) encoded images %% and then recompress images with Flate (zlib, same as PNG) encode %% filter. If you are using recent version of gs, then please remove %% -dAutoFilterXXXImages and -dXXXImageFilter. %% /FlateEncode is introduced to avoid quality loss of "EPS JPEG" %% images. %% %% Also note that PAPERSIZE=a0 is specified below. This convert PS %% files (including EPS) to A0 papersize PDF. This is necessary to %% prevent gs from clipping PS figure at some papersize. (A0 above %% simply means large size paper)%% %% GhostScript (Unix/Linux): %D "gs -q -dNOPAUSE -dBATCH -sPAPERSIZE=a0 -sDEVICE=pdfwrite -dCompatibilityLevel=1.3 -dAutoFilterGrayImages=false -dGrayImageFilter=/FlateEncode -dAutoFilterColorImages=false -dColorImageFilter=/FlateEncode -dUseFlateCompression=true -sOutputFile=%o %i -c quit" (略)
全部コメントアウトされてるじゃん… ってことで、gsのコメントアウトをはずして、めでたしめでたし。 ちなみに、Windowsの場合はその次のgswin32cを使えばいいだろうし、 Acrobat持ってるひとはdistill使えばいいってことでしょうね。 MacOSXのAcrobatではDistillerを単独起動できなさそうなので、 まあgsを使って満足。
2006年8月 8日
GNU idutils-4.2
気づいてなかったんですが、 GNU idutils-4.2がリリースされてました。 4.1からは不具合修正だけらしいですが、 4系は3系からjavaやlispのサポートが追加されているので、 このあたりを使ってるひとにはうれしいかもしれないですね。
2006年8月26日
Ruby 1.8.5
おお、若干予定から遅れてRuby 1.8.5がリリースされたようだ。
2006年9月10日
Subversionのcommit logを変更する
普通、commit logを後から変更するなんてことはしてはいけないんですが、 「あー、typoした!!!」なんてこともたまにあったりして、 そんなときにはお願いだから変更させてください神様、 とか思ったりするものです(しないって
svn:ignoreを設定したことがある人ならpropeditでsvn:logあたりを変更すればいんじゃない? と思い至るような気もするんですが、 さすがにrevisionに対するpropedit(revprop)は、デフォルトでは禁止されているのです。 これを有効にするには、hooksにpre-revprop-changeを追加します。 pre-revprop-change.tmplがあるので、それをcpしてchmod +xしてください。
$ cd /path/to/svn/repository/hooks $ sudo cp pre-revprop-change.tmpl pre-revprop-change $ sudo chmod +x pre-revprop-change $
pre-revprop-change.tmplの内容はsvn:logに対する変更だけ許可するというものなので、 通常は上記の設定で十分でしょう。 これができたら、propeditします。
$ svn propedit --revprop -r N svn:log
ちなみに、このあたりに説明があります。
2006年9月12日
Subversion 1.4.0
Subversionネタを書いたからというわけではないでしょうが、 1.4.0が出てました。 svnsyncの提供とさまざまな高速化が主な変更点のようですが、 OSX使いにはうれしい変更が。 なんと、 通常のパスワード認証のパスワードがキーチェーンを使って保存されるようになりました。 もちろん、今までにキャッシュされたパスワードはそのまま利用できます。 が、キーチェーンに移動したい場合は~/.subversion/auth/svn.simple/にあるファイルを消せば、 次に聞かれたパスワードはキーチェーンに保存されることになります。
残念ながら、キーチェーンを使ったクライアント証明書のサポートまでは追加されていませんでした…
2007年1月29日
BibTeXで日本語の参考文献
この時期、あちらこちらでBibTeXが使われまくってると思うんですが、 日本語の文章でBibTeXを使うとちょっとした問題があります。 それは、日本語の参考文献の日付が英語になってしまうことです。 以下みたいに。
[31] 参考花子, 参考太郎. BibTeXで日本語を使う際の日付の出力に関する研究. BibTeX研究会 (BibTeX2005), pp.141-144, July 2005.
ちうわけで、このださい“July 2005”を“2005年7月”にしましょう、 というのが今回の趣旨です。
といっても、なかなかきれいな方法はありません(しらないだけかも)。 戦略としては、
- エントリが日本語か英語かで出力を変える
- 日付を出力する場面では月が既にマクロ展開されているので、 展開後の文字列に対応する月の数値(ややこしいな)に変換する
という感じ。 以下ではjunsrt.bstに対するパッチを掲載していますが、 月の名称と数字の対応だけに気をつけて変更すれば、 同じ戦略で他のスタイルにも適用できるでしょう。
--- junsrt.bst 2007-01-29 11:34:14.000000000 +0900
+++ junsrt.bst 2007-01-29 12:35:30.000000000 +0900
@@ -355,19 +355,45 @@
}
while$
}
+FUNCTION {format.date.month.to.number}
+{ month empty$ { "" }
+ { month "January" = { "1" }
+ { month "February" = { "2" }
+ { month "March" = { "3" }
+ { month "April" = { "4" }
+ { month "May" = { "5" }
+ { month "June" = { "6" }
+ { month "July" = { "7" }
+ { month "August" = { "8" }
+ { month "September" = { "9" }
+ { month "October" = { "10" }
+ { month "November" = { "11" }
+ { month "December" = { "12" }
+ 'month
+ if$ } if$ } if$ } if$ } if$ } if$ }
+ if$ } if$ } if$ } if$ } if$ } if$ }
+ if$
+}
FUNCTION {format.date}
{ year empty$
{ month empty$
{ "" }
{ "there's a month but no year in " cite$ * warning$
- month
+ is.kanji.entry
+ { format.date.month.to.number "月" * }
+ 'month
+ if$
}
if$
}
{ month empty$
'year
- { month " " * year * }
+ { is.kanji.entry
+ { year "年" * format.date.month.to.number "月" * * }
+ { month " " * year * }
+ if$
+ }
if$
}
if$
まあ見たまんまなんですが、戦略の前半はformat.dateに手を入れることで、 後半は関数format.date.month.to.numberを追加することで実現しています。 このスタイルだと、最初の例は以下のようになります。
[31] 参考花子, 参考太郎. BibTeXで日本語を使う際の日付の出力に関する研究. BibTeX研究会 (BibTeX2005), pp.141-144, 2005年7月.
2007年2月19日
IP/TCP/UDPのチェックサム
昨日のエントリでip_sumは自前で計算してもいいよと書いたこともあって、次はIPチェックサムについての話題です。 IPチェックサムとはいえ、TCPやUDPのチェックサムも計算の仕方は同じです。 SOCK_RAWでは、IPデータグラムのチェックサムの面倒は見てくれますが、そのペイロードは知ったことではありません。 すなわち、SOCK_RAW経由でTCPセグメントやUDPデータグラムを送信する場合は、自分でチェックサムの計算をしなければならないと云うことです。
さて、IP/TCP/UDPのチェックサムの導出方法ですが、ずばり「1の補数和の1の補数」を計算すればいいのです…って、じゃあ「1の補数和の1の補数」ってなんやねん、と。 まあそうなりますよね、普通。 というわけで、こいつの謎を解明するのがこのエントリの主な目的です。
それではまず、「補数」から見ていきましょう。
補数ってなんだ
補数の詳しい定義は情報処理の教科書とかこのへんを見ていただきたいのですが、要するにN進数のある数値αに対して、
- Nの補数
- αに足して桁がひとつ増えるような最小の自然数
- (N-1)の補数
- Nの補数より1小さい自然数(αとの和が「αと桁数が同じ自然数のうち最大のもの」になるような自然数)
ということです。たとえば、十進数26の10の補数は74です。 両者の和をとると100で桁がちょうど3桁になりますね。 ということは、上記から1を引いた数、すなわち73が26の9の補数ということになります。 もうひとつ例をあげておきましょう。二進数101bの2の補数は011bです。 両者の和をとると1000bでちょうど桁がひとつ増えることが確認できます。 さらに、ここから1を引いた010bが101bの1の補数です。
このふたつの例から、補数の直感的な求め方がなんとなくわかるでしょうか。
まず、N進数αの(N-1)の補数を考えてみましょう。 「αと桁数が同じ自然数のうち最大のもの」は(N-1)を桁数だけ並べたものになります。 たとえば、十進数26に対しては99、二進数101bに対しては111bです。 (N-1)の補数は合計してこれになるような正数、すなわち、これからαを引いた正数になるわけですが、各桁が(N-1)ですから位をまたがる引き算は発生しません。 つまり、(N-1)から各桁をそれぞれ引いたものを並べればいいわけです。 十進数26では、9-2=7、9-6=3ですから73、二進数101bでは1-1=0、1-0=1、1-1=0ですから010bになります。
ところで、この例からもわかるように、二進数に対する1の補数は簡単に求められます。 αのある桁が0であれば1の補数では1-0=1に、1であれば1-1=0になりますが、これは明らかにαのビット単位否定をとったものです。 つまり、単純に0と1を入れ替えるだけでいいのです。
ここまで来れば簡単。 N進数αのNの補数は(N-1)の補数に1加えたものとして求めることができます。 十進数26では73+1=74、二進数101bでは010b+1=011bです。
2の補数と負数の内部表現
さて、2の補数は計算機内で負数を表すための表現に応用されています。 このため、「2の補数=計算機における内部表現」と覚えている人もいるかもしれませんが、上記の通り、補数というのは単なる定義です。 では、なぜ2の補数を負数の内部表現に使うのでしょうか。 その前に、計算機における「2の補数」について説明しなければなりません。
計算機では、整数を表すためにある決まったビット数の表現を使います。 たとえば、C言語で云うshortは16ビットの整数表現です。 計算機では、この整数を(内部にどんな値が格納されていようと)16桁と考えます。 そして、2の補数は16桁に対して計算するのです。
例えば、0xFF0Eという16ビットの値があったとしましょう。 これに対する(二進数の)2の補数は…そうですね、0x00F2です。 これらの和をとると0x10000で17ビットになりました。 それでは、0x00F2という16ビットの値に対する2の補数はどうでしょうか。 答えは、0xFF0Eです! 0x00F2は8桁の二進数で、0xEを加えれば0x100と9桁になりますから、上記の定義によればこの値が2の補数になりそうなものです。 しかし、ここでは0x00F2を16ビット(16桁)の二進数と考えていますので、17桁になる最小の整数である0xFF0Eが0x00F2の2の補数になるわけです。 ちなみに、0x0000にどんな16ビットの整数を加えても16ビットで収まりますから、16ビット整数0x0000に対する2の補数は存在しません。
さて、ではどうして上記のような2の補数が計算機で負数の内部表現に応用されているのでしょうか。 それは、2の補数があらわす負数をうまく決めると非常に計算に都合がいいからです。 たとえば、16ビットのある整数α(≠0x0000)とその2の補数βを考えましょう。 この場合、任意のαに対してα+β=0x10000になりますね。 これを16ビットで表現するためにあふれた桁を無視すると、0x0000です。 つまり、βはαの符号を反転させた整数であると定義すれば、加算してオーバーフローした桁を無視することで、減算処理を負数の加算処理に変換することができるのです。
計算機内部ではこのような性質を利用し、最上位ビットが0のものを正数とし、それに対応する2の補数(最上位ビットは必ず1)を対応する負数として扱っています。 たとえば、0x0001 (=1)に対する2の補数0xFFFFは-1を表しますし、以下同様に、0x0002 (=2)に対する2の補数0xFFFEは-2を表すわけです。
最大の正数と最小の負数 (ぐるぐるまわる!)
ところで、計算機内部の整数表現は有限の桁数ですから、最大値と最小値が存在します。 たとえば、16ビットの正数表現では0xFFFF (=65535)が最大値で0x0000 (=0)が最小値です。 それでは、16ビットの整数表現の最大値と最小値はなにになるでしょうか。 答えは、最大値が0x7FFF (=32767)、最小値が0x8000 (=-32768)です。
おっと、なにか違和感を感じませんか? そうです、和をとると0になる2の補数が必ず存在するはずなのに、最大値と最小値が違います! 0x8000に対応する2の補数はどこにいってしまったのでしょうか… と、少し考えるとさらに驚くことに気がつきます。 0x8000の2の補数は自分自身なのです! そのため、0x8000は「最上位ビットが1のものは負数を表現するものとする」という定義に基づいて-32768を表し、和をとって0になるものは存在しないのです。
と、違和感が解消したところで次の疑問。 16ビットの正数表現であれば、最大値である0xFFFFに1を加えるとオーバーフローして0x0000に戻りました。 それでは、16ビットの整数表現における最大値0x7FFFに1を加えると何が起きるでしょうか? ここは実際にコードを書いて実験してみましょう。
#include <stdio.h>
int
main(void)
{
int i = 0x7fff;
printf("i = %hd(0x%04x)\n", i, i);
printf("i + 1 = %hd(0x%04x)\n", i + 1, i + 1);
return 0;
}
このコードを実行してみると、次のようになるはずです。
i = 32767(0x7fff) i + 1 = -32768(0x8000)
![]() |
なんと、計算機内部の整数表現では、最大値に1を加えると最小値になるのです。 これが計算機内部における負数表現と2の補数の、もうひとつの秘密です。 そもそも内部的にはただの16ビット正数ですから、0x7FFFに1を加えると0x8000になるのは当然なのです。 それを符号付き整数のときにも拡張して、最大正数に1を加えると最小負数になるというふうに決めましょう、ということです。
今まで説明してきた計算機内部の整数表現を図に描くと右のようになります。 これまでの説明とあわせるために16ビット整数の場合のようすを示しています。 一応、2の補数を負数の表現として利用するという説明に基づいて描いてありますが、 この図ははっきり云って負数とかなんとかはもうどうでもいい世界です。 大事なことは、
- 加算して16ビットからあふれるとオーバーフローは無視
- (この性質から) 0xFFFFに1加えると0x0000
- 0x7FFFと0x8000の間は最小の負数と最大の正数の境界とはいえ実際はただの整数なので、0x7FFFに1を加えると0x8000
ということです。 このような、2の補数を内部表現として使った場合の加算を、「2の補数和」と云います。
内部表現として1の補数をつかってみると
さて、ここまでは計算機の内部表現として一般的に利用されている、2の補数を説明してきました。 しかし、ふと疑問が過ぎります。過ぎらない?いや、過ぎってください、過ぎらないと話が続きません。 内部表現として1の補数を使うと何が起きるんでしょうか?
負数を計算機の内部でどのように表現するかは、計算機の黎明期にかなり議論されたようです。 結局、いくつかの方式が生き残ったのですが、2の補数も1の補数も生き残った候補のひとつです。 2の補数を利用した場合、ある整数αの2の補数βは、αの符号を反転したものであるという定義でした。 同様に、1の補数を利用した場合は、ある整数αの1の補数β'は、αの符号を反転したものであるという定義です。 既に述べたように、αとβの和が(オーバーフローを無視して)0になる利点によって、今日では負数表現といえば2の補数、というふうになっています。 しかし、歴史的には1の補数を負数表現に使おうと思っていたひともいたでしょうし、実際、IP/TCP/UDPのチェックサムには1の補数が応用されているわけです。
さて、1の補数の例を挙げてみましょう。 0x0001 (=1)の符号を反転させた-1は、2の補数表現では0xFFFFであったのに対し、1の補数表現では0xFFFEです。 同様に、0x0002 (=2)の符号を反転させた-2は、2の補数表現0xFFFEに対して1の補数表現0xFFFDです。
![]() |
1の補数表現では、いくつかおもしろい現象が見られます。 まず、0x0000に対する2の補数が存在しなかったのに対し、1の補数は0xFFFFとして存在します。 1の補数表現の世界では、0x0000を+0、0xFFFFを-0と書くこともありますが、いずれにしても0を表現する値がふたつ存在するわけです。
整数αとその1の補数の和はどうなるでしょうか。 考えるまでもなく、1の補数の定義から常に「αと桁数が同じ自然数のうち最大のもの」になります。 αが16ビット正数であれば、常に0xFFFFになるわけです。 先ほど見たように0xFFFFもまた0を表す値ですから、特に矛盾はありません。
もうひとつ、最大の正数と最小の負数についてです。 1の補数表現における符号付き16ビット整数での最大の正数は0x7FFF (=32767)で、これは2の補数表現とかわりません。 一方、最小の負数は0x8000 (=-32767)で、2の補数表現と異なります。 2の補数表現のときに存在しなかった、0x8000の相方が1の補数の世界では存在するのですね。 ただし、最大の正数0x7FFFに1を加えると最小の負数0x8000になるのは、2の補数の世界とかわりはありません。
以上を図に描くと右のようになります。 ここでもまた、正数とか負数という話はどうでもよくなって、重要なのは以下の点になります。
- 0x0000の1の補数表現である0xFFFFもまた0を表す
- ある整数とその1の補数の和は0xFFFF (=0)
- 0x7FFFと0x8000の間は最小の負数と最大の正数の境界とはいえ実際はただの整数なので、0x7FFFに1を加えると0x8000
1の補数の世界におけるオーバーフロー
さきほど、1の補数で重要な点を3つ挙げました。 しかし、もうひとつ、意図的に書かなかった重要な点があります。 それは、オーバーフローの扱いです。 すなわち、16ビット整数同士を加算して桁があふれた場合の処理ですが、 ここではいろんなパターンを見ながらどのようになっているかを見てみましょう。
まず、符号付き16ビット整数の正数aとb (0x0000≦{a, b}≦0x7FFF)の加算です。
この場合、明らかに桁あふれは起こりませんから除外してよさそうです。
次に、正数a (0x0000≦a≦0x7FFF)と負数b (0x8000≦b≦0xFFFF)の場合。
a≦(0xFFFF-b)であれば桁あふれは起こりませんから、この場合も除外します。
そしていよいよ、本日のその時桁あふれするときがやって参りました。
a>(0xFFFF-b)の場合です。
わかりやすさのために、具体例を挙げながら説明しましょう。 a=0x0004(=4)、b=0xFFFE(=-1)とします。 正しい答えは明らかに3ですね。
まず、bをa'+b'に分解します。 ここで、a'はaと加算して0xFFFFになる値(仮に0xFFFF-aと表記します)、すなわち、aの1の補数です。 0xFFFFは0を表すのでしたから、上記の分割は「aと加算して0になる部分a'と加算の答えb'に分割する」とも云えます。 なぜb'が加算の答えかというと、a+b=a+(a'+b')=(a+a')+b'=b'だからですね。 上記の例では、a'=0xFFFBですからb=0xFFFB+0x0003と分解することになり、 確かにb'が答えである0x0003になっています。
では、実際に計算してみるとどうなるでしょうか。 明らかにa+b=0x0004+0xFFFE=0x0002(オーバーフロー無視)ですから… あれれ!答えが合いません! これは一体どういうことでしょうか?
答えは、図の中にあります。 a+bをa+a'+b'に分解して前者の和をとると、0xFFFFになりますね。 0xFFFFは0を表しますから、本来はそれに1を加えると1にならなければなりません。 しかし実際には、0xFFFFの次もまた0をあらわす0x0000なのです。 すなわち、1の補数における加算では、オーバーフローするとひとつ多い0のために答えが1だけ小さくなってしまうので、1を加えて補正しなければならないのです。 この補正は、図の円における12時の部分(0xFFFF/0x0000)の部分を通過するたびに必要になります。
最後のパターンは負数同士の加算です。これはかならずオーバーフローすることになりますが、補正が必要なのは先ほどと同じなので、省略することにします。
というわけで、1の補数表現における加算では、前述の3つに加えて以下も非常に重要なポイントになります。
- 加算時にオーバーフローした場合は、図の円における12時の部分を通過する度に1を加えて補正する
これらの規則に基づき、 1の補数を内部表現として使った場合の加算を、「1の補数和」と云います。
1の補数和の計算を簡単に
ああすっきり…と云いたいところですが、実際に加算する場合に円とにらめっこするわけにはいきません。 もうちょっといい方法はないのでしょうか。
上記のオーバーフローの規則は「12時を越えるたびに1を加算する」ですから、12時の部分を通過した回数を加算結果に加える、とも云えます。 つまり、12時を越えた回数をうまく数えることができればいいわけですね。 なんと、なんの苦労もなく自動的にこの回数を数える方法があるのです。
12時をこえる、ということはなにを意味するでしょうか。 これは、現在の桁数では表現ができなくなるために、ひとつうえの桁を利用すると云うことです。 これは、一度越えるとひとつうえの桁に1を加算するということです。 先ほどの例では0x0004+0xFFFE=0x10002となっていて、12時を越えたことを17ビット目が表しています。 このまま加算してもう一度12時を越えると… 同様に上位の桁に1が加えられるので、0x2XXXXという値になるはずです。 すなわち、桁あふれの部分の数値がそのまま12時を越えた回数を表すわけです。
ここまでくれば、もうわかりますね。 1の補数和を求めるためには、単純に和をとってから、オーバーフローしたぶんを和に足し込めばいいわけです。
チェックサムを求めよう
長い道のりでした。 ようやく、IP/TCP/UDPのチェックサムを求めることができます。 このチェックサムは、16ビットごとの1の補数和の補数と定義されており、対象のデータが奇数バイトの場合は最後に0を補うことになっています。 これを表すと以下のようなコードになります。
u_int16_t
in_checksum(data, len)
void *data;
size_t len;
{
u_int32_t sum = 0;
union {
u_int8_t ch[2];
u_int16_t si;
} *s_data = data, s_data0 = { .ch[1] = 0 };
#define CARRY(x) (x = (x & 0xffff) + (x >> 16))
for ( ; len > 0; s_data++, len -= 2, CARRY(sum))
sum += s_data->si;
if (len > 0) {
s_data0.ch[0] = s_data->ch[0];
sum += s_data0.si;
CARRY(sum);
}
return ~(u_int16_t)sum;
}
forループの中で加算と補正をおこなっているのがわかるとおもいます。 なお、説明の通り、最後にまとめて補正をかけることもできますが、 sumが32ビットなので、32ビットがあふれる前に補正してあげないといけません。 その判定が面倒なので毎回補正をかけています。 速度が必要な場面では、可能なだけ和をとって補正という処理を繰り返すといいでしょう。
ああ、そうそう、速度といえば、1の補数和の性質を使うと以下のようなおもしろいこともできます。 16ビットの1の補数和を求める際、
- 最初に32ビットの1の補数和を計算
- その答えを16ビットに分割し、16ビットの1の補数和を計算
という手順を踏むこともできるのです。 これは、32ビットじゃなくて64ビットで和をとってもいいですし、128ビットでもかまいません。 なぜこれが可能なのかは今までの説明を元にみなさんで考えていただくとして (下の方にもかなりいろいろヒントがありますし)、速度が必要な場合はこんな性質もうまく利用するといいかもしれません。
それで、なんで「1の補数和の1の補数」やねん?
![]() |
ではなぜ、チェックサムは「1の補数和の1の補数」なんでしょうか。 まず、1の補数和から考えましょう。 ひとつには、1の補数和はバイトオーダに非依存であるということがあります。
右の図は、16ビット整数を異なるバイトオーダで保持して加算した場合を表しています。 例えば、上側の図はをホスト・バイトオーダです。 直感的にはビッグ・エンディアンのアーキテクチャに見えると思いますが、どちらでもかまいません。というのは、ここでは加算だけを問題としているからです。 この場合、LSBの和でオーバーフローするとその分はMSBに桁上がりします。 一方、MSBの和でオーバーフローした場合は、2の補数和か1の補数和かによって処理が異なるのでした。 前者は無視し、後者はLSBにもどって桁上がり(?)します。
下の図は、ホスト・バイトオーダと逆のエンディアンで保持している場合を表しています。 この場合、MSBの和のオーバーフローはLSBに加算されます。一方、LSBのオーバーフローは、2の補数和で無視、1の補数和でMSBに加算です。
これらをまとめると、1の補数和の場合は、いずれのエンディアンでも、LSBのオーバーフローはMSBへ、MSBのオーバーフローはLSBへ加算されます。 すなわち、エンディアンによる影響はありません。 一方、2の補数の場合は、ホスト・バイトオーダと逆のエンディアンで保持して和をとると、誤ったった結果になる可能性があることがわかります。
1の補数和を利用するもうひとつの理由は、1の補数和では加算結果が0x0000になることがほぼない、ということでしょう。 今まで見てきたように、加算和が0になる場合、内部的にはほぼ0xFFFFになります。 正確には、0x0000同士以外で和が0x0000になるものはありません。 UDPではこの性質を利用して、 チェックサムが0x0000であればチェックサムを計算していないこととして扱うよう決められています (個人的には、後で述べる理由により0xFFFFをこの目的に使った方がいいと思うのですけれど)。
さて、次に、どうして最後に「1の補数」をとるのか、です。 これは、受信側でチェックサムの検証を簡単にするためです。
送信側がチェックサムを計算するときは、チェックサムのフィールド自体を0にして1の補数和を計算します。 この値をαとしましょう。 そして、その1の補数和の1の補数をチェックサム・フィールドに埋めるわけです。 これをβと呼ぶことにします。 一方、受信側はそのまま全体の1の補数和の1の補数を計算します。 データが正しく受信されているとすれば、算出された値は、 チェックサム・フィールド以外の1の補数和αにチェックサム・フィールドの値βを加えたものになるはずです。 βはαの1の補数和ですから、この値は必ず0xFFFFになりますね。 そして最後に、この値を1の補数をとると…結果は0x0000です。 すなわち、受信側は送信側と全く同じ手順で(チェックサム・フィールドを特別扱いすることなく)チェックサムを計算し、その結果が0になることで検証できるのです。
ところで、現実的に送信するデータの1の補数和が0x0000になることがない、というのは前述の通りです。 そのため、1の補数和の1の補数であるチェックサム・フィールドの値は、0xFFFFになることはありません。 これが、「UDPでチェックサムを利用しないことを示すために、0xFFFFのほうがよさそうなのにな」と書いた理由だったわけです。 現在の仕様では、チェックサムの計算結果が0x0000になったら0xFFFFに置き換える、というふうに決まっています。 ただ、このように置き換えても所詮0のままですから、検証側ではなんら気にすることはありません。
以上、とてもとても長いエントリになりましたが、IP/TCP/UDPチェックサムにまつわるお話でした。
2007年3月16日
Ruby 1.8.6 & Rails 1.2.3
気がつけば、Ruby 1.8.6がリリースされていました。 で、それと併せてRailsも1.2.3がリリースされたようです。
2007年10月25日
初音ミクはたまたま
本日の、というか、日付的にはもう昨日ですけど、ヤフーの中間期決算説明会で例の初音ミク問題についても説明があったそうです。
その説明というのが、
たまたまというか、機械的に拾ってくる中でそこが対象になっていなかっただけ。
だ、そうです。MSN Live Searchで365件ヒットするのに、たまたま対象になってないなんてことがありうるのかと。もし本当にすべてのページがクロールの対象から外れているのであれば、ヤフーの検索は使い物にならないということを公言したに等しいでしょう。
ま、真実はどこかにあるわけですが、こんな説明では陰謀論は消えませんな。
2007年11月12日
Open Virtual Machine Tools
VMware nisetoolsってのを公開してましたが、というかまだ公開してますけど、本家からOpen Virtual Machine Toolsが公開されてたんですねえ。しばらくVMwareつかってなかったから気付かなかったよ。
とはいえ、Parallelsにもそろそろ嫌気がさしてきたのでFusionに移行するかも。そしたらお世話になる…のかなあ?Windowsしか使わないかも…
2007年11月20日
1.3.6.1.4.1.29969
とってみました。唯一のIDであることが保証されるってのは大事でしょ?





