因個人工作需要,寫了一個穿透 跳板機主機 直接連接內網服務器主機的Shell腳本,用於直接連接內網主機,執行sshsftp命令。其中涉及到 隨機生成的未被佔用的端口 ,本文主要介紹本人的處理思路及其實現方式。值得一提的是,在該腳本中成功實現了函數自身的遞歸(recursive)調用。

該腳本在CentOS Linux release 7.2.1511 (Core)系統下撰寫。

Personal Requests

以下是個人需求

  1. 生成隨機數
  2. 該隨機數必須大於1024(前1024個端口號只能root用戶使用)
  3. 該隨機數必須在當前系統允許的端口號範圍內
  4. 該隨機數必須不能佔用文件/etc/services中被分配的端口
  5. 該隨機數必須是未被佔用/使用的端口號

Thinking

  1. 在命令sysctl -a的輸出結果中查詢net.ipv4.ip_local_port_range獲取當前系統允許的端口號範圍,使用awk提取數據;
  2. 使用命令ss -tunlnetstat -tupln查看當前系統被監聽/佔用的端口信息,使用awk提取數據;
  3. 使用命令sed -n -r '[email protected]*[[:space:]]+([0-9]+)/.*@\[email protected]' /etc/services從文件/etc/services中提取端口數值(含重複數值)
  4. 通過讀取文件/dev/urandom中內容獲取隨機信息,使用cksum命令獲取隨機數,可使用如下命令格式

    1
    2
    3
    4
    5
    # print the first K bytes of each  file;
    head -c K /dev/urandom| cksum

    # print the first K lines instead of the first 10;
    head -n K /dev/urandom| cksum
  5. 在自定義函數內部進行判斷,是否符合預期要求,通過函數遞歸調用實現生成符合要求的隨機數

: 對於默認端口分配,可查看文件/etc/services,也可訪問Service Name and Transport Protocol Port Number Registry 查看詳細介紹。


Implementations

以下是具體代碼

腳本下載 GitHub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/bin/bash
#Target:該腳本用於隨機生成未被佔用的端口號
#Author:lempstacker
#Blog : https://lempstacker.com/
#CreateTime:2016.09.08 23:00 Thu Asia/Shanghai
#UpdateTime:2016.11.01 11:23 Thu Asia/Shanghai

# 1.通過UID檢測執行腳本的用戶是否是root或有sudo權限
if [[ "$UID" -ne 0 ]]; then
printf "對不起,執行該腳本須擁有root或sudo權限!\n" && exit 1
fi

# font color
c_red='\e[31;1m'
c_blue='\e[34m'
c_end='\e[0m'


# 2. 查看可用端口範圍
lport_from=`sysctl -a | awk '$0~/ip_local_port_range/{print $(NF-1)}'` #可用端口號起
lport_to=`sysctl -a | awk '$0~/ip_local_port_range/{print $NF}'` #可用端口號訖
# echo 'Local port is from '$lport_from' to '$lport_to


# 3. 查看系統正在被監聽的端口
tempPortUsedlist=`mktemp -t tempXXXXX.txt` #临时文件

if [ ! -z `which ss` ]; then
ss -tunl | awk '$1~/udp|tcp/{print $(NF-1)}' | awk -v FS=':' '$0!~/^*$/{arr[$NF]++}END{for(i in arr)print i}' > $tempPortUsedlist
elif [ ! -z `which netstat` ]; then
netstat -tupln | awk '$1~/^[udp|tcp]/{print $4}' | awk -v FS=':' '$0!~/^*$/{arr[$NF]++}END{for(i in arr)print i}' > $tempPortUsedlist
fi


# 4. 查看文件/etc/services中被分配的端口號,去重後寫入臨時文件
sed -n -r '[email protected]*[[:space:]]+([0-9]+)/.*@\[email protected]' /etc/services | awk '!a[$0]++{if($0>1024){print $0}}' >> $tempPortUsedlist


# 5. 生成符合條件的隨機端口 自定義函數
generate_random_port(){ #需要指定參數
local port_from="$1"
local port_to="$2"
random_num=`head -n 9 /dev/urandom| cksum | awk '{print $1}'` # 讀取行數根據實際需要更改
port=$(($random_num%$port_to))

[[ `grep -c -w $port $tempPortUsedlist` -gt 0 || $port -lt $port_from ]] && port=$(generate_random_port "$port_from" "$port_to") # 進行函數迭代 iterate

echo $port #輸出
}

