一歩進んだ Rexx

Classic REXX (REstructured eXtended eXecutor)
File #10

DNS, Recursive query -- 2000.7.11

さてさて, マニアックな名前の解決だけんど, ソレがついに完成する時が来たにょ。 今回のは, サーバーから送られてきたものを表示する時に使うもの。 んで, 今回までのを集めて(?)組み立てると, ロボットに変身するぞっ。 (←しない)

IPtxt: procedure
   parse arg a1 +1 a2 +1 a3 +1 a4 +1
return c2d(a1)'.'c2d(a2)'.'c2d(a3)'.'c2d(a4)

(↑)コレは, 単にドット10進表記にするだけのものにょ。

(↓)こちらは, パックされている名前を, 読む事ができるように変換するもの。

NameUnpack: parse arg ,NameUnpack; return NameUnpackCore(arg(1),, arg(3));
NameUnpackCore: procedure expose (NameUnpack)
parse arg msg,, ini
res = ''
if datatype(ini, 'w') then p = ini; else p = value(NameUnpack);
do while p <= length(msg)
   ch = substr(msg, p, 1)
   mf = bitand(ch, 'c0'x)
   select
      when ch == '00'x then leave
      when mf == '00'x then do
         n = c2d(ch)
         res = res''substr(msg, p +1, n)'.'
         p = p +1 +n
      end
      when mf == 'c0'x then do
         ch = bitand(substr(msg, p, 2), bitxor('c000'x,, 'ff'x))
         push p +1
         p = c2d(ch) +1
      end
   otherwise
      say 'BitError' x2b(c2x(ch))
      p = p +1
   end
end
res = left(res, length(res) -1)
do while queued() > 0; parse pull p; end;
if NameUnpack > '' then call value NameUnpack, p +1
return res

・・えーと, なんだっけ, コレ(↓)。

UnOrdWord: procedure
parse arg list, n
base = 0
do i = 1 to words(list)
   w = word(list, i)
   if datatype(w, 'w') then base = w -i -1
   else if base +i == n then return w
end
return ''

名前解決の詳しい仕組み ってのは知らなくてもよいんだけっちょ, ついでにゆっとくと (↓)こーなるのら。

例えば, 尋ねる名前が www.vector.co.jp だったとしたら, ソレをトップの方から順々に調べていくんだお。

  1. jp(日本)の中の
  2. co(会社・・だったっけ)の中の
  3. vectorとゆー名前のとこの
  4. wwwをアレしているホスト
だけれど, コレを一つ一つ尋ねるのはクライアントの仕事じゃない。
クライアントはネームサーバーに, リカーシブに調べてちょ てゆー具合にリクエストすると, ネームサーバーが (↑)の手順を代わりにやってくれちゃうのだ。うむうむ。

そーか, 表裏使えるのか。ソレはリバーシブル。 ・・・ ダメだ, こんなんじゃ。


DNSの Responseの分解 -- 2000.7.3

さて, 前回の続きだお。

実はココで紹介しているプログラム, とってもマニアックな方法で, ホントはここまでする必要はないのら。わっはっはー。
ホントは GetHostByNameで, 調べたい名前を指定するだけ。

んじゃ なぜここまで手の込んだことをするのか ・・・ それは, そこに越えるべき山があるから。ってウソ。

コレを使う理由っつーのは, やっぱし, ネーム・サーバーもどきを作って あんなことや そんなことを色々アレするのに ・・ ああっ, だめっ, これ以上は言えないっっ。 (←何もゆってないやんか)

DNSpktDmp: procedure expose nl
parse value 'Query' '_'/*Response*/ with QR.0 QR.1
parse value '_'/*QUERY*/ 'IQUERY STATUS' with OP.0000 OP.0001 OP.0010
parse value '_'/*No_error*/ 'Format_error Server_failure Name_Error Not_Implemented Refused',
       with CC.0000 CC.0001 CC.0010 CC.0011 CC.0100 CC.0101
valType = 'A NS MD(Obsolete) MF(Obsolete) CNAME SOA MB MG MR NULL WKS PTR HINFO MINFO MX TXT'
valQType = '252 AXFR MAILB MAILA(Obsolete) *'
valClass = 'IN CS(Obsolete) CH HS 255 *'

