Making connect()'s password prompt opt-in
[stem.git] / stem / socket.py
1 # Copyright 2011-2014, Damian Johnson and The Tor Project
2 # See LICENSE for licensing information
3
4 """
5 Supports communication with sockets speaking the Tor control protocol. This
6 allows us to send messages as basic strings, and receive responses as
7 :class:`~stem.response.ControlMessage` instances.
8
9 **This module only consists of low level components, and is not intended for
10 users.** See our `tutorials <tutorials.html>`_ and `Control Module
11 <api/control.html>`_ if you're new to Stem and looking to get started.
12
13 With that aside, these can still be used for raw socket communication with
14 Tor...
15
16 ::
17
18 import stem
19 import stem.connection
20 import stem.socket
21
22 if __name__ == '__main__':
23 try:
24 control_socket = stem.socket.ControlPort(port = 9051)
25 stem.connection.authenticate(control_socket)
26 except stem.SocketError as exc:
27 print "Unable to connect to tor on port 9051: %s" % exc
28 sys.exit(1)
29 except stem.connection.AuthenticationFailure as exc:
30 print "Unable to authenticate: %s" % exc
31 sys.exit(1)
32
33 print "Issuing 'GETINFO version' query...\\n"
34 control_socket.send('GETINFO version')
35 print control_socket.recv()
36
37 ::
38
39 % python example.py
40 Issuing 'GETINFO version' query...
41
42 version=0.2.4.10-alpha-dev (git-8be6058d8f31e578)
43 OK
44
45 **Module Overview:**
46
47 ::
48
49 ControlSocket - Socket wrapper that speaks the tor control protocol.
50 |- ControlPort - Control connection via a port.
51 | |- get_address - provides the ip address of our socket
52 | +- get_port - provides the port of our socket
53 |
54 |- ControlSocketFile - Control connection via a local file socket.
55 | +- get_socket_path - provides the path of the socket we connect to
56 |
57 |- send - sends a message to the socket
58 |- recv - receives a ControlMessage from the socket
59 |- is_alive - reports if the socket is known to be closed
60 |- is_localhost - returns if the socket is for the local system or not
61 |- connect - connects a new socket
62 |- close - shuts down the socket
63 +- __enter__ / __exit__ - manages socket connection
64
65 send_message - Writes a message to a control socket.
66 recv_message - Reads a ControlMessage from a control socket.
67 send_formatting - Performs the formatting expected from sent messages.
68 """
69
70 from __future__ import absolute_import
71
72 import re
73 import socket
74 import threading
75
76 import stem.prereq
77 import stem.response
78 import stem.util.str_tools
79
80 from stem.util import log
81
82
83 class ControlSocket(object):
84 """
85 Wrapper for a socket connection that speaks the Tor control protocol. To the
86 better part this transparently handles the formatting for sending and
87 receiving complete messages. All methods are thread safe.
88
89 Callers should not instantiate this class directly, but rather use subclasses
90 which are expected to implement the **_make_socket()** method.
91 """
92
93 def __init__(self):
94 self._socket, self._socket_file = None, None
95 self._is_alive = False
96
97 # Tracks sending and receiving separately. This should be safe, and doing
98 # so prevents deadlock where we block writes because we're waiting to read
99 # a message that isn't coming.
100
101 self._send_lock = threading.RLock()
102 self._recv_lock = threading.RLock()
103
104 def send(self, message, raw = False):
105 """
106 Formats and sends a message to the control socket. For more information see
107 the :func:`~stem.socket.send_message` function.
108
109 :param str message: message to be formatted and sent to the socket
110 :param bool raw: leaves the message formatting untouched, passing it to the socket as-is
111
112 :raises:
113 * :class:`stem.SocketError` if a problem arises in using the socket
114 * :class:`stem.SocketClosed` if the socket is known to be shut down
115 """
116
117 with self._send_lock:
118 try:
119 if not self.is_alive():
120 raise stem.SocketClosed()
121
122 send_message(self._socket_file, message, raw)
123 except stem.SocketClosed as exc:
124 # if send_message raises a SocketClosed then we should properly shut
125 # everything down
126
127 if self.is_alive():
128 self.close()
129
130 raise exc
131
132 def recv(self):
133 """
134 Receives a message from the control socket, blocking until we've received
135 one. For more information see the :func:`~stem.socket.recv_message` function.
136
137 :returns: :class:`~stem.response.ControlMessage` for the message received
138
139 :raises:
140 * :class:`stem.ProtocolError` the content from the socket is malformed
141 * :class:`stem.SocketClosed` if the socket closes before we receive a complete message
142 """
143
144 with self._recv_lock:
145 try:
146 # makes a temporary reference to the _socket_file because connect()
147 # and close() may set or unset it
148
149 socket_file = self._socket_file
150
151 if not socket_file:
152 raise stem.SocketClosed()
153
154 return recv_message(socket_file)
155 except stem.SocketClosed as exc:
156 # If recv_message raises a SocketClosed then we should properly shut
157 # everything down. However, there's a couple cases where this will
158 # cause deadlock...
159 #
160 # * this socketClosed was *caused by* a close() call, which is joining
161 # on our thread
162 #
163 # * a send() call that's currently in flight is about to call close(),
164 # also attempting to join on us
165 #
166 # To resolve this we make a non-blocking call to acquire the send lock.
167 # If we get it then great, we can close safely. If not then one of the
168 # above are in progress and we leave the close to them.
169
170 if self.is_alive():
171 if self._send_lock.acquire(False):
172 self.close()
173 self._send_lock.release()
174
175 raise exc
176
177 def is_alive(self):
178 """
179 Checks if the socket is known to be closed. We won't be aware if it is
180 until we either use it or have explicitily shut it down.
181
182 In practice a socket derived from a port knows about its disconnection
183 after a failed :func:`~stem.socket.ControlSocket.recv` call. Socket file
184 derived connections know after either a
185 :func:`~stem.socket.ControlSocket.send` or
186 :func:`~stem.socket.ControlSocket.recv`.
187
188 This means that to have reliable detection for when we're disconnected
189 you need to continually pull from the socket (which is part of what the
190 :class:`~stem.control.BaseController` does).
191
192 :returns: **bool** that's **True** if our socket is connected and **False** otherwise
193 """
194
195 return self._is_alive
196
197 def is_localhost(self):
198 """
199 Returns if the connection is for the local system or not.
200
201 :returns: **bool** that's **True** if the connection is for the local host and **False** otherwise
202 """
203
204 return False
205
206 def connect(self):
207 """
208 Connects to a new socket, closing our previous one if we're already
209 attached.
210
211 :raises: :class:`stem.SocketError` if unable to make a socket
212 """
213
214 with self._send_lock:
215 # Closes the socket if we're currently attached to one. Once we're no
216 # longer alive it'll be safe to acquire the recv lock because recv()
217 # calls no longer block (raising SocketClosed instead).
218
219 if self.is_alive():
220 self.close()
221
222 with self._recv_lock:
223 self._socket = self._make_socket()
224 self._socket_file = self._socket.makefile(mode = "rwb")
225 self._is_alive = True
226
227 # It's possible for this to have a transient failure...
228 # SocketError: [Errno 4] Interrupted system call
229 #
230 # It's safe to retry, so give it another try if it fails.
231
232 try:
233 self._connect()
234 except stem.SocketError:
235 self._connect() # single retry
236
237 def close(self):
238 """
239 Shuts down the socket. If it's already closed then this is a no-op.
240 """
241
242 with self._send_lock:
243 # Function is idempotent with one exception: we notify _close() if this
244 # is causing our is_alive() state to change.
245
246 is_change = self.is_alive()
247
248 if self._socket:
249 # if we haven't yet established a connection then this raises an error
250 # socket.error: [Errno 107] Transport endpoint is not connected
251
252 try:
253 self._socket.shutdown(socket.SHUT_RDWR)
254 except socket.error:
255 pass
256
257 # Suppressing unexpected exceptions from close. For instance, if the
258 # socket's file has already been closed then with python 2.7 that raises
259 # with...
260 # error: [Errno 32] Broken pipe
261
262 try:
263 self._socket.close()
264 except:
265 pass
266
267 if self._socket_file:
268 try:
269 self._socket_file.close()
270 except:
271 pass
272
273 self._socket = None
274 self._socket_file = None
275 self._is_alive = False
276
277 if is_change:
278 self._close()
279
280 def _get_send_lock(self):
281 """
282 The send lock is useful to classes that interact with us at a deep level
283 because it's used to lock :func:`stem.socket.ControlSocket.connect` /
284 :func:`stem.socket.ControlSocket.close`, and by extension our
285 :func:`stem.socket.ControlSocket.is_alive` state changes.
286
287 :returns: **threading.RLock** that governs sending messages to our socket
288 and state changes
289 """
290
291 return self._send_lock
292
293 def __enter__(self):
294 return self
295
296 def __exit__(self, exit_type, value, traceback):
297 self.close()
298
299 def _connect(self):
300 """
301 Connection callback that can be overwritten by subclasses and wrappers.
302 """
303
304 pass
305
306 def _close(self):
307 """
308 Disconnection callback that can be overwritten by subclasses and wrappers.
309 """
310
311 pass
312
313 def _make_socket(self):
314 """
315 Constructs and connects new socket. This is implemented by subclasses.
316
317 :returns: **socket.socket** for our configuration
318
319 :raises:
320 * :class:`stem.SocketError` if unable to make a socket
321 * **NotImplementedError** if not implemented by a subclass
322 """
323
324 raise NotImplementedError("Unsupported Operation: this should be implemented by the ControlSocket subclass")
325
326
327 class ControlPort(ControlSocket):
328 """
329 Control connection to tor. For more information see tor's ControlPort torrc
330 option.
331 """
332
333 def __init__(self, address = "127.0.0.1", port = 9051, connect = True):
334 """
335 ControlPort constructor.
336
337 :param str address: ip address of the controller
338 :param int port: port number of the controller
339 :param bool connect: connects to the socket if True, leaves it unconnected otherwise
340
341 :raises: :class:`stem.SocketError` if connect is **True** and we're
342 unable to establish a connection
343 """
344
345 super(ControlPort, self).__init__()
346 self._control_addr = address
347 self._control_port = port
348
349 if connect:
350 self.connect()
351
352 def get_address(self):
353 """
354 Provides the ip address our socket connects to.
355
356 :returns: str with the ip address of our socket
357 """
358
359 return self._control_addr
360
361 def get_port(self):
362 """
363 Provides the port our socket connects to.
364
365 :returns: int with the port of our socket
366 """
367
368 return self._control_port
369
370 def is_localhost(self):
371 return self._control_addr == "127.0.0.1"
372
373 def _make_socket(self):
374 try:
375 control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
376 control_socket.connect((self._control_addr, self._control_port))
377 return control_socket
378 except socket.error as exc:
379 raise stem.SocketError(exc)
380
381
382 class ControlSocketFile(ControlSocket):
383 """
384 Control connection to tor. For more information see tor's ControlSocket torrc
385 option.
386 """
387
388 def __init__(self, path = "/var/run/tor/control", connect = True):
389 """
390 ControlSocketFile constructor.
391
392 :param str socket_path: path where the control socket is located
393 :param bool connect: connects to the socket if True, leaves it unconnected otherwise
394
395 :raises: :class:`stem.SocketError` if connect is **True** and we're
396 unable to establish a connection
397 """
398
399 super(ControlSocketFile, self).__init__()
400 self._socket_path = path
401
402 if connect:
403 self.connect()
404
405 def get_socket_path(self):
406 """
407 Provides the path our socket connects to.
408
409 :returns: str with the path for our control socket
410 """
411
412 return self._socket_path
413
414 def is_localhost(self):
415 return True
416
417 def _make_socket(self):
418 try:
419 control_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
420 control_socket.connect(self._socket_path)
421 return control_socket
422 except socket.error as exc:
423 raise stem.SocketError(exc)
424
425
426 def send_message(control_file, message, raw = False):
427 """
428 Sends a message to the control socket, adding the expected formatting for
429 single verses multi-line messages. Neither message type should contain an
430 ending newline (if so it'll be treated as a multi-line message with a blank
431 line at the end). If the message doesn't contain a newline then it's sent
432 as...
433
434 ::
435
436 <message>\\r\\n
437
438 and if it does contain newlines then it's split on ``\\n`` and sent as...
439
440 ::
441
442 +<line 1>\\r\\n
443 <line 2>\\r\\n
444 <line 3>\\r\\n
445 .\\r\\n
446
447 :param file control_file: file derived from the control socket (see the
448 socket's makefile() method for more information)
449 :param str message: message to be sent on the control socket
450 :param bool raw: leaves the message formatting untouched, passing it to the
451 socket as-is
452
453 :raises:
454 * :class:`stem.SocketError` if a problem arises in using the socket
455 * :class:`stem.SocketClosed` if the socket is known to be shut down
456 """
457
458 if not raw:
459 message = send_formatting(message)
460
461 try:
462 control_file.write(stem.util.str_tools._to_bytes(message))
463 control_file.flush()
464
465 log_message = message.replace("\r\n", "\n").rstrip()
466 log.trace("Sent to tor:\n" + log_message)
467 except socket.error as exc:
468 log.info("Failed to send message: %s" % exc)
469
470 # When sending there doesn't seem to be a reliable method for
471 # distinguishing between failures from a disconnect verses other things.
472 # Just accounting for known disconnection responses.
473
474 if str(exc) == "[Errno 32] Broken pipe":
475 raise stem.SocketClosed(exc)
476 else:
477 raise stem.SocketError(exc)
478 except AttributeError:
479 # if the control_file has been closed then flush will receive:
480 # AttributeError: 'NoneType' object has no attribute 'sendall'
481
482 log.info("Failed to send message: file has been closed")
483 raise stem.SocketClosed("file has been closed")
484
485
486 def recv_message(control_file):
487 """
488 Pulls from a control socket until we either have a complete message or
489 encounter a problem.
490
491 :param file control_file: file derived from the control socket (see the
492 socket's makefile() method for more information)
493
494 :returns: :class:`~stem.response.ControlMessage` read from the socket
495
496 :raises:
497 * :class:`stem.ProtocolError` the content from the socket is malformed
498 * :class:`stem.SocketClosed` if the socket closes before we receive
499 a complete message
500 """
501
502 parsed_content, raw_content = [], b""
503 logging_prefix = "Error while receiving a control message (%s): "
504
505 while True:
506 try:
507 # From a real socket readline() would always provide bytes, but during
508 # tests we might be given a StringIO in which case it's unicode under
509 # python 3.x.
510
511 line = stem.util.str_tools._to_bytes(control_file.readline())
512 except AttributeError:
513 # if the control_file has been closed then we will receive:
514 # AttributeError: 'NoneType' object has no attribute 'recv'
515
516 prefix = logging_prefix % "SocketClosed"
517 log.info(prefix + "socket file has been closed")
518 raise stem.SocketClosed("socket file has been closed")
519 except (socket.error, ValueError) as exc:
520 # When disconnected we get...
521 #
522 # Python 2:
523 # socket.error: [Errno 107] Transport endpoint is not connected
524 #
525 # Python 3:
526 # ValueError: I/O operation on closed file.
527
528 prefix = logging_prefix % "SocketClosed"
529 log.info(prefix + "received exception \"%s\"" % exc)
530 raise stem.SocketClosed(exc)
531
532 raw_content += line
533
534 # Parses the tor control lines. These are of the form...
535 # <status code><divider><content>\r\n
536
537 if len(line) == 0:
538 # if the socket is disconnected then the readline() method will provide
539 # empty content
540
541 prefix = logging_prefix % "SocketClosed"
542 log.info(prefix + "empty socket content")
543 raise stem.SocketClosed("Received empty socket content.")
544 elif len(line) < 4:
545 prefix = logging_prefix % "ProtocolError"
546 log.info(prefix + "line too short, \"%s\"" % log.escape(line))
547 raise stem.ProtocolError("Badly formatted reply line: too short")
548 elif not re.match(b'^[a-zA-Z0-9]{3}[-+ ]', line):
549 prefix = logging_prefix % "ProtocolError"
550 log.info(prefix + "malformed status code/divider, \"%s\"" % log.escape(line))
551 raise stem.ProtocolError("Badly formatted reply line: beginning is malformed")
552 elif not line.endswith(b"\r\n"):
553 prefix = logging_prefix % "ProtocolError"
554 log.info(prefix + "no CRLF linebreak, \"%s\"" % log.escape(line))
555 raise stem.ProtocolError("All lines should end with CRLF")
556
557 line = line[:-2] # strips off the CRLF
558 status_code, divider, content = line[:3], line[3:4], line[4:]
559
560 if stem.prereq.is_python_3():
561 status_code = stem.util.str_tools._to_unicode(status_code)
562 divider = stem.util.str_tools._to_unicode(divider)
563
564 if divider == "-":
565 # mid-reply line, keep pulling for more content
566 parsed_content.append((status_code, divider, content))
567 elif divider == " ":
568 # end of the message, return the message
569 parsed_content.append((status_code, divider, content))
570
571 log_message = raw_content.replace(b"\r\n", b"\n").rstrip()
572 log.trace("Received from tor:\n" + stem.util.str_tools._to_unicode(log_message))
573
574 return stem.response.ControlMessage(parsed_content, raw_content)
575 elif divider == "+":
576 # data entry, all of the following lines belong to the content until we
577 # get a line with just a period
578
579 while True:
580 try:
581 line = stem.util.str_tools._to_bytes(control_file.readline())
582 except socket.error as exc:
583 prefix = logging_prefix % "SocketClosed"
584 log.info(prefix + "received an exception while mid-way through a data reply (exception: \"%s\", read content: \"%s\")" % (exc, log.escape(raw_content)))
585 raise stem.SocketClosed(exc)
586
587 raw_content += line
588
589 if not line.endswith(b"\r\n"):
590 prefix = logging_prefix % "ProtocolError"
591 log.info(prefix + "CRLF linebreaks missing from a data reply, \"%s\"" % log.escape(raw_content))
592 raise stem.ProtocolError("All lines should end with CRLF")
593 elif line == b".\r\n":
594 break # data block termination
595
596 line = line[:-2] # strips off the CRLF
597
598 # lines starting with a period are escaped by a second period (as per
599 # section 2.4 of the control-spec)
600
601 if line.startswith(b".."):
602 line = line[1:]
603
604 # appends to previous content, using a newline rather than CRLF
605 # separator (more conventional for multi-line string content outside
606 # the windows world)
607
608 content += b"\n" + line
609
610 parsed_content.append((status_code, divider, content))
611 else:
612 # this should never be reached due to the prefix regex, but might as well
613 # be safe...
614 prefix = logging_prefix % "ProtocolError"
615 log.warn(prefix + "\"%s\" isn't a recognized divider type" % divider)
616 raise stem.ProtocolError("Unrecognized divider type '%s': %s" % (divider, stem.util.str_tools._to_unicode(line)))
617
618
619 def send_formatting(message):
620 """
621 Performs the formatting expected from sent control messages. For more
622 information see the :func:`~stem.socket.send_message` function.
623
624 :param str message: message to be formatted
625
626 :returns: **str** of the message wrapped by the formatting expected from
627 controllers
628 """
629
630 # From control-spec section 2.2...
631 # Command = Keyword OptArguments CRLF / "+" Keyword OptArguments CRLF CmdData
632 # Keyword = 1*ALPHA
633 # OptArguments = [ SP *(SP / VCHAR) ]
634 #
635 # A command is either a single line containing a Keyword and arguments, or a
636 # multiline command whose initial keyword begins with +, and whose data
637 # section ends with a single "." on a line of its own.
638
639 # if we already have \r\n entries then standardize on \n to start with
640 message = message.replace("\r\n", "\n")
641
642 if "\n" in message:
643 return "+%s\r\n.\r\n" % message.replace("\n", "\r\n")
644 else:
645 return message + "\r\n"