.NETプログラミング研究

.NETプログラミング研究 第114号


┏第114号 ━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         .NETプログラミング研究         ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

──<メニュー>───────────────────────
・DNSサーバーと直接やり取りして、情報を取得する
  ・具体例
  ・補足説明
  ・最後に
───────────────────────────────

───────────────────────────────
■Web版について
───────────────────────────────
メールでは表示できない画像や表などはWeb版に掲載しています。

[URL].NETプログラミング研究 第114号 Web版
http://wiki.dobon.net/index.php?.NET%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%B8%A6%B5%E6%2F114

───────────────────────────────
■DNSサーバーと直接やり取りして、情報を取得する
───────────────────────────────
.NET Frameworkでは、「ホスト名からIPアドレス、IPアドレスからホ
スト名を取得する」で説明しているように、Dns.GetHostEntryメソッ
ドを使用することで、DNSに問い合わせをして、逆引き(IPアドレス
からドメイン名を取得)と正引き(ドメイン名からIPアドレスを取得)
を行うことができます。しかし、NSやMXなどのレコードを検索したり、
DNSサーバーを指定して検索することはできません。

[URL]ホスト名からIPアドレス、IPアドレスからホスト名を取得する
http://dobon.net/vb/dotnet/internet/dnslookup.html

もしこのようなDNSクライアントの機能が必要であれば、サードパー
ティー製のライブラリを使用するのが手っ取り早いでしょう。
例えば、フリーで公開されているライブラリには、以下のようなもの
があります。

[URL]C# .NET DNS query component
http://www.codeproject.com/Articles/12072/C-NET-DNS-query-component

[URL]DNS.NET Resolver
http://www.codeproject.com/Articles/23673/DNS-NET-Resolver-C

[URL]DNS Client Library for .NET
http://www.simpledns.com/dns-client-lib.aspx

[URL]OpenDNS.NET
http://sourceforge.net/projects/opendnsnet/

[URL]ARSoft.Tools.Net - C#/.Net DNS client/server, SPF and
SenderID Library
http://arsofttoolsnet.codeplex.com/

[URL]DnDns and PocketDnDns - A .NET DNS Client Resolver
Library
http://dndns.codeplex.com/

しかしここでは、あえてSocket(サンプルでは、UdpClientクラス)
を使用して、直接DNSサーバーとやり取りすることに挑戦します。

●具体例

本来なら、RFC(注釈がWebにあります)に書かれているDNSの決まり事
をここで説明すべきなのだと思いますが、私にはそれだけの能力も余
裕もありませんので、申し訳ありませんが、サンプルコードのみを示
し、説明はコード内のコメントに記述しておきます。

このサンプルはコンソールアプリケーションで、指定されたDNSサー
バーを使用して、Aレコードを検索し、ホスト名「microsoft.com」か
らIPアドレスを取得(正引き)しています。

このサンプルは、Aレコードの他、NS、CNAME、PTR、MXにも対応して
います。検索するレコードを変更する場合は、MainメソッドのqType
の値を変更してください。その場合、domainOrAddressの文字列も適
当に(例えば、PTRならばIPアドレスに)変更してください。

このサンプルでは、DNSサーバーとして「Google Public DNS」を使用
するようにしていますが、適当なDNSサーバーに変更してください。

[URL]Google Public DNS
https://developers.google.com/speed/public-dns/

[URL]Free and Public DNS Server List
http://pcsupport.about.com/od/tipstricks/a/free-public-dns-servers.htm

このサンプルを作成するにあたり、以下の記事を参考にさせていただ
きました。

[URL]RFC 1035
http://www.faqs.org/rfcs/rfc1035.html

[URL]DNS クライアントを作ってみよう (2)
http://x68000.q-e-d.net/~68user/net/resolver-2.html

[URL]c# - How to get mx records for a dns name with System.Net.
DNS? - Stack Overflow
http://stackoverflow.com/questions/2669841/how-to-get-mx-records-for-a-dns-name-with-system-net-dns/7546721

‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
Imports System.Net
Imports System.Text
Imports System.Collections.Generic

