[TCP/IP] TCPクライアントをCで実装

TCPEchoClient.c

#include <stdio.h> /* printf(), fprintf()に必要 */
#include <sys/socket.h> /* socket(), connect(), send(), recv() */
#include <arpa/inet.h> /* sockaddr_in, inet_addr() */
#include <stdlib.h> /* atoi() */
#include <string.h> /* memset() */
#include <unistd.h> /* close() */

#define RCVBUFSIZE 32 /* 受信バッファのサイズ */

void DieWithError(char *errorMessage); /* エラー処理関数 */

int main(int argc, char *argv[]){
    int sock; /* ソケットディスクリプタ */
    struct sockaddr_in echoServAddr; /* エコーサーバアドレス */
    unsigned short echoServPort; /* エコーサーバポート */
    char *servIP;  /* サーバのIP */
    char *echoString; /* 送信する文字列 */
    char echoBuffer[RCVBUFSIZE]; /* エコー文字列用バッファ */
    unsigned int echoStringLen; /* エコーする文字列サイズ */
    int bytesRcvd, totalBytesRcvd; /* 1回のrecv()で読み取られるバイト数と全バイト数 */

    if((argc < 3) || (argc > 4)){
        fprintf(stderr, "Usage: %s <Server IP><Echo Word>[<Echo Port>]\n", argv[0]);
        exit(1);
    }

    servIP = argv[1]; /* 1つ目の引数: サーバのIP */
    echoString = argv[2]; /* 2つ目の引数: エコー文字列 */

    if (argc == 4)
        echoServPort = atoi(argv[3]); /* 指定ポートがあれば使用 */
    else
        echoServPort = 7; /* 7はエコーサービスのwellknown port */

    /* ストリームソケット作成 */
    if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        DieWithError("socket() failed");

    /* サーバのアドレス構造体 */
    memset(&echoServAddr, 0, sizeof(echoServAddr)); /* 構造体に0埋め */
    echoServAddr.sin_family = AF_INET; /* インターネットアドレスファミリ */
    echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* サーバIP */
    echoServAddr.sin_port = htons(echoServPort); /* サーバポート */

    /* サーバへの接続 */
    if(connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAdd)) < 0)
        DieWithError("connect() failed");

    echoStringLen = strlen(echoString); /* 入力データの長さ */

    /* 文字列をサーバに送信 */
    if (send(sock, echoString, echoStringLen, 0) != echoStringLen)
        DieWithError("send () sent a different number of bytes than expected");

    /* 同じ文字列をサーバから受信 */
    totalBytesRcvd = 0;
    printf("Received: ");

    while (totalBytesRcvd < echoStringLen) {
        /* バッファサイズに達するまでデータ受信 */
        if((bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0)) <= 0)
            DieWithError("recv() failed or connection closed prematurely");
        totalBytesRcvd += bytesRcvd; /* バイト数集計 */
        echoBuffer[bytesRcvd] = '\0'; /* 文字列終了 */
        printf(echoBuffer); /* バッファの表示 */
    }

    printf("\n");

    close(sock);
    exit(0);
}

DieWithError.c

#include <stdio.h> /* perror()に必要 */
#include <stdlib.h> /* exit()に必要 */

void DieWithError(char *errorMessage) {
    perror(errorMessage);
    exit(1);
}

$ gcc -o TCPEchoClient TCPEchoClient.c DieWithError.c

[TCP/IP] ソケット

TCPまたUDPで通信を行うにはソケットの作成をOSに依頼する必要がある。プログラムの必要に応じてソケットタイプを指定
int socket(int protocolFamily, int type, int protocol)
アプリケーションでソケットを終了させるにはclose()を使用する
int close(int socket)

ソケットAPIにはsockaddrという汎用のデータ型が定義されている
struct sockadddr {
unsigned short sa_family; // アドレスファミリ
char sa_data[14]; // アドレス情報
}

sockaddr構造体をTCP/IPのソケットアドレスに特化したのがsockaddr_in構造体
struct in_addr {
unsigned long s_addr; // IP address
}

