ジャンル不定の日記です。

socatでwebsocketサーバーができなかった件

socatでwebsocketサーバーを作ろうとしてうまく行かなかったが、わかった。
socatに-vオプションつけてターミナルに出力してみたが、なんかバイナリぽいのが出力された。

socatから実行するシェルスクリプトではreadで1行ずつ読んでたが、readじゃバイナリが読めずに変数に空文字が入ってた感じぽい。

今回はクライアントから"test"をsendしてたんだが、
readではなくcatでファイルに出力してバイナリエディタで見てみたが、
"8184E4F2CFA39097BCD7"
↑こんなデータだった。

WebSocketの仕様について調べたが、ハンドシェイクのあとはデータは特殊なフレーム化されて送られるのね。
仕様によると、

1bit目: FIN
最後のフレームの場合1で、続きがある場合は0

byteじゃなくてbitなのね。
今回の"8184E4F2CFA39097BCD7"を2進に変換すると、
"10000001100001001110010011110010110011111010001110010000100101111011110011010111"
1bit目は1なんで1フレームで終わり。

2bit目からの3bitは予約bitで、仕様を拡張するのに使うようで、そんなものはないので今回は"000"。

5bit目からの4bitはデータの種別を表すようで、テキストデータの場合は"0001"

9bit目はマスクの有無
クライアントから送るデータはマスク必須で、サーバーから送るデータはマスク不可。
というわけで"1"

10bit目からの7bitがデータの長さで単位はbyteなんだが、値が126の場合は続く2byteがデータ長。127の場合は続く8byteがデータ長。
125以下の場合は続かずその値がデータ帳となる。
今回は"test"と4byteをsendしたんで、"0000100"(4)

ここまでbit単位になっているが、合計すると16bitで2byteになる。
残りのデータはbyte単位。

3byte目からの4byteはマスク用keyで、7byte目から残り全部はペイロードデータ。
ペイロードデータは、拡張データとアプリデータを連結したもので、予約bitが"000"で何も拡張されてなければ拡張データは存在しないので、ペイロードデータ=アプリデータになる。

アプリデータは単なる平文ではなく、
マスク用keyと元のデータをXORしたものになっている。
マスク用keyは4byteだが、データが5byte以上ある場合はループして5byte目はマスク用keyの1byte目とXORされる感じ。

今回のマスク用keyは2進で
"11100100 11110010 11001111 10100011"
になってる。

アプリデータは
"10010000 10010111 10111100 11010111"

元のデータにマスク用keyをXORしてアプリデータになったわけだが、XOR演算は再度XORすれば元のデータに戻る。
というわけで、XORしてみたが、
"01110100 01100101 01110011 01110100"
ASCIIコードの"test"ですね。

eth0が不安定だったようだが・・・

Orange Pi R1で
Feb 14 22:49:11 OrangePi_R1 kern.info kernel: [ 501.127514] dwmac-sun8i 1c30000.ethernet eth0: Link is Down
Feb 14 22:49:13 OrangePi_R1 kern.info kernel: [ 503.207650] dwmac-sun8i 1c30000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
が数分おきにシステムログに出力されてたんだが、
ケーブル代えてみたら治ったぽい。

元々ついてたのはCAT5の記載があった。
代えたやつも記載がないんでCAT5相当の古いやつ?
今どきカテゴリ5のケーブルはないらしいが、Orange Pi R1は100Mbpsだし未開封のケーブル使ったから問題ないのかな。

コマンドラインでBase64のSHA1ハッシュを出力する方法

先日からOrange Pi R1の管理用CGI関連を暇な時間にやってて、とりあえずHTTP+CGIでできるのは公開もして一段落してるが、
websocketの実験も兼ねてsocat+shでwebsocketサーバー作ってる。

WebSocketの接続は
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Version: 13
リクエストがこんな感じで、
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
レスポンスはこんな感じになるみたい。

ここで重要なのは、
リクエストのSec-WebSocket-Keyを元にSec-WebSocket-Acceptを作る。
作り方は、
Sec-WebSocket-Keyの値に"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"を追加した文字列のSHA1ハッシュをBase64した文字列がSec-WebSocket-Acceptになる。
なので、
"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
を変換して
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
にできればいいんだが、
sha1sumコマンドにはBase64を出力する機能もバイナリを出力する機能もない・・・