parse arg msg =1 id +2 parm +2 qdc +2 anc +2 nsc +2 arc +2
id = c2d(id); qdc = c2d(qdc); anc = c2d(anc); nsc = c2d(nsc); arc = c2d(arc);
parse value x2b(c2x(parm)) with qr +1 opcode +4 ATRR +4 +3 rcode +4
txt = 'ID='id QR.qr OP.opcode CC.rcode
do i = 1 to length(ATRR)
   if substr(ATRR, i, 1) then
      txt = txt word('Authoritative_Answer TrunCation Recursion_Desired Recursion_Available', i)
end
say txt nl qdc anc nsc arc

pm = 13
do qdc
   txt = NameUnpack(msg, 'pm')
   parse var msg =(pm) t +2 c +2
   say txt c2x(t) c2x(c)
   pm = pm +4
end
say copies('-', 20)
do anc +nsc +arc
   txt = NameUnpack(msg, 'pm')
   parse var msg =(pm) t +2 c +2 ttl +4 ln +2 rdata
   t = c2d(t); c = c2d(c); ttl = c2d(ttl);
   txt = txt',' word(valType, t) UnOrdWord(valClass, c) ttl ';'
   select
      when t ==  1 then txt = txt IPtxt(left(rdata, c2d(ln)))
      when t == 15 then
         txt = txt 'Pref='c2d(left(rdata, 2)) NameUnpack(msg,, pm +12)

      when t ==  2 then txt = txt NameUnpack(msg,, pm +10)
      when t ==  6 then do
         n = pm +10
         txt = txt NameUnpack(msg, 'n') NameUnpack(msg, 'n')
         parse var msg =(n) seri +4 refr +4 retry +4 expi +4 mini +4
         txt = txt c2x(seri) c2x(refr) c2x(retry) c2x(expi) c2x(mini)
      end
      when t ==  5 then txt = txt NameUnpack(msg,, pm +10)
   otherwise txt = txt c2x(left(rdata, c2d(ln)))
   end
   say txt
   pm = pm +10 +c2d(ln)
end
return

