2012年2月19日日曜日

BackTrack 5 R1でGUIログイン

意外とぐぐっても引っかからないのでメモメモ。。。

# apt-get install gdm
# vi /etc/rc.local
 exit 0
 の上に
  /usr/sbin/gdm&
 追加
# reboot

2012年2月2日木曜日

サーブレットのgetRequestURI()が取扱い注意な件 - その2

先のブログ記事「サーブレットのgetRequestURIが取扱い注意な件」で書いた、URLのパス中にスクリプト等を仕込める問題は当然サーブレットだけの問題だと思っていました。

ところがGoogleの複数のサービスに、同様にパス中のセミコロン以降を無視するものが多くありました。その中の幾つかは、同じサービス内へのリンクにわざわざそれらを引き継いでいたり、JavaScriptに渡しているものがありました。
正規化されたURLと同じように「"<>」等はエンコードされていましたが、「'」はそのままだったので、シングルクォーテーションで囲まれている部分についてXSSできました。

以前晒したスクリーンショットですが、特にURLを短くできたものについてリクエストURL付きで再度晒します。

2010年12月のBlog Searchは、JavaScriptに仕込めたのでページ表示時に発動しました。


2011年05月のMapsの再発したものはリンクのHREFです。qパラメータのイコールはエンコードされましたが、URL中のパスのイコールは放置でした。


合計で5ページをGoogleのセキュリティチームに報告し、$3,000の報奨金を頂きました。

他にも同様の問題を持っていた製品等がありましたが、また今度にします。
おそらく半年後くらいに「その3」を書くと思います。

2012年2月1日水曜日

サーブレットのgetRequestURI()が取扱い注意な件

いきなり本題ですが、

サーブレットのURLのパス中にセミコロンでつなげられた文字列は、サーブレットの呼び出しについては無視されますが、HttpServletRequest#getRequestURI()の戻り値には含まれます。

どうゆうことかというと、以下のようなサーブレットにアクセスするためのURLがあったとします。
http://www.acchi.jp/kocchi/OneServlet?hoge=moge
 これは以下のようにアクセスしても正常に呼び出されます。
 http://www.acchi.jp/kocchi/OneServlet;(-_-;)?hoge=moge
 このときgetRequestURI()の戻り値には、セミコロンで追加された文字列も含まれます。
/kocchi/OneServlet;(-_-;)
セッション管理が必要な携帯サイトをサーブレットで構築した方ならご存知と思いますが、URLリライティングによりURLのパスの後ろ、パラメータの前にセミコロンでつなげてセッションIDが埋め込まれます。
http://www.acchi.jp/kocchi/OneServlet;jsessionid=...?hoge=moge
過去に、同様に別のパラメータを追加できないか試したのと、そのアプリで独自のアクセスログを実装、URLの取得に最初getRequestURI()を使っていたことで、経験的にこのことを知っていました。

これは何か?

RFC3986 - Uniform Resource Identifier (URI): Generic Syntax の3.3.Pathによると、パスの後ろに追加するのではなく、パスセグメント(上の例のパス/kocchi/OneServletでいえば、kocchiと OneServlet)に付加情報を追加するための機能のようです。確かにTomcatをインストールした直後のExamplesで試してみると、サンプルのサーブレット、
 http://localhost:8080/examples/servlets/servlet/HelloWorldExample
は以下のようにしても正常に呼び出されます。
http://localhost:8080/examples;orz/servlets;o.rz/servlet;o..rz/HelloWorldExample;o...rz
一方、海外のサイトではこのような追加パラメータをPath Parameterと呼び、ファイル名の後ろにつけるもの、と説明しているものもあります。日本語で「パスパラメータ」で検索しても、それらしいものは見つかりません。

何が問題か?

開発者が意図しない文字列をパス中に挿入できるわけですから、 もし開発者が「正常にサーブレットが呼び出されたのだから、getRequestURI()の戻り値に不正な文字が含まれていることはない」という前提でコーディングをしていれば、Webアプリの脆弱性につながる場合もあるでしょう。そのままDBに保存すればSQLインジェクション、そのまま画面上に表示すればXSSされる可能性があります。

特にサーブレットの場合はweb.xmlファイルの中で<url-pattern>を指定するので、正常に呼び出されたサーブレットやJSPの パス中に、SQL文やJavaScriptが埋め込まれるはずなどないと思いがちでしょう。ところが上のサンプルサーブレットはweb.xmlで以下のように登録されています。