generated_port=$(generate_random_port "$lport_from" "$lport_to") #調用函數

printf $c_blue"Newly Generated Port NO. is$c_end $c_red$generated_port$c_end\n"


# 6 销毁变量、删除临时文件
unset c_red
unset c_blue
unset c_end
unset lport_from
unset lport_to
unset usedPorts
unset generated_port

rm -f $tempPortUsedlist

#Script End

Usage

命令格式bash FILEPATH/BASH_NAME,建議使用絕對路徑,如果其中含有空格,須用雙引號""將其包裹。

此处假设Shell脚本的路径为/tmp/port.sh

Executation

通過for循環生成多個隨機端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[[email protected] ~]$ for ((i=0;i<15;i++));do sudo bash /tmp/port.sh; done
Newly Generated Port NO. is 59612
Newly Generated Port NO. is 44238
Newly Generated Port NO. is 64898
Newly Generated Port NO. is 30770
Newly Generated Port NO. is 57741
Newly Generated Port NO. is 20995
Newly Generated Port NO. is 25898
Newly Generated Port NO. is 37325
Newly Generated Port NO. is 29044
Newly Generated Port NO. is 42332
Newly Generated Port NO. is 40999
Newly Generated Port NO. is 30149
Newly Generated Port NO. is 38957
Newly Generated Port NO. is 7215
Newly Generated Port NO. is 41652
[[email protected] ~]$

Executation Procedure

以下是腳本的具體執行過程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[[email protected] ~]$ bash -x /tmp/port.sh
+ [[ 1000 -ne 0 ]]
+ printf '對不起,執行該腳本須擁有root或sudo權限!\n'
對不起,執行該腳本須擁有root或sudo權限!
+ exit 1
[[email protected] ~]$ sudo bash -x /tmp/port.sh
+ [[ 0 -ne 0 ]]
+ c_red='\e[31;1m'
+ c_blue='\e[34m'
+ c_end='\e[0m'
++ sysctl -a
++ awk '$0~/ip_local_port_range/{print $(NF-1)}'
+ lport_from=1024
++ sysctl -a
++ awk '$0~/ip_local_port_range/{print $NF}'
+ lport_to=65000
++ mktemp -t tempXXXXX.txt
+ tempPortUsedlist=/tmp/tempsVOEH.txt
++ which ss
+ '[' '!' -z /sbin/ss ']'
+ ss -tunl
+ awk '$1~/udp|tcp/{print $(NF-1)}'
+ awk -v FS=: '$0!~/^*$/{arr[$NF]++}END{for(i in arr)print i}'
+ sed -n -r '[email protected]*[[:space:]]+([0-9]+)/.*@\[email protected]' /etc/services
+ awk '!a[$0]++{if($0>1024){print $0}}'
++ generate_random_port 1024 65000
++ local port_from=1024
++ local port_to=65000
+++ head -n 9 /dev/urandom
+++ cksum
+++ awk '{print $1}'
++ random_num=2778524653
++ port=34653
+++ grep -c -w 34653 /tmp/tempsVOEH.txt
++ [[ 0 -gt 0 ]]
++ [[ 34653 -lt 1024 ]]
++ echo 34653
+ generated_port=34653
+ printf '\e[34mNewly Generated Port NO. is\e[0m \e[31;1m34653\e[0m\n'
Newly Generated Port NO. is 34653
+ unset c_red
+ unset c_blue
+ unset c_end
+ unset lport_from
+ unset lport_to
+ unset usedPorts
+ unset generated_port
+ rm -f /tmp/tempsVOEH.txt
[[email protected] ~]$

References


Change Logs

  • 2016.09.09 10:47 Fri Asia/Shanghai
    • 初稿完成
  • 2016.11.01 11:58 Tue Asia/Shanghai
    • 脚本重构,添加执行权限判断、文件/etc/services中端口过滤等
  • 2016.12.21 15:52 Wed Asia/Shanghai
    • 代碼優化,更改判斷命令是否存在的方式

  • Note Time: 2016.09.09 10:47 Fri
  • Note Location: Asia/Shanghai
  • Writer: lempstacker