struct sockaddr_in {
unsigned short sin_family; // TCP/IP
unsigned short sin_port; // address port
struct in_addr sin_addr; // ip address
char sin_zero[8];
}

### TCP接続
1. socket()を実行してTCPソケットを作成
2. connect()を実行してサーバへの接続を確立
 int connect(int socket, struct socketaddr *foreignAddress, unsigned int addressLength)
3. send()とrecv()を実行して通信を行う
int send(int socket, const void *msg, unsigned int msgLength, int flags)
int recv(int socket, void *rcvBuffer, unsigned int bufferLength, int flags)
4. closeを実行して接続クローズ

[TCP/IP]基礎

Internetはsocketというプログラミングインターフェイスを利用してネットワーク通信サービスにアクセスしている。
通信チャネルは、ブロードバンド技術を利用したイーサネット、ダイヤルアップモデムを使った接続などが考えられる。
プログラムによって組み立てられ解釈される情報をパケットして扱う
特定の問題を解決するプロトコルを切り分けて、モジュールのような形式で利用する仕組みが取られている
TCP/IPは、このようなプロトコルの集まり

### ソケット
一方のマシンでアプリケーションがソケットに書き込んだ情報を、相手方のマシンのアプリケーションが読み取る
TCP/IPプロトコルファミリのソケットはストリームソケット(TCP)とデータグラムソケット(UDP)
ソケットがリモートのアプルケーションプログラムからメッセージを受信できるのは、ソケットがポート番号にバインドされた後

DMZとは?

DeMilitarized Zoneの略で、外部ネットワークと社内ネットワークの中間に作られるネットワーク上のセグメント(区域)のこと。
外部ネットワークからも、内部ネットワークからも見ることができる
外部ネットワークからも内部ネットワークからもファイヤーウォールによって隔離されている
社内ネットワークと隔離することで、不正侵入された後のマルウェアの感染拡大を防ぐ

DMZの構成には2台のファイヤーウォールを設置して、インターネット – FW – DMZ – FW – 社内ネットワークとする方法がある

DMZホストとなるパソコンはIP固定が必要

### DMZ
IPアドレス単位で転送するため、公開するポートを指定する必要がない
セキュリティ: 無条件で全通信がサーバにたどり着くため注意が必要
ルータ設定: 簡単
IPアドレスの有効活用: 1つのグローバルIPアドレスに1つのサーバ

DMZの作成方法がわからん…

BGPルータとは

BGPとは、複数の独立したネットワークを接続したTCP/IPネットワークにおいて、ネットワーク間の経路情報を通信機器間で交換する手順を定めたプロトコル(通信規約)の一つ。
BGPは異なるAS間を結ぶ境界上のルータ同士がAS単位の経路情報(自ASが他のどのASと繋がっているか等)を交換するeBGP(exterior BGP)と、AS内のルータ間でAS間経路の情報を交換するiBGP(interior BGP)で構成される。

ルーティングプロトコルにはIGBとEGPがある
1. 同じ縄張りの中で情報交換: IGP
2. 縄張りを跨いで情報交換: EGP … EGPの中にBGPルータがある(Border Gateway Protocol)

### AS(Autonomous System)
共通のポリシーや同じ管理下で運用されているルータやネットワークの集合体を意味する
各AS内では独立したIGPやポリシーが運用されている
ASを識別するための番号がAS番号 JPNICが割り当ての代行を行なっている
16ビット(1~65535)の数字を用いる

### BGPルータ
BGPはASの経路交換のパス 
通常は短いものを最短ルートとして使用する

### BGPの仕組み
2台のルータ間でTCP(ポート番号179)による接続を行い経路情報を交換
隣接ルータをネイバー、ピアと呼ぶ
1対1のセッションを確立し、その上でBGPの基本情報、お互いのルータが知っている経路情報を交換
ルーティングテーブルが更新された場合は、経路情報を交換
KEEPALIVEメッセージをアナウンスしあうことでルータの生存確認を行う

### パス属性
ORIGIN
AS_PATH(ASパス)
NEXT_HOP
MULTI_EXIT_DISC
LOCAL_PREF
ATOMIC_AGGREGATE
AGGREGATOR
COMMUNITY

