Skip to content

Commit

Permalink
Merge pull request #2 from jborean93/socket-async
Browse files Browse the repository at this point in the history
Fix move from Socket threading to non-blocking calls
  • Loading branch information
jborean93 authored Feb 22, 2018
2 parents a09cb49 + 30ac4ea commit 7c3502a
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 23 deletions.
7 changes: 1 addition & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,7 @@ env:
- SMB_PORT: 445

install:
- |
if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then
export SMB_SKIP="True";
else
docker run -d -p $SMB_PORT:445 -v $(pwd)/build-scripts:/app -w /app -e SMB_USER=$SMB_USER -e SMB_PASSWORD=$SMB_PASSWORD -e SMB_SHARE=$SMB_SHARE centos:7 /bin/bash /app/setup_samba.sh;
fi
- docker run -d -p $SMB_PORT:445 -v $(pwd)/build-scripts:/app -w /app -e SMB_USER=$SMB_USER -e SMB_PASSWORD=$SMB_PASSWORD -e SMB_SHARE=$SMB_SHARE centos:7 /bin/bash /app/setup_samba.sh;
- pip install -U pip setuptools
- pip install .
- pip install -r requirements-test.txt
Expand Down
2 changes: 1 addition & 1 deletion smbprotocol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ def emit(self, record):
logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())