Class Program
    'エントリポイント
    Public Shared Sub Main(args As String())
        'DNSのIPアドレスまたはホスト名
        Dim dns As String = "8.8.8.8"
        'DNSのポート番号
        Dim dnsPort As Integer = 53

        '検索するホスト名またはIPv4アドレス
        Dim domainOrAddress As String = "microsoft.com"
        'Dim domainOrAddress As String = "134.170.185.46"
        'レコード種別
        'A=1 NS=2 CNAME=5 PTR=12 MX=15 のみに対応
        Dim qType As Byte = 1

        '送信するデータを作成する
        Dim req As Byte() = CreateRequestMessage(domainOrAddress, qType)
        Console.WriteLine(BitConverter.ToString(req))

        Dim res As Byte() = Nothing
        Using udpc As New System.Net.Sockets.UdpClient(dns, dnsPort)
            'UDPでリクエストメッセージを送信する
            udpc.Send(req, req.Length)

            'レスポンスを受信する
            Dim ep As IPEndPoint = Nothing
            res = udpc.Receive(ep)
        End Using
        Console.WriteLine(BitConverter.ToString(res))

        '受信したデータから回答を取り出す
        Dim answers As String() = GetAnswersFromResponse(res, req.Length)

        '回答を表示する
        Console.WriteLine("回答:")
        For Each s As String In answers
            Console.WriteLine(s)
        Next

        Console.ReadLine()
    End Sub

    'リクエストメッセージを作成する
    Private Shared Function CreateRequestMessage( _
            qName As String, qType As Byte) As Byte()
        qName = qName.TrimEnd("."c)

        Using ms As New System.IO.MemoryStream()
            'Header section
            'ID
            ms.WriteByte(0)
            ms.WriteByte(0)
            'QR、Opcode、AA、TC、RD(RDのみ1)
            ms.WriteByte(1)
            'RA、Z、RCODE
            ms.WriteByte(0)
            'QDCOUNT(質問数)
            ms.WriteByte(0)
            ms.WriteByte(1)
            'ANCOUNT(回答数)
            ms.WriteByte(0)
            ms.WriteByte(0)
            'NSCOUNT
            ms.WriteByte(0)
            ms.WriteByte(0)
            'ADCOUNT
            ms.WriteByte(0)
            ms.WriteByte(0)

            'Question section
            'QNAME を作成
            Dim buf1 As String() = qName.Split("."c)
            If qType = 12 Then
                'PTRの時は、IPアドレスを逆に並べて、.in-addr.arpa を付ける
                buf1 = New String() _
                    {buf1(3), buf1(2), buf1(1), buf1(0), "in-addr", "arpa"}
            End If
            For Each s As String In buf1
                '文字列をバイト配列に変換
                Dim bs1 As Byte() = Encoding.ASCII.GetBytes(s)
                '長さと文字列データを書き込む
                ms.WriteByte(CByte(bs1.Length))
                ms.Write(bs1, 0, bs1.Length)
            Next
            ms.WriteByte(0)
            'QTYPE
            ms.WriteByte(0)
            ms.WriteByte(qType)
            'QCLASS(Internetは1)
            ms.WriteByte(0)
            ms.WriteByte(1)

            Return ms.ToArray()
        End Using
    End Function

    'レスポンスメッセージから回答を取得する
    Private Shared Function GetAnswersFromResponse( _
            data As Byte(), startPos As Integer) As String()
        'データの読み込み位置
        Dim pos As Integer = 0

        'RCODE (0=No error condition)
        Dim resCode As Integer = data(3) And &HF
        Console.WriteLine("Response Code:{0}", resCode)
        'ANCOUNT(回答数)
        pos = 6
        Dim anCount As Integer = ConvertToShort(data, pos)
        Console.WriteLine("Answers Count:{0}", anCount)
        'NSCOUNT
        Dim nsCount As Integer = ConvertToShort(data, pos)
        'ARCOUNT
        Dim arCount As Integer = ConvertToShort(data, pos)
        'Questionを飛ばす
        pos = startPos

        Dim list As New List(Of String)()
        '回答数だけ繰り返す
        For i As Integer = 0 To anCount - 1
            'ドメイン名を取り出す
            Dim domainName As String = GetDomainName(data, pos)
            Console.WriteLine("Domain:{0}", domainName)
            'TYPE
            Dim ppType As Short = ConvertToShort(data, pos)
            'CLASS、TTLを飛ばす
            pos += 6
            'RDLENGTH
            Dim rdLength As Short = ConvertToShort(data, pos)

            Dim str As String = "???"
            Select Case ppType
                Case 1
                    'A
                    'IPアドレスを抜き出す
                    str = GetIPv4Address(data, pos)
                    list.Add(str)
                    Exit Select
                Case 2, 5, 12
