Skip to content

Instantly share code, notes, and snippets.

@Tatakinov
Last active October 16, 2024 12:04
Show Gist options
  • Save Tatakinov/4961d66f9d0ff2031a425246c4a8abe1 to your computer and use it in GitHub Desktop.
Save Tatakinov/4961d66f9d0ff2031a425246c4a8abe1 to your computer and use it in GitHub Desktop.
POSIX環境に於けるFMO/DirectSSTPの実装案

起動判定

POSIX

ベースウェアが起動しているかは shm_open('/ninix', O_RDWR, 0)が成功するかで判定する。

Windows

OpenMutex(SYNCHRONIZE, FALSE, "/ninix_mutex")が成功するかで判定する。

FMO

ninixではFMOはベースウェアの情報ではなくベースウェアの情報を取得できる UNIXソケットがあるディレクトリを保持している。 末尾は/(Windows環境下では\)で終わっている。 例:/home/user/.ninix/sock/C:\ninix-kagari\.ninix\sock\


2024/10/15 追記

ninix-kagariではパスの区切り文字はOSに依らず/で固定で末尾もそれで終わる。 ただしベースウェアはWindowsでは追記前の\を使用しても良い。 とにかく、後述のUNIXソケットを開く際にユーザーがパスの区切り文字を意識しなくて済むようにすること。 例:/home/user/.ninix/sock/C:/ninix-kagari/.ninix/sock/


UTF-8な文字列なので非ASCII文字を含む場合は注意!

POSIX環境とWindowsで内部構造が違うので以下に記載する。 ただし、エラーハンドリングはほとんどしていないので各自で実装すること。

POSIX環境

CでFMOの取得を行う場合を以下に記す。 なお、32/64bit間ではやり取りできないので注意すること。

#include <fcntl.h>
#include <limits.h>
#include <semaphore.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

struct shm_t {
    uint32_t size;
    sem_t sem;
    char buf[PATH_MAX];
};

char *getFMO() {
    struct shm_t *shm;
    int fd = shm_open("/ninix", O_RDWR, 0);
    shm = mmap(NULL, sizeof(struct shm_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    sem_wait(&shm->sem);
    char *ptr = strndup(shm->buf, shm->size);
    sem_post(&shm->sem);
    return ptr;
}

int main() {
    char *fmo = getFMO();
    puts(fmo);
    free(fmo);
    return 0;
}

Windows

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

struct shm_t {
    uint32_t size;
    char buf[MAX_PATH];
};

char *strndup_(char *s, size_t n) {
	char *p = malloc(sizeof(char) * (n + 1));
	if (p == NULL) {
		return NULL;
	}
	memcpy(p, s, n);
	p[n] = '\0';
	return p;
}

char *getFMO() {
    char *ptr = NULL;
    HANDLE mutex = OpenMutex(SYNCHRONIZE, FALSE, "/ninix_mutex");
    if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) {
        goto error_wait_for_single_object;
    }
    HANDLE fmo = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "/ninix");
    struct shm_t *shm = (struct shm_t *) MapViewOfFile(fmo, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    ptr = strndup_(shm->buf, shm->size);
    UnmapViewOfFile(shm);
    CloseHandle(fmo);
    ReleaseMutex(mutex);
error_wait_for_single_object:
    CloseHandle(mutex);
    return ptr;
}

int main() {
    char *fmo = getFMO();
    puts(fmo);
    free(fmo);
    return 0;
}

Unixソケット

ninix-kagari本体のUNIXソケット

ninix-kagariはPOSIX環境のデフォルトでは 【FMOで取得できるパス】ninix_kagariなるUNIXソケットをlistenしている。

(例:/home/user/.ninix/sock/ninix_kagari)


2024/10/16 追記

【FMOで取得できるパス】ninixをlistenするようにした。 (例:/home/user/.ninix/sock/ninix)


クライアントは受信専用。送信しても無視される。 ソケットにconnectすると、以下のようなデータが流れた後、closeされる。

uint32_t
id1.key1\x01value1\x0d\x0a
id1.key2\x01value2\x0d\x0a
...
idn.keym\x01valuem\x0d\x0a
null-terminated

見やすいように改行を行っているが、実際は改行は\x0d\x0aの部分だけ。

SSPのFMO相当のデータが含まれるが、hwnd系は存在しない。

uint32_tは符号なし32bit整数型で、null-terminatedを含まないサイズ。

idはUUIDを使う。

各ゴーストのUNIXソケット

各ゴーストは【FMOで取得できるパス】#{id}なるUNIXソケットをlistenしている。 #{id}は前述のUUID。

(例:/home/user/.ninix/sock/01234567-89ab-cdef-0123-456789abcdef)

このソケットに対しSSTPを送るとSSPのDirectSSTP相当の動作をする。

FAQ

Q. FMOにninix-kagari本体のUNIXソケットの情報も含めれば良くね?

A. そうすると共有メモリが固定長じゃなくなって面倒くさい。

Q. WindowsでUNIXソケット対応してるのWindows 10以降なんだけど?

A. クロスプラットフォームでプロセス間通信出来るのこれくらいしかない気がする。

Q. UNIXソケットの方の実装例は無いの?

A. 固有の処理はないので、一般的なUNIXソケットの通信方法を使えば大丈夫。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment