nakagami / pydrda

Pure python Db2 and Apache Derby database driver
MIT License
12 stars 7 forks source link

Prevent infinite loop when using invalid credentials #9

Closed Walz closed 2 years ago

Walz commented 2 years ago

Hello,

This PR is meant to be prevent a bug where we enter an infinite loop when trying to connect with invalid credentials.

Step to reproduce

Start a DB2 instance (I use docker):

$ docker run -itd --name mydb2 --privileged=true -p 50000:50000 -e LICENSE=accept -e DB2INST1_PASSWORD=password -e DBNAME=testdb -v /tmp/db2:/database ibmcom/db2

Connect with the right credentials and wrong credentials:

>>> import drda
>>> conn = drda.connect(host='localhost', database='testdb', user='db2inst1', password='password', port=50000)
>>> drda.connect(host='localhost', database='testdb', user='db2inst1', password='password', port=50000)
<drda.connection.Connection object at 0x1075d4160>
>>> drda.connect(host='localhost', database='testdb', user='db2inst1', password='wrong_password', port=50000)
# After a few second -> ctrl+c
^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/sguillaume/dev/oss/pydrda/drda/__init__.py", line 122, in connect
    return Connection(host, database, port, user, password, use_ssl, db_type, timeout)
  File "/Users/sguillaume/dev/oss/pydrda/drda/connection.py", line 229, in __init__
    self._set_valiables()
  File "/Users/sguillaume/dev/oss/pydrda/drda/connection.py", line 267, in _set_valiables
    self._parse_response()
  File "/Users/sguillaume/dev/oss/pydrda/drda/connection.py", line 50, in _parse_response
    dds_type, chained, number, code_point, obj = ddm.read_dds(self.sock)
  File "/Users/sguillaume/dev/oss/pydrda/drda/ddm.py", line 229, in read_dds
    b = _recv_from_sock(sock, 6)
  File "/Users/sguillaume/dev/oss/pydrda/drda/ddm.py", line 39, in _recv_from_sock
    recieved += bs
KeyboardInterrupt

Fix

I've added a maximum number of attempts when receiving empty packets from the server, and I've added ConnectionError when _recv_from_sock is not able to return the expected number of bytes.

>>> import drda
>>> drda.connect(host='localhost', database='testdb', user='db2inst1', password='password', port=50000)
<drda.connection.Connection object at 0x10ceaa340>
>>> drda.connect(host='localhost', database='testdb', user='db2inst1', password='wrong_password', port=50000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/sguillaume/dev/oss/pydrda/drda/__init__.py", line 122, in connect
    return Connection(host, database, port, user, password, use_ssl, db_type, timeout)
  File "/Users/sguillaume/dev/oss/pydrda/drda/connection.py", line 229, in __init__
    self._set_valiables()
  File "/Users/sguillaume/dev/oss/pydrda/drda/connection.py", line 267, in _set_valiables
    self._parse_response()
  File "/Users/sguillaume/dev/oss/pydrda/drda/connection.py", line 50, in _parse_response
    dds_type, chained, number, code_point, obj = ddm.read_dds(self.sock)
  File "/Users/sguillaume/dev/oss/pydrda/drda/ddm.py", line 236, in read_dds
    raise ConnectionError("invalid DDS packet from socket")
ConnectionError: invalid DDS packet from socket

I hope this is the right way to implement this.

nakagami commented 2 years ago

thanks, github actions environment is broken right now, but I've checked the status.

nakagami commented 2 years ago

It seems good!