Skip to content

Commit 921870c

Browse files
committed
Adding embedded dbwire library
1 parent d5ff557 commit 921870c

13 files changed

Lines changed: 1682 additions & 3 deletions

File tree

data/xml/queries.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@
222222
</columns>
223223
<dump_table>
224224
<inband query="SELECT %s FROM %s.%s"/>
225-
<blind query="SELECT MIN(%s) FROM %s WHERE CONVERT(NVARCHAR(4000),%s)>'%s'" query2="SELECT MAX(%s) FROM %s WHERE CONVERT(NVARCHAR(4000),%s) LIKE '%s'" query3="SELECT %s FROM (SELECT %s, ROW_NUMBER() OVER (ORDER BY %s) AS CAP FROM %s)x WHERE CAP=%d" count="SELECT LTRIM(STR(COUNT(*))) FROM %s" count2="SELECT LTRIM(STR(COUNT(DISTINCT(%s)))) FROM %s" keyset_first="SELECT MIN(%s) FROM %s" keyset_next="SELECT MIN(%s) FROM %s WHERE %s>'%s'" keyset_by="SELECT MAX(%s) FROM %s WHERE %s='%s'" keyset_seed="SELECT %s FROM %s ORDER BY %s OFFSET %d ROWS FETCH NEXT 1 ROWS ONLY" keyset_ordered="SELECT TOP 1 %s FROM %s WHERE %s ORDER BY %s" keyset_where="SELECT MAX(%s) FROM %s WHERE %s"/>
225+
<blind query="SELECT MIN(%s) FROM %s WHERE CONVERT(NVARCHAR(4000),%s)>'%s'" query2="SELECT MAX(%s) FROM %s WHERE CONVERT(NVARCHAR(4000),%s) LIKE '%s'" query3="SELECT %s FROM (SELECT *, %s AS SQLMAPCAPVAL, ROW_NUMBER() OVER (ORDER BY %s) AS CAP FROM %s)x WHERE CAP=%d" count="SELECT LTRIM(STR(COUNT(*))) FROM %s" count2="SELECT LTRIM(STR(COUNT(DISTINCT(%s)))) FROM %s" keyset_first="SELECT MIN(%s) FROM %s" keyset_next="SELECT MIN(%s) FROM %s WHERE %s>'%s'" keyset_by="SELECT MAX(%s) FROM %s WHERE %s='%s'" keyset_seed="SELECT %s FROM %s ORDER BY %s OFFSET %d ROWS FETCH NEXT 1 ROWS ONLY" keyset_ordered="SELECT TOP 1 %s FROM %s WHERE %s ORDER BY %s" keyset_where="SELECT MAX(%s) FROM %s WHERE %s"/>
226226
<primary_key count="SELECT COUNT(*) FROM sys.indexes i JOIN sys.index_columns ic ON i.object_id=ic.object_id AND i.index_id=ic.index_id WHERE i.is_primary_key=1 AND i.object_id=OBJECT_ID('%s.dbo.%s')" query="SELECT name FROM (SELECT c.name AS name, ROW_NUMBER() OVER (ORDER BY ic.key_ordinal) AS rn FROM sys.indexes i JOIN sys.index_columns ic ON i.object_id=ic.object_id AND i.index_id=ic.index_id JOIN sys.columns c ON ic.object_id=c.object_id AND c.column_id=ic.column_id WHERE i.is_primary_key=1 AND i.object_id=OBJECT_ID('%s.dbo.%s')) x WHERE rn=%d+1"/>
227227
</dump_table>
228228
<search_db>