<servlet>
  <servlet-name>HelloWorldExample</servlet-name>
  <servlet-class>HelloWorldExample</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>HelloWorldExample</servlet-name>
  <url-pattern>/servlets/servlet/HelloWorldExample</url-pattern>
</servlet-mapping>

このように、<url-pattern>でPATHを固定に設定していても、上記の方法で任意の文字を挿入することができてしまいます。またweb.xmlに登録されていないJSPファイルについても同様に、
http://localhost:8080/index.jsp;(-_-;)
 というリクエストで通常どおり表示されてしまいます。


 どういった文字が挿入可能か

Servlet APIのHttpServletrequest#getRequestURI()の説明によればHTTPリクエストをデコードしない、とあるので、リクエストの送り手に依存することになります。
次のようなリクエスト、
http://localhost:8080/test.jsp;!"$&'()*+,-.:<=>@[\]^_`{|}~
 を各ブラウザで試した結果は以下のとおりでした。
FireFox(9.0.1) on Windows7
 /test.jsp;!%22$&%27()*+,-.:%3C=%3E@%5B%5C%5D%5E_%60%7B|%7D~
 IE(8) on Windows7(スラッシュに変換されてしまうので、バックスラッシュは除く)
 /test.jsp;!%22$&'()*+,-.:%3C=%3E@[]%5E_%60%7B%7C%7D~
 telnet直入力
 /test.jsp;!"$&'()*+,-.:<=>@[\]^_`{|}~
インターネットに公開しているサービスなどでは、フロントエンドにApacheを立てて、mod_proxy_ajp等を経由する構成が一般的でしょう。このような構成の場合、デフォルトではmod_proxyはURLを正規化します。その結果エンコードされる文字、デコードされる文字については過去にこのブログに書い たので、そちらを参照してください。FireFoxでアクセスした結果は次のようになりました。
mod_proxy
 /test.jsp;!%22$&'()*+,-.:%3C=%3E@%5B%5C%5D%5E_%60%7B%7C%7D~
mod_proxy経由で正規化された場合は、ブラウザに依存することなく「!$&'()*+,-.:;=@_~」がそのまま挿入可能になります。

どの程度危険か

サーブレットコンテナ直接接続の場合、SQLインジェクションのような直接攻撃(という言い方でいい?)については、telnetでほぼすべての特殊文字が利用可能になります。XSSのような間接攻撃(?)は利用者(被害者)の使用するブラウザに依存することになります。

mod_proxy 経由の場合、シングルクォーテーションが入力できることからSQLインジェクションが可能で、XSSについてもタグ内やJavaScript内でシングルクォーテーションで囲まれていればXSS可能になります。いずれの場合も挿入可能な文字種「!$&'()*+,-.:;=@_~」からすると、かなりのことができそうです。

対策

当たり前のことですが、getRequestURI()からの戻りはクライアントから送られてくるものなので、基本疑ってかかる。これに尽きると思います。適切にエスケープ、エンコード処理を行うべきでしょう。

参考

http://www.springsource.com/security/cve-2010-3700
CVE-2010-3700ではパス中のセミコロン以降に文字列を追加することでSpring Securityの制限がバイパスされてしまう問題を報告しています。

https://superevr.com/blog/2011/three-semicolon-vulnerabilities/
どうしてもこの件書けない事情があり、書けるようになるまで待ってたら、去年の11月に先に書かれたw
getRequestURI()以外の問題についても書かれています。

戻ってきました。

はてなダイアリープラスの1ヶ月無料お試し期間過ぎたので、戻ってきました。
さっそくセキュリティネタ1本書きます。

2011年12月31日土曜日

引越しました

はてなに引越しました。
http://d.hatena.ne.jp/minetosh/
アフィリエイトが芳しくなかったら、戻ってくるかもです。

2011年9月13日火曜日

GDD2011JP DevQuiz スライドパズルの回答ソースコード

はっきり言って恥ずかしいコードだけど晒します。

基本、双方向幅優先で物理パワーでねじ伏せる作戦。
優先して残すのは、ゴールまでのマンハッタン距離が近いもの。

