Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add more mssql datatypes #19105

Merged
merged 1 commit into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 121 additions & 2 deletions lib/rex/proto/mssql/client_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ def mssql_parse_tds_reply(data, info)
col[:utype] = data.slice!(0, 2).unpack('v')[0]
col[:flags] = data.slice!(0, 2).unpack('v')[0]
col[:type] = data.slice!(0, 1).unpack('C')[0]

case col[:type]
when 48
col[:id] = :tinyint
Expand All @@ -195,6 +194,50 @@ def mssql_parse_tds_reply(data, info)
col[:value_length] = data.slice!(0, 2).unpack('v')[0]
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')

when 109
col[:id] = :float
col[:value_length] = data.slice!(0, 1).unpack('C')[0]

when 108
col[:id] = :numeric
col[:value_length] = data.slice!(0, 1).unpack('C')[0]
col[:precision] = data.slice!(0, 1).unpack('C')[0]
col[:scale] = data.slice!(0, 1).unpack('C')[0]

when 60
col[:id] = :money

when 110
col[:value_length] = data.slice!(0, 1).unpack('C')[0]
case col[:value_length]
when 8
col[:id] = :money
when 4
col[:id] = :smallmoney
else
col[:id] = :unknown
end

when 111
col[:value_length] = data.slice!(0, 1).unpack('C')[0]
case col[:value_length]
when 4
col[:id] = :smalldatetime
when 8
col[:id] = :datetime
else
col[:id] = :unknown
end

when 122
col[:id] = :smallmoney

when 59
col[:id] = :float

when 58
col[:id] = :smalldatetime

when 36
col[:id] = :guid
col[:value_length] = data.slice!(0, 1).unpack('C')[0]
Expand Down Expand Up @@ -352,9 +395,85 @@ def mssql_parse_tds_row(data, info)
end
row << str

when :float
datalen = data.slice!(0, 1).unpack('C')[0]
case datalen
when 8
row << data.slice!(0, datalen).unpack('E')[0]
when 4
row << data.slice!(0, datalen).unpack('e')[0]
else
row << nil
end

when :numeric
varlen = data.slice!(0, 1).unpack('C')[0]
if varlen == 0
row << nil
else
sign = data.slice!(0, 1).unpack('C')[0]
raw = data.slice!(0, varlen - 1)
value = ''

case varlen
when 5
value = raw.unpack('L')[0]/(10**col[:scale]).to_f
when 9
value = raw.unpack('Q')[0]/(10**col[:scale]).to_f
when 13
chunks = raw.unpack('L3')
value = chunks[2] << 64 | chunks[1] << 32 | chunks[0]
value /= (10**col[:scale]).to_f
when 17
chunks = raw.unpack('L4')
value = chunks[3] << 96 | chunks[2] << 64 | chunks[1] << 32 | chunks[0]
value /= (10**col[:scale]).to_f
end
case sign
when 1
row << value
when 0
row << value * -1
end
end

when :money
datalen = data.slice!(0, 1).unpack('C')[0]
if datalen == 0
row << nil
else
raw = data.slice!(0, datalen)
rev = raw.slice(4, 4) << raw.slice(0, 4)
row << rev.unpack('q')[0]/10000.0
end

when :smallmoney
datalen = data.slice!(0, 1).unpack('C')[0]
if datalen == 0
row << nil
else
row << data.slice!(0, datalen).unpack('l')[0] / 10000.0
end

when :smalldatetime
datalen = data.slice!(0, 1).unpack('C')[0]
if datalen == 0
row << nil
else
days = data.slice!(0, 2).unpack('S')[0]
minutes = data.slice!(0, 2).unpack('S')[0] / 1440.0
row << DateTime.new(1900, 1, 1) + days + minutes
end

when :datetime
row << data.slice!(0, 8).unpack("H*")[0]
datalen = data.slice!(0, 1).unpack('C')[0]
if datalen == 0
row << nil
else
days = data.slice!(0, 4).unpack('l')[0]
minutes = data.slice!(0, 4).unpack('l')[0] / 1440.0
row << DateTime.new(1900, 1, 1) + days + minutes
end

when :rawint
row << data.slice!(0, 4).unpack('V')[0]
Expand Down
30 changes: 25 additions & 5 deletions test/modules/post/test/mssql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,31 @@ def test_console_query
end

def test_datatypes
it "should support ntext TDS datatype" do
stdout = with_mocked_console(session) {|console| console.run_single(%{ query "select cast('foo' as ntext);"})}
ret = true
ret &&= stdout.buf.match?(/0 foo/)
ret
[
{query: "select cast('1990-01-02' as datetime);", expected: [[DateTime.new(1990, 1, 2)]]},
{query: "select cast(null as datetime);", expected: [[nil]]},
{query: "select cast('1990-01-02' as smalldatetime);", expected: [[DateTime.new(1990, 1, 2)]]},
{query: "select cast(null as smalldatetime);", expected: [[nil]]},
{query: "select cast('19900' as float);", expected: [[19900.0]]},
{query: "select cast(null as float);", expected: [[nil]]},
{query: "select cast('19900' as real);", expected: [[19900.0]]},
{query: "select cast(null as real);", expected: [[nil]]},
{query: "select cast('12.50' as money);", expected: [[12.5]]},
{query: "select cast(null as money);", expected: [[nil]]},
{query: "select cast('12.50' as smallmoney);", expected: [[12.5]]},
{query: "select cast(null as smallmoney);", expected: [[nil]]},
{query: "select cast('1999999900' as numeric(16, 6));", expected: [[1999999900.0]]},
{query: "select cast(null as numeric(16, 6));", expected: [[nil]]},
adfoster-r7 marked this conversation as resolved.
Show resolved Hide resolved
{query: "select cast('foo' as ntext);", expected: [['foo']]},
{query: "select cast(null as ntext);", expected: [[nil]]},
].each do |test|
it "should execute the query #{test[:query]} and return #{test[:expected].inspect}" do
console = session.console
result = console.client.query(test[:query])
ret = result[:rows] == test[:expected]
ret &&= result[:errors].empty?
ret
end
end
end

Expand Down
Loading