PTR:
                    'NS
                    'CNAME
                    'PTR
                    'ドメイン名を抜き出す
                    str = GetDomainName(data, pos)
                    list.Add(str)
                    Exit Select
                Case 15
                    'MX
                    'Preference
                    Dim preference As Short = ConvertToShort(data, pos)
                    Console.WriteLine("Preference:{0}", preference)
                    'ドメイン名を取得する
                    GoTo PTR
            End Select
            Console.WriteLine(str)
        Next

        Return list.ToArray()
    End Function

    'データの指定位置からDomain名を取得する
    Private Shared Function GetDomainName( _
            data As Byte(), ByRef pos As Integer) As String
        Dim sb As New StringBuilder()

        While pos < data.Length
            'labelの長さを取得
            Dim len As Integer = data(pos)
            pos += 1
            '長さが0の時、終了
            If len = 0 Then
                Exit While
            End If
            If (len And &HC0) = &HC0 Then
                'Message compressionの時
                '新たな位置を取得
                Dim newpos As Integer = _
                    ConvertToShort(CByte(len And &H3F), data(pos))
                pos += 1
                '新たな位置から取得したDomainを追加
                If sb.Length > 0 Then
                    sb.Append("."c)
                End If
                sb.Append(GetDomainName(data, newpos))
                Exit While
            End If

            'byteを文字列に変換して、labelを取得し、追加する
            If sb.Length > 0 Then
                sb.Append("."c)
            End If
            sb.Append(Encoding.ASCII.GetString(data, pos, len))
            pos += len
        End While

        Return sb.ToString()
    End Function

    'データの指定位置からIPアドレスを取得する
    Private Shared Function GetIPv4Address( _
            data As Byte(), ByRef pos As Integer) As String
        pos += 4
        Return String.Format("{0}.{1}.{2}.{3}", _
            data(pos - 4), data(pos - 3), data(pos - 2), data(pos - 1))
    End Function

    '2つのバイトからInt16を作成
    Private Shared Function ConvertToShort(b1 As Byte, b2 As Byte) As Short
        Return CShort(b1 << 8 Or b2)
    End Function
    Private Shared Function ConvertToShort( _
            data As Byte(), ByRef pos As Integer) As Short
        pos += 2
        Return ConvertToShort(data(pos - 2), data(pos - 1))
    End Function
End Class
‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥

‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥
using System;
using System.Net;
using System.Text;
using System.Collections.Generic;

class Program
{
    //エントリポイント
    public static void Main(string[] args)
    {
        //DNSのIPアドレスまたはホスト名
        string dns = "8.8.8.8";
        //DNSのポート番号
        int dnsPort = 53;

        //検索するホスト名またはIPv4アドレス
        string domainOrAddress = "microsoft.com";
        //string domainOrAddress = "134.170.185.46";
        //レコード種別
        //A=1 NS=2 CNAME=5 PTR=12 MX=15 のみに対応
        byte qType = 1;

        //送信するデータを作成する
        byte[] req = CreateRequestMessage(domainOrAddress, qType);
        Console.WriteLine(BitConverter.ToString(req));

        byte[] res = null;
        using (System.Net.Sockets.UdpClient udpc =
            new System.Net.Sockets.UdpClient(dns, dnsPort))
        {
            //UDPでリクエストメッセージを送信する
            udpc.Send(req, req.Length);

            //レスポンスを受信する
            IPEndPoint ep = null;
            res = udpc.Receive(ref ep);
        }
        Console.WriteLine(BitConverter.ToString(res));

        //受信したデータから回答を取り出す
        string[] answers = GetAnswersFromResponse(res, req.Length);

        //結果を表示する
        Console.WriteLine("結果:");
        foreach (string s in answers)
        {
            Console.WriteLine(s);
        }

        Console.ReadLine();
    }