extra/dbwire/__init__.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
5+
See the file 'LICENSE' for copying permission
6+
"""
7+
8+
"""
9+
dbwire - minimal, dependency-free (stdlib-only) database wire-protocol clients used as a fallback for
10+
sqlmap's direct ('-d') connection when no native driver (and no SQLAlchemy) is installed.
11+
12+
Design note: connectors speak a *wire protocol*, not a product, so a single client covers the whole
13+
compatible family - e.g. the PostgreSQL client also serves CockroachDB, CrateDB, Redshift and Greenplum;
14+
a MySQL client serves MariaDB/TiDB/Aurora; a TDS client serves MSSQL/Sybase. Each module exposes a small
15+
PEP 249 (DB-API 2.0) subset (connect(), Connection.cursor()/commit()/close(), Cursor.execute()/fetchall()).
16+
"""
17+
18+
__version__ = "0.1"
19+
20+
apilevel = "2.0"
21+
threadsafety = 1
22+
paramstyle = "pyformat"
23+
24+
# PEP 249 exception hierarchy (shared by every wire module)
25+
class Error(Exception):
26+
pass
27+
28+
class InterfaceError(Error):
29+
pass
30+
31+
class DatabaseError(Error):
32+
pass
33+
34+
class OperationalError(DatabaseError):
35+
pass
36+
37+
class DataError(DatabaseError):
38+
pass
39+
40+
class IntegrityError(DatabaseError):
41+
pass
42+
43+
class ProgrammingError(DatabaseError):
44+
pass
45+
46+
class InternalError(DatabaseError):
47+
pass
48+
49+
class NotSupportedError(DatabaseError):
50+
pass

extra/dbwire/clickhouse.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
5+
See the file 'LICENSE' for copying permission
6+
"""
7+
8+
"""
9+
Minimal pure-python ClickHouse client over its native HTTP interface (stdlib only, no clickhouse_connect).
10+
11+
ClickHouse exposes an HTTP endpoint that runs a query in the request body and streams the result back in a
12+
chosen format; we use TabSeparatedWithNames (first line = column names, then tab-separated rows with
13+
backslash escaping and \\N for NULL). Covers ClickHouse and its HTTP-compatible forks.
14+
"""
15+
16+
import base64
17+
18+
try:
19+
from urllib.request import Request, urlopen # Python 3
20+
from urllib.error import HTTPError, URLError
21+
except ImportError:
22+
from urllib2 import Request, urlopen, HTTPError, URLError # Python 2
23+
24+
from extra.dbwire import OperationalError
25+
from extra.dbwire import ProgrammingError
26+
27+
def _unescape(value):
28+
if value == "\\N":
29+
return None
30+
if "\\" not in value:
31+
return value
32+
out, it = [], iter(range(len(value)))
33+
i = 0
34+
n = len(value)
35+
while i < n:
36+
ch = value[i]
37+
if ch == "\\" and i + 1 < n:
38+
nxt = value[i + 1]
39+
out.append({"t": "\t", "n": "\n", "r": "\r", "0": "\0", "\\": "\\", "'": "'"}.get(nxt, nxt))
40+
i += 2
41+
else:
42+
out.append(ch)
43+
i += 1
44+
return "".join(out)
45+
46+
class Cursor(object):
47+
def __init__(self, connection):
48+
self.connection = connection
49+
self.description = None
50+
self.rowcount = -1
51+
self._rows = []
52+
self._pos = 0
53+
54+
def execute(self, query, params=None):
55+
if params is not None:
56+
raise ProgrammingError("parameter binding is not supported; pass a fully-formed query string")
57+
self.description, self.rowcount, self._rows, self._pos = None, -1, [], 0
58+
self.description, self._rows = self.connection._query(query)
59+
self.rowcount = len(self._rows)
60+
return self
61+
62+
def fetchall(self):
63+
retVal = self._rows[self._pos:]
64+
self._pos = len(self._rows)
65+
return retVal
66+
67+
def fetchone(self):
68+
if self._pos >= len(self._rows):
69+
return None
70+
retVal = self._rows[self._pos]
71+
self._pos += 1
72+
return retVal
73+
74+
def close(self):
75+
self._rows = []
76+
77+
class Connection(object):
78+
def __init__(self, host, port, user, password, database, timeout):
79+
self._url = "http://%s:%d/?database=%s&default_format=TabSeparatedWithNames" % (host, port, database or "default")
80+
self._headers = {}
81+
if user or password:
82+
token = base64.b64encode(("%s:%s" % (user or "", password or "")).encode("utf-8")).decode("ascii")
83+
self._headers["Authorization"] = "Basic %s" % token
84+
self._timeout = timeout
85+
86+
def cursor(self):
87+
return Cursor(self)
88+
89+
def commit(self):
90+
pass # ClickHouse statements are executed immediately (no client-side transaction)
91+
92+
def rollback(self):
93+
pass
94+
95+
def close(self):
96+
pass # HTTP is stateless
97+
98+
def _query(self, query):
99+
req = Request(self._url, data=query.encode("utf-8"), headers=self._headers)
100+
try:
101+
body = urlopen(req, timeout=self._timeout).read().decode("utf-8", "replace")
102+
except HTTPError as ex:
103+
raise ProgrammingError("(remote) %s" % ex.read().decode("utf-8", "replace").strip())
104+
except URLError as ex:
105+
raise OperationalError("(remote) %s" % ex)
106+
107+
if not body:
108+
return None, []
109+
lines = body.split("\n")
110+
if lines and lines[-1] == "":
111+
lines.pop()
112+
if not lines:
113+
return None, []
114+
description = [(name, None, None, None, None, None, None) for name in (_unescape(_) for _ in lines[0].split("\t"))]
115+
rows = [tuple(_unescape(_) for _ in line.split("\t")) for line in lines[1:]]
116+
return description, rows
117+
118+
def connect(host=None, port=8123, user=None, password=None, database=None, connect_timeout=None, **kwargs):
119+
connection = Connection(host or "localhost", int(port or 8123), user, password, database, connect_timeout)
120+
try:
121+
connection._query("SELECT 1") # verify connectivity/credentials up front
122+
except ProgrammingError:
123+
raise
124+
except Exception as ex:
125+
raise OperationalError("could not connect to '%s:%s' (%s)" % (host, port, ex))
126+
return connection

0 commit comments

Comments
 (0)