以下のコードで-Xmx4608M付けてXeonの6G、64ビットUbuntu10.10マシンで40時間かけると4872問解けます(残り128)。
更に残ったものについて、幅増やしたり上限増やしたりして残り49。
さらに、最初の10手はスタートからの遠さ優先その後ゴールまでの近さ優先、次最初の20手は...と繰り返して行って、残り14。あとは手動で小さいサイズの問題に帰着させて解かせた。

一番原始的な気がする。
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.Hashtable;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Arrays;
import java.util.Enumeration;

public class Panel2 {
    public static final int MAX_NUM = 10000;
    public static final int MAX_COUNT = 100;

    public class InnerPanel {
        int width, height;
        char maxc;
        int maxcount;
        String panel[];
        int distance;
        int zerox, zeroy;
        StringBuffer route;

        /**
          * コンストラクタ(初期状態用).
         */
        public InnerPanel(int width, int height, String panelstr) {
            // 幅高さ
            this.width = width;
            this.height = height;
            // 配列化
            panel = new String[height];
            for (int i = 0; i < height; i++)
                panel[i] = panelstr.substring(width * i, width * (i + 1));
            // 一番でかい文字保存&0位置保存
            maxc = '0';
            char c;
            for (int i = 0; i < panelstr.length(); i++) {
                c = panelstr.charAt(i);
                if (c == '=')
                    continue;
                if (c == '0') {
                    zeroy = i / width;
                    zerox = i % width;
                }
                if (maxc < c)
                    maxc = c;
            }
            maxcount = toInt(maxc);
            route = new StringBuffer();
        }

        /**
          * コンストラクタ(ゴール作成用).
         */
        private InnerPanel(int width, int height) {
            // 幅高さ
            this.width = width;
            this.height = height;
            this.route = new StringBuffer();
        }

        /**
          * ゴール作成.
         */
        public InnerPanel makeGoal() {
            InnerPanel goal = new InnerPanel(width, height);
            goal.maxc = maxc;
            goal.maxcount = maxcount;

            // 整列した状態作成
            goal.panel = new String[height];
            int count = 0;
            String line = null;
            char c;
            for (int i = 0; i < height; i++) {
                line = panel[i];
                StringBuffer sb = new StringBuffer();
                for (int j = 0; j < width; j++) {
                    count++;
                    c = line.charAt(j);
                    if (c == '=') {
                        sb.append(c);
                    } else if (count > maxcount) {
                        sb.append('0');
                        goal.zeroy = i;
                        goal.zerox = j;
                    } else {
                        sb.append(toChar(count));
                    }
                }
                goal.panel[i] = sb.toString();
            }
            return goal;
        }

        /**
         * 文字から数値へ.
         */
        public int toInt(char c) {
            if ('0' <= c && c <= '9')
                return c - '0';
            else
                return c - 'A' + 10;
        }

        /**
         * 数値から文字へ.
         */
        public char toChar(int i) {
            if (i < 10)
                return (char)('0' + i);
            else
                return (char)('A' + i - 10);
        }

        /**
         * パネル内容の表示.
         */
        public void print() {
            System.out.println("======");
            for (int i = 0; i < panel.length; i++)
                System.out.println(panel[i]);
        }

        public int getDistance(InnerPanel target) {
            String p1[] = panel;
            String p2[] = target.panel;

            // 文字->座標のHashtable作成
            Hashtable<Character, Integer> p1hash =
                new Hashtable<Character, Integer>();
            Hashtable<Character, Integer> p2hash =
                new Hashtable<Character, Integer>();
            String p1line = null, p2line = null;
            char c1, c2;
            for (int i = 0; i < p1.length; i++) {
                p1line = p1[i];
                p2line = p2[i];
                for (int j = 0; j < p1line.length(); j++) {
                    c1 = p1line.charAt(j);
                    if (c1 != '=' && c1 != '0') {
                        p1hash.put(c1, i * 10 + j);
                    }
                    c2 = p2line.charAt(j);
                    if (c2 != '=' && c2 != '0')
                        p2hash.put(c2, i * 10 + j);
                }
            }
            char c;
            Integer xy1, xy2;
            int sa, total = 0;
            for (int i = 1; i <= maxcount; i++) {
                c = toChar(i);
                xy1 = p1hash.get(c);
                if (xy1 == null)
                    continue;
                xy2 = p2hash.get(c);
                sa = Math.abs(xy1 / 10 - xy2 / 10) +
                    Math.abs(xy1 % 10 - xy2 % 10);
                total += sa;
            }
            return total;
        }

