這裡示範如何使用 bash 指令稿開啟 TCP/UDP 的 socket,進行各種網路診斷工作。

作為網管或是 Linux 系統管理者,使用 netcatwgetcurl 這類的指令檢查遠端伺服器的網路服務應該算是基本技能,而且是時常會需要做的工作之一,但是如果遇到系統上沒有這類的工具可用時,我們就可以改用 bash shell 內建的一些功能來達到類似的效果。


在 Linux 系統上我們可以藉由 /dev/tcp/dev/udp 底下的設備檔來開啟 TCP 與 UDP 的網路連線,以下是一些開啟 TCP 與 UDP 連線的 bash 指令稿範例。

Bash 開啟 TCP/UCP 連線

在 bash 中,我們可以使用以下這個語法來開啟 TCP 或是 UDP 的 socket:

# 開啟輸入與輸出的 socket
exec FILE_DESCRIPTOR<>/dev/PROTOCOL/HOST/PORT

這個指令各部份的意義如下:

FILE_DESCRIPTOR
socket 的檔案代碼,值為大於或等於零的整數,而 012 分別代表系統的標準輸入(stdin)、標準輸出(stdout)與標準錯誤(stderr),所以我們能使用的值是從 3 開始。
<>
代表此 socket 同時以讀取與寫入的模式開啟。
PROTOCOL
傳輸協定,可為 tcp 或是 udp
HOST
主機名稱或是 IP 位址。
PORT
連接埠號碼。

若要關閉 socket,則執行:

# 關閉輸入 socket
exec FILE_DESCRIPTOR<&-

# 關閉輸出 socket
exec FILE_DESCRIPTOR>&-

測試網頁伺服器

以下是一個實際的範例,這個範例會使用 bash 的只令開啟連線至 Google 網頁的 socket,下載網頁資料:

#!/bin/bash
# 開啟連線至 Google 網頁的 socket
exec 3<>/dev/tcp/www.google.com.tw/80

# 送出 HTTP 請求
echo -e "GET / HTTP/1.1\n\n" >&3

# 接收網頁內容,1 秒後自動停止接收資料
timeout 1 cat <&3

# 關閉輸入與輸出 socket
exec 3<&-
exec 3>&-

這個範例可分為四個部份:

  1. 開啟一個 TCP socket 連到 www.google.com.tw 這台主機的 80 連接埠,將檔案代碼設定為 3
  2. 使用 echo 將 HTTP 請求的內容透過檔案代碼 3 送給 Google 網頁伺服器。
  3. 從檔案代碼 3 讀取來自於 Google 網頁伺服器的網頁資料,用 cat 輸出。
  4. 關閉輸入與輸出的 socket。

執行之後就會輸出 Google 網頁的原始 HTML 碼。

查詢 SSH 伺服器版本

這是一個檢查 SSH 伺服器版本的範例,用法大同小異:

#!/bin/bash
# 開啟 socket,連線至 192.168.0.1 的 SSH 伺服器
exec 3</dev/tcp/192.168.0.1/22

# 接收 SSH 的版本資訊,1 秒後自動停止接收資料
timeout 1 cat <&3

# 關閉輸入與輸出 socket
exec 3<&-
exec 3>&-

而上面這個檢查 SSH 版本的範例可以改寫為以下這個精簡的版本:

#!/bin/bash
# 檢查 192.168.0.1 的 SSH 伺服器版本
timeout 1 cat </dev/tcp/192.168.0.1/22

取得時間

這是一個透過 DAYTIME 協定,從伺服器取得目前時間的範例:

#!/bin/bash
# 取的時間資訊
cat </dev/tcp/time.nist.gov/13

檢查網頁連線

這個範例可以用來檢查網頁伺服器是否有正常運作,也就是測試伺服器的 80 連接埠是否可以正常連接:

#!/bin/bash

# 伺服器資訊
HOST=www.mit.edu
PORT=80

(echo >/dev/tcp/${HOST}/${PORT}) &>/dev/null
if [ $? -eq 0 ]; then
  echo "伺服器連接成功。"
else
  echo "伺服器連接失敗!"
fi

掃描連接埠

這個範例可以用來掃描主機的連接埠(port),看看哪些服務有開啟:

#!/bin/bash
# 要掃描的主機
HOST=192.168.0.1

# 要掃描的連接埠範圍
PORT_BEGIN=1
PORT_END=65535
for ((port=$PORT_BEGIN; port<=$PORT_END; port++))
do
  (echo >/dev/tcp/$HOST/$port) >/dev/null 2>&1 && echo "連接埠 $port 有開啟"
done

參考資料:NerotuxXmodulo