    //リクエストメッセージを作成する
    private static byte[] CreateRequestMessage(string qName, byte qType)
    {
        qName = qName.TrimEnd('.');

        using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
        {
            //Header section
            //ID
            ms.WriteByte(0);
            ms.WriteByte(0);
            //QR、Opcode、AA、TC、RD(RDのみ1)
            ms.WriteByte(1);
            //RA、Z、RCODE
            ms.WriteByte(0);
            //QDCOUNT(質問数)
            ms.WriteByte(0);
            ms.WriteByte(1);
            //ANCOUNT(回答数)
            ms.WriteByte(0);
            ms.WriteByte(0);
            //NSCOUNT
            ms.WriteByte(0);
            ms.WriteByte(0);
            //ADCOUNT
            ms.WriteByte(0);
            ms.WriteByte(0);

            //Question section
            //QNAME を作成
            string[] buf1 = qName.Split('.');
            if (qType == 12)
            {
                //PTRの時は、IPアドレスを逆に並べて、.in-addr.arpa を付ける
                buf1 = new string[] {
                    buf1[3], buf1[2], buf1[1], buf1[0], "in-addr", "arpa" };
            }
            foreach (string s in buf1)
            {
                //文字列をバイト配列に変換
                byte[] bs1 = System.Text.Encoding.ASCII.GetBytes(s);
                //長さと文字列データを書き込む
                ms.WriteByte((byte)bs1.Length);
                ms.Write(bs1, 0, bs1.Length);
            }
            ms.WriteByte(0);
            //QTYPE
            ms.WriteByte(0);
            ms.WriteByte(qType);
            //QCLASS(Internetは1)
            ms.WriteByte(0);
            ms.WriteByte(1);

            return ms.ToArray();
        }
    }

    //レスポンスメッセージから回答を取得する
    private static string[] GetAnswersFromResponse(byte[] data, int startPos)
    {
        //データの読み込み位置
        int pos = 0;

        //RCODE (0=No error condition)
        int resCode = data[3] & 0xF;
        Console.WriteLine("Response Code:{0}", resCode);
        //ANCOUNT(回答数)
        pos = 6;
        int anCount = ConvertToShort(data, ref pos);
        Console.WriteLine("Answers Count:{0}", anCount);
        //NSCOUNT
        int nsCount = ConvertToShort(data, ref pos);
        //ARCOUNT
        int arCount = ConvertToShort(data, ref pos);
        //Questionを飛ばす
        pos = startPos;

        List<string> list = new List<string>();
        //回答数だけ繰り返す
        for (int i = 0; i < anCount; i++)
        {
            //ドメイン名を取り出す
            string domainName = GetDomainName(data, ref pos);
            Console.WriteLine("Domain:{0}", domainName);
            //TYPE
            short ppType = ConvertToShort(data, ref pos);
            //CLASS、TTLを飛ばす
            pos += 6;
            //RDLENGTH
            short rdLength = ConvertToShort(data, ref pos);

            string str = "???";
            switch (ppType)
            {
                case 1:
                    //A
                    //IPアドレスを抜き出す
                    str = GetIPv4Address(data, ref pos);
                    list.Add(str);
                    break;
                case 2:
                    //NS
                case 5:
                    //CNAME
                case 12:
                    //PTR
                    //ドメイン名を抜き出す
                    str = GetDomainName(data, ref pos);
                    list.Add(str);
                    break;
                case 15:
                    //MX
                    //Preference
                    short preference = ConvertToShort(data, ref pos);
                    Console.WriteLine("Preference:{0}", preference);
                    //ドメイン名を取得する
                    goto case 2;
            }
            Console.WriteLine(str);
        }

        return list.ToArray();
    }

