AIによるプログラミングの実験として、無料版Claudeを使ってUDPファイル転送プログラムを作ってみました。エラーやバグの修正もClaudeにまかせ、一切手動でのソース修正などはしないことにしました。AI(Claude)のみで、どこまで出来るかやってみたかったので。
最初に与えたプロンプトは以下のとおり。
以下の要件を満たすUDP通信を用いたファイル転送プログラムのソースコードを作成してください。
サーバのソースコードとクライアントのソースコードの2つを作成してください。対象とする端末のOSはSolaris11 for X86で、使用する言語はC言語です。
サーバとクライアントのソースコードは別々とし、適宜ヘッダーファイル等も作成してください。またそれらを元に実行ファイルをmakeするMakefileも作成してください。
以下に要件を記述します。
・サーバ・クライアント形式で、サーバはデーモンプロセスとして動作する。
・ユーザ認証はopensshの認証を流用する。
・コントロール用のセッションとデータ転送のセッションは分離し、コントロール用のセッションはTCPを、データ転送のセッションはUDPを使う。各セッションはOpenSSLを使って通信を暗号化する。方法はopensshの認証・暗号化を流用する。
・送信側は送信するファイルのサイズを予め取得して受信側に知らせる。
・受信側は送信側から知らされたファイルサイズと同じサイズの空のファイルを作成しておく。
・送信側はファイルサイズを1000で割って、いくつのパケットを送信するかを受信側に知らせる。例えばファイルサイズが5423Byteなら必要なパケット数は6個となる。
・送信側は元ファイルを1000Byte毎に分割し、先頭から順番にシリアル番号を付けてUDPパケットで送信する。シリアル番号は0から始める。
・受信側は受け取ったUDPパケットに格納されたシリアル番号を読み出し、予め作成しておいた空のファイルの先頭からシリアル番号に1000を掛けた番地に同一のUDPパケットに格納されたデータを入れる。例えばシリアル番号が4なら4000番地からデータを入れていく。これによりパケットが順番通りに届かなくてもシリアル番号を元に格納する番地が判り、正しい位置にデータを格納できる。
・受信側は自身のパケットバッファの使用率が80%に達したら送信側に送信を一時停止するように知らせる。
・送信側は受信側から送信の一時停止の知らせを受けたら送信を一時停する。
・受信側は自身のパケットバッファの使用率が30%以下になったら、送信側に送信再開を知らせる。
・送信側は受信側から送信再開の知らせを受けたら、送信を再開する。
・送信側は全パケットを送信し追えたら、受信側に送信完了を知らせる。
・受信側はシリアル番号を控えておき、受信してないシリアル番号のパケットがあった場合、送信側から送信完了の知らせを受け取った後に該当するシリアル番号を送信側に知らせて再送を要求する。
・送信側は受信側から再送要求を受けたら、知らされたシリアル番号のパケットを再度作って受信側に再送する。再送要求されたパケットを全て送信したら受信側に送信完了を知らせる。
・受信側は全パケットを受信するまで送信側に再送を要求する。全パケットを受信したら送信側に受信完了を知らせる。
・送信側は受信完了の知らせを受け取ったら、データ転送のセッションを終了することを受信側に知らせてデータ転送のセッションを閉じる。
・受信側は送信側からデータ転送のセッションの終了の知らせを受け取ったら、データ転送のセッションを閉じる。
・getaddrinfo()関数を使ってIPv4、IPv6の両方のプロトコルに対応する。
・コントロール用セッションでは以下の事を行う。
・通信全体の開始。
・ユーザ認証。
・各種コマンドの送信及びデータ転送以外の結果の受信。
・送信するファイルのサイズを送信先に伝える。
・通信やセッションの終了。
・データ転送のセッションでは以下の事を行う。
・1000Byteに分割したデータ及びシリアル番号を格納したUDPパケットの送受信。
・実際の使い方は以下の通り。
(1)クライアントがサーバに接続し、ユーザ認証を行う。認証直後はユーザのホームディレクトリに移動する。
(2)“ls”コマンドでファイルを表示する。“cd”コマンドでディレクトリを移動する。
(3)ファイルをダウンロードする場合は、目的のファイルを探して“get"コマンドでダウンロードする。
getの後に空白を入れてファイル名を記述する。「get ファイル名」。
(4)ファイルをアップロードする場合は、目的のファイルを指定して“put”コマンドでアップロードする。
putの後に空白を入れてファイル名を記述する。「put ファイル名」。
(5)“exit“コマンドで通信を終了する。
このプロンプトに対するClaudeからの回答は、以下のとおり。
要件を確認しました。Solaris 11 x86向けのUDP/TCPハイブリッドファイル転送システムを実装します。
まずスキルを確認してから実装を始めます。
2個のコマンドを実行しました
では全ファイルを順番に作成します。
8個のファイルを作成しました, 4個のコマンドを実行しました
警告とエラーを修正します。
9個のコマンドを実行しました, 5個のファイルを編集しました, 2個のファイルを表示しました
全ファイルがエラーなしです。出力ディレクトリにコピーします。
コマンドを実行しました, ファイルを読み取りました
全8ファイルを生成しました。構成と要件の対応を説明します。
ファイル構成
ファイル 役割
src/server.c サーバ本体 (約750行)
src/client.c クライアント本体 (約600行)
src/common.c 共通ユーティリティ (SSL/TCP/ログ)
include/common.h 共通定義・プロトコル定数・構造体
include/server.h サーバ内部の定義
include/client.h クライアント内部の定義
Makefile Solaris 11 向けビルドルール
README.md ビルド・使用手順
主な実装ポイント
プロトコル分離: コントロールセッションはTCP+TLS (CTRL_PORT=22220)、データ転送はUDP (DATA_PORT=22221) で完全分離しています。
認証: Solarisの getspnam() + crypt() でshadow passwordと照合します。パスワードはTLS暗号化済みチャネル上で送受信されます。
UDPパケット設計: udp_packet_t 構造体にシリアル番号(4byte)+データ長(2byte)+データ(1000byte)を格納。受信側は pwrite(fd, data, len, serial * 1000) でファイルの正しい位置に直接書き込むため、順番通りでなくても正確に配置されます。
フロー制御: 受信側は FIONREAD + SO_RCVBUF でバッファ使用率を計算し、80%到達でコントロールチャネルに PAUSE、30%以下で RESUME を送信します。
欠落再送: 受信済みフラグ配列 received で管理し、DONE 受信後に未受信シリアル番号を RESEND n s0 s1 s2… 形式で送信側に要求します。全受信完了後に COMPLETE を送信して正常終了します。
IPv4/IPv6対応: getaddrinfo() に AF_UNSPEC を指定し、TCPリスナー・UDPソケットともに両プロトコルに対応しています。
ビルド手順 (Solaris 11)
sh
pkg install library/security/openssl
make cert # 開発用自己署名証明書を /etc/udpft/ に生成
make # udpftd (サーバ) と udpft (クライアント) をビルド
./udpftd -f # フォアグラウンド起動でテスト
./udpft <サーバIP>
Server
C
Client
C
Common
C
Common
H
Server
H
Client
H
Makefile
Readme
ドキュメント · MD
Claudeが作成したファイルを元にmakeを実行したところエラーが出たので、そのエラーを修正させるプロンプトを作成した。
前回作っていただいたソースコード、ヘッダーファイル、Makefileですが、Solaris11.4 for X86じょうでmakeコマンドを実行すると以下のエラーが出ます。
エラーを修正するとともに、コンパイラをgccからOracleのdeveloperstudio12.6のコンパイラに対応させたソースコード、ヘッダーファイル、Makefileの作成をお願いします。
エラー内容は以下の通りです。
% make
gcc -Wall -Wextra -O2 -D_REENTRANT -I./include -I/usr/local/ssl/include -I/usr/include/openssl -o udpftd src/server.c src/common.c -L/usr/local/ssl/lib -lssl -lcrypto -lsocket -lnsl -lresolv -lpthread
src/server.c: 関数 ‘udp_recv_file’ 内:
src/server.c:498:35: エラー: ‘FIONREAD’ undeclared (first use in this function); did you mean ‘UIO_READ’?
498 | if (ioctl(udp_fd, FIONREAD, &pending) == 0 && so_rcvbuf > 0) {
| ^~~~~~~~
| UIO_READ
src/server.c:498:35: 備考: 未宣言の識別子は出現した各関数内で一回のみ報告されます
*** Error code 1
make: Fatal error: Command failed for target `udpftd'
このプロンプトに対するClaudeからの回答は、以下のとおり。
エラーを確認して修正します。SolarisでのFIONREADは<sys/filio.h>に定義されています。あわせてOracle Developer Studio 12.6対応も行います。
7個のファイルを表示しました
全体の問題点が把握できました。修正します。
問題点の整理:
FIONREAD → Solarisでは <sys/filio.h> が必要
Developer Studio 12.6 はデフォルトで C99 モード。ブロック途中の変数宣言、int maxfd・int sel・int pending などが関数の途中で宣言されており、-xc99 オプションが必要、またはすべて関数先頭へ移動
MSG_DONTWAIT → Solarisでは O_NONBLOCK + fcntl で代替
Makefileのコンパイラをccに変更し、Solaris固有フラグを設定
2個のコマンドを実行しました
次にserver.cを完全に書き直します。Developer Studio 12.6は厳密なC99モードのため、すべての変数宣言をブロック先頭に移動します。
2個のコマンドを実行しました
次にclient.hのシグネチャを修正します(serverパラメータを削除)。
Update client.h to remove unused server param from udp_recv_file_client
次にMakefileをDeveloper Studio 12.6対応に書き直します。
Rewrite Makefile for Oracle Developer Studio 12.6
最後にgccで構文チェックを行い、問題がないことを確認します。
3個のコマンドを実行しました
エラーゼロを確認できました。出力ディレクトリにコピーします。
コマンドを実行しました, ファイルを読み取りました
修正内容を説明します。
修正内容まとめ
- FIONREAD エラーの根本原因と修正 (common.h)
Solarisでは FIONREAD は <sys/filio.h> で定義されており、<sys/ioctl.h> だけでは展開されません。
c
/* 修正前 (common.h) */
#include <sys/ioctl.h>
/* 修正後 (common.h) — 両方必要 /
#include <sys/filio.h> / Solaris: FIONREAD はここに定義されている */
#include <sys/ioctl.h>
- MSG_DONTWAIT を廃止し fcntl(O_NONBLOCK) に変更 (server.c / client.c)
MSG_DONTWAIT はLinux拡張でSolarisのPOSIX準拠環境では使えません。
c
/* 修正前 */
n = recvfrom(fd, &pkt, sizeof(pkt), MSG_DONTWAIT, ...);
/* 修正後: O_NONBLOCK で一時的にノンブロッキングに切替 /
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
n = recvfrom(fd, &pkt, sizeof(pkt), 0, ...); / フラグは 0 /
fcntl(fd, F_SETFL, flags); / 元に戻す */
- 変数宣言をすべてブロック先頭に移動 (server.c / client.c)
Developer Studio 12.6 の -xc99 モードは標準C99ですが、コードブロック途中への変数宣言(int maxfd = …、int sel = …など)がある箇所でエラーになる場合がありました。すべての変数宣言をブロック先頭に移動しました。
4. Makefile をDeveloper Studio 12.6対応に変更
変更点 前 後
コンパイラ gcc cc
C99有効化 -std=c99 -xc99
最適化 -O2 -xO2
マルチスレッド -D_REENTRANT -mt
警告強化 -Wall -Wextra (DSの既定警告)
ビルド前の確認事項
sh
Developer Studio 12.6 を PATH に追加
export PATH=/opt/solarisstudio12.6/bin:$PATH
which cc # /opt/solarisstudio12.6/bin/cc と出ればOK
OpenSSL インストール確認
pkg install library/security/openssl
ビルド
make cert # 開発用証明書生成
make
Common
H
Server
C
Client
C
Common
C
Server
H
Client
H
Makefile
Claudeが作成したソースコード等をOpenIndiana上でgmake使用とするとエラーがでるので、その修正させるプロンプトを作成した。
前回作っていただいたソースコード、ヘッダーファイル、Makefileですが、OpenIndiana(illumos-bb9fdf4347)上でmakeできる用に修正をお願いします。
OpenIndianaではCコンパイラとしてgcc version 14.3.0 (OpenIndiana 14.3.0-oi-1)を使っています。
このプロンプトに対するClaudeからの回答は、以下のとおり。
現在のファイルをすべて確認してから修正します。
8個のファイルを表示しました
全ファイルを確認できました。OpenIndianaでの問題点を整理します。
問題点:
<shadow.h> / <crypt.h> → OpenIndianaのillumosでは getspnam() は <shadow.h> にあるが、crypt() は <crypt.h> ではなく <unistd.h> で宣言され -lcrypt が必要
<sys/filio.h> → OpenIndianaでも存在するが、-D__EXTENSIONS__ が必要な場合がある
peer_addr が未設定 → accept() の結果を sess.peer_addr にコピーしていない致命的バグ
SSL_library_init() / SSL_load_error_strings() / OpenSSL_add_all_algorithms() → OpenSSL 3.x では非推奨で警告が出る(OI の OpenSSL は 3.x)
Makefile → cc → gcc、-xc99/-xO2/-mt → gcc フラグに変更、OpenSSL/ヘッダパスをOI標準に合わせる
-lresolv → OI では不要、-lsocket/-lnsl は引き続き必要
6個のコマンドを実行しました
最後に構文チェックを実施します。
2個のコマンドを実行しました, ファイルを読み取りました
修正内容をまとめます。
OpenIndiana 対応 修正内容
- Makefile — コンパイラとフラグを全面改訂
項目 変更前 (DS12.6) 変更後 (OI gcc 14)
コンパイラ cc gcc
C標準 -xc99 -std=gnu11
最適化 -xO2 -O2
マルチスレッド -mt -D_REENTRANT
crypt ライブラリ (なし) -lcrypt を追加
resolv ライブラリ -lresolv 削除 (OI では不要)
-std=gnu11 を使う理由は、-std=c99 では illumos の一部拡張ヘッダが意図通りに動作しない場合があるためです。
2. common.h — illumos 拡張マクロを追加
c
#define EXTENSIONS /* sys/filio.h の FIONREAD 等を公開 /
#define _XOPEN_SOURCE 600 / getspnam, fseeko, pwrite, usleep 等 /
#define _POSIX_C_SOURCE 200112L
#include <strings.h> / strcasecmp は illumos では strings.h */
-
common.c — OpenSSL 3.x API に移行
c /* 変更前: OpenSSL 1.x (deprecated, 3.x で警告) */ SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); /* 変更後: OpenSSL 3.x (OI 標準の OpenSSL は 3.x) */ OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); -
server.c — peer_addr 未設定バグを修正 (重要)
以前は accept() で取得したクライアントアドレスが sess.peer_addr にコピーされておらず、udp_send_file() でゴミアドレスに UDP を送信していました。handle_client() のシグネチャを変更してアドレスを正しく引き渡すよう修正しました。
c
/* server.h: シグネチャ変更 */
void handle_client(int ctrl_fd, SSL_CTX *ctx,
struct sockaddr_storage *peer_addr, socklen_t peer_len);
/* main(): accept 結果を渡す */
handle_client(client_fd, ctx, &caddr, caddrlen);
/* handle_client(): セッションにコピー */
memcpy(&sess.peer_addr, peer_addr, sizeof(sess.peer_addr));
-
ビルド前に必要なパッケージ
sh pkg install developer/gcc pkg install library/security/openssl pkg install system/header pkg install developer/build/make
証明書生成とビルド
sudo make cert
make
Makefile
Common
H
Common
C
Server
C
Client
C
Server
H
Client
Claudeが作成したOpenIndiana用のソースコード等を使ってgmakeしたところエラーが出たので、その修正させるプロンプトを作成した。
前回修正していただいたソースコード、ヘッダーファイルMakefleを使ってOpenIndiana 14.3.0-oi-1でmakeしたところ、以下のエラーが出てmake出来ませんでした。
エラー内容を修正したソースコード、ヘッダーファイル、Makefileを作成してください。
エラーは以下のとおりです。
gmake
gcc -std=gnu11 -O2 -D_REENTRANT -D__EXTENSIONS__ -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L -Wall -Wextra -Wno-unused-parameter -I./include -I/usr/include/openssl -o udpftd src/server.c src/common.c -lssl -lcrypto -lsocket -lnsl -lcrypt -lpthread
src/server.c: In function 'cmd_cd':
src/server.c:175:50: warning: 'snprintf' output may be truncated before the last format character [-Wformat-truncation=]
175 | snprintf(newpath, sizeof(newpath), "%s/%s", sess->cwd, dir);
| ^
src/server.c:175:9: note: 'snprintf' output 2 or more bytes (assuming 4097) into a destination of size 4096
175 | snprintf(newpath, sizeof(newpath), "%s/%s", sess->cwd, dir);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/server.c: In function 'cmd_get':
src/server.c:235:48: warning: 'snprintf' output may be truncated before the last format character [-Wformat-truncation=]
235 | snprintf(filepath, sizeof(filepath), "%s/%s", sess->cwd, filename);
| ^
src/server.c:235:5: note: 'snprintf' output 2 or more bytes (assuming 4097) into a destination of size 4096
235 | snprintf(filepath, sizeof(filepath), "%s/%s", sess->cwd, filename);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/server.c: In function 'cmd_put':
src/server.c:275:46: warning: '%s' directive output may be truncated writing up to 255 bytes into a region of size between 0 and 4095 [-Wformat-truncation=]
275 | snprintf(filepath, sizeof(filepath), "%s/%s", sess->cwd, fname_only);
| ^~ ~~~~~~~~~~
src/server.c:275:5: note: 'snprintf' output between 2 and 4352 bytes into a destination of size 4096
275 | snprintf(filepath, sizeof(filepath), "%s/%s", sess->cwd, fname_only);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/common.c: In function 'create_ssl_ctx_server':
src/common.c:48:5: error: implicit declaration of function 'OPENSSL_init_ssl'; did you mean 'OPENSSL_init'? [-Wimplicit-function-declaration]
48 | OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
| ^~~~~~~~~~~~~~~~
| OPENSSL_init
src/common.c:48:22: error: 'OPENSSL_INIT_LOAD_SSL_STRINGS' undeclared (first use in this function)
48 | OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/common.c:48:22: note: each undeclared identifier is reported only once for each function it appears in
src/common.c:49:22: error: 'OPENSSL_INIT_LOAD_CRYPTO_STRINGS' undeclared (first use in this function)
49 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/common.c:51:23: error: implicit declaration of function 'TLS_server_method'; did you mean 'DTLS_server_method'? [-Wimplicit-function-declaration]
51 | ctx = SSL_CTX_new(TLS_server_method());
| ^~~~~~~~~~~~~~~~~
| DTLS_server_method
src/common.c:51:23: error: passing argument 1 of 'SSL_CTX_new' makes pointer from integer without a cast [-Wint-conversion]
51 | ctx = SSL_CTX_new(TLS_server_method());
| ^~~~~~~~~~~~~~~~~~~
| |
| int
In file included from ./include/common.h:53,
from src/common.c:14:
/usr/include/openssl/ssl.h:2131:40: note: expected 'const SSL_METHOD *' {aka 'const struct ssl_method_st *'} but argument is of type 'int'
2131 | SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
| ^~
src/common.c:58:5: error: implicit declaration of function 'SSL_CTX_set_min_proto_version'; did you mean 'SSL_CTX_set_ssl_version'? [-Wimplicit-function-declaration]
58 | SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
| ^~~~~~~~~~~~~
| SSL_CTX_set_ssl_version
src/common.c: In function 'create_ssl_ctx_client':
src/common.c:83:22: error: 'OPENSSL_INIT_LOAD_SSL_STRINGS' undeclared (first use in this function)
83 | OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/common.c:84:22: error: 'OPENSSL_INIT_LOAD_CRYPTO_STRINGS' undeclared (first use in this function)
84 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/common.c:86:23: error: implicit declaration of function 'TLS_client_method'; did you mean 'DTLS_client_method'? [-Wimplicit-function-declaration]
86 | ctx = SSL_CTX_new(TLS_client_method());
| ^~~~~~~~~~~~~~~~~
| DTLS_client_method
src/common.c:86:23: error: passing argument 1 of 'SSL_CTX_new' makes pointer from integer without a cast [-Wint-conversion]
86 | ctx = SSL_CTX_new(TLS_client_method());
| ^~~~~~~~~~~~~~~~~~~
| |
| int
/usr/include/openssl/ssl.h:2131:40: note: expected 'const SSL_METHOD *' {aka 'const struct ssl_method_st *'} but argument is of type 'int'
2131 | SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
| ~~~~~~~~~~~~~~~~^~
gmake: *** [Makefile:97: udpftd] エラー 1
[5回目]-----------------------------------------------------------------------------------------------------------
作成していただいたソースコード、ヘッダーファイル、Makefileを使ってSolaris11.4 for x86およびOpenIndiana 14.3.0-oi-1でmakeできました。
しかし、Solaris11.4 for x86でudpftdを“-d”スイッチ付きで起動し、OpenIndiana 14.3.0-oi-1側からudpftで接続したところ、ユーザ認証は通り“ls”コマンドでファイル一覧を取得することはできましたたが、“get”コマンドでfileをダウンロードしようとしたところ下記のエラーがudpftdから出力されてダウンロードできませんでした。
エラーを修正したSolaris11.4 for x86用のソースコード、ヘッダーファイル、Makefileを作成してください。
出力されたエラーは以下のものになります。
[ERR] udp_send_file: socket: Address family not supported by protocol family
このプロンプトに対するClaudeからの回答は、以下のとおり。
エラーを分析します。OpenIndianaにインストールされているOpenSSLが1.0.2系であることが原因です(TLS_server_methodやOPENSSL_init_sslはOpenSSL 1.1.0以降の関数です)。また-Wformat-truncationの警告もエラー扱いになっています。
まず現在のファイルを確認してから修正します。
2個のコマンドを実行しました
前回のOI版を作業ディレクトリに復元して作業します。
Restore OI source to working directory
エラーの根本原因を正確に把握します。
3個のコマンドを実行しました
原因が確定しました。OpenIndianaのlibrary/security/opensslパッケージはOpenSSL 1.0.2系であり、前回使用したOPENSSL_init_ssl()・TLS_server_method()・SSL_CTX_set_min_proto_version()はすべてOpenSSL 1.1.0以降のAPIです。またエラーログのssl.h:2131の記述もOpenSSL 1.0.2系のものと一致します。
修正方針:
common.c: OpenSSL 1.0.2のAPIに戻す(SSL_library_init・SSLv23_server_method・SSL_CTX_set_optionsでTLS 1.2以上を強制)
common.h: バージョン判定マクロで1.0.2/1.1.0両対応のラッパーを定義
server.c: snprintfのパス結合をバッファオーバーフロー警告が出ないよう修正
Makefile: -Wno-format-truncationを追加(警告をエラー扱いにしない)
2個のコマンドを実行しました
次にserver.cのsnprintfパス結合による-Wformat-truncation警告を修正します。パスを安全に結合するヘルパーを使います。
2個のファイルを表示しました
-Wformat-truncationの警告は、cwd(4095バイト最大) + / + ファイル名 = 4097バイト超になる可能性をgccが検出しているものです。バッファを拡大するか、事前に長さチェックを行う形で修正します。
2個のコマンドを実行しました
最後に構文チェックを行います。
6個のコマンドを実行しました
全ファイルエラーゼロを確認。出力します。
コマンドを実行しました, ファイルを読み取りました
修正内容を説明します。
修正内容まとめ
根本原因の特定
エラーログの ssl.h:2131 の記述と「OPENSSL_init_ssl → did you mean OPENSSL_init?」「TLS_server_method → did you mean DTLS_server_method?」というメッセージから、OpenIndianaのlibrary/security/opensslパッケージが OpenSSL 1.0.2系 であることが確定しました。前回使用したAPIはすべてOpenSSL 1.1.0以降のものでした。
修正 1: common.h — OpenSSLバージョン自動判定マクロを追加(主要修正)
OPENSSL_VERSION_NUMBERの値でコンパイル時に分岐し、1.0.2と1.1.0+の両方に対応するラッパーマクロを定義しました。
c
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* OpenSSL 1.0.2 系 (OI標準) /
#define UDPFT_SSL_INIT() SSL_library_init(); ...
#define UDPFT_TLS_SERVER_METHOD() SSLv23_server_method()
#define UDPFT_SET_MIN_TLS12(ctx) SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|...|SSL_OP_NO_TLSv1_1)
#else*
/* OpenSSL 1.1.0+ */
#define UDPFT_SSL_INIT() OPENSSL_init_ssl(...)
#define UDPFT_TLS_SERVER_METHOD() TLS_server_method()
#define UDPFT_SET_MIN_TLS12(ctx) SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)
#endif
修正 2: common.c — マクロを使用するよう全面書き換え
変更前(1.1.0+専用) 変更後(バージョン吸収)
OPENSSL_init_ssl(...) UDPFT_SSL_INIT()
TLS_server_method() UDPFT_TLS_SERVER_METHOD()
TLS_client_method() UDPFT_TLS_CLIENT_METHOD()
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) UDPFT_SET_MIN_TLS12(ctx)
修正 3: server.c — -Wformat-truncation 警告を解消
cwd(最大4095バイト) + / + ファイル名 の合計が4096バイトを超えうるという警告です。該当する3箇所のパスバッファを 4096 → 8192 バイトに拡張して解消しました。
修正 4: Makefile — -Wno-deprecated-declarations を追加
OpenSSL 1.0.2の一部API(SSLv23_*など)は1.1.0以降でdeprecatedマークされており、gcc 14の新しい警告機構で検出されます。この警告フラグで抑制します。
Common
H
Common
C
Server
C
Makefile
Client
C
Server
H
Client
makeは完了し実行ファイルもできあがったが、実際に動かしてみるとバグがあった。そのバグの修正をさせるプロンプトを作成した。
作成していただいたソースコード、ヘッダーファイル、Makefileを使ってSolaris11.4 for x86およびOpenIndiana 14.3.0-oi-1でmakeできました。
しかし、Solaris11.4 for x86でudpftdを“-d”スイッチ付きで起動し、OpenIndiana 14.3.0-oi-1側からudpftで接続したところ、ユーザ認証は通り“ls”コマンドでファイル一覧を取得することはできましたたが、“get”コマンドでfileをダウンロードしようとしたところ下記のエラーがudpftdから出力されてダウンロードできませんでした。
エラーを修正したSolaris11.4 for x86用のソースコード、ヘッダーファイル、Makefileを作成してください。
出力されたエラーは以下のものになります。
[ERR] udp_send_file: socket: Address family not supported by protocol family
このプロンプトに対するClaudeからの回答は、以下のとおり。
エラーを分析します。socket: Address family not supported by protocol family は、sess->peer_addr.ss_family に入っているアドレスファミリで UDP ソケットを作ろうとして失敗しているものです。原因を詳しく調べます。
6個のファイルを表示しました
原因が完全に特定できました。詳しく分析します。
根本原因: Solaris 11.4 では getaddrinfo(NULL, port, hints_with_AF_UNSPEC, …) の AF_UNSPEC に対し、AF_INET6 のソケットを優先して返します。その IPv6 ソケットで accept() すると、IPv4 クライアントからの接続は IPv4-mapped IPv6 アドレス (::ffff:x.x.x.x、ss_family = AF_INET6) として渡されます。
その ss_family = AF_INET6 で socket(AF_INET6, SOCK_DGRAM, 0) を呼ぼうとしても、Solaris 11.4 では IPv4-mapped アドレスへの UDP 送信が AF_INET6 UDPソケットから制限されているか、またはクライアント側 (OI) が AF_INET の UDP ポートでバインド待ちしているため、ファミリ不一致でエラーになります。
修正方針:
tcp_listen() を AF_INET 専用に固定(IPv4/IPv6 両対応は2ソケット構成が本来正しいが、今回はIPv4固定で確実に動かす)— ただしこれは IPv6 を捨てる
より正しい修正: accept() で受け取った peer_addr が IPv4-mapped IPv6 (::ffff:x.x.x.x) の場合、IPv4 アドレスに変換してから UDP ソケットを AF_INET で作成する
sendto() の送信先アドレスサイズも sizeof(struct sockaddr_in) と正確に指定する
方針2が正しいアプローチです。加えて sendto() に渡すアドレス長も正確にする必要があります。
2個のコマンドを実行しました
まず common.h に IPv4-mapped アドレス変換ユーティリティを追加します。
Rewrite common.h with IPv4-mapped address normalization utilities
次に tcp_listen() を修正し、IPV6_V6ONLY=1 を確実に設定して IPv4-mapped を受け取らないようにした上で、handle_client() の先頭で normalize_peer_addr() を呼ぶよう server.c を修正します。
6個のコマンドを実行しました, 4個のファイルを表示しました
修正1でblanked outしてしまったので直接書き直します。
2個のコマンドを実行しました
次に Makefile を Solaris 11.4 / Developer Studio 12.6 向けに更新します。
Write Solaris 11.4 Developer Studio 12.6 Makefile
最終確認。
6個のコマンドを実行しました, ファイルを読み取りました
修正内容を説明します。
バグの根本原因と修正内容
根本原因: IPv4-mapped IPv6 アドレス問題
Solaris 11.4 での接続フロー:
getaddrinfo(AF_UNSPEC, AI_PASSIVE)
→ AF_INET6 ソケットが優先して返る (Solaris の挙動)
→ AF_INET6 ソケットで listen/accept
→ IPv4 クライアント (OI) からの接続を受け付けると
peer_addr.ss_family = AF_INET6
peer_addr の中身 = ::ffff:x.x.x.x (IPv4-mapped IPv6)
→ udp_send_file() で socket(AF_INET6, SOCK_DGRAM, 0) を試みる
→ Solaris では AF_INET6 UDP から ::ffff:x.x.x.x への sendto が非対応
→ “Address family not supported by protocol family” エラー
修正 1: common.h — normalize_peer_addr() を追加(主要修正)
accept() で受け取った IPv4-mapped IPv6 アドレスを AF_INET に変換するインライン関数を追加しました。
c
static inline void normalize_peer_addr(struct sockaddr_storage *ss)
{
// ss_family が AF_INET6 かつ IN6_IS_ADDR_V4MAPPED なら
// ::ffff:a.b.c.d の末尾4バイト(IPv4部) を取り出して
// ss_family = AF_INET の sockaddr_in に書き換える
}
また sendto() に渡すアドレス長を正確にするヘルパー ss_addrlen() も追加しました。
c
static inline socklen_t ss_addrlen(const struct sockaddr_storage *ss)
{
if (ss->ss_family == AF_INET) return sizeof(struct sockaddr_in);
if (ss->ss_family == AF_INET6) return sizeof(struct sockaddr_in6);
...
}
修正 2: server.c — handle_client() で正規化を呼ぶ
c
memcpy(&sess.peer_addr, peer_addr, sizeof(sess.peer_addr));
normalize_peer_addr(&sess.peer_addr); // ← 追加: IPv4-mapped → AF_INET
sess.peer_len = ss_addrlen(&sess.peer_addr);
修正 3: server.c — sendto() のアドレス長を正確に指定
c
// 変更前
sendto(udp_fd, ..., (struct sockaddr *)&caddr, sizeof(caddr)); // 常に128バイト
// 変更後
sendto(udp_fd, ..., (struct sockaddr *)&caddr, ss_addrlen(&caddr)); // 16 or 28バイト
Solaris では sendto() に sizeof(sockaddr_storage)=128 を渡すとエラーになる場合があります。AF_INET なら sizeof(sockaddr_in)=16、AF_INET6 なら sizeof(sockaddr_in6)=28 を正確に渡す必要があります。
Common
H
Server
C
Common
C
Makefile
Client
C
Server
H
Client
無事ファイル転送できたので、転送中に転送速度を表示させるようにしたいと、次のプロンプトを作成した。
作成していただいたソースコード、ヘッダーファイル、Makefileを使ってmakeし、無事ファイル転送ができました。
次の段階として、クラアントにファイル転送速度を表示させたいです。ファイル転送後、毎秒何バイト転送できたか(Byte/sec)の表示がされるように修正したOpenIndiana 14.3.0-oi-1用のソースコード、ヘッダーファイル、Makefileを作成してください。
このプロンプトに対するClaudeからの回答は、以下のとおり。
現在のファイルを確認してから修正します。
4個のファイルを表示しました
全体を把握しました。設計を整理してから実装します。
計測方針:
gettimeofday() で転送開始〜完了(再送含む全パケット受信まで)の経過時間を計測
表示する速度は「実際に転送されたバイト数 ÷ 経過秒数」(= 実効スループット)
get(受信)・put(送信)両方に対応
進捗表示も「毎秒更新のリアルタイム速度表示」に改善(1秒ごとに現在の瞬間速度と累積速度を表示)
完了時に総転送量・総時間・平均速度をまとめて表示
2個のコマンドを実行しました
次にclient.hを更新します(シグネチャは変わらないので、対象OSとコメントのみ更新)。
Write updated client.h
Makefileは変更不要ですが、念のためOI向けのものを正しくコピーします。
3個のコマンドを実行しました
全ファイルエラーなし。出力します。
コマンドを実行しました, ファイルを読み取りました
変更したのは client.c と client.h のみです(server.c / common.c / common.h / Makefile は前回から変更なし)。
追加した速度表示機能の説明
3つの新規ヘルパー関数 (client.c 内 static 関数)
format_speed(bps, buf, bufsz)
速度値を単位付き文字列に変換します。
1 MiB/s 以上 → 1.23 MiB/s
1 KiB/s 以上 → 456.78 KiB/s
それ以下 → 789 Byte/s
print_progress(done, total, inst_bps, avg_bps)
転送中に1行を上書きしてリアルタイム進捗を表示します。
[===============> ] 52% 5242880/10000000 B 瞬間:1.23 MiB/s 平均:1.10 MiB/s
print_transfer_result(fsize, elapsed, rcv_pkts, total_pkts)
転送完了後にサマリを表示します。
転送完了
ファイルサイズ : 10485760 Byte (10.00 MiB)
パケット数 : 10486 / 10486
所要時間 : 8.432 秒
平均速度 : 1.19 MiB/s (1243210 Byte/s)
計測区間と方法
get (受信) put (送信)
開始 DATAREADY 送信直前 DATAREADY 受信直後
終了 COMPLETE 送信直前 COMPLETE 受信直後
対象 再送含む全受信バイト 初回送信バイト(再送除く)
gettimeofday() でマイクロ秒精度の計測(<sys/time.h>、POSIX標準でOI上で使用可能)
瞬間速度は直近1秒間に受信/送信したバイト数から算出
進捗は select() タイムアウト(1秒)のたびに更新するため、CPU負荷への影響は最小限
Client
C
Client
H
Makefile
Common
C
Server
C
Common
H
Server
H
修正されたソースコード等をmakeして実行するとバグがあったので、そのバグを修正させるプロンプトを作成した。
作成していただいたOpenIndiana 14.3.0-oi-1用ソースコード、ヘッダーファイル、Makefileを使ってmakeし、無事ファイル転送ができました。
しかし、Solaris11.4 for X86上のサーバに接続して“ls”コマンドを実行すると、9455184720Byteあるファイルが表示されません。またこのファイルを“get”コマンドでダウンロードすることもできません。
一定以上のサイズのファイルを取り扱うことができないのではと想像します。
取り扱うファイルのサイズ制限を無くしたOpenIndiana 14.3.0-oi-1用のソースコード、ヘッダーファイル、Makefileを作成してください。
このプロンプトに対するClaudeからの回答は、以下のとおり。
原因を分析します。9455184720バイトは約8.8GiBで、これは明らかに32ビット整数のオーバーフローが原因です。
3個のコマンドを実行しました
問題箇所が全て把握できました。整理します。
Analyze all large file issues comprehensively
全問題点を把握しました。修正を開始します。
2個のコマンドを実行しました
次にserver.cの sscanf と fseeko の安全な修正を行います。
2個のコマンドを実行しました
Makefileに-D_FILE_OFFSET_BITS=64等を追加します。
3個のコマンドを実行しました
off_t が確実に 64bit になっており、9.4GB の演算も正確です。
コマンドを実行しました, ファイルを読み取りました
修正内容を説明します。
根本原因と修正内容
根本原因:off_t が 32bit のままだった
illumos / Solaris では、LFS(Large File Support)マクロを明示的に定義しない限り off_t は 32bit のままです。32bit の off_t が扱える最大値は 2^31 - 1 = 2,147,483,647 byte(約 2GB) であり、9.4GB のファイルではオーバーフローして正しい値が得られません。
修正 1:common.h — LFS マクロをシステムヘッダより前に定義(最重要)
c
/* 全 #include より前に定義しなければならない /
#define _FILE_OFFSET_BITS 64 / off_t, ino_t を 64bit に切り替え /
#define _LARGEFILE_SOURCE 1 / fseeko(), ftello() を公開 /
#define _LARGEFILE64_SOURCE 1 / stat64() 等の明示 64bit 版を公開 */
これにより off_t が 64bit になり、stat().st_size・ftruncate()・pwrite()・fseeko() が 2GB 超のファイルを正しく扱えるようになります。
修正 2:Makefile — コンパイルフラグに LFS マクロを追加
makefile
CFLAGS = ...
-D_FILE_OFFSET_BITS=64
-D_LARGEFILE_SOURCE
-D_LARGEFILE64_SOURCE \
common.h での定義と二重になりますが、これにより翻訳単位全体で確実に LFS が有効になります。
修正 3:server.c / client.c — sscanf の安全な書き換え
c
/* 修正前: off_t が 32bit の場合ポインタキャストが危険 */
sscanf(msg, "FILESIZE %lld", (long long *)&fsize);
/* 修正後: long long で受けてから off_t に代入 /
long long fsize_ll = 0;
sscanf(msg, "FILESIZE %lld", &fsize_ll);
fsize = (off_t)fsize_ll; / off_t は LFS で 64bit */
修正 4:server.c / client.c — オフセット演算を明示的に off_t 同士で実施
c
/* 修正前: serial(uint32_t) * 1000 が 32bit 演算になる可能性 */
fseeko(fp, (off_t)serial * DATA_CHUNK_SIZE, SEEK_SET);
/* 修正後: 両辺を off_t にキャストして 64bit 演算を保証 */
fseeko(fp, (off_t)serial * (off_t)DATA_CHUNK_SIZE, SEEK_SET);
serial の最大値 4,294,967,295 × 1000 = 約4PB まで対応可能です。
Common
H
Makefile
Server
C
Client
C
Common
C
Server
H
Client
H
Solaris側も転送速度を表示されるように修正をさせるために、以下のプロンプトを作成した。
OpenIndiana 14.3.0-oi-1用のソースコード、ヘッダーファイル、Makefileの修正ありがとうございました。
同様にSolaris11.4 for x86用のソースコード、ヘッダーファイル、Makefileのの作成をお願いします。
修正箇所はOpenIndiana 14.3.0-oi-1と同じく、
・クラアントにファイル転送速度を表示させたいです。ファイル転送後、毎秒何バイト転送できたか(Byte/sec)の表示がされるように修正
・取り扱うファイルのサイズ制限を無くす
です。
このプロンプトに対するClaudeからの回答は、以下のとおり。
現在のファイルを確認してから作業します。
3個のコマンドを実行しました
差分を完全に把握しました。Solaris版に必要な変更は明確です:
Solaris版固有の保持事項: normalize_peer_addr() / ss_addrlen() / handle_clientでのIPv4-mapped正規化 / sendto()でのss_addrlen()
OI版から取り込む事項: LFS マクロ / 速度表示 / sscanf安全化 / fseeko・pwrite演算の明示化
Makefile: cc -xc99 に LFS フラグ追加
Copy Solaris 11.4 source as base
まず common.h を書き直します(LFS マクロ追加 + Solaris固有の normalize_peer_addr/ss_addrlen は維持)。
Write common.h with LFS + Solaris IPv4-mapped fix both included
次に server.c を修正します(LFS演算の明示化 + sscanf安全化、Solaris固有部分は維持)。
Patch server.c for LFS: safe sscanf and explicit off_t arithmetic
client.c はOI版(速度表示+LFS対応済み)をベースに、Solaris固有の点だけ調整して使います。
Copy OI client.c (speed+LFS) and update OS comment for Solaris
Makefile を Solaris 11.4 / Developer Studio 12.6 向けに書き直します(LFSフラグ追加)。
Write Makefile for Solaris 11.4 with LFS flags added to cc -xc99
最終確認を行います。
Full verification: syntax, Solaris-specific features, LFS, speed display
全項目確認済み。出力します。
コマンドを実行しました, ファイルを読み取りました
修正内容の概要を説明します。
修正内容
今回の Solaris 11.4 版は、前回の OI 版(LFS + 速度表示)に加えて Solaris 11.4 固有の修正を全て維持 した構成です。
変更したファイル
ファイル 変更内容
include/common.h LFS マクロ追加 + Solaris固有の normalize_peer_addr / ss_addrlen 維持
src/server.c sscanf 安全化 / fseeko・pwrite の (off_t)*(off_t) 明示
src/client.c OI版(速度表示 + LFS対応済み)をそのまま採用、対象OS名のみ更新
Makefile cc -xc99 に -D_FILE_OFFSET_BITS=64 / -D_LARGEFILE_SOURCE / -D_LARGEFILE64_SOURCE を追加
OI版との相違点(Solaris 11.4 固有部分)
common.h の normalize_peer_addr() と ss_addrlen()
Solaris 11.4 では getaddrinfo(AF_UNSPEC) が AF_INET6 を優先するため、IPv4 クライアントからの接続が ::ffff:x.x.x.x(IPv4-mapped IPv6)として渡されます。normalize_peer_addr() でこれを AF_INET に正規化し、ss_addrlen() で sendto() に正確なアドレス長を渡します。これは以前の修正(「Address family not supported」エラーの対処)です。
Makefile のコンパイラ
OI 版: gcc -std=gnu11
Solaris 版: cc -xc99(Developer Studio 12.6)
Common
H
Makefile
Server
C
Client
C
Common
C
Server
H
Client
H
Solaris側がサーバの場合はOKだったが、OpenIndiana側がサーバだとNGだったので、修正をさせるためのプロンプトを作成した。
Solaris11.4 for x86用及びOpenIndiana 14.3.0-oi-1用のソースコード、ヘッダーファイル、Makefileを使って実行ファイルを作成しました。
Solaris側でデーモンを動かし、OpenIndiana側でクライアントを使ってSolaris側にあるファイルをgetすることは出来ました。
逆にOpenIndiana側でデーモンを立ち上げSolaris側でクライアントを使ってOpenIndiana側にあるファイルをgetすることに失敗しました。
修正をお願いします。
各エラーメッセージは以下のとおりです。
・Solaris11.4 for x86側のudpftの該当する出力部分
Receiving 109310 bytes (110 packets)...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
再送要求: 110 パケット...
・OpenIndiana 14.3.0-oi-1側のudpftd -fの該当する出力部分
2026-06-16 15:06:14 [INFO] udp_send_file: initial send done, 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:14 [INFO] udp_send_file: resend request for 110 pkts
2026-06-16 15:06:15 [INFO] udp_send_file: resend request for 110 pkts
このプロンプトに対するClaudeからの回答は、以下のとおり。
IPv6マッピングアドレスの双方向互換性問題を診断し解決策を構想した。
クライアントの出力(受信側)とサーバーの出力(送信側)を見ると、サーバーは「110パケット送信完了」とログに出しているのに、クライアントは1つもパケットを受信できていない状態です。エラーが出ていないのに届かない、という症状から、**送信先アドレスのファミリ不一致による「サイレントな配送失敗」**が疑われます。
これは以前 Solaris で発生した「Address family not supported」と根本原因が同じですが、OpenIndiana ではエラーにならずに黒洞(パケロス)してしまうケースです。OI 側にはまだ normalize_peer_addr() の修正を適用していませんでした。
現在のOI版ファイルを確認します。
2個のコマンドを実行しました
作業ディレクトリがリセットされています。最新のOI出力から取得します。
List all output directories to find latest OI version
udpft_largefile が最新のOI版(速度表示+LFS対応)です。これをベースに修正します。
2個のコマンドを実行しました
予想通り、OI版には normalize_peer_addr / ss_addrlen の修正が一切適用されていません。これがOI起動時に「データのみが配送されない」原因です。修正します。
まず common.h に Solaris版から同じユーティリティを移植します。
2個のファイルを表示しました
udp_packet_t の定義の後に正規化ユーティリティを挿入します。
Insert normalize_peer_addr/ss_addrlen utilities after udp_packet_t in common.h
ヘッダーのコメントも更新します。
Update common.h header comment to mention IPv4-mapped fix
次に server.c を修正します。handle_client() の peer_addr コピー部分と udp_send_file() の sendto() 呼び出しを修正します。
ファイルを表示しました, ファイルを編集しました
次に udp_send_file() の sendto() 呼び出し2箇所を sizeof(caddr) → ss_addrlen(&caddr) に修正します。
2個のファイルを編集しました, ファイルを表示しました
ファイル先頭のコメントも更新します。
ソケット設定と送受信方向の互換性を検証した。
tcp_listen() のIPv6設定や create_udp_server_socket()(put受信用)は今回の問題(送信側のsendto失敗)とは無関係なので変更不要です。最終確認を行います。
Final syntax and patch verification
全項目確認できました。出力します。
コマンドを実行しました, ファイルを読み取りました
修正内容を説明します。
根本原因と修正内容
症状の分析
サーバー(OI)側のログには udp_send_file: initial send done と表示され、sendto() 自体はエラーなく完了しています。しかしクライアント(Solaris)側は 1パケットも受信できず、何度再送要求しても結果は変わりません。エラーが出ないのにデータが届かない、という症状から「送信先アドレスのファミリ不一致によるサイレントなパケットロス」と判断しました。
根本原因:OI側に IPv4-mapped IPv6 アドレス対策が未適用だった
以前、Solaris をサーバーにした場合に「Address family not supported」エラーが発生し、normalize_peer_addr() という修正を適用しました。これは illumos 系 OS(Solaris・OpenIndiana 共通の系譜)で、TCP の accept() 結果が ::ffff:x.x.x.x(IPv4-mapped IPv6)になることがあるための対策でした。
このときの修正は Solaris版だけに適用し、OpenIndiana版には移植していませんでした。OpenIndiana では、IPv4-mapped アドレスのまま UDP ソケットを作って送信しても Solaris のように明示的エラーにはなりませんが、カーネルがパケットを実際には配送せずに静かに捨てるという形で同じ問題が再現していました。
[OI server] peer_addr.ss_family = AF_INET6 (::ffff:x.x.x.x のまま)
→ socket(AF_INET6, SOCK_DGRAM, 0) で UDP ソケット作成(成功)
→ sendto(::ffff:x.x.x.x) (エラーなし、しかし配送されない)
→ Solaris クライアントには何も届かない
修正内容
include/common.h — normalize_peer_addr() と ss_addrlen() を追加(Solaris版と同一のユーティリティ)。
src/server.c — 2点修正:
handle_client() で peer_addr を保存した直後に normalize_peer_addr() を呼び、IPv4-mapped を AF_INET に正規化
c
memcpy(&sess.peer_addr, peer_addr, sizeof(sess.peer_addr));
normalize_peer_addr(&sess.peer_addr); // ← 追加
sess.peer_len = ss_addrlen(&sess.peer_addr);
udp_send_file() 内の sendto() 2箇所のアドレス長を sizeof(caddr) から ss_addrlen(&caddr) に変更(正規化後のファミリに応じた正確な長さを渡す)
これにより、Solaris と OpenIndiana のどちらをサーバーにしても、UDP送信前に必ず実アドレスファミリ(AF_INET)へ変換されるようになり、双方向で正常にファイル転送ができるようになります。
Common
H
Server
C
Makefile
Client
C
Common
C
Server
H
Client
H
転送速度が遅いので、チューニングを依頼するプロンプトを作成した。
Solaris11.4 for x86用及びOpenIndiana 14.3.0-oi-1用のソースコード、ヘッダーファイル、Makefileを使って実行ファイルを作成しました。
実際に409456932 Byteのバイナリーファイルを転送したところ、平均:913.90 KiB/sしか転送速度が出ませんでした。同じファイルをscpで転送したところ110.6MB/s出ました。
かなりの差が出てますので、転送速度を速めるためのチューニングをしたいのですが、そのチューニング方法を教えてください。