君は心理学者なのか?

大学時代に心理学を専攻しなぜかプログラマになった、サイコ(心理学)プログラマかろてんの雑記。

【Java言語で学ぶリファクタリング入門】クラスの抽出

いきさつ

昔書いたコードが悲しい感じだった&どこから手を付ければよいかわからない感じだったので、

リファクタリングの知識が欲しかった。

そしたら手元にこの本があった。

リファクタリング

Playerクラス

class Player {
    private _currentMedia;
    private _musicData;
    private _videoData;

    public setMedia() { };
    public play() { };
    public loop() { };
    public stop() { };

    private playMusic() { };
    private loopMusic() { };
    private stopMusic() { };
    private playVideo() { };
    private loopVideo() { };
    private stopVideo() { };
}

playerが責務を持ちすぎ。

  • musicの再生・停止・ループ方法を知っている
  • videoの再生・停止・ループ方法を知っている

なにがしんどいのか

新しいmediaを追加するたびにmethodを追加する必要がある。

playerが再生されるものに対しての知識を持ちすぎているので、密結合。

リファクタリング方法

クラスの抽出を行なう。

リファクタリング

新たにMusicMediaとVideoMediaクラスを作った。

Mediaクラス

/** 共通インターフェース */
interface Playable {
    play();
    loop();
    stop();
}

/**MusicMedia */
class MusicMedia implements Playable {
    public play() { };
    public stop() { };
    public loop() { };
}

/** VideoMedia */
class VideoMedia implements Playable {
    public play() { };
    public stop() { };
    public loop() { };
}

Playerクラス

class Player {
    private _currentMedia: Playable;

    public setMedia(media: Playable) {
        this._currentMedia = media;
    }
    public play() {
        this._currentMedia.play();
    }
    public stop() {
        this._currentMedia.stop();
    }
    public loop() {
        this._currentMedia.loop();
    }
}

何がうれしいのか

  • 新たにMediaを追加する時に、既存のコードに手を加えなくて良い(拡張に対して開いている)

  • 既存のmediaの修正をする時に、クラス内で修正が収まる(修正に対して閉じている)

  • PlayerクラスはPlayableインターフェースを実装しているクラスなら何でも扱えるので、疎結合

【Node.js】Expressをinstallして起動(npm start)するまで

f:id:karoten512:20180322123340j:plain

いきさつ

少し作りたいものがあったので、

Expressをinstallして起動してみる

手順

express-generatorをグローバルインストール

$ npm install express-generator -g  

expressコマンドでプロジェクトを作成

$ express restapi

必要なnode_moduleをinstall

$ cd restapi; npm install

アプリケーションの起動

$ npm start

> restapi@0.0.0 start /home/vagrant/dev/restapi
> node ./bin/www

ポートにアクセス

ipアドレス:3000

にアクセスすると、

f:id:karoten512:20180322123120p:plain

と表示され、起動が確認できる。

おまけ

ポート3000がLISTENになっているか見てみる。

$ netstat -antp
tcp        0      0 :::3000    :::*   LISTEN      22175/node

ポート3000をnodeプロセスがLISTENしている。

【CTF】サーバをハックする〜Simple Auth IIとSQLiteと〜

f:id:karoten512:20180322104803j:plain

いきさつ

セキュリティの勉強も兼ねてCTFの問題を解いてみた。

サーバをハックするより前にソースコードが見えているので

ハックするような大したことはしていない。

問題

ksnctf.sweetduet.info

20ptなので超簡単ということだが。。。?

考えたこと

ログイン画面

f:id:karoten512:20180322104951p:plain

適当なID&パスワードを入れ、ログインしてみる。

f:id:karoten512:20180322105024p:plain

ソースコード

<?php