VPN接続入門

### VPN概要
– 内部ネットワークあどれすは通常インターネットからDNSの名前解決ができない
– 接続先がプライベートアドレスを使っている場合もインターネットを介して接続できない
– 誰でも内部ネットワークに通信できるとセキュリティ上問題
— VPNだと認証後に接続先ネットワークと直接接続されているように使うことができる
— VPNはファイヤーウォールと一緒、個別に構築する場合はDMZに配置する
– 専用線は第三者がアクセスできないので通信の盗聴を予防できる
– VPNであれば既存の公衆回線を利用できる
– 離れた場所にあるネットワークに対し、同じLANにいるようなアクセスが実現できる

### トンネリング
元々のパケットにインターネットで使われるIPアドレスを追加することでインターネットでルーティングできるようになる。受信側では、追加されたIPアドレスを外して送信先のIPアドレスと通信
パケットにアドレス等を追加することをカプセル化と言う
データの暗号化には共通鍵暗号方式がよく使われる(DES, 3DES, RC4, AES, RSAなど)

### VPN接続の認証や共通鍵作成
ID, Passwordハッシュが使われる。MD5, SHA1, SHA2等が使われる
データ等をハッシュを利用してMAC値を生成(HMACと言う)

閉域網はインターネットとは分離隔離された形で閉域網を構築している

なるほどー、閉域網かあ

AWS Direct connectとは?

AWSが提供する専用接続サービスで、ユーザのネットワーク環境からAWSまでインターネットを経由せずプライベートな接続を確立することができる。
ユーザがAWSのパートナー設備に用意された相互接続ポイント(AWS Direct Connectロケーション)と間接的に接続する
「安定性」「セキュリティ」「コスト」

HULFTとは?

HULFTとは、ファイルという形になった情報を転送し、その情報を転送する際に発生する前後の処理を統合的に扱うためのソフトウェア

→ TPC SYN (TCP接続を確立する際にクライアントから送られるパケット)
← TCP SYN+ACK(接続を受け入れる際にはACKフラグが一緒になったSYN/ACKパケットで応答する。TCPの3way handshake)
→ TPC ACK
→ 配信情報を送信
← 応答
→ ファイルデータ送信(小分けにしてデータを送信)
← 受信済みデータ量返信
→ TPC FIN
← TCP FIN+ACK
→ TPC ACK
TCP接続確立後、配信側で必要に応じてコード変換、圧縮、暗号化などの前処理が行われる
配信側は前処理が終わったファイルをTCP接続で集信側に送信
集信側は、暗号化されたデータの複合、圧縮されたデータの解凍、コード変換などの処理を必要に応じて行う
ファイル転送終了後、TCP接続が切断され、HULFT配信が終了する

ファイル転送前処理: ファイル入力、コード返還(各種汎用機で使われている文字コードに対応)、圧縮(ランレングス法に似た圧縮とDeflate圧縮の2種類)、暗号化(HULFT暗号、C4S暗号、AES)、転送の順番で配信が行われる
※メモリ上で行われるため、前後処理のための中間ファイル等は生成されない

hulftのistall
# pwd
/usr/local/HULFT/insttmp
# ./installer
インストールの開始
シリアル番号とプロダクトキーの入力
利用期間ライセンスキー
インストール方法の選択
トレースログ出力方式の選択
インストール条件の確認
インストールの終了
環境変数の設定
実行モジュール格納ディレクトリの設定
L HULFTの実行モジュールがある、実行モジュール格納ディレクトリをシェルの環境変数に設定します。環境変数名は、「HULEXEP」です。この環境変数を設定することにより、HULFTが実行するプログラムのPATHを自動的に検索できます。また、このディレクトリをPATHに設定することにより、HULFTの実行モジュールがあるディレクトリ以外からもHULFTを起動できます。
# HULEXEP=/usr/local/HULFT/bin
# export HULEXEP
# PATH=/usr/local/HULFT/bin:$PATH
# export PATH

