-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathpdu_helper.lua
More file actions
327 lines (275 loc) · 11.9 KB
/
Copy pathpdu_helper.lua
File metadata and controls
327 lines (275 loc) · 11.9 KB
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
--[[
PDU helper
大部分代码来自 https://github.com/chenxuuu/sms_forwarding/blob/master/script/pdu.lua
--]]
local pdu_helper = {}
local constants = require("constants")
--[[
GSM字符集
https://en.wikipedia.org/wiki/GSM_03.38#GSM_7-bit_default_alphabet_and_extension_table_of_3GPP_TS_23.038_/_GSM_03.38
--]]
local charmap = {
[0] = 0x40, 0xa3, 0x24, 0xa5, 0xe8, 0xE9, 0xF9, 0xEC, 0xF2, 0xC7, 0x0A, 0xD8, 0xF8, 0x0D, 0xC5, 0xE5
, 0x0394, 0x5F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8, 0x03A3, 0x0398, 0x039E, 0x1B, 0xC6, 0xE5, 0xDF, 0xA9
, 0x20, 0x21, 0x22, 0x23, 0xA4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F
, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
, 0xA1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F
, 0X50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC4, 0xD6, 0xD1, 0xDC, 0xA7
, 0xBF, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F
, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE4, 0xF6, 0xF1, 0xFC, 0xE0}
--[[
GSM扩展字符集
https://en.wikipedia.org/wiki/GSM_03.38#GSM_7-bit_default_alphabet_and_extension_table_of_3GPP_TS_23.038_/_GSM_03.38
--]]
local charmap_ext = {[10] = 0x0C, [20] = 0x5E, [40] = 0x7B, [41] = 0x7D, [47] = 0x5C, [60] = 0x5B, [61] = 0x7E
, [62] = 0x5D, [64] = 0x7C, [101] = 0xA4}
local function number_to_bcd_number(number)
local number_length = #number
local prefix
local converted_number = ""
if string.sub(number, 1, 1) == "+" then
prefix = constants.pdu_sms_center_type.global
number_length = number_length - 1
number = number:sub(2, -1)
else
prefix = constants.pdu_sms_center_type.domestic
end
-- 每次取两位,前后颠倒后,拼接至converted_number
for i = 1, (number_length - (number_length % 2)) / 2 do
converted_number = converted_number..number:sub(i * 2, i * 2)..number:sub(i * 2 - 1, i * 2 - 1)
end
-- 如果号码长度为奇数,那么在末尾补一个F
if number_length % 2 ~= 0 then
converted_number = converted_number.."F"..number:sub(number_length, number_length)
end
return prefix..converted_number
end
local function bcd_number_to_ascii(bcd_number)
local length = #bcd_number
local prefix = ""
local converted_number = ""
if length % 2 ~= 0 then
log.warn("pdu_helper", "BCD数字\""..bcd_number.."\"无效")
return
end
if bcd_number:sub(1, 2) == constants.pdu_sms_center_type.global then
prefix = "+"
end
-- 去掉本地/国际标识部分
length = length - 2
bcd_number = bcd_number:sub(3, -1)
-- 每次取两位,前后颠倒后,拼接至converted_number
for i = 1, (length - (length % 2)) / 2 do
converted_number = converted_number..bcd_number:sub(i * 2, i * 2)..bcd_number:sub(i * 2 - 1, i * 2 - 1)
end
if converted_number:sub(length, length):upper() == "F" then
converted_number = converted_number:sub(1, -2)
end
return prefix..converted_number
end
-- 解码GSM 8-bit编码
local function gsm_8bit_decode(data)
local ucs_data = ""
local lpcnt = #data / 2
for i = 1, lpcnt do
ucs_data = ucs_data.."00"..data:sub((i - 1) * 2 + 1, i * 2)
end
return ucs_data, lpcnt
end
-- 解码GSM 7-bit编码
local function gsm_7bit_decode(data, longsms)
local ucsdata, lpcnt, tmpdata, resdata, nbyte, nleft, ucslen, olddat = "", #data / 2, 0, 0, 0, 0, 0
if longsms then
tmpdata = tonumber("0x" .. data:sub(1, 2))
resdata = tmpdata >> 1
if olddat == 27 then
if charmap_ext[resdata] then --特殊字符
olddat, resdata = resdata, charmap_ext[resdata]
ucsdata = ucsdata:sub(1, -5)
else
olddat, resdata = resdata, charmap[resdata]
end
else
olddat, resdata = resdata, charmap[resdata]
end
ucsdata = ucsdata .. string.format("%04X", resdata)
else
tmpdata = tonumber("0x" .. data:sub(1, 2))
resdata = ((tmpdata<<nbyte)|nleft)&0x7f
if olddat == 27 then
if charmap_ext[resdata] then --特殊字符
olddat, resdata = resdata, charmap_ext[resdata]
ucsdata = ucsdata:sub(1, -5)
else
olddat, resdata = resdata, charmap[resdata]
end
else
olddat, resdata = resdata, charmap[resdata]
end
ucsdata = ucsdata .. string.format("%04X", resdata)
nleft = tmpdata >> (7 - nbyte)
nbyte = nbyte + 1
ucslen = ucslen + 1
end
for i = 2, lpcnt do
tmpdata = tonumber("0x" .. data:sub((i - 1) * 2 + 1, i * 2))
if tmpdata == nil then break end
resdata = ((tmpdata<<nbyte)|nleft)&0x7f
if olddat == 27 then
if charmap_ext[resdata] then --特殊字符
olddat, resdata = resdata, charmap_ext[resdata]
ucsdata = ucsdata:sub(1, -5)
else
olddat, resdata = resdata, charmap[resdata]
end
else
olddat, resdata = resdata, charmap[resdata]
end
ucsdata = ucsdata .. string.format("%04X", resdata)
nleft = tmpdata >> (7 - nbyte)
nbyte = nbyte + 1
ucslen = ucslen + 1
if nbyte == 7 then
if olddat == 27 then
if charmap_ext[nleft] then --特殊字符
olddat, nleft = nleft, charmap_ext[nleft]
ucsdata = ucsdata:sub(1, -5)
else
olddat, nleft = nleft, charmap[nleft]
end
else
olddat, nleft = nleft, charmap[nleft]
end
ucsdata = ucsdata .. string.format("%04X", nleft)
nbyte, nleft = 0, 0
ucslen = ucslen + 1
end
end
return ucsdata, ucslen
end
local function ucs2_to_utf8(s)
local temp = {}
for i=1, #s, 2 do
local d1, d2 = s:byte(i), s:byte(i + 1)
if d1 == 0 and d2 <= 0x7f then --不大于0x007F
table.insert(temp, string.char(d2))
elseif d1 < 0x07 then --不大于0x07FF 00000aaa bbbbbbbb ==> 110aaabb 10bbbbbb
table.insert(temp, string.char(0xc0 + (d1 << 2) + (d2 >> 6), 0x80 + (d2 & 0x3f)))
else --aaaaaaaa bbbbbbbb ==> 1110aaaa 10aaaabb 10bbbbbb
table.insert(temp,string.char(0xe0 + (d1 >> 4), 0x80 + ((d1 & 0x0f) << 2) + (d2 >> 6), 0x80 + (d2 & 0x3f)))
end
end
return table.concat(temp)
end
--[[
解析PDU短信
返回值:
发送者号码
短信内容
接收时间
是否为长短信
如果为长短信,分了几包
如果为长短信,当前是第几包
--]]
function pdu_helper.decode_pdu(pdu, len)
collectgarbage("collect")
log.debug("pdu_helper", "原始PDU信息:\""..pdu.."\",长度:"..len)
--[[
不包括短信息中心号码的PDU数据
计算:
1. #pdu / 2
字符串中每两个字符为1位16进制数,即计算完整PDU的字节数
2. (#pdu / 2 - len)
len为从PDU报头开始计算的报文字节数,如此相减即可得到PDU First Octet的上一字节的位置
3. (#pdu / 2 - len) * 2 + 1
乘二,得到PDU First Octet上一字节在PDU字符串中的位置;加一将偏移量指向PDU First Octet的第一个字符
--]]
pdu = pdu:sub((#pdu / 2 - len) * 2 + 1)
local long_sms = false
-- TP-Message-Type-Indicator
-- https://www.cnblogs.com/dajianshi/archive/2013/01/25/2876151.html
local first_octet = tonumber("0x"..pdu:sub(1, 1))
log.debug("pdu_helper", "First Octet: "..first_octet)
if first_octet & 0x4 ~= 0 then
long_sms = true
log.debug("pdu_helper", "Long SMS")
end
local offset = 3
-- 源地址数字个数
local sender_address_length = tonumber(string.format("%d", "0x"..pdu:sub(offset, offset + 1)))
log.debug("pdu_helper", "sender address length: "..sender_address_length)
offset = offset + 2
-- 加上号码类型2位,如果号码长度为奇数,那么再加1位F
sender_address_length = sender_address_length % 2 == 0 and sender_address_length + 2 or sender_address_length + 3
local sender_number_bcd = pdu:sub(offset, offset + sender_address_length - 1)
local sender_number = bcd_number_to_ascii(sender_number_bcd)
log.debug("pdu_helper", "sender_number: "..sender_number)
offset = offset + sender_address_length
-- 协议标识 (TP-PID)
local protocol_identifier = tonumber(string.format("%d", "0x"..pdu:sub(offset, offset + 1)))
log.debug("pdu_helper", "TP-PID: "..protocol_identifier)
offset = offset + 2
-- 用户信息编码方式
local dcs = tonumber(string.format("%d", "0x"..pdu:sub(offset, offset + 1)))
log.debug("pdu_helper", "Data Coding Scheme: "..dcs)
offset = offset + 2
local timestamp = pdu:sub(offset, offset + 13)--时区7个字节
log.debug("pdu_helper", "timestamp: "..timestamp)
offset = offset + 14
local sms_receive_time = ""
for i = 1, 7 do
sms_receive_time = sms_receive_time .. timestamp:sub(i * 2, i * 2) .. timestamp:sub(i * 2 - 1, i * 2 - 1)
if i <= 3 then
sms_receive_time = i < 3 and (sms_receive_time .. "/") or (sms_receive_time .. ",")
elseif i <= 6 then
sms_receive_time = i < 6 and (sms_receive_time .. ":") or (sms_receive_time .. "+")
end
end
-- 短信文本长度
local content_length = tonumber(string.format("%d", "0x"..pdu:sub(offset, offset + 1)))
log.debug("pdu_helper", "Content Length: "..content_length)
offset = offset + 2
local current_idx
local total_message_count
if long_sms then
local header_length = tonumber("0x"..pdu:sub(offset, offset + 1))
log.debug("pdu_helper", "Header length: "..header_length)
-- 指针走到header中的长短信总条数
-- header有两种,6位header的剩余协议头长度为5,7位header的剩余协议头长度为6
if header_length == 5 then
offset = offset + 8
else
offset = offset + 10
end
total_message_count = tonumber("0x"..pdu:sub(offset, offset + 1))
offset = offset + 2
current_idx = tonumber("0x"..pdu:sub(offset, offset + 1))
offset = offset + 2
log.debug("pdu_helper", "current index: "..current_idx..", total: "..total_message_count)
end
-- 短信文本
local data = pdu:sub(offset, offset + content_length * 2 - 1)
local decoded_sms_content
local sms_content_in_utf8
if dcs == 0x00 then -- 7bit encode
log.debug("pdu_helper", "Incoming GSM-7 data: "..data..", is long SMS: "..tostring(long_sms))
decoded_sms_content = gsm_7bit_decode(data, long_sms)
log.debug("pdu_helper", "GSM-7 decoded, data: \""..decoded_sms_content.."\"")
decoded_sms_content = decoded_sms_content:fromHex()
sms_content_in_utf8 = ucs2_to_utf8(decoded_sms_content)
log.debug("pdu_helper", "SMS content in UTF-8: "..sms_content_in_utf8)
elseif dcs == 0x04 then -- 8bit encode
log.debug("pdu_helper", "Incoming 8 bit data:", data)
decoded_sms_content = gsm_8bit_decode(data)
log.debug("pdu_helper", "GSM-8 decoded, data: \""..decoded_sms_content.."\"")
sms_content_in_utf8 = decoded_sms_content:fromHex()
log.debug("pdu_helper", "SMS content in UTF-8: "..sms_content_in_utf8)
elseif dcs == 0x08 then -- UCS2
log.debug("pdu_helper", "Incoming UCS2 data: "..data)
sms_content_in_utf8 = data:fromHex()
sms_content_in_utf8 = ucs2_to_utf8(sms_content_in_utf8)
log.debug("pdu_helper", "Decoded UCS2 data: "..sms_content_in_utf8)
end
return sender_number, sms_content_in_utf8, sms_receive_time, long_sms, total_message_count, current_idx
end
return pdu_helper