@@ -66,39 +66,75 @@ def _feed_xml(self, data):
6666 "read {0}" .format (data ), e )
6767
6868
69- class GvmConnection :
69+ class GvmConnection ( XmlReader ) :
7070 """
7171 Base class for establishing a connection to a remote server daemon.
72+
73+ Arguments:
74+ timeout (int, optional): Timeout in seconds for the connection.
7275 """
7376
7477 def __init__ (self , timeout = DEFAULT_TIMEOUT ):
75- """
76- Arguments:
77- socket -- A socket
78- """
7978 self ._socket = None
8079 self ._timeout = timeout
8180
81+ def _read (self ):
82+ return self ._socket .recv (BUF_SIZE )
83+
8284 def connect (self ):
83- """Establish a connection to gvmd
85+ """Establish a connection to a remote server
8486 """
8587 raise NotImplementedError
8688
8789 def send (self , data ):
88- """Send data to gvmd
90+ """Send data to the connected remote server
91+
92+ Arguments:
93+ data (str or bytes): Data to be send to the server. Either utf-8
94+ encoded string or bytes.
8995 """
9096 if isinstance (data , str ):
91- self ._socket .send (data .encode ())
97+ self ._socket .sendall (data .encode ())
9298 else :
93- self ._socket .send (data )
99+ self ._socket .sendall (data )
94100
95101 def read (self ):
96- """Read data from gvmd
102+ """Read data from the remote server
103+
104+ Returns:
105+ str: data as utf-8 encoded string
97106 """
98- raise NotImplementedError
107+ response = ''
108+
109+ self ._start_xml ()
110+
111+ now = time .time ()
112+
113+ break_timeout = now + self ._timeout
114+
115+ while True :
116+ data = self ._read ()
117+
118+ if not data :
119+ # Connection was closed by server
120+ raise GvmError ('Remote closed the connection' )
121+
122+ self ._feed_xml (data )
123+
124+ response += data .decode ('utf-8' , errors = 'ignore' )
125+
126+ if self ._is_end_xml ():
127+ break
128+
129+ now = time .time ()
130+
131+ if now > break_timeout :
132+ raise GvmError ('Timeout while reading the response' )
133+
134+ return response
99135
100136 def disconnect (self ):
101- """Close the connection to gvmd
137+ """Disconnect and close the connection to the remote server
102138 """
103139 try :
104140 if self ._socket is not None :
@@ -107,9 +143,17 @@ def disconnect(self):
107143 logger .debug ('Connection closing error: %s' , e )
108144
109145
110- class SSHConnection (GvmConnection , XmlReader ):
146+ class SSHConnection (GvmConnection ):
111147 """
112148 SSH Class to connect, read and write from GVM via SSH
149+
150+ Arguments:
151+ timeout (int, optional): Timeout in seconds for the connection.
152+ hostname (str, optional): DNS name or IP address of the remote server.
153+ Default is 127.0.0.1.
154+ port (int, optional): Port of the remote SSH server.
155+ username (str, optional): Username to use for SSH login.
156+ password (str, optional): Passwort to use for SSH login.
113157 """
114158
115159 def __init__ (self , timeout = DEFAULT_TIMEOUT , hostname = '127.0.0.1' , port = 22 ,
@@ -143,6 +187,9 @@ def _send_in_chunks(self, data, chunk_size):
143187 return sent_bytes
144188
145189 def connect (self ):
190+ """
191+ Connect to the SSH server and authenticate to it
192+ """
146193 self ._socket = paramiko .SSHClient ()
147194 self ._socket .set_missing_host_key_policy (paramiko .AutoAddPolicy ())
148195
@@ -160,35 +207,16 @@ def connect(self):
160207
161208 except (paramiko .BadHostKeyException ,
162209 paramiko .AuthenticationException ,
163- paramiko .SSHException , OSError ) as e :
164- logger .debug ('SSH Connection failed: %s' , e )
165- raise
166-
167- def read (self ):
168- response = ''
169-
170- self ._start_xml ()
171-
172- while True :
173- data = self ._stdout .channel .recv (BUF_SIZE )
174- # Connection was closed by server
175- if not data :
176- break
210+ paramiko .SSHException ,
211+ ) as e :
212+ raise GvmError ('SSH Connection failed' , e )
177213
178- self ._feed_xml (data )
179-
180- response += data .decode ('utf-8' , errors = 'ignore' )
181-
182- if self ._is_end_xml ():
183- break
184-
185- return response
214+ def _read (self ):
215+ return self ._stdout .channel .recv (BUF_SIZE )
186216
187217 def send (self , data ):
188- logger .debug ('SSH:send(): %s' , data )
189218 if len (data ) > MAX_SSH_DATA_LENGTH :
190- sent_bytes = self ._send_in_chunks (data , MAX_SSH_DATA_LENGTH )
191- logger .debug ("SSH: %s bytes sent." , sent_bytes )
219+ self ._send_in_chunks (data , MAX_SSH_DATA_LENGTH )
192220 else :
193221 self ._stdin .channel .send (data )
194222
@@ -197,10 +225,28 @@ class TLSConnection(GvmConnection):
197225 """
198226 TLS class to connect, read and write from a remote GVM daemon via TLS
199227 secured socket.
228+
229+ Arguments:
230+ timeout (int, optional): Timeout in seconds for the connection.
231+ hostname (str, optional): DNS name or IP address of the remote TLS
232+ server.
233+ port (str, optional): Port for the TLS connection. Default is 9390.
234+ certfile (str, optional): Path to PEM encoded certificate file. See
235+ `python certificates`_ for details.
236+ cafile (str, optional): Path to PEM encoded CA file. See
237+ `python certificates`_ for details.
238+ keyfile (str, optional): Path to PEM encoded private key. See
239+ `python certificates`_ for details.
240+ password (str, optional): Password for the private key. If the password
241+ argument is not specified and a password is required it will be
242+ interactively prompt the user for a password.
243+
244+ .. _python certificates:
245+ https://docs.python.org/3.5/library/ssl.html#certificates
200246 """
201247
202248 def __init__ (self , certfile = None , cafile = None , keyfile = None ,
203- hostname = '127.0.0.1' , port = DEFAULT_GVM_PORT ,
249+ hostname = '127.0.0.1' , port = DEFAULT_GVM_PORT , password = None ,
204250 timeout = DEFAULT_TIMEOUT ):
205251 super ().__init__ (timeout = timeout )
206252
@@ -209,45 +255,41 @@ def __init__(self, certfile=None, cafile=None, keyfile=None,
209255 self .certfile = certfile
210256 self .cafile = cafile
211257 self .keyfile = keyfile
258+ self .password = password
212259
213260 def _new_socket (self ):
261+ transport_socket = socketlib .socket (socketlib .AF_INET ,
262+ socketlib .SOCK_STREAM )
263+
214264 if self .certfile and self .cafile and self .keyfile :
215265 context = ssl .create_default_context (ssl .Purpose .SERVER_AUTH ,
216266 cafile = self .cafile )
217267 context .check_hostname = False
218268 context .load_cert_chain (
219- certfile = self .certfile , keyfile = self .keyfile )
220- new_socket = socketlib .socket (socketlib .AF_INET ,
221- socketlib .SOCK_STREAM )
222- sock = context .wrap_socket (new_socket , server_side = False )
269+ certfile = self .certfile , keyfile = self .keyfile ,
270+ password = self .password )
271+ sock = context .wrap_socket (transport_socket , server_side = False )
223272 else :
224273 context = ssl .SSLContext (ssl .PROTOCOL_TLSv1_2 )
225- sock = context .wrap_socket (socketlib .socket (socketlib .AF_INET ))
226- return sock
274+ sock = context .wrap_socket (transport_socket )
227275
276+ sock .settimeout (self ._timeout )
277+
278+ return sock
228279
229280 def connect (self ):
230281 self ._socket = self ._new_socket ()
231- self ._socket .settimeout (self ._timeout )
232282 self ._socket .connect ((self .hostname , int (self .port )))
233283
234- def read (self ):
235- response = ''
236284
237- while True :
238- data = self ._socket .read (BUF_SIZE )
239-
240- response += data .decode ('utf-8' , errors = 'ignore' )
241- if len (data ) < BUF_SIZE :
242- break
243-
244- return response
245-
246-
247- class UnixSocketConnection (GvmConnection , XmlReader ):
285+ class UnixSocketConnection (GvmConnection ):
248286 """
249287 UNIX-Socket class to connect, read, write from a GVM server daemon via
250288 direct communicating UNIX-Socket
289+
290+ Arguments:
291+ path (str, optional): Path to the socket.
292+ timeout (int, optional): Timeout in seconds for the connection.
251293 """
252294
253295 def __init__ (self , path = DEFAULT_UNIX_SOCKET_PATH , timeout = DEFAULT_TIMEOUT ,
@@ -265,34 +307,33 @@ def connect(self):
265307 self ._socket .settimeout (self ._timeout )
266308 self ._socket .connect (self .path )
267309
310+
311+ class DebugConnection :
312+
313+ def __init__ (self , connection ):
314+ self ._connection = connection
315+
268316 def read (self ):
269- """Read from the UNIX socket
270- """
271- response = ''
317+ data = self ._connection .read ()
272318
273- break_timeout = time .time () + self .read_timeout
274- old_timeout = self ._socket .gettimeout ()
275- self ._socket .settimeout (5 ) # in seconds
319+ logger .debug ('Read %s characters. Data %s' , len (data ), data )
276320
277- self ._start_xml ()
321+ self .last_read_data = data
322+ return data
278323
279- while time . time () < break_timeout :
280- data = b''
324+ def send ( self , data ) :
325+ self . last_send_data = data
281326
282- try :
283- data = self ._socket .recv (BUF_SIZE )
284- except (socketlib .timeout ) as exception :
285- logger .debug ('Warning: No data received '
286- 'from server: %s' , exception )
287- continue
327+ logger .debug ('Sending %s characters. Data %s' , len (data ), data )
288328
289- self ._feed_xml (data )
329+ return self ._connection . send (data )
290330
291- response += data .decode ('utf-8' , errors = 'ignore' )
331+ def connect (self ):
332+ logger .debug ('Connecting' )
292333
293- if len (data ) < BUF_SIZE :
294- if self ._is_end_xml ():
295- break
334+ return self ._connection .connect ()
296335
297- self ._socket .settimeout (old_timeout )
298- return response
336+ def disconnect (self ):
337+ logger .debug ('Disconnecting' )
338+
339+ return self ._connection .disconnect ()
0 commit comments