図 2 指定された文字列がホストのアドレスもしくはホスト名と一致するかを調べる } else { / * anything else * / return (string—match (tOk , eval—hostaddr (host) ) Ⅱ (NOT_INADDR(tok) & & string_match(tok, eval_hostname(host) ) ) ) ; 図 3 IPv4 身彡 IPv6 アドレスの IPv4 アドレスへの変換 #ifdef INET6 / * convert IPv4 mapped IPv6 address to IPv4 address * / if (STRN_EQ(string, & & dot—quad—addr(string + 7 ) ! = INADDR_NONE) { string 十 = 7 ; #endif なくホスト名と一致するかを調べます ( 図 2 ) 。 図では、指定された文字列がアドレス形式でないことを 確認するために NOT-INADDR マクロを使っています。 このマクロは tcpd. h ファイルで次のように定義されてい ます。 #define NOT-INADDR(s) (s [strspn(s , , " 01234567890. / " ) ] ! = 0 ) strspn 関数は、第 1 引数に指定された文字列の頁から、 第 2 引数に指定された文字列に含まれる文字のみで構成さ れる部分文字列を取り出し、その長さを返す関数です。っ まり、第 1 引数の何文字目までが第 2 引数に含まれている かを返します。この値を文字列の添字として使うと、第 2 引数に含まれていない先頭の文字を参照することになりま す。これがヌル文字であれば、文字列全体が第 2 引数に含 まれることになるので、数字とドットとスラッシュのみで 構成された文字列だということが分かります。つまり、ア ドレスを表す文字列であると判断しているわけです。 最後は、文字列比較をおこなう string-match 関数です。 たんに文字列を比較するだけなら簡単ですが、実際には特 殊なキーワードの処理もおこなうためにやや複雑なものと なっています。 引数は 2 つで、どちらも文字列です。ただし、第 1 引数 は設定ファイルに杢された文字列で、第 2 引数が要求か ら生成した文字列となります。 static int string—match(tok, string) Char *tok; char *string; string match 関数 102 関数の本体では、まず IPv4 射影 IPv6 アドレスを通常 の IPv4 アドレスに変換しています ( 図 3 ) 。 先頭が、、 : : 仕仕 : " のアドレスは IPv4 射影 IPv6 アドレス です。この場合、後続の部分が IPv4 アドレス形式である ことを確認して、頁部分を取り去ります。 次は、指定された形式にもとづいて処理を変える部分で す。まずは、ドットで始まる文字列が指定された場合です。 この場合はドメイン名の一部が指定されたとみなし、要求 のホスト名の末尾部分と一致するかを検査します。 if (tokCO] strlen(string) strlen(tok) ; return (n > 0 & & STR-EQ(tok, string + Ⅱ ) ) ; そのため、まず要求から得た文字列の長さが指定された 文字列よりも長いことを確認し、文字列の後ろの部分と比 較します。 指定された文字列が ALL ならすべてのものに一致する ことになるため、かならず YES を返します。 } else if (STR-EQ(tok, "ALL")) { return (YES) ; 指定された文字列が KNOWN の場合、 UNKNOWN でなければ一致します。このためのコードもいたって簡単 です。 } else if (STR—EQ(tok, "KNOWN")) { return (STR-NE(string, unknown) ) ; 指定された文字列の末尾がドットの場合、前方から調べ ー 1 ] return (STRN_EQ(tok, string, Ⅱ ) ) ; / * prefix * / } else if (tok[(n = strlen(tok)) てそこまでが一 -- ・致していれば OK です。 UNIX MAGAZ 工 NE 2004. 12
StartupInf0 1pStartupInf0 , ProcessInformation 1pProcessInformation [D111mport("user32")] internal static extern int InsertMenuItem(uint hmenu, uint uposition, uint uflags , ref MENUITEMINFO mii) ; CD111mport ( " She1132 " ) ] internal static extern Int32 SHGetPathFromIDList (IntPtr pidl , StringBui1der pszpath) ; protected const string guid = ” { 2DA8923C ー 8A40 ー 4D01 ー 95C9 ー F208491ACBC6 } " protected const string SHELL—EX—APPROVED—REG—KEY @"Software*Microsoft*Windows*CurrentVersion*She11 Extensions*Approved" ・ protected const string MENU—DATA—REG_KEY = @"Directory*she11" protected const string DIRBG—CONTEXT MENU_EX_REG_KEY @"Directory*Background*she11ex*ContextMenuHandIers*RunCmd" ・ protected string foIderPath; protected MenuItem ロ m_items ; int IShe11ExtInit. lnitialize (IntPtr pid1F01der , IntPtr lpdobj , uint hKeyProgID) if ( lpdobj ! = (IntPtr)O ) { / / this becomes true when a folder tree entry is right—clicked m—items = new MenuItem [ 0 ] , return 0 ; try { StringBui1der path = new StringBui1der ( 1024 ) ; SHGetPathFromIDList (pid1F01der , path) ; f01derPath = path. ToString ( ) ; catch ( Exception e ) { MessageBox. Show(). TOString()) ; / / Now retrieve the menu information from the registry RegistryKey sc = Registry. C1assesRoot ; sc . OpenSubKey(MENU_DATA_REG_KEY , true) ; S C if (sc. SubKeyCount > 0 ) { RegistryKey entryKey = sc . OpenSubKey (keyName , true) ; try { foreach ( string keyName in sc . GetSubKeyNames() ) { int i = 0 ; m—items = new MenuItem [sc. SubKeyCount] ; (string)entryKey. GetVa1ue(' string menuStr if ( menuStr Ⅱ u11 Ⅱ menuStr continue ; RegistryKey cmdKey = entryKey. OpenSubKey ( " command" (string) cmdKey. GetVa1ue (' string cmdStr MenuItem m = new MenuItem() ; 94 true) ; UNIX MAGAZINE 2004. 12
図 1 ホスト名が@で始まっている場合のタ歩里 if (tok [ 0 ] #ifdef NETGROUP static char *mydomain = 0 ; if (mydomain yp-get-default—domain(&mydomain) ; return (innetgr(tok + 1 , eval—hostname(host) , tcpd—warn("netgroup support is disabled") ; #else = の され、なければ NULL が返されるので、これを使って判 ます。 @がある場合はその後ろの部分が host 変数に代入 区切られているので、 split-at 関数を使ってこれを分離し #endif return (NO) ; (char * ) 0 , mydomain) ) ; / * 取 ot tcpd-jump() * / host_match 関数 次に紹介するのは、ホスト名が一致しているかどうか 断しています。 split—at(tok + 1 , ' @ ) ) ) if ( (host return (string—match (tOk , - eval-daemon(request) ) ) ; 100 ます。 が、基本的には server-match 関数と同じものとなってい host-match のどちらを用いるかといった点が異なります 求のなかから取り出す値や、比較の際に string-match と ーザー名とホスト名を書くことになります。そのため、要 を書きますが、クライアントの場合には、ホスト名のみかユ 場合には、デーモン名のみかデーモン名とホスト名の両方 クライアントの指定方法が異なるところです。サーバーの er-match 関数との違いは、設疋ファイル内でのサーバーと かを調べる client-match 関数も定義されています。 serv- サーバーではなく、クライアントが要求と一致している request—>server) ) ; & & host—match(host , - eval—daemon(request) ) return (string—match(tok, - } else { 合にのみ真を返します。 match 関数を使って両者を調べ、両方とも一致している場 ため、 string-match 関数とホスト名の一致を調べる host- 両方が一致するかどうかを調べなければなりません。その ホスト名も含まれる場合には、ホスト名とデーモン名の 結果を返します。 関数を呼び出して文字列がデーモン名と一致するかを調べ、 求のなかからデーモン名の部分を取り出し、 string-match デーモン名だけの場合には、 eval-daemon マクロで要 を調べる host-match 関数です。この関数は、調べるホ スト名を表す文字列と、要求のなかでホストを示すための host-info 構造体へのポインタを引数にとります。 static int host—match(tok, host) char *tOk ; struct host_info *host ; 関数の本体は、設定ファイルに書かれた文字列の種類に よって場合分けをしながら、ホスト名が一 - 一致しているかを 順番に調べる構造になっています。最初はホスト名が@で 始まっている場合です。この場合には、指定されたホスト が NIS のネットグループに含まれているかどうかを調べま す ( 図 1 ) 。 ネットグループの機能を使うかどうかはマクロで制御で きるようになっていますが、この機能を使う場合には、 yp- get-default-domain 関数により NIS のドメイン名を取 得し、さらに innetgr ライプラリ関数を利用して、指定さ れたホストがネットグループに含まれているかどうかを調 べます。ネットグループの機能を使わない設定になってい る場合には、 NO を返して関数を終了します。 ホスト名の部分が、、 / " で始まる場合、実際の指定は別の ファイルに言当されていることを示します。 } else if (tokCO] return (hostfile—match(tok, host) ) ; この場合には、 hostfile-match 関数を呼び出してファ イルの中身との一致を調べます。 hostfile-match 関数は、 ファイルをオープンし、その各行ごとに host-match 関数 を呼び出すことで、ファイルの中身をすべて調べます。 ホスト名として KNOWN が指定されていれば、ホスト UN 工 X MAGAZINE 2004. 12
マンド行引数はない。 CLSID についてもうすこし説明しておく。 Windows では COM オプジェクトのクラス以外にも、さまざまなも のを 16 バイトの値で識別している。その 16 バイトの値 を GUID (Globally Unique IDentifier) と呼ぶ。そし て、 CLSID は GUID の 1 つの利用形態である。だから こそ、このプログラムは genclsid ではなく、 genguid と いう名前なのである。 ☆ Cygwin の環境改善のために、かなり Windows に深 リスト 1 FolderBackgroundRunCmd. cs 集 Open Cygwin WindowHere 入りしてしまった。これまで UNIX を利用し続けてきた ものの、最近は Windows も使わなければならない人にと って、この記事は Windows でちょっと突っ込んだことを するためのよいきっかけ、あるいは手掛かりになるのでは ないだろうか。 はしだ・まさひで ) / / A she11 extension for a folder background context menu usmg 1.1SIng uslng uslng 1.1SIng uslng 1-1SIng uSIng uslng t1SIng System; System. G10ba1ization; System. IO; System. Ref1ection; System. Runtime . Compi1erServices ; System. Runtime . lnteropServices ; System. Text ; Microsoft . Wi Ⅱ 32 ; System. Security. Permissions ; System. Windows . Forms ; Cassemb1y:Assemb1yVersion("1.0.0.0 " ) ] [assemb1y:Assemb1yKeyFi1e("fbrc. snk")] namespace She11Ext CGuid ( " 2DA8923C ー 8A40 ー 4D01 ー 95C9 ー F208491ACBC6 " ) ] public class F01derBackgroundRunCmd : 工 She11Extlnit , lContextMenu / / lmported from Win32 API [D111mport ("kerne132. d11 " ) ] internal static extern B001ean CreateProcess( string string uint uint B001ea れ uint uint string 1pApp1icationName , 1pCommandLine , IpProcessAttributes, 1pThreadAttributes , bInheritHand1es , dwCreationF1ags , 1pEnvironment , IpCurrentDirectory , UN 工 X MAGAZ 工 NE 2004. 12 93
if ( str ! = Ⅱ u11 ) { if ( str. Length > = cchMax ) { str = str. Substring(), cchMax str 十 " \ 0 " ・ str charArray = str. ToCharArray ( ) ; Marsha1. Copy (charArray , 0 , commandString , private VOid ExecuteCommand (string command) StartupInfo si = new StartupInfo ( ) ; charArray. Length) ; ProcessInformation pi = new ProcessInformation() ; si . cb = 68 ; //sizeof(si); try { if ( ! CreateProcess ( Ⅱ u11 , command. Rep1ace("%L" , f01derPath) , false, 0 , 0 , f01derPath. Substring(), 2 ) , MessageBox. Show("UnabIe t0 execute " 十 command 十 ” : E て ro て in F01derBackgroundRunCmd" ) ; catch ( Exception e ) { MessageBox. Show(e . ToString() ) ; void IContextMenu. InvokeCommand(IntPtr pici) try { 0 , 0 , si, pi) Type typINVOKECOMMANDINFO = Type. GetType ( "She11Ext . INVOKECOMMAND 工 NFO" ) ; INVOKECOMMANDINFO ici (INVOKECOMMANDINFO)Marsha1. PtrToStructure (pici , typINVOKECOMMANDINFO) ; if ( ici . verb く = m—items. Length ) ExecuteCommand(m—items [ici. verb ー 1 ] . Command) ; catch(Exception e) { MessageBox. Show(). TOString()) ; [System. Runtime. InteropServices. ComRegisterFunctionAttribute()] static void RegisterServer(String strl) try { RegistryKey root ; RegistryKey rk ; root = Registry. CurrentUser; 96 UNIX MAGAZINE 2004. 12
集 Open Cygmn WindowHere m . Text = menuStr ; 1 , m . Command m. He1pText m—items [i] cmdStr ; menuStr. Rep1ace("&" m ; catch( Exception e ) { MessageBox. Show(e . TOString() ) ; else { m_items return 0 ; int new MenuItem [ 0 ] ; IContextMenu. QueryContextMenu (uint hMenu , uint iMenu , int idCmdFirst , if ( m—items . Length int id = 1 ; if ( (uF1ags & 0xf) MENUITEMINFO mii mii . CbSize mii . fMask int idCmdLast , uint uF1ags) 0 ) return 1 ; 0 Ⅱ (uF1ags & (uint) CMF. CMF_EXPLORE) ! = new MENUITEMINFO ( ) ; = 48 ; (uint)MIIM . ID ー (uint)MIIM . TYPE ー (uint)MIIM . STATE; = の { foreach ( MenuItem item in m—items ) { mii . 工 D mii . fType idCmdFirst + id ; (uint)MF. STRING ; mii . dwTypeData item. Text ; (uint) MF . ENABLED ; mii . fState InsertMenu 工 tem (hMenu , iMenu + (uint) id , id 十十・ return id ; VOid ref mii) ; IContextMenu. GetCommandString (int idCmd, uint uF1ags , int pwReserved , String str; Char ロ charArray ; switch ( uF1ags ) { case (uint)GCS . VERBW: str = m—items CidCmd break ; case (uint) GCS . HELPTEXTW : m—items CidCmd str UNIX MAGAZ 工 NE 2004. 12 break; str = nu11 ; default : break; IntPtr commandString , int cchMax) 1 ] . Command ; 1 ] . He1pText ; 95
この場合には文字列の長さを調べ、その長さだけ要求か ら得た文字列と比較します。 これら以外の場合には文字列としての正確な一致を調べ ますが、 IPv6 アドレスの可能性もあるため、まずはそち らを確認します。 IPv6 アドレスであれは全体が、、「と ] で括られているはすなので、まずはそのような形式になっ ているかどうかを調べます。 strlen(tok) ; len ' [ ' & & tok[len ー 1 ] if (*tok この条件が成立する場合、設疋ファイルで指定されてい る文字列は IPv6 アドレスのはずです。アドレスとしての 構造が正しいかを確認するために、 getaddrinfo ライプラ tok[len ー 1 ] ch ; freeaddrinfo(res) ; memcpy(&pat , res->ai—addr, sizeof (pat) ) ; &res)) if ( (ret = getaddrinfo(tok + 1 , NULL, &hints , hints. ai—flags = AI-PASSIVE ー AI—NUMERICHOST; hints . ai—socktype SOCK_STREAM ; hints . ai-family = AF—INET6; memset (&hints , 0 , sizeof (hints) ) ; tok C1en ー 1 ] ch = tok[len ー 1 ] ; リ関数を使ってアドレスをバイナリ形式に変換します。 UNIX MAGAZ 工 NE 2004. 12 ていることを確認しています。 れていたら、それが要求のほうのアドレスにも同様に付い 設疋ファイルから得たアドレスにスコーフ。識別子がイ寸けら これらも等しいことを確認する必要があります。そこで、 スコーフ。識別子を用いる場合には、アドレスだけでなく addr にオ内されます。 れていたアドレスが pat に、要求から得られたアドレスが アドレス変換が成功した場合には、設疋ファイルに書か freeaddrinfo(res) ; memcpy(&addr, res¯>ai—addr, sizeof (addr) ) ; return NO ; &hints, &res) ! = 0 ) if (ret ! = 0 Ⅱ getaddrinfo(string, NULL, - 致しないことを示します。 らかの getaddrinfo が失敗した場合には NO を返し、 イナリ形式の IPv6 アドレスに変換します。ここで、どち さらに、要求から得られた文字列についても、同様にバ プログラミング・テクニック : #ifdef NI_WITHSCOPEID return NO ; pat . sin6-scope-id) addr. sin6—scope—id ! = - if (pat . sin6-scope-id ! = 0 & & #endif &addr . sin6—addr , return ( ! memcmp (&pat . sin6—addr , , し、値カ賻しいかどうかを返します。 あとは、バイナリ形式となっているアドレスを直接比較 ことを示します。 スコーオ嬲リ子が異なる場合は NO を返し、一致しない in6-addr)) ) ; sizeof (struct 。 # , #endif return (ret) ; return (STR-EQ (tok, string) ) ; どうしの比較をおこない、一致するかどうかを判断します。 IPv6 アドレス形式でなかった場合には、単純に文字列 ☆ いずれ TCP Wrappers でも strsep 関数を使うように変 ルには strsep 関数を利用すべきと明記されていますから、 ある実装です。ただ、 strtok 関数のオンライン・マニュア イプラリとして利用することを考えると、ちょっと問題の 把握していれば、問題にはならないからです。しかし、ラ ードのほかの部分で strtok 関数を使っているかどうかを はありません。というのも、 TCP Wrappers の作者がコ Wrappers のソースコードとして考えるのならとくに支障 では strtok 関数をそのまま使っています。これは、 TCP 「 list-match 関数」の節で述べたように、ソースコード る関数群を紹介しました。 れた条件にマッチしているかどうかを検査する際に利用す 今回は、サービスに関する要求カ羸疋ファイルに指定さ 更されるのでしよう。 103 ( たじみ・ひさかず )
集 Open Cygwin WindowHere root = Registry. LocaIMachine ; rk = root . OpenSubKey (SHELL-EX-APPROVED-REG-KEY , true) ; rk. SetVa1ue(guid. ToString() , "F01derBackgroundRunCmd shell extension") ; rk. C10se() ; / / Set D 工 RGB-CONTEXT_MENU_EX_REG_KEY to my guid root = Registry. C1assesR00t ; rk = て 00t . CreateSubKey (DIRBG—CONTEXT-MENU—EX_REG_KEY) ; rk. SetVa1ue('"' , guid. ToString() ) ; rk . C10se() ; catch ( Exception e ) { MessageBox. Show(e . ToString()) ; [System. Runtime . InteropServices . ComUnregisterFunctionAttribute ( ) ] static void UnregisterServer(String strl) try { RegistryKey て 00t ; RegistryKey rk; て 00t = Registry. LocaIMachine ; rk = root . OpenSubKey(SHELL—EX-APPROVED_REG_KEY , true) ; rk. DeIeteVaIue (guid) ; rk. C10se() ; root = Registry. C1assesR00t ; root . De1eteSubKey (DIRBG-CONTEXT—MENU_EX_REG_KEY) ; catch ( Exception e ) { MessageBox. Show(e . TOString() ) ; 1 「 - 1 工エー - 97 UNIX MAGAZ 工 NE 2004. 12
図 10 の左上にあるべインが、アプリケーションの実行 時の外観をデザインまたは確認する VisuaI Editor 工デ イタ、その下にあるべインがアプリケーションのコードを 編集する Java 工デイタです。 Visual Editor 工デイタ、 Java 工デイタ、 properties ビューの 3 者は連動しており、 いずれかのウインドウの内容を編集すると、その結果がほ かの 2 つのウインドウに反映されます。 図を見ると、 VisuaI Editor 工デイタにウインドウか表 示され、 JFrame クラスのインスタンスがぶじに生成され たようです。どのように実現されているのかをみてみまし 0 Visual Editor 工デイタと Java 工デイタのあいだにあ る△ボタンをクリックし、 Java 工デイタの表小領域を拡大 します。 MusicCatalog クラスの最初のほうに、このクラ スのデフォルト・コンストラクタ 3 が定義されています。 public MusicCata10g() { super() ; initialize() ; 図 9 Java VisuaI CIass ペ ーン Ja 0 C 55 ・ The ・新・面 f 製ま” ek ・亟 00 町 0 d - M リ朝 cC ・ 0 “ 0 New Java Visual ( に 55 : 当、 Sou 「 0 ・ Fo ・ r &SWT &Swng 第 App D ・叩 P•n ・ D 物 lo 要 紕 n Fr 聞 ロ P 聞 は S い翻 P 0 第釦 Pan ・ 朝 0 ト me 新 od ー wo ⅵ d u 版・ t00 「をい 凶 0 面献 9M0 池 ma 氛印 ) こ献′、改研第を研れを叩 0 , 凶物・・ d 市純 tn ・ th0 図 10 MusicCatalog クラス 図ま 0 ・ 0 , ・ ! のを砂・ : つ・ 0 , を属ツ「鬻を第を 0 : 物 Jav ・新一以 P 純 k ・第 0 颪・ - ロ Java ・ MusicCatalogoava - Echpse 円 a 日 0 丨 m 、 「こ第ツ第いを 町。朝・血。 OJS 局 第町赴聞 、る 0 「 0 やを物 に震 J 丨物 戛ー 1 ト Cok 第、、 訂・ P ・ 式増 P 新・ MusicCatalog クラスは JFrame クラスの派生クラス なので、 Jh•ame クラスのメソッドやプロバティを継承し ます。しかし、これらの機能を利用するには、 JFrame ク ラスのインスタンスを生成しなければなりません。それに は、 MusicCatalog クラスのコンストラクタに含まれてい る super() メソッドを呼び出します。 MusicCatalog クラスのコンストラクタには、もう 1 つ initialize() メソッドの呼出しがあります。このメソッド は、コンストラクタのあとで定義されています。 2. 左のペインで Java → Swing を選び、右のペインで JFrame Visual Class" を選択したあと、 [Next] ポ private VOid initialize() { this . setSize(300, 200 ) ; タンををクリックする。 this . setContentPane (getJContentPane ( ) ) ; 3. Java Visual Class ページが表示される ( 図 9 ) 。 this . setTit1e("JFrame") ; JFrame クラスの派生クラスの名前として、 Name フィ ールドに、、 MusicCatalog" と入力します。 MusicCat- this は、 MusicCatalog クラス自体を表すキーワード alog はアプリケーションで最初に実行されるクラスであ です。、、 this. setSize(300, 200 ) ; " は、 JFrame クラスの setSize() メソッドを呼び出して、フレームのサイズを 300 り、 main() メソッドをもっていなければならないので、 public static void main(String[] args)" をチェッ x 200 に言定します。 クします。その他はデフォルトのままで [Finish] をク 3 コンストラクタとは、クラスと同じ名前をもち、クラスのインスタンスを生成 リックします。 するときにかならず実行される匆期化処理を定義したメソッドです。引数の ないコンストラクタをデフォルト・コンストラクタといいます。クラスにコ 4. MusicCatalog クラスが生成され、 MusicCatalog プ ンストラクタを 1 つも定義しないと、コンパイル時になんの処理も実行しな ロジェクトに追加される ( 図 10 ) 。 いデフォルト・コンストラクタが自動的に生成されます。 2 褓、加 4 003 興。 20 ) 0 第 0 1 ヨ 2 S 、就”ぐ。“第物・ 1 ・も肄 : 0 0 を 、・ 0 ⅱ、破を、朝ィー ni れ・ⅱこ 00 ー 駅 " 、 - を 0 ー引ユ ( 3 ) を , - “ tC 聞【・戒は JCont ・物りト 曲 i いをい新「第′つ・ ・物ををい、 0 : をなー」臧を新 ! を都 0 ⅱ ! を、一・ⅱ場製・一一 JC ・を n ー p 町第 0 ー ー朝 [ 霞をを 02 を店町第 162 UNIX MAGAZINE 2004. 12