Skip to content

Commit 3762d19

Browse files
author
Pan
committed
Added connector implementation and tests. Updated options.
1 parent e007fb0 commit 3762d19

File tree

7 files changed

+181
-43
lines changed

7 files changed

+181
-43
lines changed

ssh/connector.pxd

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This file is part of ssh-python.
2+
# Copyright (C) 2018 Panos Kittenis
3+
#
4+
# This library is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU Lesser General Public
6+
# License as published by the Free Software Foundation, version 2.1.
7+
#
8+
# This library is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+
# Lesser General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU Lesser General Public
14+
# License along with this library; if not, write to the Free Software
15+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-130
16+
17+
from session cimport Session
18+
19+
cimport c_ssh
20+
21+
22+
cdef class Flag:
23+
cdef c_ssh.ssh_connector_flags_e _flag
24+
25+
@staticmethod
26+
cdef Flag from_flag(c_ssh.ssh_connector_flags_e flag)
27+
28+
29+
cdef class Connector:
30+
cdef c_ssh.ssh_connector _connector
31+
cdef Session session
32+
33+
@staticmethod
34+
cdef Connector from_ptr(c_ssh.ssh_connector _connector, Session session)

ssh/connector.pyx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# This file is part of ssh-python.
2+
# Copyright (C) 2018 Panos Kittenis
3+
#
4+
# This library is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU Lesser General Public
6+
# License as published by the Free Software Foundation, version 2.1.
7+
#
8+
# This library is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+
# Lesser General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU Lesser General Public
14+
# License along with this library; if not, write to the Free Software
15+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-130
16+
17+
from cpython cimport PyObject_AsFileDescriptor
18+
19+
from channel cimport Channel
20+
from session cimport Session
21+
22+
cimport c_ssh
23+
24+
from utils cimport handle_ssh_error_codes
25+
26+
27+
cdef class Flag:
28+
29+
@staticmethod
30+
cdef Flag from_flag(c_ssh.ssh_connector_flags_e flag):
31+
cdef Flag _flag = Flag.__new__(Flag)
32+
_flag._flag = flag
33+
return _flag
34+
35+
def __eq__(self, Flag other):
36+
return self._flag == other._flag
37+
38+
def __str__(self):
39+
return str(self._flag)
40+
41+
def __repr__(self):
42+
return self.__str__()
43+
44+
45+
CONNECTOR_STDOUT = Flag.from_flag(
46+
c_ssh.ssh_connector_flags_e.SSH_CONNECTOR_STDOUT)
47+
CONNECTOR_STDERR = Flag.from_flag(
48+
c_ssh.ssh_connector_flags_e.SSH_CONNECTOR_STDERR)
49+
CONNECTOR_BOTH = Flag.from_flag(
50+
c_ssh.ssh_connector_flags_e.SSH_CONNECTOR_BOTH)
51+
52+
53+
cdef class Connector:
54+
55+
def __cinit__(self, Session session):
56+
self.session = session
57+
58+
def __dealloc__(self):
59+
if self._connector is not NULL:
60+
c_ssh.ssh_connector_free(self._connector)
61+
self._connector = NULL
62+
63+
@staticmethod
64+
cdef Connector from_ptr(c_ssh.ssh_connector _connector, Session session):
65+
cdef Connector connector = Connector.__new__(Connector, session)
66+
connector._connector = _connector
67+
return connector
68+
69+
def set_in_channel(self, Channel channel, Flag flag):
70+
cdef int rc
71+
with nogil:
72+
rc = c_ssh.ssh_connector_set_in_channel(
73+
self._connector, channel._channel, flag._flag)
74+
return handle_ssh_error_codes(rc, self.session._session)
75+
76+
def set_out_channel(self, Channel channel, Flag flag):
77+
cdef int rc
78+
with nogil:
79+
rc = c_ssh.ssh_connector_set_out_channel(
80+
self._connector, channel._channel, flag._flag)
81+
return handle_ssh_error_codes(rc, self.session._session)
82+
83+
def set_in_fd(self, socket):
84+
cdef c_ssh.socket_t _sock = PyObject_AsFileDescriptor(socket)
85+
with nogil:
86+
c_ssh.ssh_connector_set_in_fd(self._connector, _sock)
87+
88+
def set_out_fd(self, socket):
89+
cdef c_ssh.socket_t _sock = PyObject_AsFileDescriptor(socket)
90+
with nogil:
91+
c_ssh.ssh_connector_set_out_fd(self._connector, _sock)

ssh/options.pxd

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,44 +21,4 @@ cdef class Option:
2121
cdef ssh_options_e _option
2222

