OpenBSD httpd (3)
先週の半ば、女房からユーチューブが映らないと主訴を受けた。オイラーに聞かれたって、 そんなん知らんわと言うと、悶着が起きそうなので試してみた。
依頼は受け取りましたが、返事出来ませんとか言われたかな。動画が表示されるべく所が、真っ暗。古いipadなんで、新種の動画は見れなくなったか? それとも、動画表示機構がいかれたか。
自分で出来る対策は、ipadのコールドスタートぐらいだな。やってみたけど、やはり動画だけが 表示されない。こういう場合は、ツイターを見るのが一番だろうけど、生憎オペレーションを知らない原人です。yahooのリアルタイム検索で、youtube down って検索したら、阿鼻叫喚な状態になってたよ。ユーチューブが映らないってね。
これで一安心。おもむろに威厳をもって女房に宣告。ユーチューブのサーバーがおかしくなって ますから、時間をおいてからアクセスしてみてください。
ユーチューブが映らないと、 YouTubeの世界的ダウンにより人気ポルノサイト「Pornhub」へのトラフィックが急増していたことが判明したそうですよ。みんな、ネット中毒だなあ。
稀有な現場に居合わせた人よりでした。ああ、国民放送局へ投稿するの忘れてた。 確か、スクープBOXとか言うやつだったな。
tcsh
前回の最後に出来心で、tcshの動きをfstatしちゃった。あの時は、tcshの入力にしか注目しなかったけど、出力はどう扱われるのだろう? 標準出力を次の段のコマンドが受け取ってくれて、入力が有るまでブロックさせたい。
俄かに都合の良いコマンドを思い付かなかったので、下記をでっちあげた。
#include <stdio.h> int main(){ char buf[1024]; for (;;){ fgets(buf, sizeof(buf), stdin); printf("From stdin: %s", buf); } return (0); }
最近はC語でものを考えるようになったので、実験プログラムもC語で書く大胆な事をしてます。 なんせ、C語は透き通った言語ですからね。
sakae@fb:/tmp % cat - | tcsh | ./a.out pwd From stdin: /tmp ls From stdin: a.out From stdin: rpl.c From stdin: tmux-1001
まずは、動作確認。作ったアプリは、ちゃんと動いています。これ、replのeが抜けた版です。 evalは、tcshに押し付けてます。
sakae@fb:~ % fstat | grep pipe : sakae a.out 729 0* pipe fffff80003af2be0 <-> fffff80003af2d48 0 rw sakae tcsh 728 16* pipe fffff80003af7000 <-> fffff80003af7168 0 rw sakae tcsh 728 17* pipe fffff80003af2d48 <-> fffff80003af2be0 0 rw sakae tcsh 728 19* pipe fffff80003af7000 <-> fffff80003af7168 0 rw sakae cat 727 1* pipe fffff80003af7168 <-> fffff80003af7000 0 rw
FreeBSDのfstatは、情報過多過ぎるきらいがあるので(lsofは情報FAT過ぎて、横にはみ出してる)、pipeだけを抜き出しました。何段もパイプ接続した場合、最終段からプロセスが起動されます。それから、stdin/outと言っても、OS的にはR/W出来る代物なんだな。これ、以前、login_tty()だかで、調べたよ。別の確度から再確認出来ましたな。
sakae@fb:~ % fstat -p 728 USER CMD PID FD MOUNT INUM MODE SZ|DV R/W sakae tcsh 728 text / 321059 -r-xr-xr-x 427888 r sakae tcsh 728 ctty /dev 84 crw--w---- pts/1 rw sakae tcsh 728 wd /tmp 2 drwxrwxrwt 448 r sakae tcsh 728 root / 2 drwxr-xr-x 1024 r sakae tcsh 728 16* pipe fffff80003af7000 <-> fffff80003af7168 0 rw sakae tcsh 728 17* pipe fffff80003af2d48 <-> fffff80003af2be0 0 rw sakae tcsh 728 18 /dev 84 crw--w---- pts/1 rw sakae tcsh 728 19* pipe fffff80003af7000 <-> fffff80003af7168 0 rw
そしてこちらは、tcshだけをzoomしたもの。fd=18が、stderrでしょう。fd=19って、非常用の入力なのかなあ? 4000行を超えるtcshのマニュアルを精査する元気は有りません。ご存知の方がいらしたら、こっそり教えてください。
OpenBSDだと下記のようになった。
ob6$ cat - | sh | ./a.out pwd From stdin: /tmp
USER CMD PID FD MOUNT INUM MODE R/W SZ|DV sakae a.out 62584 wd /tmp 2 drwxrwxrwt r 512 sakae a.out 62584 0 pipe 0x0 state: R sakae a.out 62584 1 / 889403 crw--w---- rw ttyp1 sakae a.out 62584 2 / 889403 crw--w---- rw ttyp1 sakae sh 37063 wd /tmp 2 drwxrwxrwt r 512 sakae sh 37063 0 pipe 0x0 state: R sakae sh 37063 1 pipe 0x0 state: sakae sh 37063 2 / 889403 crw--w---- rw ttyp1 sakae cat 7554 wd /tmp 2 drwxrwxrwt r 512 sakae cat 7554 0 / 889403 crw--w---- rw ttyp1 sakae cat 7554 1 pipe 0x0 state: sakae cat 7554 2 / 889403 crw--w---- rw ttyp1
fstatは、BSD系OSに搭載されてる、節度あるlsofだ。fstatのお仲間に、fuserってコマンドがある。fstatのソースツリーに同梱されてて知った。(いや、忘れていたのが思い出されたのだ)
このfuserは、リナにも有る。主として、リソースを使ってるプロセスを見つけ出す(連携して、プロセスを殺す)のに使う。 世間一般的に言うと、地上げ屋だな。その土地誰の持ち物だ? マンション建てるに邪魔なんで、持ち主をヒットしてこい。こういう時に便利です。
[sakae@cent tmp]$ ps -ejH : 1689 1689 1689 pts/1 00:00:00 bash 1851 1851 1689 pts/1 00:00:00 cat 1852 1851 1689 pts/1 00:00:00 sh 1853 1851 1689 pts/1 00:00:00 a.out
上の実験で使ったパイプラインをCentOSにも施設してみました。この状態で、/tmp/a.outを どのプロセスが使用しているか確認します。
[sakae@cent tmp]$ fuser /tmp/a.out /tmp/a.out: 1853e
コマンドぐらいなら、psで検出出来るけど、ライブラリーとかになると、普通はお手上げ。 そんな時、fuserは威力を発揮します。
[sakae@cent tmp]$ ldd a.out linux-vdso.so.1 => (0x00007ffe419ab000) libc.so.6 => /lib64/libc.so.6 (0x00007f5f0a8da000) /lib64/ld-linux-x86-64.so.2 (0x00007f5f0aca7000) [sakae@cent tmp]$ fuser /lib64/ld-linux-x86-64.so.2 /usr/lib64/ld-2.17.so: 1620m 1686m 1688m 1689m 1851m 1852m 1853m 1854m
a.outに内蔵されてるライブラリーをリストアップ。ld-linux-x86-64.so.2をどのプロセスが使ってるか、確認。ちゃんと、a.outのプロセスが出てきました。正に、地の果てまで追い詰める、すごい(怖い)ツールです。
なお、プロセス名の末尾に付いてる、eとかmの意味は、
c current directory. e executable being run. f open file. f is omitted in default display mode. F open file for writing. F is omitted in default display mode. r root directory. m mmap'ed file or shared library.
だよと、fuser(1)に、説明がありました。fuserを使って、プロセスをどうやって殺すかは、取り扱い説明書を熟読されたし。きっと、役に立つ場面があるはずです。ご用命をお待ち申し上げております。最近のヤーさんは、礼儀正しいです。
WebDAV
随分と道草を喰ってしまったので、本道へ戻る。OpenBSDのhttpd。ソースが随分あるので、まずはヘッダーファイルを眺めるか。http.hってのが、サポートしてるプロトコルとかを定義してるんだろう。最低限のWebサーバーと言うけど、どんなの?
enum httpmethod { HTTP_METHOD_NONE = 0, /* HTTP/1.1, RFC 7231 */ HTTP_METHOD_GET, HTTP_METHOD_HEAD, HTTP_METHOD_POST, : /* WebDAV, RFC 4918 */ HTTP_METHOD_PROPFIND, HTTP_METHOD_PROPPATCH, HTTP_METHOD_MKCOL, HTTP_METHOD_COPY, HTTP_METHOD_MOVE, HTTP_METHOD_LOCK, HTTP_METHOD_UNLOCK, /* WebDAV Versioning Extension, RFC 3253 */ HTTP_METHOD_VERSION_CONTROL, HTTP_METHOD_REPORT, :
HTTP/1.1に混じって、WebDAVなんてのが顔を出しているぞ。なんじゃいWebDAVって? 名前は聞いた記憶があるけど。。。
OpenBSDでのwebdavは基本的なものだけをサポートしたらしい。正式には、こんなに有る。
sakae@fb:~ % rfc -k webdav | egrep '^[0-9]' 2518 HTTP Extensions for Distributed Authoring -- WEBDAV. Y. Goland, E. 3253 Versioning Extensions to WebDAV (Web Distributed Authoring and 3648 Web Distributed Authoring and Versioning (WebDAV) Ordered 3744 Web Distributed Authoring and Versioning (WebDAV) Access Control 4316 Datatypes for Web Distributed Authoring and Versioning (WebDAV) 4437 Web Distributed Authoring and Versioning (WebDAV) Redirect Reference 4709 Mounting Web Distributed Authoring and Versioning (WebDAV) Servers. 4791 Calendaring Extensions to WebDAV (CalDAV). C. Daboo, B. 4918 HTTP Extensions for Web Distributed Authoring and Versioning 5323 Web Distributed Authoring and Versioning (WebDAV) SEARCH. J. 5397 WebDAV Current Principal Extension. W. Sanchez, C. Daboo. December 5689 Extended MKCOL for Web Distributed Authoring and Versioning 5842 Binding Extensions to Web Distributed Authoring and Versioning 5995 Using POST to Add Members to Web Distributed Authoring and 6352 CardDAV: vCard Extensions to Web Distributed Authoring and 6578 Collection Synchronization for Web Distributed Authoring and 6764 Locating Services for Calendaring Extensions to WebDAV (CalDAV) and 7809 Calendaring Extensions to WebDAV (CalDAV): Time Zones by Reference. 8144 Use of the Prefer Header Field in Web Distributed Authoring and
WebDAV on wiki ftpの代替プロトコルとな。ただ、http(s)に載せてるので、大容量の転送時は、不安定かつパフォーマンスが出ない事が有るとな。提唱したのがM$って、産婆だけじゃ喰っていけないから、Web業界にも、くさびを打ち込んでおきたいって魂胆なんだな。
WebDAVとは?その機能と使い方 で、より実用的な実例が出てた。オペレーションはWindows10で出来ればよい。何とかドライブの代替品と考えると、対向するサーバーの容量は無限大が望ましい。まてまて、オイラーのテキストだけのページだと、年間3Mにも満たないぞ。仮に10G使えるとしても、ほぼ無限大だ。余った容量を、各種バックアップに使えばいいのか。
レンタルサーバーの選び方と基礎知識 そして、こちらもWebDAVについて解説してたけど、もっと一般的にサーバーの選び方が出てた。 ロリポップって所でWebDAVしてるのか。Pageの最後の所にレンタルサーバーの評価表が有るな。
このhttpdシリーズを始めた理由が、無料の長屋を追い出されるって危機感からだった。 ようやく、ここに来て具体的な所と結びつき出したな。
WebDAV 技術仕様徹底解説 こういうのを見ると、OpenBSD httpdで、気軽に試してみたくなる。 telnetで叩けばいいかと思ってたら、現代風に curlコマンドでWebDAVサーバアクセスをやれ、とな。
ftpの代わりって事は、ユーザー認証も必要だろうし、特別なエリアを割り当てる必要がありそう。httpd.conf(5)とかを見ても、それらしい記述は無い。
みんなどうしてる?って聞いてみても、Apacheでなんたらかんたらの記事ばかり。唯一、WebDAVで引っかかったのは、 www/nextcloud and httpd.conf subdomain configなんだけど、なんだかなあの記事である。
エラーになるのを承知で、
ob6$ curl -sv --url http://localhost/index.html -X PROPFIND : > PROPFIND /index.html HTTP/1.1 > Host: localhost > User-Agent: curl/7.59.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 405 Method Not Allowed
httpd.confに書いてなくても、ソースにヒントが隠されているかも知れないと思って、 parse.yを調べてみたけど、何も無かったぞ。(parse.yって、あの人が大好きだったな。) 大体、apacheだとDAVって言うキーワードで、WebDAV機能がonになるんだけどな。かすりもしないよ。
PROPFINDで、ソースファイルを検索すると、server_http.cがヒットした。 POST/PUTと同列に扱われている。このあたりにBPを置いてみるかな。これが有るのは、server_read_httpの中。
ob6# ps awx 89417 ?? Isp 0:00.01 a.out: server (a.out) 30733 ?? Isp 0:00.01 ./a.out 79151 ?? Isp 0:00.01 a.out: logger (a.out) ob6# cgdb -q a.out 89417
こんな具合に、働き蜂の方を指定してgdbを起動し、BPを置いたよ。
(gdb) bt #0 server_read_http (bev=0x1effc4f44200, arg=0x1effb4675800) at server_http.c:300 #1 0x00001effc343df50 in bufferevent_readcb (fd=<optimized out>, event=<optimized out>, arg=0x1effc4f44200) at /usr/src/lib/libevent/evbuffer.c:140 #2 0x00001effc343b361 in event_process_active (base=0x1effb467a400) at /usr/src/lib/libevent/event.c:350 #3 event_base_loop (base=0x1effb467a400, flags=0) at /usr/src/lib/libevent/event.c:502 #4 0x00001efd09d154b7 in proc_run (ps=0x1effb4675000, p=0x1efd09f34020 <procs>, procs=0x1efd09f34140 <procs>, nproc=2, run=0x1efd09d15af0 <server_init>, arg=0x0) at proc.c:594 #5 0x00001efd09d15ad8 in server (ps=0x1effb4675000, p=0x1efd09f34020 <procs>) at server.c:87 #6 0x00001efd09d14047 in proc_init (ps=0x1effb4675000, procs=0x1efd09f34020 <procs>, nproc=2, argc=5, argv=0x7f7ffffed668, proc_id=PROC_SERVER) at proc.c:249 #7 0x00001efd09d0b4ca in main (argc=0, argv=0x7f7ffffed668) at httpd.c:218
クライアントから送られてきたヘッダーの最初のワードを見て、それが有効なメソッドがどうか判定してる。 ここを無事に通過出来ると、ヘッダーの解析が始まる。いきなりgdbで追うと、流れがわからんわ。gdbで流れの目星をつけてから、ソースを見るのが良さそうです。
responseは、server_fileの中で、server_fcgi を呼ぶか(cgiの場合)、そのまま、そこで処理するか決定してる。server_fcgiは、httpd.confの設定で、socketの冒頭に :1234とか設定すると、AF_INET が使えるみたい。
WebDAVで、エラーになるのは、やっぱりサーバー側の設定がきちんと出来ていないせいだろうな。認証だと、htpasswd(1)で、認証ファイルを作って、それを/var/www/htdocs/my.domain/に置く。
/etc/httpd.conf に追加
location "/safe/*" { authenticate MyRealm with "/htdocs/my.domain/.htpasswd" } location "/.htpasswd" { # don't let people download the .htpasswd file block return 403 }
ob6$ w3m http://localhost/safe/index.html HTTP/1.0 401 Unauthorized Username for MyRealm: sakae Password for MyRealm: ************
これで、ダイジェスト認証付の金庫が出来たな。apacheの時代にベーシック認証で、同じ事をやった覚えがあるぞ。なんだか、それより簡単に思えるな。
WebDAVどう動くはず?
ネットを検索しても、WebDAVの為のOpenBSD httpdの例は出てこない。でも、上でちょい見した、/cgi-bin/now をアクセスする例を追っていたら、server_file.c/server_file(...)の冒頭付近に、
if (srv_conf->flags & SRVFLAG_FCGI) return (server_fcgi(env, clt));
こんなコードが有って、fcgiの起動へと導かれていた。これの判定で、sev_conf->flagsが、現在サーバーがサポート出来る機能(リクエストヘッダーにより、動的に変わる)。右辺は、それを抽出するフラグだと分かる。ならば、この右辺に定義されてる群れに、WebDAVを許す定義が有るんではなかろうか?
話はちょっと逸れるけど、Webサーバーへのリクエストヘッダーとか良く聞く言葉だ。今までは素直にヘッダーって理解してたけど、違う見方も出来るね。
リクエストヘッダーの冒頭には、GET /path ... って並んでる。htdocs内のファイルをgetするのも、/cgi-bin の下のコマンドを起動するのも、同じメソッドのgetが使われる。それの区別をしてるのは、pathの違いしかない。
これって、コマンドの引数じゃん。そう思って次の行とかを見ると、xxx: yyyって形式が並んでいて、最後は何も有りませんの空行だ。そう、これって、コマンドに対するオプションを、名前: 値って具合に並べた、引数行の連なりだ。C語で言う、argv[]か。いや、名前: 値って事からすれば、env配列だな。よーく分かりましたよ。
で、話は戻って、SRVFLAG_FCGIは、httpd.hに定義されてる。
#define SRVFLAG_INDEX 0x00000001 #define SRVFLAG_NO_INDEX 0x00000002 #define SRVFLAG_AUTO_INDEX 0x00000004 #define SRVFLAG_NO_AUTO_INDEX 0x00000008 #define SRVFLAG_ROOT 0x00000010 #define SRVFLAG_LOCATION 0x00000020 #define SRVFLAG_FCGI 0x00000040 #define SRVFLAG_NO_FCGI 0x00000080 :
ここにDAVとかが出てこなければ、WevDAV関係は、未実装って事だ。かすりしなかったね。 未実装間違い無し、残念無念でした。OpenBSD 6.4 が、つい最近リリースされたけど、ちゃんとサポートされてるかなあ? 6.4の更新情報には、
httpd(8) now supports client certificate authentication.
こんな案内があったけど、正直期待薄。6.4に上げるのは、急がなくてもいいか。
今思い付いた。WebDAV用のエラーコードがちゃんとソースに散りばめられているか? ヘッダーだけの張りぼてじゃ駄目よ。どんなエラーがDAV用に新設されてるかと言うと、 102,207.422.423.424,507とからしい。これも、かすらなかったぞ。
でも、最後の悪あがきをしておく。
nginx
どうもOpenBSD httpdのconfは、nginx流っぽいので、参考に調べてみた。
nginxでWebDAV (nginx-dav-ext-moduleはなし)
コマンドラインツールでアップロードする場合はcurlが便利。 $ curl --upload somefile https://example.com/foo/ パスワードをファイルに保管しておきたいので、こんな感じ。 $ touch upload_url.private $ chmod 600 upload_url.private $ vi upload_url.private https://user:password@example.com/foo/ $ $ curl --upload somefile `cat upload_url.private` --verboseオプションを付けると分かりやすい。 $ curl --verbose --upload somefile `cat upload_url.private`
ブラウザーで認証付のURLへアクセスする方法かな。これも、まだら模様に覚えている。
ob6$ w3m http://sakae:*********@localhost/safe/index.html ob6$ curl -sv http://sakae:*********@localhost/safe/index.html : * Connected to localhost (127.0.0.1) port 80 (#0) * Server auth using Basic with user 'sakae' > GET /safe/index.html HTTP/1.1 > Host: localhost > Authorization: Basic ccccccaaaaaa3333333111111==
あれ? すっかりダイジェスト認証かと思ったら、昔ながらの方法だった。危ないな。 bcrypt(Blowfish crypt)を、htpasswdが採用してるから大丈夫と思っていたんだけどな。
ob6$ cat /var/www/htdocs/my.domain/.htpasswd sakae:$2b$10$dm5tB.VltiiJHN4sssssqugWwQtinAhvZCoXtfffffy4pu69k/HZm
httpd at golang
C語に疲れたら、現代風C語である、golangに足を延ばすのが良いと思われる。http関連がどうなっているか、覗いてみよう。まあ、母体がググルさんですから、充実してるとは思いますがね。
Go言語でhttpサーバーを立ち上げてHello Worldをする
GO で WebDAV の PROPFIND を使ってファイルの URL 一覧をとってみる
ほーら、簡単そう。