    //データの指定位置からDomain名を取得する
    private static string GetDomainName(byte[] data, ref int pos)
    {
        StringBuilder sb = new StringBuilder();

        while (pos < data.Length)
        {
            //labelの長さを取得
            int len = data[pos++];
            //長さが0の時、終了
            if (len == 0) { break; }
            if ((len & 0xC0) == 0xC0)
            {
                //Message compressionの時
                //新たな位置を取得
                int newpos = ConvertToShort((byte)(len & 0x3F), data[pos]);
                pos++;
                //新たな位置から取得したDomainを追加
                if (sb.Length > 0) { sb.Append('.'); }
                sb.Append(GetDomainName(data, ref newpos));
                break;
            }

            //byteを文字列に変換して、labelを取得し、追加する
            if (sb.Length > 0) { sb.Append('.'); }
            sb.Append(Encoding.ASCII.GetString(data, pos, len));
            pos += len;
        }

        return sb.ToString();
    }

    //データの指定位置からIPアドレスを取得する
    private static string GetIPv4Address(byte[] data, ref int pos)
    {
        return string.Format("{0}.{1}.{2}.{3}",
            data[pos++], data[pos++], data[pos++], data[pos++]);
    }

    //2つのバイトからInt16を作成
    private static short ConvertToShort(byte b1, byte b2)
    {
        return (short)(b1 << 8 | b2);
    }
    private static short ConvertToShort(byte[] data, ref int pos)
    {
        return ConvertToShort(data[pos++], data[pos++]);
    }
}
‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥

●補足説明

上記サンプルについて、少し補足の説明をします。

CreateRequestMessageメソッドで、DNSサーバーに送信するデータを
作成しています。ここでは、ホスト名とレコード種別をバイト配列に
変換する以外は、すべて定数を使っています。IDは、00 00 に固定し
ています。QCLASSは、"IN"を表す 1 にしています。

GetAnswersFromResponseメソッドで、DNSサーバーから受信したデー
タを解析しています。ここでは、Header部分は、RCODE(Response
Code)とANCOUNT(Answer数)だけを取得して、それ以外は無視して
います(NSCOUNTとARCOUNTも取得はしていますが、使用していません)。
Question部分は、送信したQuestionと同じ長さだと決めつけて、読み
飛ばしています。その後のAnswer部分のみを解析して、Authorityと
Additionalはあっても(NSCOUNTかARCOUNTが0でなくても)無視して
います。

Answer部分の解析は、NAME(ドメイン名)、TYPE(レコード種別)、
RDLENGTH(RDATAの長さ)、RDATAの取得だけを行い、他(CLASSとTTL)
は無視しています。RDATAの解析は、それがドメイン名ならば
GetDomainNameメソッド、IPアドレスならばGetIPv4Addressメソッド
で行っています。MXレコードの場合は、その前にPreference(優先度)
を取得しています。

ConvertToShortメソッドでは、2つのバイト値から1つのInt16値を作
成しています。.NET Frameworkには、よく似た機能のメソッドに
BitConverter.ToInt16メソッドがありますが、このメソッドはシステ
ムによって結果が変わる可能性があります(Windowsの場合は、その
ままのバイト配列順では、間違った値になってしまいます)。
BitConverterを使用した場合は、その後でIPAddress.
NetworkToHostOrderメソッドを使用してバイト順を正しく並べ直す必
要があります。

●最後に

皆様のおかげで、今年も一年無事に過ごすことができました。ありが
とうございました。来年もよろしくお願いいたします。それでは、よ
いお年を。

===============================
■ここで示したコードの多くはまずC#で書き、それを「Snippet
  Converter」でVB.NETのコードに変換し、修正を加えたものです。

[URL]Snippet Converter for .NET 4.0
http://codeconverter.sharpdevelop.net/SnippetConverter.aspx

■このマガジンの購読、購読中止、バックナンバー、説明に関しては
 次のページをご覧ください。
 http://dobon.net/vb/melma/index.html

■発行人・編集人:どぼん!
 http://dobon.net

■ご質問等はメールではなく、掲示板へお願いいたします。
 http://dobon.net/vb/bbs.html

■メールは下記URLのフォームメールから送信してください。
 http://dobon.net/mail.html

Copyright (c) DOBON!  All rights reserved.
===============================

このメルマガは現在休刊中です

ついでに読みたい

.NETプログラミング研究

RSSを登録する
発行周期 不定期
最新号 2014/12/30
部数 4,401部

このメルマガは
現在休刊中です

ついでに読みたい

今週のおすすめ!メルマガ3選

