コアダンプの数だけ強くなれるよ

見習いエンジニアの備忘log

socatで装置内通信のデータをのぞき見る

socatを使ってUNIXドメイン通信を中継し通信内容をのぞいてみる。


構成のイメージは下記。

--------------------------------------------------
 ________      ________      ________
|        |    |        |    |        | 
| Client |--->| socat  |--->| Sever  | 
|________|    |________|    |________| 
                  |
                   ---> 標準出力に通信内容を表示。

--------------------------------------------------

プログラム

local.h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

#define MSGID_HELLO_REQUEST (0x0101)

#define MAX_USER (1)

typedef enum {
    MSG_KIND_REQUEST = 0,
    MSG_KIND_MAX
} msgkind_t;

typedef struct {
    uint32_t    id;
    uint16_t    kind;
    uint16_t    seqno;
} msg_header_t;


client.c

#include "local.h"

#define UNIX_SOCKET_FILEPATH "./usocket.client"

int main(void)
{
    int sfd = -1;
    struct sockaddr_un addr = {
        .sun_family = AF_UNIX,
        .sun_path   = UNIX_SOCKET_FILEPATH,
    };

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    int rc = connect(sfd, (struct sockaddr* )&addr, sizeof(addr));
    if (rc < 0) {
        perror("connect");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    uint16_t    seqno = 0;
    while(1) {
        msg_header_t    req = {
            .id     = MSGID_HELLO_REQUEST,
            .kind   = MSG_KIND_REQUEST,
            .seqno  = seqno,
        };

        ssize_t ss = send(sfd, (void*)&req, sizeof(req), 0);
        if (ss < 0) {
            perror("send");
            close(sfd);
            break;
        }

        if (ss == 0 || ss < 0) {
            printf("connection closed.\n");
            unlink(UNIX_SOCKET_FILEPATH);
            break;
        }

        printf("sent request, wait 3 sec (-.-)zzZ\n");
        sleep(3);
        seqno++;
    }

    return 0;
}


server.c

#include "local.h"

#define UNIX_SOCKET_FILEPATH "./usocket.server"

int main(void)
{
    int sfd = -1;
    struct sockaddr_un addr = {
        .sun_family = AF_UNIX,
        .sun_path   = UNIX_SOCKET_FILEPATH,
    };

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    unlink(UNIX_SOCKET_FILEPATH);

    int rc = bind(sfd, (struct sockaddr* )&addr, sizeof(addr));
    if (rc < 0) {
        perror("bind");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    rc = listen(sfd, MAX_USER);
    if (rc < 0) {
        perror("listen");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    socklen_t   addrlen;
    struct sockaddr_un caddr;

    int acsfd = accept(sfd, (struct sockaddr *)&caddr, &addrlen);
    if (acsfd < 0) {
        perror("accept");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    while(1) {
        uint16_t    seqno = 0;
        msg_header_t    req;

        ssize_t rs = recv(acsfd, (void*)&req, sizeof(req), 0);
        if (rs < 0) {
            perror("recv");
            close(sfd);
            break;
        }

        printf("recv size=%d, id=%#x, kind=%#x, seqno=%d\n",
               rs, req.id, req.kind, req.seqno);

        if (rs == 0) {
            printf("connection closed.\n");
            unlink(UNIX_SOCKET_FILEPATH);
            break;
        }
    }

    return 0;
}

実行結果

3つの端末を開き各端末で下記コマンドを実行。

$ sudo yum -y install socat
$ socat -x -v UNIX-LISTEN:./usocket.client, UNIX-CONNECT:./usocket.server
$ gcc -o server server.c
$ ./server
$ gcc -o client client.c
$ ./client


f:id:segmentation-fault:20170301224030p:plain



UNIXドメイン通信に対しtcpdumpライクなキャプチャができている。
単体テストとかで使えそう。