function h($s)
{
    return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

if (!isset($_POST['id']) or !is_string($_POST['id']))
    $_POST['id'] = '';
if (!isset($_POST['password']) or !is_string($_POST['password']))
    $_POST['password'] = '';

$try = false;
$ok = false;

if ($_POST['id']!=='' or $_POST['password']!=='')
{
    $try = true;
    $db = new PDO('sqlite:database.db');
    $s = $db->prepare('SELECT * FROM user WHERE id=? AND password=?');
    $s->execute(array($_POST['id'], $_POST['password']));
    $ok = $s->fetch() !== false;
}

postされたidとpasswordでuserテーブルを検索、

該当レコードが存在する場合はログイン成功ということらしい。

SQLインジェクション使える?→使えないよね

プリペアドステートメントを使用しているので、

execute内に渡された値はエスケープされる。

この時点で

やれることがなくなってしまった。。。

ググる

timeislong.hatenablog.com

この記事をみると、

「ダウンロード」という言葉がある。

どういうこと?

SQLiteについて調べる

どうやらSQLiteのDB情報は、

****.dbというファイル形式で保存されるらしい。

そして、

<?php
//...

$db = new PDO('sqlite:database.db');

このsqliteの後で指定されているのはDBのファイルで、

相対パス指定されているということらしい。

DBファイルをダウンロード

sourceのurlのauth.phpの部分を、database.dbに変えるとDBファイルがダウンロードできる。

(普通じゃ考えられないけど)

SQLiteで読み込む

$ sqlite3 ~/Downloads/database.db 
sqlite>

適当なsqlを発行

phpソースコードから、userテーブルが有ることがわかっているので、

selectで全部持ってくる。

sqlite> select * from user;
root|FLAG_****

でてきた。

まとめ

この手の問題を特には様々な知識が求められる。

今回で言えば、

などなど。

なかなか勉強になった。

#Cでsocket通信をしてみる。ついでにnetstatでlistenしているポートを調べる。

C#でソケット通信

職場でC#を使うのと、

ソケット通信に興味があるのでちょっと練習してみた。

ソースコード

server側

using System;

public class Server
{
  public static void Main()
  {
    string ipString = "127.0.0.1";

    System.Net.IPAddress ipAdd = System.Net.IPAddress.Parse(ipString);

    int port = 2001;

    System.Net.Sockets.TcpListener listener =
      new System.Net.Sockets.TcpListener(ipAdd, port);

    listener.Start();

    Console.WriteLine("Listenを開始しました({0}:{1})。",
        ((System.Net.IPEndPoint)listener.LocalEndpoint).Address,
        ((System.Net.IPEndPoint)listener.LocalEndpoint).Port);


    // 接続要求があったら受け入れる
    System.Net.Sockets.TcpClient client = listener.AcceptTcpClient();
    Console.WriteLine("クライアント({0}:{1})と接続しました。",
        ((System.Net.IPEndPoint)client.Client.RemoteEndPoint).Address,
        ((System.Net.IPEndPoint)client.Client.RemoteEndPoint).Port);

    // NetworkStreamを取得データの流れ
    System.Net.Sockets.NetworkStream ns = client.GetStream();

    ns.ReadTimeout = 10000;
    ns.WriteTimeout = 10000;

    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    bool disconnected = false;
    System.IO.MemoryStream ms = new System.IO.MemoryStream();

    byte[] resBytes = new byte[256];
    int resSize = 0;


    do
    {
      //データの一部を受信する
      resSize = ns.Read(resBytes, 0, resBytes.Length);
      //Readが0を返した時はクライアントが切断したと判断
      if (resSize == 0)
      {
        disconnected = true;
        Console.WriteLine("クライアントが切断しました。");
        break;
      }
      //受信したデータを蓄積する
      ms.Write(resBytes, 0, resSize);
      //まだ読み取れるデータがあるか、データの最後が\nでない時は、
      // 受信を続ける

    } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');

    //受信したデータを文字列に変換
    string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
    ms.Close();

    resMsg = resMsg.TrimEnd('\n');
    Console.WriteLine(resMsg);

    if (!disconnected)
    {
      //クライアントにデータを送信する
      //クライアントに送信する文字列を作成
      string sendMsg = resMsg.Length.ToString();

      //文字列をByte型配列に変換
      byte[] sendBytes = enc.GetBytes(sendMsg + '\n');

      //データを送信する
      ns.Write(sendBytes, 0, sendBytes.Length);
      Console.WriteLine(sendMsg);
    }

    ns.Close();
    client.Close();
    Console.WriteLine("クライアントとの接続を閉じました。");

    listener.Stop();
    Console.WriteLine("Listenerを閉じました。");

    Console.ReadLine();
  }

}

clinent側

using System;

public class Client
{
  public static void Main()
  {
    Console.WriteLine("入力してください");
    string sendMsg = Console.ReadLine();

    if (sendMsg == null || sendMsg.Length == 0)
    {
      return;
    }

    string ipOrHost = "127.0.0.1";
    int port = 2001;


    System.Net.Sockets.TcpClient tcp =
      new System.Net.Sockets.TcpClient(ipOrHost, port);
    Console.WriteLine("サーバー({0}:{1})と接続しました({2}:{3})。",
        ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Address,
        ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Port,
        ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Address,
        ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Port);


    System.Net.Sockets.NetworkStream ns = tcp.GetStream();

    ns.ReadTimeout = 10000;
    ns.WriteTimeout = 10000;


    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    byte[] sendBytes = enc.GetBytes(sendMsg + '\n');

    ns.Write(sendBytes, 0, sendBytes.Length);
    Console.WriteLine(sendMsg);


    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    byte[] resBytes = new byte[256];
    int resSize = 0;
    do
    {
      resSize = ns.Read(resBytes, 0, resBytes.Length);

      if (resSize == 0)
      {
        Console.WriteLine("サーバーが切断しました。");
        break;
      }

      ms.Write(resBytes, 0, resSize);

    } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');

    string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
    ms.Close();

    resMsg = resMsg.TrimEnd('\n');
    Console.WriteLine(resMsg);

    ns.Close();
    tcp.Close();
    Console.WriteLine("切断しました。");

    Console.ReadLine();
  }
}

コンパイル

mcs client.cs
mcs server.cs

実行

サーバの実行

$ mono server.exe
Listenを開始しました(127.0.0.1:2001)。

クライアントの実行

$ mono client.exe 
入力してください
hello world

サーバの反応

クライアント(127.0.0.1:32924)と接続しました。
hello world
11
クライアントとの接続を閉じました。
Listenerを閉じました。

まとめ

TCP通信というのは、IPアドレスとポートの組み合わせ。

だからlocalhost上でポートさえ違えば、通信ができる。

今回は

・サーバ  IP 127.0.0.1  ポート 2001

・クライアント  IP 127.0.0.1  ポート 32924

で通信を行った。

なお、クライアントのポートは通信毎に自動で割り振られる。

netstatでポートがLISTENしているかどうかを調べてみる

$ netstat -anpt
tcp        0      0 127.0.0.1:2001              0.0.0.0:*                   LISTEN      14145/mono  

monoというプロセスが、 2001ポートを使って通信していることがわかる。

-aオプション

LISTEN状態も含むすべてのソケットを表示する。

-nオプション

IPアドレス・ポート番号をそのまま表示する。

これがない場合、ドメイン名として表示されたり、sshhttpdとか表示されたりする。

-pオプション

通信しているプロセスを表示する。

iphoneを探していたはずなのに、いつしか私は死に場所を探していた。

f:id:karoten512:20180318212418j:plain

※ 学生の頃の話です

その1

眩しい。どこかで蛍光灯が光っている。

背中にはぺらぺらの布団と、硬い床の感覚。

目を開いた。見覚えのある部屋。

どうやら無事に家に帰ってきたらしい。

その2

眼鏡を探す。ない。

スマホを探す。これもない。

目がさめると、私の生活インフラの大部分を失っていた。

その3

「彼らを取り戻さなくては」そう思った。

残念ながらこのような展開には慣れていた。

酒をのんで記憶を飛ばし、現場に何かしらの置き土産をするのは割りとよくある話だ。

少なくとも私には。

これだけ簡単に記憶が飛ぶのだから、人間の記憶なんてもろいもんだなーと思う。

メモリだったら揮発性。どうせ大した記憶じゃないから別に揮発していいんだけれど。

その4

Macを広げ、「iphoneを探す」

画面上にiphoneの現在位置が表示される。

その5

指し示された位置は、入ったことのない森だった。

その6

うそでしょ。そう思う。

昨日は鴨川で楽しくお花見をしていた。

なんでこんな「河原から小一時間ほど歩いた森の中」にiphoneが。

でも確かに森の中だった。

周りには目印がなにもなく、あるのは等高線。

丁度緩やかな谷になっているところに、たしかにiphoneが存在している。

少なくともGPS上ではそうなっていた。

その7

多分位置情報がずれてるんだ。とにかく現場に行こう。

そしたら近くの公道にiphoneが落ちているはずだ。

そう思った私は、Macを持って現場に向かった。

その8

現場近くでMacを広げる。

iphoneには緊急モードというものが搭載されていて、

それをMacから遠隔で起動することができる。

起動すると、

画面ロックしたり、

iphoneの画面上に連絡先電話番号を表示したり、

……iphoneに断末魔の叫びを上げさせることができる。

それはただの甲高い発信音。でも聞くたびに「これ断末魔だよなぁ」と感じる。

その9

森の近くの公道にて、その時は来た。

Mac画面上のボタンを押した。

iphoneが叫んだ。

…その断末魔は、森の方から聞こえた。

声のする方へ、少しづつ足をすすめる。

足元が悪い。どうしてこんなところに昨日の私は存在していたのか。

どう考えても死にたかったとしか思えない。

無意識に死にたかったとしか、思えないのだ。

その10

断末魔が近くなる。

更に悪くなる足場。

鳴り続ける断末魔。

ついに……見つけた。

その11

iphoneと。その横に眼鏡が見つかった。

まるで昨日の私は、上空からUFOに吸い上げられ、

眼鏡とiphoneをその場に置き忘れてしまったようだ。

異様な光景がそこにあった。

その12

昨日そこで何があったのか、いろいろな人の証言を確認した。

わかったのは、

「河原で呑んでいたお前すごいニコニコしてた」

「でも突然フラフラと何処かに歩きだした」

という2点だけ。

その13

僕は考えるのをやめた。

酒もしばらくやめた。

その14

3日後に日本酒を呑んだ。美味しかった。

以前勤めていた会社で、どうやら宇宙飛行士の募集を開始したらしい

なんとなく

前勤めていたシステム会社の求人を見ていて驚いた。

面接の流れ のページには確かにこう書いてあった。

「面接は1次〜7次面接まであります」

これ宇宙飛行士の選考かよ!

が、落ち着いてよくよく考えてみると、、、

これマジで宇宙飛行士採用しようとしてんじゃねーの。

そう、思い始めた。

おそらく僕がいなくなった後、

最後のフロンティア・宇宙を目指し、会社の経営を大きく転換したのだ。

もうインターネットにはフロンティアがなくなってしまった。

社長はそう感じたんだろう(かっこいい)

「開発」って言葉は、今では多分、宇宙開発を指しているんだろう。

「サービスローンチ」だって、

今やもうロケット発射の方の「ローンチ(launch)」だ。

一回のローンチで大騒ぎだ。

「障害」は、

宇宙を流れる太陽風による電波障害のことをいっているんだろう。

全ての電子機器が壊れるから、システム障害より余計にタチが悪い。

ただし、いいこともある。

太陽風のせいで窓の外からはたまーにオーロラが見える。

ちょっと顔を左に向けるだけでオーロラ。圧巻。

ちなみに、会社のオフィスに行くまでのエレベーターは、

宇宙エレベーターだ。

なんて素晴らしい会社なんだ。

もっと早く経営転換していれば、前の会社辞めなかったのに。

その時思い出した

「いつでも戻ってきていいよ」

という社長の言葉を。

僕の1次面接が、今、始まる。

目が覚めると、女物のパンツが、ポケットに2枚、入っていた。

※ ノンフィクション

その1

この間ものすごく酔っ払って家に帰った。

朝起きたら目線がすごく低かった。そして寒い。

どうやら昨夜(ほぼ朝)、

家につくなりすべての鎧を脱ぎ捨て、そのまま床で寝てしまったらしい。

あたりには服が散乱し、僕の肌は寒さで粟立っていた。

こういう時の感覚は独特である。

世界がふわふわ浮いている感じ。そんな世界を歩く僕もふわふわ浮いている。

ふわふわしている割に重そうに体を上げ、

「道半ば行き倒れました」みたいなメッセージ性のある置かれ方をしているジーンズを手に取り、

何気なくポケットをまさぐった。

その2

床に落ちた。

女物のパンツが2枚。

ショッキングピングとイエローだった。

その3

いったい昨夜、何があったのか。

ブラックライトを当てたら今にも光りだしそうな、蛍光色の2枚を見つめる。

昨夜、職質されたら僕はどうなっていたのだろう。

若干体温が上がってきた僕の肌が、再び粟立つ。今度は悪寒だ。

その4

部屋の隅にその2枚を投げ捨て、

何事もなかったかのように、とりあえず会社に行った。

仕事しながら記憶を辿った。霞をつかむかのように何も思い出せない。

「このまま、何ごともなかったかのように生活をしていく」

そんな選択肢が浮かぶが、

「警察が家宅捜索にくるイメージ」がその上に浮かび、

完全に上書きしていった。頭を抱えた。

記憶のない中、罪を犯した。

自分には、そうとしか思えなかった。

その5

いや。何かがおかしい。

数が、合わないのだ。

今しがた、若干記憶が戻ってきた。

そう。

昨日行った店には、男性が2人、女性が1人いたのだった。

その6

昨日いた店に問い合わせてみて、僕は安堵した。

僕は犯罪を犯していなかった。

店長「常連のおじさんが、君に女物のパンツを2枚くれた」

安堵しながら僕は思った。

「あれ?結局意味わかんねーぞ」

(つづく)