__version__ = '0.0.1.dev1'
__version__ = '0.0.1.dev2'
4 changes: 4 additions & 0 deletions smbprotocol/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def get_supported_ciphers():
pass
try:
aead.AESCCM(b"\x00" * 16)
supported_ciphers.append(Ciphers.AES_128_CCM)
except UnsupportedAlgorithm: # pragma: no cover
pass
return supported_ciphers
Expand All @@ -185,13 +186,16 @@ class NtStatus(object):
"""
STATUS_SUCCESS = 0x00000000
STATUS_PENDING = 0x00000103
STATUS_BUFFER_OVERFLOW = 0x80000005
STATUS_EA_LIST_INCONSISTENT = 0x80000014
STATUS_STOPPED_ON_SYMLINK = 0x8000002D
STATUS_INVALID_PARAMETER = 0xC000000D
STATUS_NO_SUCH_FILE = 0xC000000F
STATUS_END_OF_FILE = 0xC0000011
STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016
STATUS_ACCESS_DENIED = 0xC0000022
STATUS_BUFFER_TOO_SMALL = 0xC0000023
STATUS_OBJECT_NAME_INVALID = 0xC0000033
STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
STATUS_OBJECT_NAME_COLLISION = 0xC0000035
STATUS_OBJECT_PATH_INVALID = 0xC0000039
Expand Down
17 changes: 14 additions & 3 deletions smbprotocol/open.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def __init__(self):
)),
('name_length', IntField(
size=2,
default=lambda s: len(s['buffer_path'])
default=lambda s: self._name_length(s)
)),
('create_contexts_offset', IntField(
size=4,
Expand All @@ -340,7 +340,7 @@ def __init__(self):
# Technically these are all under buffer but we split it to make
# things easier
('buffer_path', BytesField(
size=lambda s: s['name_length'].get_value(),
size=lambda s: self._buffer_path_size(s),
)),
('padding', BytesField(
size=lambda s: self._padding_size(s),
Expand All @@ -356,13 +356,21 @@ def __init__(self):
])
super(SMB2CreateRequest, self).__init__()

def _name_length(self, structure):
buffer_path = structure['buffer_path'].get_value()
return len(buffer_path) if buffer_path != b"\x00\x00" else 0

def _create_contexts_offset(self, structure):
if len(structure['buffer_contexts']) == 0:
return 0
else:
return structure['name_offset'].get_value() + \
len(structure['padding']) + len(structure['buffer_path'])

def _buffer_path_size(self, structure):
name_length = structure['name_length'].get_value()
return name_length if name_length != 0 else 2

def _padding_size(self, structure):
# no padding is needed if there are no contexts
if structure['create_contexts_length'].get_value() == 0:
Expand Down Expand Up @@ -962,7 +970,10 @@ def open(self, impersonation_level, desired_access, file_attributes,
create['share_access'] = share_access
create['create_disposition'] = create_disposition
create['create_options'] = create_options
create['buffer_path'] = self.file_name.encode('utf-16-le')
if self.file_name == "":
create['buffer_path'] = b"\x00\x00"
else:
create['buffer_path'] = self.file_name.encode('utf-16-le')
if create_contexts:
create['buffer_contexts'] = smbprotocol.create_contexts.\
SMB2CreateContextRequest.pack_multiple(create_contexts)
Expand Down
12 changes: 6 additions & 6 deletions smbprotocol/transport.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import errno
import logging
import socket
import struct
Expand Down Expand Up @@ -76,8 +77,7 @@ def send(self, request):
try:
sent = self._sock.send(data)
except socket.error as err:
# errno: 35 == Resource temporarily unavailable, try again
if err.errno != 35:
if err.errno not in [errno.EAGAIN, errno.EWOULDBLOCK]:
raise err
data = data[sent:]

Expand All @@ -101,11 +101,11 @@ def _recv(self, buffer):
data = self._sock.recv(buffer - len(bytes))
bytes += data
except socket.error as err:
# errno: 35 == Resource temporarily unavailable
if err.errno != 35:
if err.errno not in [errno.EAGAIN, errno.EWOULDBLOCK]:
raise err
# we didn't get any bytes so return None
# this was the first request so return None
elif bytes == b"":
return None
# there is still data remaining so continue trying ot read
# we started getting data and there is still some remaining
# so try again
return bytes
57 changes: 52 additions & 5 deletions tests/test_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,9 @@ def test_dialect_2_1_0(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_dialect_3_0_0(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_0)
Expand Down Expand Up @@ -1084,6 +1087,9 @@ def test_dialect_3_0_0(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_dialect_3_0_2(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand Down Expand Up @@ -1170,7 +1176,33 @@ def test_dialect_3_1_1(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.name == "nt",
reason="Sharing violation occurs when running with "
"Windows")
def test_open_root_directory(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_1_1)
session = Session(connection, smb_real[0], smb_real[1])
tree = TreeConnect(session, smb_real[5])
dir_open = Open(tree, "")
try:
session.connect()
tree.connect()

dir_open.open(ImpersonationLevel.Impersonation,
DirectoryAccessMask.MAXIMUM_ALLOWED,
FileAttributes.FILE_ATTRIBUTE_DIRECTORY,
0,
CreateDisposition.FILE_OPEN_IF,
CreateOptions.FILE_DIRECTORY_FILE)
dir_open.close(get_attributes=False)
finally:
connection.disconnect(True)

# test more file operations here
@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_create_directory(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_0)
Expand Down Expand Up @@ -1215,6 +1247,9 @@ def test_create_directory(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_create_file_create_contexts(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_0)
Expand Down Expand Up @@ -1267,7 +1302,7 @@ def test_create_file_create_contexts(self, smb_real):

def test_create_read_write_from_file(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
connection.connect()
session = Session(connection, smb_real[0], smb_real[1])
tree = TreeConnect(session, smb_real[4])
open = Open(tree, "file-read-write.txt")
Expand All @@ -1289,6 +1324,9 @@ def test_create_read_write_from_file(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_flush_file(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand Down Expand Up @@ -1363,6 +1401,9 @@ def test_close_file_get_attributes(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_read_file_unbuffered(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand All @@ -1386,6 +1427,9 @@ def test_read_file_unbuffered(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_read_file_unbuffered_unsupported(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_0)
Expand Down Expand Up @@ -1421,7 +1465,7 @@ def test_read_file_unbuffered_unsupported(self, smb_real):
reason="write-through writes don't work on windows?")
def test_write_file_write_through(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
connection.connect()
session = Session(connection, smb_real[0], smb_real[1])
tree = TreeConnect(session, smb_real[4])
open = Open(tree, "file-read-write.txt")
Expand Down Expand Up @@ -1479,7 +1523,7 @@ def test_write_file_write_through_unsupported(self, smb_real):
reason="unbufferred writes don't work on windows?")
def test_write_file_unbuffered(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
connection.connect()
session = Session(connection, smb_real[0], smb_real[1])
tree = TreeConnect(session, smb_real[4])
open = Open(tree, "file-read-write.txt")
Expand Down Expand Up @@ -1535,7 +1579,7 @@ def test_write_file_unbuffered_unsupported(self, smb_real):

def test_query_directory(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
connection.connect()
session = Session(connection, smb_real[0], smb_real[1])
tree = TreeConnect(session, smb_real[4])
open = Open(tree, "directory")
Expand Down Expand Up @@ -1599,7 +1643,7 @@ def test_query_directory(self, smb_real):
reason="flush in compound does't work on windows")
def test_compounding_open_requests(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
connection.connect()
session = Session(connection, smb_real[0], smb_real[1])
tree = TreeConnect(session, smb_real[4])
open = Open(tree, "directory")
Expand Down Expand Up @@ -1787,6 +1831,9 @@ def test_compounding_open_requests_unencrypted(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_close_file_already_closed(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_session.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import uuid

import pytest
Expand Down Expand Up @@ -145,6 +146,9 @@ def test_dialect_2_1_0(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_dialect_3_0_0(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_0)
Expand All @@ -168,6 +172,9 @@ def test_dialect_3_0_0(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_dialect_3_0_2(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand Down
13 changes: 13 additions & 0 deletions tests/test_tree.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import uuid

import pytest
Expand Down Expand Up @@ -139,6 +140,9 @@ def test_dialect_2_1_0(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_dialect_3_0_0(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_0)
Expand All @@ -155,6 +159,9 @@ def test_dialect_3_0_0(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_dialect_3_0_2(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand Down Expand Up @@ -218,6 +225,9 @@ def test_dialect_3_encrypted_share(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_secure_negotiation_verification_failed(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand All @@ -233,6 +243,9 @@ def test_secure_negotiation_verification_failed(self, smb_real):
finally:
connection.disconnect(True)

@pytest.mark.skipif(os.environ.get("TRAVIS_PYTHON_VERSION", "") == '2.6',
reason="Travis-CI Python 2.6 does not support AES CCM "
"required for Dialect 3.0.0 and 3.0.2 enc")
def test_secure_ignore_negotiation_verification_failed(self, smb_real):
connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
connection.connect(Dialects.SMB_3_0_2)
Expand Down
3 changes: 1 addition & 2 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ def smb_real():
server = os.environ.get('SMB_SERVER', None)
port = os.environ.get('SMB_PORT', None)
share = os.environ.get('SMB_SHARE', None)
skip = os.environ.get('SMB_SKIP', "False") == "True"

if username and password and server and port and share and not skip:
if username and password and server and port and share:
share = r"\\%s\%s" % (server, share)
encrypted_share = "%s-encrypted" % share
return username, password, server, int(port), share, encrypted_share
Expand Down

0 comments on commit 7c3502a

Please sign in to comment.