相馬一進の、1億稼ぐビジネスの本質|マーケティングと心理学
・天職で1億稼いだ相馬一進が、お金、自己啓発、仕事について、 心理学とマーケティングの側面から語る本格派メールマガジン。 ・著者の相馬一進は、ダライ・ラマ14世や、『7つの習慣』の スティーブン・コヴィーや、リチャード・ブランソンなどの、 海外セミナーへの日本人ツアーを企画、集客した実績を持つ。 ・最先端の起業・副業のノウハウや、マーケティングの原理原則、 生活の中からストレスを減らす方法、モチベーションの心理学、 複数の収入源を持つ方法、働く時間を減らす秘密などを配信中。
  • メールアドレスを入力

  • 規約に同意して

川島和正の日刊インターネットビジネスニュース
■読者数32万部超、日本一の個人メルマガ(まぐまぐ総合ランキング調べ) ■9年連続で年収1億円以上になり、70か国以上を旅行して、 190平方メートルの豪邸に住んで、スーパーカーに乗れるようになり、 さらに、著書は、日本を代表する超有名人2人に帯を書いてもらい、 累計50万部のベストセラーとなった、現在香港在住の川島和正が、 最新のビジネスノウハウ、自己啓発ノウハウ、健康ノウハウ、恋愛ノウハウ さらに「今チェックしておくべき情報リスト」などを配信中!
  • メールアドレスを入力

  • 規約に同意して

一緒に歩もう!小富豪への道
富裕層むけ、富裕層入りを目指す方むけの究極の資産防衛メルマガ!一国だけに資産を集めておくのは危険な時代がやってきました。海外ヘッジファンド、貴金属、不動産からアンティーク・コインまで、金融不安に負けない世界分散ポートフォリオを、経験豊富なファイナンシャル・プランナーが誠意をもってご案内します。
  • メールアドレスを入力

  • 規約に同意して

今週のおすすめ!メルマガ3選

日本株投資家「坂本彰」公式メールマガジン
サラリーマン時代に始めた株式投資から株で勝つための独自ルールを作り上げる。2009年10月、130万円だった株式資産は2015年に5000万円を突破。定期預金などを合わせた資産は1億円に。 平成24年より投資顧問業(助言)を取得。現在、著者自身が実践してきた株で成功するための投資ノウハウや有望株情報を会員向けに提供しているかたわら、ブログやコラム等の執筆活動も行う。 メールマガジン「日本株投資家 坂本彰 公式メールマガジン」は2014年まぐまぐマネー大賞を受賞。読者数2万人。雑誌等のメディア掲載歴多数。 2016年12月1日『「小売お宝株」だけで1億円儲ける法』が日本実業出版社より発売!
  • メールアドレスを入力

  • 規約に同意して

サラリーマンで年収1000万円を目指せ。
高卒、派遣社員という負け組から、外資系IT企業の部長になった男の、成功法則を全て公開します。誰にでも、どんな状況、状態からでも自分の力で人生を変えるための情報と知性を発信しています。人生を意のままにするには、脳みそとこころの両方が進化しなければなりません。そんな進化とは何か?をお届けする四コママンガ付きメルマガです。2014年から3年連続でまぐまぐ大賞部門賞を受賞しました 学歴やバックグラウンドに拘わらず、人生を思いのままに生きるために必要な考え方が書かれた、「良書リスト」も希望者に差し上げています。
  • メールアドレスを入力

  • 規約に同意して

ダメおやじの全財産をかけた崖っぷちFX通信
【1日に数万人が熟読する人気FXブログのメルマガ版】 相場歴30年以上のダメおやじがFXノウハウを大公開! 毎朝配信!毎日の経済指標情報や攻略法を無料で解説しています。 ●損切りがうまくできない、利食いが浅い ●ポジポジ病(ポジションを不要に持ってしまう) ●コツコツドカーン(小さく勝っても大きく負ける) ●エントリータイミングわからない ●メンタル面が弱い このようなお悩みがあれば購読してみてください。 FX初心者から経験者まで、FXの悩みをこのメルマガで解消します。 期間限定でメルマガ内で数万円相当分のFX情報商材をプレゼント中!
  • メールアドレスを入力

  • 規約に同意して

他のメルマガを読む

ウィークリーランキング