2323
@staticmethod
24-
cdef object from_option(ssh_options_e option)
25-
26-
27-
# cdef ssh_options_e HOST
28-
# cdef ssh_options_e USER
29-
# cdef ssh_options_e PORT
30-
# cdef ssh_options_e PORT_STR
31-
# cdef ssh_options_e FD
32-
# cdef ssh_options_e USER
33-
# cdef ssh_options_e SSH_DIR
34-
# cdef ssh_options_e IDENTITY
35-
# cdef ssh_options_e ADD_IDENTITY
36-
# cdef ssh_options_e KNOWNHOSTS
37-
# cdef ssh_options_e TIMEOUT
38-
# cdef ssh_options_e TIMEOUT_USEC
39-
# cdef ssh_options_e SSH1
40-
# cdef ssh_options_e SSH2
41-
# cdef ssh_options_e LOG_VERBOSITY
42-
# cdef ssh_options_e LOG_VERBOSITY_STR
43-
# cdef ssh_options_e CIPHERS_C_S
44-
# cdef ssh_options_e CIPHERS_S_C
45-
# cdef ssh_options_e COMPRESSION_C_S
46-
# cdef ssh_options_e COMPRESSION_S_C
47-
# cdef ssh_options_e PROXYCOMMAND
48-
# cdef ssh_options_e BINDADDR
49-
# cdef ssh_options_e STRICTHOSTKEYCHECK
50-
# cdef ssh_options_e COMPRESSION
51-
# cdef ssh_options_e COMPRESSION_LEVEL
52-
# cdef ssh_options_e KEY_EXCHANGE
53-
# cdef ssh_options_e HOSTKEYS
54-
# cdef ssh_options_e GSSAPI_SERVER_IDENTITY
55-
# cdef ssh_options_e GSSAPI_CLIENT_IDENTITY
56-
# cdef ssh_options_e GSSAPI_DELEGATE_CREDENTIALS
57-
# cdef ssh_options_e HMAC_C_S
58-
# cdef ssh_options_e HMAC_S_C
59-
# cdef ssh_options_e PASSWORD_AUTH
60-
# cdef ssh_options_e PUBKEY_AUTH
61-
# cdef ssh_options_e KBDINT_AUTH
62-
# cdef ssh_options_e GSSAPI_AUTH
63-
# cdef ssh_options_e GLOBAL_KNOWNHOSTS
64-
# cdef ssh_options_e NODELAY
24+
cdef Option from_option(ssh_options_e option)

ssh/options.pyx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ cdef class Option:
2121
"""Class for representing an SSH option."""
2222

2323
@staticmethod
24-
cdef object from_option(ssh_options_e option):
24+
cdef Option from_option(ssh_options_e option):
2525
cdef Option _option = Option.__new__(Option)
2626
_option._option = option
2727
return _option
2828

29+
def __eq__(self, Option other):
30+
return self._option == other._option
31+
2932
@property
3033
def value(self):
3134
return self._option

ssh/session.pyx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ from libc.stdlib cimport malloc, free
1919
from libc.string cimport const_char
2020

2121
from channel cimport Channel
22+
from connector cimport Connector
2223
from utils cimport to_bytes, to_str, handle_ssh_error_codes, \
2324
handle_auth_error_codes
2425
from options cimport Option
@@ -91,16 +92,23 @@ cdef class Session:
9192
with nogil:
9293
c_ssh.ssh_disconnect(self._session)
9394

94-
def new_connector(self):
95+
def connector_new(self):
9596
cdef c_ssh.ssh_connector _connector
9697
with nogil:
9798
_connector = c_ssh.ssh_connector_new(self._session)
99+
if _connector is NULL:
100+
return
101+
return Connector.from_ptr(_connector, self)
98102

99103
def accept_forward(self, int timeout, int dest_port):
100104
cdef c_ssh.ssh_channel _channel
101105
with nogil:
106+
_check_connected(self._session)
102107
_channel = c_ssh.ssh_channel_accept_forward(
103108
self._session, timeout, &dest_port)
109+
if _channel is NULL:
110+
return
111+
return Channel.from_ptr(_channel, self)
104112

105113
def cancel_forward(self, address, int port):
106114
cdef bytes b_address = to_bytes(address)

tests/test_connector.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This file is part of ssh-python.
2+
# Copyright (C) 2018 Panos Kittenis
3+
#
4+
# This library is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU Lesser General Public
6+
# License as published by the Free Software Foundation, version 2.1.
7+
#
8+
# This library is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+
# Lesser General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU Lesser General Public
14+
# License along with this library; if not, write to the Free Software
15+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-130
16+
17+
import unittest
18+
import socket
19+
20+
from .base_test import SSHTestCase
21+
22+
from ssh.connector import Connector, CONNECTOR_STDOUT, CONNECTOR_STDERR, \
23+
CONNECTOR_BOTH
24+
25+
26+
class ConnectorTest(SSHTestCase):
27+
28+
def test_connector(self):
29+
self._auth()
30+
connector = self.session.connector_new()
31+
self.assertIsInstance(connector, Connector)
32+
chan = self.session.channel_new()
33+
self.assertEqual(
34+
connector.set_in_channel(chan, CONNECTOR_STDOUT), 0)
35+
self.assertEqual(
36+
connector.set_out_channel(chan, CONNECTOR_STDERR), 0)
37+
self.assertEqual(
38+
connector.set_in_channel(chan, CONNECTOR_BOTH), 0)
39+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
40+
self.assertIsNone(connector.set_in_fd(sock))
41+
self.assertIsNone(connector.set_out_fd(sock))

tests/test_session.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def test_should_not_segfault(self):
4949
self.assertIsNone(session.get_hmac_in())
5050
self.assertIsNone(session.get_hmac_out())
5151
self.assertIsNotNone(session.get_error_code())
52+
session.connector_new()
5253

5354
def test_socket_connect(self):
5455
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

0 commit comments

Comments
 (0)