        /**
         * 距離を計算して保存.
         */
        public int calculateDistance(InnerPanel target) {
            distance = getDistance(target);
            return distance;
        }

        /**
         * String表現.
         */
        public String toString() {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < height; i++)
                sb.append(panel[i]);
            return sb.toString();
        }

        /**
         * 次の状態.
         */
        public InnerPanel[] nextPanels() {
            ArrayList<InnerPanel> panel_array = new ArrayList<InnerPanel>();
            char up = 0, down = 0, left = 0, right = 0;
            if (zeroy > 0)
                up = panel[zeroy - 1].charAt(zerox);
            if (zeroy < height - 1)
                down = panel[zeroy + 1].charAt(zerox);
            if (zerox > 0)
                left = panel[zeroy].charAt(zerox - 1);
            if (zerox < width - 1)
                right = panel[zeroy].charAt(zerox + 1);

            try {
                if (up != 0 && up != '=')
                    panel_array.add(makeUp());
                if (down != 0 && down != '=')
                    panel_array.add(makeDown());
                if (left != 0 && left != '=')
                    panel_array.add(makeLeft());
                if (right != 0 && right != '=')
                    panel_array.add(makeRight());
            } catch (IOException ex) {
                ;
            }

            return (InnerPanel[])panel_array.toArray(new InnerPanel[0]);
        }

        /**
         * コピー作成.
         */
        public InnerPanel makeCopy() {
            InnerPanel copy = new InnerPanel(width, height);
            copy.maxc = maxc;
            copy.maxcount = maxcount;
            copy.panel = new String[panel.length];
            for (int i = 0; i < panel.length; i++)
                copy.panel[i] = panel[i];
            copy.distance = distance;
            copy.zerox = zerox;
            copy.zeroy = zeroy;
            copy.route = new StringBuffer(route.toString());
            return copy;
        }