環境設定ファイル格納ディレクトリの設定
L HULFTが使用する環境設定ファイルのある、環境設定ファイル格納ディレクトリをシェルの環境変数に設定します

環境変数の確認
# echo $HULPATH
/usr/local/HULFT/etc
# echo $HULEXEP
/usr/local/HULFT/bin

# echo $PATH (sh系の場合)
/usr/local/HULFT/bin:/usr/local/bin:/bin:/usr/bin:/usr/sbin ..

配信処理を行うためには、配信デーモンを起動する必要があります。
# hulsndd
集信デーモンの起動
L 集信処理を行うためには、集信デーモンを起動する必要があります。
# hulrcvd
要求受付デーモンの起動
L 相手からの要求を受け付けるためには、要求受付デーモンを起動する必要があります。
HULFTの起動確認
L 各デーモン起動コマンド入力後、以下のコマンドなどによりプロセスの状態を確認します。
# hulsndd
# hulrcvd
# hulobsd
# ps -ef | grep hul
root 21207 1 0 22:49:30 ? 0:00 hulrcvd
root 21205 1 0 22:49:26 ? 0:00 hulsndd
root 21209 1 0 22:49:32 ? 0:00 hulobsd

管理情報の登録
L HULFTでは配信側と集信側に同一のファイルIDを指定します。ファイルIDは50文字以内の英数字で指定します。ファイルIDに関連付けられた管理情報を元にファイル転送を行います。ファイルIDは1ファイルごとに配信側(配信管理情報)および集信側(集信管理情報)に登録する必要があります。
配信要求(配信側起動のファイル転送)
L 配信要求は転送するファイルを持つ側からの起動でファイル転送を行う場合に使用します。
履歴の確認(配信側起動のファイル転送)
送信要求(集信側起動のファイル転送)
要求状態の確認(集信側起動のファイル転送)
L「送信要求(集信側起動のファイル転送)」で行ったファイル転送の結果を確認します。配信側ホストには配信履歴が表示され、集信側ホストには集信履歴が表示されます。

FTP接続の場合はファイルの整合性チェックまで行わない

HULFTが何やってるかは大体わかった。

[CentOS8]ビルトインサーバにアクセス出来ない時

vagrant & CentOS8でビルトインサーバを起動します。

$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) …

なんでやねん。

Vagrantifle

  config.vm.network "private_network", ip: "192.168.34.10"

プロキシの設定等はしていない。どうやらfirewallが怪しい、ということで
$ sudo firewall-cmd –list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0 eth1
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:

あ、やっぱり。http, 8000がアクセス出来ません。

$ sudo firewall-cmd –add-service=http –zone=public –permanent
$ sudo firewall-cmd –add-port=8000/tcp –zone=public –permanent
$ firewall-cmd –reload

出来ました。アホみたいなミスでした。
CentOS8だとデフォルトでfirewallがonになってるんですね。そういえば、sakuraのvpsなどもポートフィルタリングがデフォルトでonになってます。クワバラクワバラ

[PHPQuery]プロキシ経由でスクレイピングする

無料プロキシリストからproxyを取得してスクレピングする。
http://www.freeproxylists.net/ja/?c=JP&pt=&pr=&a%5B%5D=0&a%5B%5D=1&a%5B%5D=2&u=0

$url = "https://www.google.com/";
// $proxy_url = "153.126.160.91:80";
$proxy_url = "133.167.65.45:8080";

$context = stream_context_create(array(
      'http' => array(
         'method' => 'GET',
         'header'=>'User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/00100101 Firefox/13.0.1\r\nReferer: https://www.google.com/webhp?gl=us&hl=en&gws_rd=cr&pws=0',
         'ignore_errors'=>true,
         'proxy' => $proxy_url,
      )
   ));
$html = file_get_contents($url, FALSE, $context);
$doc = phpQuery::newDocument(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));


echo $doc["title"]->text();

$ php test.php
Google

設定するプロキシによっては、接続を拒否される場合があるので、テストして確認が必要
Cannot connect to HTTPS server through proxy in /home/vagrant/test/test.php on line 17

なるほどー凄い理解した^^