で調べたが、
$ echo -n "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"|openssl dgst -binary -sha1|base64
s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
sha1sumを使わずにopensslコマンドを使う。
$ echo -n "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"|sha1sum|xxd -r -p|base64
s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
↑xxdコマンドでsha1sumの16進文字列をバイナリに変換する。
$ echo -ne "$(echo -n "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11" | sha1sum | cut -f1 -d" " | sed -e 's/\(.\{2\}\)/\\x\1/g')" | base64
s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
↑sedを使ってるがわけわからんコードw
の3種類の方法を発見した。

opensslはBuildrootの標準で入ってない。
xxdは母艦のArch Linuxに無いから
Buildrootにもないかと思ったがあった!が、-rオプションに対応していない・・・
sed使うコードはわけわからん。
というわけで考えたが、openssl入れてまで実現したくないな・・・
なんだが、websocketの実験もしたいし、とりあえず母艦で実験は続けるか。

STM32が使えない・・・

Orange Pi R1関連も一旦落ち着いた感じなんで、届いたまま放置してたSTM32を使ってみようと思った。

はんだ付けは苦労したがなんとかできたと思う。
DIP変換基板はヘッダーピンを装着してブレッドボードにさせる感じが良かったと思うんだが、
手持ちのヘッダーピンが太いようで入らなかったんでソケットを装着した。

開発ツールはSTM32CubeIDEというSTM公式のツールが情報多い感じなんで(というか、他の情報が全然ない)それ入れようとした。
AURにあったんだが依存パッケージがmakepkgできず諦めて、公式からインストーラーを持ってきて入れた。

中華ST-Linkには端子が10本で、
RST、GND、SWIM、3.3V、5.0V、SWDIO、GND、SWCLK、3.3V、5.0V
となってる。
このうちSWDIOとSWCLKをつないで、3.3VとGNDで電源供給すればいいぽい。
STM32F042K6T6は1番ピンがVDD、32番ピンがVSSとなっているが、VDDが3.3VでVSSがGNDのことみたい。
24番ピン(PA14)がSWCLKで、23番ピン(PA13)がSWDIOとのこと。
最初、角にあるPA14でLEDを点滅させようかと思ったが、SWCLKは空けておいたほうが良さげだから9番ピン(PA3)に変更した。

CubeIDEでプロジェクトを作成して、ICの画面で24番ピンをクリックしてSWCLKにしたら23番ピンもSWDIOに変わった。
9番ピンをクリックしてGPIO outputにして、
上部メニューの "Project > Generate code"したら左サイドバーの "Core > Src > main.c"の"MX_GPIO_Init"にGPIOの設定が出てきた。
mainのwhile(1){}ブロック内にコードを書けばいいみたいだが、
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_3);
    HAL_Delay(1000);
  }
  /* USER CODE END 3 */
}
とした。

で、"Run > Debung As"からビルドしてデバッグができるみたいなんだができなかった。
調べてもわからず、
統合開発環境は機能が多すぎて難しいから、CUIで使えそうなstlinkとopenocdを入れて試してみたんだが、
$ sudo st-info --probe
Found 1 stlink programmers
 serial: 413a010032124353354b4e00
openocd: "\x41\x3a\x01\x00\x32\x12\x43\x53\x35\x4b\x4e\x00"
  flash: 0 (pagesize: 0)
   sram: 0
 chipid: 0x0000
  descr: unknown device
こんな感じで、なんかST-Linkは認識しているがSTM32を認識していないような?

これまたわからないんで、最終手段としてWindowsにCubeIDEを入れて試してみたが、
Linuxのときと表示内容は違うが同じところでエラー。
やはりST-Linkにデバイスがつながっていないようなエラーが出た。

頑張ってshでCGI作ったんだが・・・

頑張ってshでCGI作ったんだが、メインPC上のApacheで問題なかったが、
Orange Pi R1のuhttpdで実行したら502 Bad Gatewayになった。
途中までOrange Pi R1の方でもテストしながら作っててできる感じだったのに、最終段階で何故か・・・

エラー原因確認したが、
shの.コマンドで設定ファイルを読み込むようにしてたんだが、
設定ファイルはCGIと同じディレクトリなんで
. ./config.txt
って感じでロードしてた。
これで502 Bad Gatewayになる。

CGIでpwdコマンド出力してみたら、CGIは/cgi-bin以下にあるんだがドキュメントルートの/var/wwwだった。
CGIの設置ディレクトリじゃなくてドキュメントルートでCGIが実行される感じなんだね。
絶対PATHで読み込めばいいんだが、設置場所変えたらダメだし、$0の場所にcdしなきゃダメな感じかな。
これ不便すぎる。