🍕

Zigでtftpクライアントを作ってみた

2023/01/09に公開

以前書いた記事でZigでUDP通信できるようになったので、これを使ってtftpのクライアントを作ってみました。

注意: tftpはユーザー認証なしで使えるのでインターネット上のサーバでなくて、 外部からアクセスされることがないNATの内側のローカルネットワークでやってください。

tftpd を動かす

Ubuntu 22.04 を使っています。

tftpとtftpdをインストールします。

$ sudo apt install tftp tftpd

/tftpboot ディレクトリを作ります。

$ sudo mkdir /tftpboot
$ sudo chmod 777 /tftpboot

/etc/xinetd.d/tftpに以下のような内容でファイルを作ります。

/etc/xinetd.d/tftp
service tftp
{
    socket_type     = dgram
    protocol        = udp
    wait            = yes
    user            = tftp
    server          = /usr/sbin/in.tftpd
    server_args     = -s /tftpboot
    disable         = no
}

xinetdを再起動します。

$ sudo systemctl restart xinetd

tftp getの動作確認。

$ echo 'Hello!' > /tftpboot/hello.txt
$ tftp localhost
tftp> get hello.txt
Received 8 bytes in 0.1 seconds
tftp> quit
$ cat hello.txt 
Hello!

tftp putの動作確認。

$ echo 'Good morning!' > hello.txt
$ sudo chmod 666 /tftpboot/hello.txt 
$ tftp localhost
tftp> put hello.txt
Sent 15 bytes in 0.0 seconds
tftp> quit
$ cat /tftpboot/hello.txt 
Good morning!

tftpdのソースコードを確認したのですが、tftp putはサーバー側にその名前のファイルが存在して、かつファイルのパーミッションがothersの書き込みが許可されていないとError code 2: Access violationになります。
tftpプロトコルはユーザー認証が無いのでこのように厳し目の条件をつけているのでしょう。

tftpプロトコル

https://datatracker.ietf.org/doc/html/rfc1350

最初にクライアント側からサーバのポート69にReadかWriteのRequestを送るとサーバが返事します。
以降の通信はその返事をくれたポートに対して送信します。
相手からDataパケットを受け取ったらそれについているblock_numberでAckパケットを返します。
送った側はAckパケットが返ってきたらblock_numberをインクリメントして次に進みます。
Dataのサイズの最大値は512バイトで、それより小さいサイズで送ってきたらそれが最後であるという合図です。

正常シーケンスに関して、シーケンス図を書いてみました。

tftp get

tftp put

Gitリポジトリ

https://github.com/tetsu-koba/tftp_client

まだ正常系しか作っていないので、これからもコードをいじってpushしていきます。

Discussion