I'd like to have a function that I can pass a whitespace trimmed string to and it will return 0 for error (not a string) 1 for ipv4 2 for ipv6 3 for a string thats not an ip.
Ipv6 has these rules:
Ipv6 is represented by 8 groups of 16-bit hexadecimal values separated by colons (:)
The hexadecimal digits are case-insensitive
Abbreviation rules:
1: Omit leading zeroes in a 16-bit value
2: Replace one or more groups of consecutive zeroes by a double colon
wiki example showing 3 ways that are all the same ipv6:
fe80:0000:0000:0000:0202:b3ff:fe1e:8329
fe80:0:0:0:202:b3ff:fe1e:8329
fe80::202:b3ff:fe1e:8329
I'm reasonably sure for ipv4 you just check for three . then check the string is all numbers and the .'s are counted as numbers and the last check for just a string would be at the end of an if statement so if its not ipv4/6 and its a string then it returns 3
Mike's solution is good, but it can be improved on in several ways. In its current form it doesn't get to ipv6 address check, but it's easy to fix. The ipv6 check fails on things like "1050!0!0+0-5#600$300c#326b" and "1050:0:0:0:5:600:300c:326babcdef" (recognizing both as valid addresses) and "1050:::600:5:1000::" (recognizing it as string).
Here is the improved version (IPv4 are assumed to be decimal numbers and IPv6 are assumed to be hexadecimal numbers):
function GetIPType(ip)
local R = {ERROR = 0, IPV4 = 1, IPV6 = 2, STRING = 3}
if type(ip) ~= "string" then return R.ERROR end
-- check for format 1.11.111.111 for ipv4
local chunks = {ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")}
if #chunks == 4 then
for _,v in pairs(chunks) do
if tonumber(v) > 255 then return R.STRING end
end
return R.IPV4
end
-- check for ipv6 format, should be 8 'chunks' of numbers/letters
-- without leading/trailing chars
-- or fewer than 8 chunks, but with only one `::` group
local chunks = {ip:match("^"..(("([a-fA-F0-9]*):"):rep(8):gsub(":$","$")))}
if #chunks == 8
or #chunks < 8 and ip:match('::') and not ip:gsub("::","",1):match('::') then
for _,v in pairs(chunks) do
if #v > 0 and tonumber(v, 16) > 65535 then return R.STRING end
end
return R.IPV6
end
return R.STRING
end
The script to check:
local IPType = {[0] = "Error", "IPv4", "IPv6", "string"}
local ips = {
"128.1.0.1", -- ipv4
"223.255.254.254", -- ipv4
"999.12345.0.0001", -- invalid ipv4
"1050:0:0:0:5:600:300c:326b", -- ipv6
"1050!0!0+0-5#600$300c#326b", -- string
"1050:0:0:0:5:600:300c:326babcdef", -- string
"1050:0000:0000:0000:0005:0600:300c:326b", -- ipv6
"fe80:0000:0000:0000:0202:b3ff:fe1e:8329", -- ipv6
"fe80:0:0:0:202:b3ff:fe1e:8329", -- ipv6
"fe80::202:b3ff:fe1e:8329", -- ipv6
"1050:::600:5:1000::", -- contracted ipv6
"::", -- ipv6
"::1", -- ipv6
"::1::", -- string
"129.garbage.9.1", -- string
"xxx127.0.0.0", -- error
"xxx1050:0000:0000:0000:0005:0600:300c:326b", -- string
129.10 -- error
}
for k,v in pairs(ips) do
print(v, IPType[GetIPType(v)])
end
And the output:
128.1.0.1 IPv4
223.255.254.254 IPv4
999.12345.0.0001 string
1050:0:0:0:5:600:300c:326b IPv6
1050!0!0+0-5#600$300c#326b string
1050:0:0:0:5:600:300c:326babcdef string
1050:0000:0000:0000:0005:0600:300c:326b IPv6
fe80:0000:0000:0000:0202:b3ff:fe1e:8329 IPv6
fe80:0:0:0:202:b3ff:fe1e:8329 IPv6
fe80::202:b3ff:fe1e:8329 IPv6
1050:::600:5:1000:: IPv6
:: IPv6
::1 IPv6
::1:: string
129.garbage.9.1 string
xxx127.0.0.0 string
xxx1050:0000:0000:0000:0005:0600:300c:326b string
129.1 Error
Updated on 9/6/2018 to add handling of garbage before/after addresses and checking for contracted ipv6, which allows for fewer than 8 groups with one empty group of two consecutive colons.
this seems like a pretty basic problem to solve. i think this function does what you need...
function GetIPType(ip)
-- must pass in a string value
if ip == nil or type(ip) ~= "string" then
return 0
end
-- check for format 1.11.111.111 for ipv4
local chunks = {ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")}
if (#chunks == 4) then
for _,v in pairs(chunks) do
if (tonumber(v) < 0 or tonumber(v) > 255) then
return 0
end
end
return 1
else
return 0
end
-- check for ipv6 format, should be 8 'chunks' of numbers/letters
local _, chunks = ip:gsub("[%a%d]+%:?", "")
if chunks == 8 then
return 2
end
-- if we get here, assume we've been given a random string
return 3
end
tested it with this code:
local IPType = {
[0] = "Error",
[1] = "IPv4",
[2] = "IPv6",
[3] = "string",
}
local ips = {
"128.1.0.1", -- ipv4
"223.255.254.254", -- ipv4
"999.12345.0.0001", -- invalid ipv4
"1050:0:0:0:5:600:300c:326b", -- ipv6
"1050:0000:0000:0000:0005:0600:300c:326b", -- ipv6
"1050:::600:5:1000::", -- contracted ipv6
"129.garbage.9.1", -- string
129.10 -- error
}
for k,v in pairs(ips) do
print(v, IPType[GetIPType(v)])
end
which generated this output:
128.1.0.1 IPv4
223.255.254.254 IPv4
1050:0:0:0:5:600:300c:326b IPv6
1050:0000:0000:0000:0005:0600:300c:326b IPv6
129.garbage.9.1 string
129.1 Error
in the future, you'll get more helpful feedback if you actually post the code you've attempted to write to solve your particular problem, and let us know where you need help. SO isn't a personal code writing service, as stated in the faq. however, i'll give you the benefit of the doubt since you look new and this is something that could potentially benefit other people. the code above is basic, so feel free to update it if it doesn't catch fringe test cases i don't know about.
This seems as something that could be easily done by using regular expressions. There is plenty of regex libraries for lua.
If, however, you are not willing or are unable to use them, I would do something like this:
Start in ipv4 state
Take a character until string ends
switch(state)
ipv4:
if it's a dot, check if we loaded at least one number
if it's a number, check if it isn't the 4th in row
if it's anything else, set state to ipv6 and proceed in this state
ipv6:
if it's a ':', check if we didnt exceed maximum number of segments
if it's a number or letter<a;f> check if it isn't 5th in row
in case anything breaks, return 3
end
I'm not posting complete lua code, because it looks like homework/learning excercise and full answer would harm you more than it would help you.
Interestingly, none of the above answers takes the test examples of the original question into account, because using them, all of the above checks would fail (because of #3):
fe80:0000:0000:0000:0202:b3ff:fe1e:8329
fe80:0:0:0:202:b3ff:fe1e:8329
fe80::202:b3ff:fe1e:8329 (!)
IPv6 representation rules say:
One or more consecutive groups of zero value may be replaced with a single empty group using two consecutive colons (::),1 but the substitution may only be applied once in the address, because multiple occurrences would create an ambiguous representation.
https://en.wikipedia.org/wiki/IPv6_address#Representation
As Lua patterns do not have support for Alternation, it is not possible to check IPv6 with a single pattern. You may see David M. Syzdek answer on the complexity of IPv6 Regex: https://stackoverflow.com/a/17871737/1895269
Still, a more standards conforming approach is the following improvement of Paul Kulchenko's answer:
function GetIPType(ip)
local R = {ERROR = 0, IPV4 = 1, IPV6 = 2, STRING = 3}
if type(ip) ~= "string" then return R.ERROR end
-- check for format 1.11.111.111 for ipv4
local chunks = { ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$") }
if (#chunks == 4) then
for _,v in pairs(chunks) do
if tonumber(v) > 255 then return R.STRING end
end
return R.IPV4
end
-- check for ipv6 format, should be max 8 'chunks' of numbers/letters
local addr = ip:match("^([a-fA-F0-9:]+)$")
if addr ~= nil and #addr > 1 then
-- address part
local nc, dc = 0, false -- chunk count, double colon
for chunk, colons in addr:gmatch("([^:]*)(:*)") do
if nc > (dc and 7 or 8) then return R.STRING end -- max allowed chunks
if #chunk > 0 and tonumber(chunk, 16) > 65535 then
return R.STRING
end
if #colons > 0 then
-- max consecutive colons allowed: 2
if #colons > 2 then return R.STRING end
-- double colon shall appear only once
if #colons == 2 and dc == true then return R.STRING end
if #colons == 2 and dc == false then dc = true end
end
nc = nc + 1
end
return R.IPV6
end
return R.STRING
end
The script to check:
local IPType = {[0] = "Error", "IPv4", "IPv6", "string"}
local ips = {
"128.1.0.1", -- ipv4
"223.255.254.254", -- ipv4
"999.12345.0.0001", -- invalid ipv4
"1050:0:0:0:5:600:300c:326b", -- ipv6
"1050!0!0+0-5#600$300c#326b", -- string
"1050:0:0:0:5:600:300c:326babcdef", -- string
"1050:0000:0000:0000:0005:0600:300c:326b", -- ipv6
"1050:::600:5:1000::", -- contracted ipv6 (invalid)
"fe80::202:b3ff:fe1e:8329", -- shortened ipv6
"fe80::202:b3ff::fe1e:8329", -- shortened ipv6 (invalid)
"fe80:0000:0000:0000:0202:b3ff:fe1e:8329:abcd", -- too many groups
"::1", -- valid IPv6
"::", -- valid IPv6
":", -- string
"129.garbage.9.1", -- string
129.10 -- error
}
for k,v in pairs(ips) do
print(v, IPType[GetIPType(v)])
end
And the output:
128.1.0.1 IPv4
223.255.254.254 IPv4
999.12345.0.0001 string
1050:0:0:0:5:600:300c:326b IPv6
1050!0!0+0-5#600$300c#326b string
1050:0:0:0:5:600:300c:326babcdef string
1050:0000:0000:0000:0005:0600:300c:326b IPv6
1050:::600:5:1000:: string
fe80::202:b3ff:fe1e:8329 IPv6
fe80::202:b3ff::fe1e:8329 string
fe80:0000:0000:0000:0202:b3ff:fe1e:8329:abcd string
::1 IPv6
:: IPv6
: string
129.garbage.9.1 string
129.1 Error
As Lua's regular expressions are not sufficiently expressive, you must proceed with an iterative algorithm.
I suggest you to check the one that I posted on Italian Wikipedia (which have been fully tested):
local R = {ERROR = 0, IPV4 = 1, IPV6 = 2, STRING = 3}
function is_ipv4(str)
local s = str:gsub("/[0-9]$", ""):gsub("/[12][0-9]$", ""):gsub("/[3][0-2]$", "")
if not s:find("^%d+%.%d+%.%d+%.%d+$") then
return nil
end
for substr in s:gmatch("(%d+)") do
if not substr:find("^[1-9]?[0-9]$")
and not substr:find("^1[0-9][0-9]$")
and not substr:find( "^2[0-4][0-9]$")
and not substr:find("^25[0-5]$") then
return nil
end
end
return R.IPV4
end
function is_ipv6(str)
local s = str
if not (s:find("^%w+:%w+:%w+:%w+:%w+:%w+:%w+:%w+$") -- there are exactly seven ":"
or (s:find("^%w*:%w*:%w*:?%w*:?%w*:?%w*:?%w*$") -- otherwise there are two to six sei ":"
and s:find("::"))) -- and there must be the substring "::"
or s:find("::.*::") -- but there cannot be neither two substrings "::"
or s:find(":::") then -- nor a substring ":::"
return nil
end
for substr in s:gmatch("(%w+)") do
if not substr:find("^[0-9A-Fa-f][0-9A-Fa-f]?[0-9A-Fa-f]?[0-9A-Fa-f]?$") then
return nil
end
end
return R.IPV6
end
function ip_type(str)
if type(str) ~= "string" then
return R.ERROR
else
return is_ipv4(str) or is_ipv6(str) or R.STRING
end
end
Edit: I altered the ip_type() function's output as requested by the OP.
Related
I'm writing a dissector (to be added to DissectorTable.get("tcp.port")) for a TCP-based application. I've gone through the Wireshark API doc but could not find out how to get TCP header's info like
SYN/ACK flags
Sequence number
ACK'ed sequence number
UPDATE:
Based on the answer I'd put example snippet here. Just FYI.
local proto = Proto("myproto", "my proto")
-- ...
-- ...
--
-- A Field object can only be created *outside* of the callback
-- functions of dissectors, post-dissectors, heuristic-dissectors,
-- and taps.
--
local F_tcp_seq_rel = Field.new('tcp.seq') -- relative seq num
local F_tcp_seq_raw = Field.new('tcp.seq_raw') -- raw seq num
function proto.dissector(tvbuf, pinfo, tree)
-- ...
-- ...
local seq_rel = F_tcp_seq_rel() -- yes the Field object is callable!
local seq_raw = F_tcp_seq_raw()
-- ...
-- ...
end
DissectorTable.get("tcp.port"):add(12345, proto)
The way to get any field data, TCP or otherwise, is via a Field Extractor. So for example:
local tcp_flags_syn = Field.new("tcp.flags.syn")
local tcp_flags_ack = Field.new("tcp.flags.ack")
-- If you want relative sequence/acknowledgment numbers:
local tcp_seq = Field.new("tcp.seq")
local tcp_ack = Field.new("tcp.ack")
-- If you want absolute sequence/acknowledgment numbers:
local tcp_seq_raw = Field.new("tcp.seq_raw")
local tcp_ack_raw = Field.new("tcp.ack_raw")
If you need additional help using these fields, you may want to look at some of the Lua examples provided on the Wireshark Examples and/or Contrib wiki pages.
An example would be: 1000000 to 1,000,000.
If you could make this in Lua that'd be preferred
Lua-users-wiki has an example implementation for your problem.
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
print(comma_value(1000000))
print(comma_value(111))
print(comma_value(3.141592))
Output:
1,000,000
111
3.141592
Alternate version
function comma_value(n) -- credit http://richard.warburton.it
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
end
local function commas(number)
return tostring(number) -- Make sure the "number" is a string
:reverse() -- Reverse the string
:gsub('%d%d%d', '%0,') -- insert one comma after every 3 numbers
:gsub(',$', '') -- Remove a trailing comma if present
:reverse() -- Reverse the string again
:sub(1) -- a little hack to get rid of the second return value 😜
end
print(commas(1000000)) -- Pass
print(commas(111)) -- Pass
print(commas(3.141592)) -- Fail
A string '321#322#323#324#325'.
here number of digits in each number is 3 but it's not limited to 3 it could be any number.
here are 5 numbers in a string but this number could be anything.
task is to get 321,322,323,324,325 and store in a table so that any operation could be performed over them.
I have tried several string functions like c = c:gsub('%W','') to eliminate those non-alphanumeric characters, but nothing helped.
function encrypter()--FUNCTION 14
c=' '
print('Please enter your message!')
local message=io.read()
lengthOfString=string.len(message)--Inbuit function to get length of a string.
newLine()
print('Please enter your key. Remember this key, otherwise your message wont be decrypted')
newLine()
key=io.read()
key=tonumber(key)
largeSpace()
print("Encrypted message is")
for s=1,lengthOfString do
--print(encryptionFormula(string.byte(message,s),key))
--inbuilt function for converting character of a string to it's respective ASCII value. First place gets character or variable having string whereas second place gets position of that character in the given string.
b=encryptionFormula(string.byte(message,s),key)
c=c..tostring(b)..'#'
--print(c)
b=0
end
print(c)
largeSpace()
print("Now share this message along with the key to the receiver. Don't share your key with anyone, if you don't want your message to be read.")
end
What you're looking for is string.gmatch().
local input = "123#546514#13#548#2315"
local numbers = {}
for number in string.gmatch(input, '%d+') do
table.insert(numbers, number)
end
-- Output the numbers
for index, number in ipairs(numbers) do
print(index, number)
-- This prints:
-- 1 123
-- 2 546514
-- 3 13
-- 4 548
-- 5 2315
end
If you don't know how Lua patterns work, you can read about them in the reference manual or you can have a look at Programming in Lua (the first edition is available for free on their website)
I have the following lua code: (based on another post here on stackoverflow)
local chunks = {ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")}
if (#chunks == 4) then
for _,v in pairs(chunks) do
if (tonumber(v) < 0 or tonumber(v) > 255) then
return false
end
end
return true
else
return false
end
Trouble with this logic for validating IPv4 Addresses is that when I test addresses like "1.2.3.4.5", the variable "chunks" still evaluates to 4.
How can I change this pattern so that it passes ONLY when there are exactly four octets?
Thanks.
You can use the anchor patterns ^ and $ which mean "match at beginning of string" and "match at end of string" respectively at the beginning/end of your pattern to require the match capture the entire string:
local chunks = {ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")}
I'm attempting to create a Lua program to monitor periodic status pings of a slave device. The slave device sends its status in 16-bit hexadecimal words, which I need to convert to a binary string since each bit pertains to a property of the device. I can receive the input string, and I have a table containing 16 keys for each parameter. But I am having a difficult time understanding how to convert the hexadecimal word into a string of 16-bits so I can monitor it.
Here is a basic function of what I'm starting to work on.
function slave_Status(IP,Port,Name)
status = path:read(IP,Port)
sTable = {}
if status then
sTable.ready=bit32.rshift(status:byte(1), 0)
sTable.paused=bit32.rshift(status:byte(1), 1)
sTable.emergency=bit32.rshift(status:byte(1), 2)
sTable.started=bit32.rshift(status:byte(1), 3)
sTable.busy=bit32.rshift(status:byte(1), 4)
sTable.reserved1=bit32.rshift(status:byte(1), 5)
sTable.reserved2=bit32.rshift(status:byte(1), 6)
sTable.reserved3=bit32.rshift(status:byte(1), 7)
sTable.reserved4=bit32.rshift(status:byte(2), 0)
sTable.delay1=bit32.rshift(status:byte(2), 1)
sTable.delay2=bit32.rshift(status:byte(2), 2)
sTable.armoff=bit32.rshift(status:byte(2), 3)
sTable.shieldoff=bit32.rshift(status:byte(2), 4)
sTable.diskerror=bit32.rshift(status:byte(2), 5)
sTable.conoff=bit32.rshift(status:byte(2), 6)
sTable.envoff=bit32.rshift(status:byte(2), 7)
end
end
I hope this approach is understandable? I'd like to receive the Hex strings, for example 0x18C2 and turn that to 0001 1000 1100 0010, shifting the right-most bit to the right and placing that into the proper key. Then later in the function I would monitor if that bit had changed for the better or worse.
If I run a similar function in Terminator in Linux, and print out the pairs I get the following return:
49
24
12
6
3
1
0
0
56
28
14
7
3
1
0
0
This is where I am not understanding how to take each value and set it to bits
I'm pretty new to this so I do not doubt that there is an easier way to do this. If I need to explain further I will try.
tonumber(s, 16) will convert hex representation to decimal and string.char will return a symbol/byte representation of a number. Check this recent SO answer for an example of how they can be used; the solution in the answer may work for you.
I'd approach this in a different fashion than the one suggested by Paul.
First, create a table storing the properties of devices:
local tProperty = {
"ready",
"paused",
"emergency",
"started",
"busy",
"reserved1",
"reserved2",
"reserved3",
"reserved4",
"delay1",
"delay2",
"armoff",
"shieldoff",
"diskerror",
"conoff",
"envoff",
}
Then, since your device sends the data as 0xYYYY, you can call tonumber directly (if not a string). Use a function to store each bit in a table:
function BitConvert( sInput )
local tReturn, iNum = {}, tonumber( sInput ) -- optionally pass 16 as second argument to tonumber
while iNum > 0 do
table.insert( tReturn, 1, iNum % 2 )
iNum = math.floor( iNum / 2 )
end
for i = #tProperty - #tReturn, 1, -1 do
table.insert( tReturn, 1, 0 )
end
return tReturn
end
And then, map both the tables together:
function Map( tKeys, tValues )
local tReturn = {}
for i = 1, #tKeys do
tReturn[ tKeys[i] ] = tValues[i]
end
return tReturn
end
In the end, you would have:
function slave_Status( IP, Port, Name )
local status = path:read( IP, Port )
local sTable = Map( tProperty, BitConvert(status) )
end