Zigでtftpクライアントを作ってみた
以前書いた記事で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
に以下のような内容でファイルを作ります。
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プロトコル
最初にクライアント側からサーバのポート69にReadかWriteのRequestを送るとサーバが返事します。
以降の通信はその返事をくれたポートに対して送信します。
相手からDataパケットを受け取ったらそれについているblock_numberでAckパケットを返します。
送った側はAckパケットが返ってきたらblock_numberをインクリメントして次に進みます。
Dataのサイズの最大値は512バイトで、それより小さいサイズで送ってきたらそれが最後であるという合図です。
正常シーケンスに関して、シーケンス図を書いてみました。
tftp get
tftp put
Gitリポジトリ
まだ正常系しか作っていないので、これからもコードをいじってpushしていきます。
Discussion