コレ (↑)が, サーバーから返ってきた情報を分解して, そこに含まれるものを色々表示しようとしているところ。 うーん, 随分前のアレなんで なにやってんだか分かんないや。てへっ。(^^)\(バキッ☆


DNS: Domain Name System -- 2000.6.26

突然だけど, DNSについてのことだお。
たとえば, www.vector.co.jp ってどこにょ? って聞くと, 尋ねられたネームサーバーが, ソレの IPアドレスはここだじょ って答えてくれるヤツ。

・・の前に, こーゆー話題に入る時, RFCxxxx って感じのをよくみるよね。 詳しく説明するときりがないんで適当にアレしとくにょ。 ってゆーかホントはボロが出るから詳しくできないんだけど。(^o^)
コレは, DNSも含めて, 色んな仕様が定義されてるもので, ソレは次々に仕様が追加されていってて, 更新するばやい新しい番号, つまり大きな番号で上書きされる。 だもんで, 同じ物があったら大きな番号を選ばなきゃいけない。 んで Obsoleteとか書いてあったら, ソレは置き換えられてるってこと。
それからコレって, まじめなものだけじゃなくって冗談ネタもあるんだにょ。

さて, 今回のソレは RFC1035。実は古いのもあって, ちと書式が違うのさっ。 サービス番号(ってゆーかポート番号)は \mptn\etc\services を見ると

・・ の二通り。普通は UDPだと思うんだけっちょ, TCPでのソレって見たことないよーな気がするにょ。

てことで, 53番を使って UDPをアレしてみよー。

/* Name resolution   */
/* Rexx使いの織華    */
/* Modification Log *//*
98.4.24 Ver 2 by ori
98.6.18
   CNAME (5) 書式追加
*/
/* == SPECIFICATION (RFC1035) == */
/* Size limits */
/*   labels          63 octets or less                          */
/*   names           255 octets or less                         */
/*   TTL             positive values of a signed 32 bit number. */
/*   UDP messages    512 octets or less                         */
/* message format */
/*   Header, Question, Answer, Authority, Additional */
/* Header section format */
/*   ID, QR:1, OPCODE:4, AA:1, TC:1, RD:1, RA:1, Z:3, RCODE:4, */
/*   QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT                        */
/* Question section format -- QD */
/*   /QNAME/, |QTYPE|, |QCLASS| */
/* Resource record (RR) format -- AN,NS,AR */
/*   /NAME/, |TYPE|, |CLASS|, |TTL|, |RDLENGTH|, /RDATA/ */
/* 'MX' RDATA format */
/*   |PREFERENCE|, /EXCHANGE/ */
/* 'A' RDATA format */
/*   |ADDRESS| */
/* 'CNAME' RDATA format (canonical name) */
/*   /CNAME/ */
options 'ETmode'
numeric digits 10   /* c2d(ttl) のため */
True = 1; False = 0;
nl = '0d0a'x

call RxFuncAdd 'SockLoadFuncs', 'rxsock', 'SockLoadFuncs'
call SockLoadFuncs

parse arg HName .
if HName = '' then exit

idx = GetTxtIniSec('RxServ.cnf', 'NameServ', 'Cnf.')
cnt = 'cnt'
NSaddr.0 = Cnf.cnt
do i = 1 to NSaddr.0
   NSaddr.i = Cnf.i
end
drop Cnf. idx

socket = SockSocket('AF_INET', 'SOCK_DGRAM', 0)
do i = 1 to NSaddr.0
   sk = socket
   remaddr.family = 'AF_INET'
   remaddr.port = 53   /* 53= domain name server */
   remaddr.addr = NSaddr.i

   id.i = random()
   msg = d2c(id.i, 2),
      || '00000001 00000000'b'0001 0000 0000 0000'x,   /* header ; 2 + 2+2*4 Bytes */
      || NamePack(HName)'0001 0001'x
   /* || NamePack(HName)'000f 0001'x*/
   Rc = SockSendTo(sk, msg, 'remaddr.')
   if Rc \= length(msg) then say 'SendError:' Rc'('length(msg)')'

   r.0 = 1; r.1 = sk;
   Rc = SockSelect('r.',,, 5)
   if Rc > 0 /* & r.1 = sk */ then do
      Rc = SockRecvFrom(sk, 'recv', 512, 'raddr.')
      call DNSpktDmp recv
   end
end
exit

ネームサーバーは, さすがにその IPアドレスは分かってないとまずい。 てことで, こんなファイル(RxServ.cnf)を容易してくれなのにゃ。

# ==========================
#    RxServ configuration
# ==========================
[NameServ]
cnt = 1
1 = xxx.xxx.xxx.xxx

何か, そーゆーのを指定しといてね。

SockSendTo() を使って, 加工したアレを送り出すんだけど, その前に名前を圧縮しなきゃダメなのら。

NamePack: procedure
parse arg domain
res = ''
do until lbl == ''
   parse var domain lbl'.'domain
   res = res''d2c(length(lbl))lbl
end
return res

さて, その応答を受け取って名前を表示するのが ・・・
ああっ, 時間がっっ, 不思議の国のネコがぁ〜〜。アレ? うさぎだっけ?


テキストINIファイルを読み込む -- 2000.4.7

今回のコレは, Windows 3.1の iniファイル形式を読み込むヤツだにょん。
最近の σ(^^) のソフトウェアってば, 好んでこの形式を使ってるんだけど, ソレには理由があるだよ。

てゆーのも, OS/2 にはちゃんと SysIni() ってのが使えるよーになってんだけど, コレがねー, ダメなんだよねー。 なぜかとゆーと, 哀しいくらい遅いんだコレが。実際 白龍シリーズのメモ帳で使ったんだけどさー, てんでダメダメ。(=_=)
一回のアクセスだけならよいみたいだけど, 数回連続でアレしたら, ・・・。て感じ。 (←どんな感じだ)

いっそのこと, ソレを直接 Rexxで扱った方が早いんでないかい? なーんて思ったんだけど, まっ, そこまでやるよかコレ(↓)のアレを使ったほうがよさそだな ってことで落ち着いたって訳。

GetTxtIniSec: parse arg ,, GetTxtIniSec; return GetTxtIniSecCore(arg(1), arg(2));
GetTxtIniSecCore: procedure expose (GetTxtIniSec)
parse upper arg fname, sec
res = ''
call stream fname, 'c', 'open read'
do while lines(fname) > 0
   parse value linein(fname) with txt =1 ch +1
   if ch == '' | pos(ch, ';#') > 0 then nop
   else if ch == '[' then parse upper var txt '['str']'
   else if str == sec then do
      parse var txt ky'='val
      tail = translate(strip(ky), '_', ' ')
      call value GetTxtIniSec'tail', strip(val, 'L')
      res = res tail
   end
end
call stream fname, 'c', 'close'
return strip(res)

で, このファイルの形式っつーのを説明しておこー。
まずは, [ ]ではさまれた行。コレはセクション名を表してて, 次のセクションが見つかるまではずっとこのセクションってこと。
んで, その間には キー名 = 値 の記述が, 1行に1項目くらいで, そらもー延々とつながってくのだ。 (←はいはい)
この関数は, ファイル名とセクション名を指定すっと, その一覧を取り出してくれる訳なのさっ。

idx = GetTxtIniSec(CnfFile, 'POPServer', 'itm.')

do i = 1 to words(idx)
   key = word(idx, i)
   say key 'には' itm.key 'が指定されてるにょ'
end
exit

最後に指定してるのは値を入れる 複合記号 (Compound Symbols), その stemを指定するってこと。 んで, 戻り値としては, そのキーの一覧が返るのだ。

ま, ちょっとしたアレには結構便利じゃないかな。うじゃ (^o^)/~


// とゆーパターン -- 2000.6.12

Altair☆の 掲示板」で, *.cmd に与えた引き数の一部が変になる と ゆーのがあったにょ。 ・・ その文字列は '//'。ゴゴゴゴゴ (← 効果音?)

そーなんだよね, Classic Rexxには内部で(?)使う '//t' っつーオプションがあって, '//' とゆーパターンが現れると無条件にソレ以降を削ってしまうっぽい。
あ, このオプションの使い方は 小物ソフトウェアで, 一つだけだけど公開してるっち。
で, 普通は オプションの引き数だとその部分の先頭に '-' だとか '/' だとかを付けるけど, Rexxその物に働きかけるんで '//'にしたんだろーね, きっと。

て訳で, "http://www〜" な〜んて文字列を与えたりなんかすると, コレが悪影響を及ぼすことになる。 そう, このばやい www以降が渡されないとゆーことになってしまうのら。 「""」でくくってもダメ, も, どーしよーもない。(T-T)
ただし, 別モジュールを呼び出すのは OK。call xxxxx とか Rc = xxxxx() とかで外部モジュールを呼び出す時, そこにそーゆー文字列を与えても大丈夫。

さて, (↑)で Classic Rexxって断ってるのには少しばかし意味があって, つまり ORexxだと大丈夫なのにゃ。 試してみたけど, グッド。(^-^)v
それに, Regina Rexxもオッケー。このばやい, (↓)の方法は使えなくなるんだけどね。
PC-DOSの Rexxでも試してみたら, こちらも OK だった。Classicだけど, 大丈夫なのは大丈夫。

んじゃ OS/2 では Classic Rexxをあきらめなきゃダメなのけ?
いーや違う。ここであきらめたら Rexx使いにはなれないじょ。きっと。

/* 渡された引き数の途中に '//' があってもソレを表示できるプログラム */

'@echo %1 %2 %3 %4 %5 |RxQueue'
do i = 1 to queued()
  say i')' LineIn('QUEUE:')   /* あるいは, parse pull */
end
(↑)の %5までしかないのは, 適当だよん。んでコレは, 渡された文字列をセッションキューを介して受け取る仕組み。 コレの原案は あちゃいんさん。 実はコレ, 知らなかったけど, Rexxの中で '%1' とか使えたんだね。いやー驚き。

ほかの方法もあって, hobbesにある cvs-1_10_6.zipのなかの rxrun.exeの引き数として指定するってヤツ。 コマンドライン引き数の取り方が変わっちゃうけど, とりあえず ばっちぐー ! (2000.10.9 追記)

キューについては (↓)にもあるんで参考にしてちょ。うじゃ (^o^)/~


WebChain/2 のタイトルの作り方 -- 2000.3.21

WebChain/2 とゆー, Webページをチェーンするのがある。って, 文章になってない気がする。(^^;
んで, そこに登録するときタイトルも指定できるんだけど, ちょくちょくソレを変更する人は大変。 てゆーのも, 入力して, 結果を取り込んでコピー, んでソレの個所を置き換える, と, 手間がソレなりにかかるから。

そこで, 毎度毎度ばかばかしいプログラムだけっちょ, σ(^^) が使ってるソレを公開してしまおーとゆー訳。

/* WebChain/2 title-textを作るプログラム */

val = 'Rexx使いの卵のページ: にょほほほほ。'

str = ''
unsafe = ';/?:@&=+'xrange(, '1f'x)'7f'x'"#%<>'
       /*';/?:@&=+'xrange('80'x, '1f'x)'7f'x'"#%<>' ← ホントはこっちがいいかも */
do forever
   p = verify(val, unsafe, 'M')
   if p == 0 then leave
   parse var val pre =(p) ch +1 val
   str = str''copies(pre, p > 1)'%'c2x(ch)
end
Title = translate(str''val, '+', ' ')
URL = 'http://hp.vector.co.jp/authors/VA014690/'

say 'url='URL'&amp;title='Title

使うばやい最初の val = の部分と, 最後の方の URL = のとこを変更して実行すること。 でないと, σ(^^) のページになってしまうので。って蛇足だね。

でも, この部分って新しい方式に変わるらしいんで, 手遅れだったかも。でへへ。 (^^)


キュッキュッキュー -- 2000.2.4

きゅきゅきゅのきゅー。今回は球, もとい, queueだ。
待ち行列とゆっても, 結果が出るまで待ち行れつーとか, そーゆーことではない。 ましてや, 大名行列が始まる訳でもない。
コレはプロセス間通信な話なのだ。通信とゆっても 「あ, いま, ピピッと電波飛ばしたな」てゆーよーな危ない話ではない。 (←もーいいって)

簡単な使い方はこう

'dir | RxQueue /LIFO'   /* あるいは, /FIFO オプションとか */
do i = 1 to queued()
   say LineIn('QUEUE:')   /* あるいは, parse pull txt って感じでも可 */
end

(↑)コレってば, 子プロセスを起動してアレするだけなんで簡単に済む訳なんだけど, でも 別の OS/2窓でも開いて, そこのプログラムと通信するときゃ, もーひと工夫が必要なのにゃ。
ソレはだにー。

newq = RxQueue('Create')
oq = RxQueue('Set', newq)

/* ここから通信をはじめる       */
/*                              */
/* 入力の場合: (以下のどれか)   */
/*   txt = LineIn('QUEUE:')     */
/*   parse pull txt             */
/*                              */
/* 出力の場合: (以下のどれか)   */
/*   call LineOut 'QUEUE:', txt */
/*   push txt                   */
/*   queue txt                  */

Rc = RxQueue('Delete', newq)
if Rc \== 0 then say 'delete error='Rc
(↑)コレは, private queue(プライベート・キュー)ってやつなのだ。ちなみに最初のは session queue(セッション・キュー)。 この private queueとゆーのは, そー呼ばれるだけあって, 自分で作って, 使い終わったら削除しなくちゃなんないやつなのだ (削除しなかったら, ずっと?残る)。 通信相手は, newqに入っている名前を使って, 同じように通信することになる。コレは英字で始まる英数字 ・・だっけ?

でも, ここに小さな問題があるよーなないよーな。ってゆーのは, その private queueの名前を, どーやって通信相手に渡すか, ってこと。 通信するために, 最初に通信を行わなければならない, とゆー, ジョークのよーなアレ。 ここはいっぱつ気合いでーとか, 以心伝心とか, やっぱしそーゆー訳にはいかないにょだ (T-T)

てことで, 最初に名前を決めておいて, 双方ともソレを使って通信するってのが一つの手段。

priq = translate('MyQueue')   /* queue名は大文字で返るため */
oldq = RxQueue('Get')
if priq \= oldq then do
   newq = RxQueue('Create', priq)
   if priq \= newq then   /* queue名が重複した場合 rexxが名前を付けるため */
      Rc = RxQueue('Delete', newq)
   Rc = RxQueue('Set', priq)
end
else oldq = ''

/* プロセス間通信を行う */

if oldq \= '' then do
   Rc = RxQueue('Set', oldq)
   Rc = RxQueue('Delete', priq)
   if Rc \= 0 then say 'Error='Rc
end

いきなし複雑になった気がする (↑)。実はコレ, RxTT16.infに入っていた処理内容。Hobbesなんかを探すと見つかるよ, コレ。
でもねー, 少しの通信のために, こんだけアレしてたら大変だね。やっぱ, 前の 2つのが簡単でよいよーな気がする。(^^;

問題はコレだけでなくって, ほかにもある。この queueって片方向通信が望ましいのだにゃ。つまり, 幾つかのプログラムで送受信するのなら, その数だけの private queueを用意した方がよい。 たとえば 2つのプログラムで送受信するなら, それぞれの受信用に, 合わせて 2つって感じ。
なんだか queueって大変? (^^)


Copyright (C) 1998-2004 Rexx使いの織華
email: ori@drive.co.jp