        /**
         * 上へ移動.
         */
        public InnerPanel makeUp() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy - 1].getBytes("UTF-8");
            byte sline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = sline[zerox];
            sline[zerox] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy - 1] = new String(tline, "UTF-8");
            copy.panel[zeroy] = new String(sline, "UTF-8");
            copy.zeroy--;
            copy.route.append('U');
            return copy;
        }

        /**
         * 下へ移動.
         */
        public InnerPanel makeDown() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy + 1].getBytes("UTF-8");
            byte sline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = sline[zerox];
            sline[zerox] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy + 1] = new String(tline, "UTF-8");
            copy.panel[zeroy] = new String(sline, "UTF-8");
            copy.zeroy++;
            copy.route.append('D');
            return copy;
        }

        /**
         * 左へ移動.
         */
        public InnerPanel makeLeft() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = tline[zerox - 1];
            tline[zerox - 1] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy] = new String(tline, "UTF-8");
            copy.zerox--;
            copy.route.append('L');
            return copy;
        }

        /**
         * 右へ移動.
         */
        public InnerPanel makeRight() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = tline[zerox + 1];
            tline[zerox + 1] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy] = new String(tline, "UTF-8");
            copy.zerox++;
            copy.route.append('R');
            return copy;
        }
    }

    /**
     * メイン.
     */
    public static void main(String args[]) {
        int skip = 0;
        if (args.length > 0)
            skip = Integer.parseInt(args[0]);
        try {
            InputStreamReader isr = null;
            try {
                isr = new InputStreamReader(System.in, "UTF-8");
                BufferedReader br = null;
                try {
                    br = new BufferedReader(isr);
                    new Panel2().play(br, skip);
                } finally {
                    if (br != null) br.close();
                }
            } finally {
                if (isr != null) isr.close();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        System.exit(0);
    }

    public int lx = 0;
    public int rx = 0;
    public int ux = 0;
    public int dx = 0;
    public int cases = 0;

    /**
     * コンストラクタ.
     */
    public Panel2() {
    }

    /**
     * 開始.
     */
    public void play(BufferedReader br, int skip) throws IOException {
        String numberstr = br.readLine();
        String numbers[] = numberstr.split(" ");
        lx = Integer.parseInt(numbers[0]);
        rx = Integer.parseInt(numbers[1]);
        ux = Integer.parseInt(numbers[2]);
        dx = Integer.parseInt(numbers[3]);
        cases = Integer.parseInt(br.readLine());

        for (int i = 0; i < skip; i++)
            br.readLine();

        for (int i = 0; i < cases; i++) {
            solve(skip + i + 1, br.readLine());
        }
    }

    /**
     * 1問解く.
     */
    public void solve(int panel_num, String line) {
        String lines[] = line.split(",");
        int width = Integer.parseInt(lines[0]);
        int height = Integer.parseInt(lines[1]);
        InnerPanel start_panel = new InnerPanel(width, height, lines[2]);
        InnerPanel goal_panel = start_panel.makeGoal();
        Hashtable<String, String> route_hash = new Hashtable<String, String>();

        // 距離設定
        start_panel.calculateDistance(goal_panel);
        goal_panel.calculateDistance(start_panel);

        // 既出状態保存
        Hashtable<String, InnerPanel> start_already_hash = new Hashtable<String, InnerPanel>();
        Hashtable<String, InnerPanel> goal_already_hash = new Hashtable<String, InnerPanel>();

        // 先端状態保存
        ArrayList<InnerPanel> start_edge_array = new ArrayList<InnerPanel>();
        Hashtable<String, InnerPanel> start_edge_hash =
            new Hashtable<String, InnerPanel>();
        ArrayList<InnerPanel> start_next_array = new ArrayList<InnerPanel>();
        ArrayList<InnerPanel> goal_edge_array = new ArrayList<InnerPanel>();
        Hashtable<String, InnerPanel> goal_edge_hash =
            new Hashtable<String, InnerPanel>();
        ArrayList<InnerPanel> goal_next_array = new ArrayList<InnerPanel>();

        // 初期状態にする
        start_edge_array.add(start_panel);
        start_edge_hash.put(start_panel.toString(), start_panel);
        start_already_hash.put(start_panel.toString(), start_panel);
        goal_edge_array.add(goal_panel);
        goal_edge_hash.put(goal_panel.toString(), goal_panel);
        goal_already_hash.put(goal_panel.toString(), goal_panel);

        // ここからぶんまわす

        InnerPanel onePanel, newPanel, newPanels[], bingo;
        String sum = null;
        boolean bingo_flg = false;
int loop_count = 0;
        while (true) {

++loop_count;

            if (loop_count > MAX_COUNT) {
                System.out.println(panel_num + ":");
                break;
            }

            // ゴール側からの比較用Hashtableクリア
            start_edge_hash = new Hashtable<String, InnerPanel>();
            start_next_array = new ArrayList<InnerPanel>();


            // スタート状態からの先端状態モノでループ
            for (int i = 0; i < start_edge_array.size(); i++) {
                onePanel = start_edge_array.get(i);
                // 次の状態取得
                newPanels = onePanel.nextPanels();
                for (int j = 0; j < newPanels.length; j++) {
                    newPanel = newPanels[j];
                    sum = newPanel.toString();

                    // 既出なら捨てる
                    if (start_already_hash.containsKey(sum))
                        continue;
                    // 追加
                    start_already_hash.put(sum, newPanel);

                    // 既出とビンゴ?
                    bingo = goal_already_hash.get(sum);
                    if (bingo != null) {
                        checkMate(newPanel, bingo, true, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // ビンゴ?
                    bingo = goal_edge_hash.get(sum);
                    if (bingo != null) {
                        checkMate(newPanel, bingo, true, panel_num, route_hash);
                        bingo_flg = true;
                    }

                    // 既出じゃなければゴールとの距離計算
                    newPanel.calculateDistance(goal_panel);
                    if (newPanel.distance == 0) {
                        checkMate(newPanel, null, true, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // 次の先端状態モノに入れる
                    start_next_array.add(newPanel);
                    start_edge_hash.put(sum, newPanel);
                }
            }
            start_edge_array = sort(start_next_array);

            // スタート側からのがビンゴ
            if (bingo_flg)
                break;

            // スタート側からの比較用Hashtableクリア
            goal_edge_hash = new Hashtable<String, InnerPanel>();
            goal_next_array = new ArrayList<InnerPanel>();
            // ゴール側からの先端状態モノでループ
            for (int i = 0; i < goal_edge_array.size(); i++) {
                onePanel = goal_edge_array.get(i);
                // 次の状態取得
                newPanels = onePanel.nextPanels();
                for (int j = 0; j < newPanels.length; j++) {
                    newPanel = newPanels[j];
                    sum = newPanel.toString();

                    // 既出なら捨てる
                    if (goal_already_hash.containsKey(sum))
                        continue;
                    // 追加
                    goal_already_hash.put(sum, newPanel);

                    // 既出とビンゴ?
                    bingo = start_already_hash.get(sum);
                    if (bingo != null) {
                        checkMate(bingo, newPanel, false, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // ビンゴ?
                    bingo = start_edge_hash.get(sum);
                    if (bingo != null) {
                        checkMate(bingo, newPanel, false, panel_num, route_hash);
                        bingo_flg = true;
                    }

                    // 既出じゃなければ初期との距離計算
                    newPanel.calculateDistance(start_panel);
                    if (newPanel.distance == 0) {
                        checkMate(null, newPanel, true, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // 次の先端状態モノに入れる
                    goal_next_array.add(newPanel);
                    goal_edge_hash.put(sum, newPanel);
                }
            }
            goal_edge_array = sort(goal_next_array);

            // ゴール側からのがビンゴ
            if (bingo_flg)
                break;
        }
        Enumeration<String> e = route_hash.keys();
        while (e.hasMoreElements()) {
            String key = e.nextElement();
            System.out.println(panel_num + ":" + route_hash.get(key) + ":" + key);
        }
    }

    public ArrayList<InnerPanel> sort(ArrayList<InnerPanel> source) {
        InnerPanel source_array[] = source.toArray(new InnerPanel[0]);
        Arrays.sort(source_array, new PanelCompare());
        ArrayList<InnerPanel> sorted_list = new ArrayList<InnerPanel>();
        for (int i = 0; i < MAX_NUM && i < source_array.length; i++)
            sorted_list.add(source_array[i]);
        return sorted_list;
    }

    public class PanelCompare implements Comparator {
        public int compare(Object o1, Object o2) {
            InnerPanel p1 = (InnerPanel)o1;
            InnerPanel p2 = (InnerPanel)o2;
            return p1.distance - p2.distance;
        }
    }

    /**
     * チェックメイト.
     */
    public void checkMate(InnerPanel start_side, InnerPanel goal_side,
        boolean even, int panel_num, Hashtable<String, String> route_hash) {
        StringBuffer resolv_sb = null;
        if (start_side != null)
            resolv_sb = new StringBuffer(start_side.route.toString());
        else
            resolv_sb = new StringBuffer();

        StringBuffer goal_sb = null;
        if (goal_side != null)
            goal_sb = goal_side.route;
        else
            goal_sb = new StringBuffer();

        char c;
        for (int i = goal_sb.length() - 1; i >= 0; i--) {
            c = goal_sb.charAt(i);
            switch (c) {
                case 'U':
                    resolv_sb.append('D');
                    break;
                case 'D':
                    resolv_sb.append('U');
                    break;
                case 'L':
                    resolv_sb.append('R');
                    break;
                case 'R':
                    resolv_sb.append('L');
                    break;
            }
        }
        String result = resolv_sb.toString();

        // カウント
        int u_count = 0, d_count = 0, l_count = 0, r_count = 0;
        for (int i = 0; i < result.length(); i++) {
            c = result.charAt(i);
            switch (c) {
                case 'U':
                    u_count++;
                    break;
                case 'D':
                    d_count++;
                    break;
                case 'L':
                    l_count++;
                    break;
                case 'R':
                    r_count++;
                    break;
            }
        }
        StringBuffer save_sb = new StringBuffer();
        save_sb.append("U=");
        save_sb.append(u_count);
        save_sb.append(",D=");
        save_sb.append(d_count);
        save_sb.append(",L=");
        save_sb.append(l_count);
        save_sb.append(",R=");
        save_sb.append(r_count);
        route_hash.put(save_sb.toString(), result);
    }
}

2011年8月4日木曜日

mod_proxyでエンコード・デコードされる文字

技術メモ。のちのち参照の可能性アリ。
apache2.2.17のap_proxy_canonencから(modules/proxy/proxy_util.cの153〜238行目)

  • /とエンコードされた/(%2F)はそのまま。
  • ~$-_.+!*'(),;:@&=(%7E%24%2D%5F%2E%2B%21%2A%27%28%29%2C%2B%2A%40%26%3D)はデコードされる。
  • 英数字でもなく上記の文字でもないものはエンコードされる。

以上の処理が行われた後、バックエンドに渡される。