summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsand <daniel@spatof.org>2013-05-16 16:15:25 (GMT)
committer sand <daniel@spatof.org>2013-05-16 16:15:25 (GMT)
commit17489225e6ae7cf8c892e4ec0149c508a5bd7066 (patch)
treed79491dd291f8d828b2b29e6ebba010a144e863f
parent259e1ebaafbe82dfe7933f16788ceee50d0e778b (diff)
Supporto SSL
-rw-r--r--pinolo/bot.py45
-rw-r--r--pinolo/irc.py27
-rw-r--r--sample.cfg3
3 files changed, 67 insertions, 8 deletions
diff --git a/pinolo/bot.py b/pinolo/bot.py
index 4cbef52..3c0428e 100644
--- a/pinolo/bot.py
+++ b/pinolo/bot.py
@@ -11,6 +11,7 @@
"""
import os
import re
+import ssl
import socket
import select
import errno
@@ -18,6 +19,7 @@ import time
import logging
import Queue
import traceback
+from pprint import pprint
import pinolo.plugins
from pinolo.signals import SignalDispatcher
from pinolo.irc import IRCConnection, COMMAND_ALIASES
@@ -90,6 +92,16 @@ class Bot(SignalDispatcher):
self.check_queue()
self.handle_cron()
+ def do_handshake(self, s):
+ try:
+ s.do_handshake()
+ except ssl.SSLError as err:
+ if err.args[0] in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE):
+ return False
+ else:
+ raise
+ return True
+
def handle_network(self):
# For the select() call we must create two distinct groups of sockets
# to watch for: all the active sockets must be checked for reading, but
@@ -119,13 +131,19 @@ class Bot(SignalDispatcher):
for s in readable:
conn_obj = self.connection_map[s]
+ # Do SSL handshake if needed
+ if conn_obj.ssl_must_handshake and conn_obj.connected:
+ result = self.do_handshake(conn_obj.socket)
+ if not result:
+ continue
+
# We must read data from socket until the syscall returns EAGAIN;
# when the OS signals EAGAIN the socket would block reading.
while True:
try:
chunk = s.recv(512)
- except socket.error, e:
- if e[0] == errno.EAGAIN:
+ except (socket.error, ssl.SSLError) as err:
+ if err.args[0] in (errno.EAGAIN, ssl.SSL_ERROR_WANT_READ):
break
else:
raise
@@ -144,6 +162,25 @@ class Bot(SignalDispatcher):
for s in writable:
conn_obj = self.connection_map[s]
+ # If this is the first time we get a "writable" status then
+ # we are actually connected to the remote server.
+ if conn_obj.connected == False:
+ conn_obj.connected = True
+
+ # SSL socket setup
+ if conn_obj.config["ssl"]:
+ conn_obj.wrap_ssl()
+ # swap the socket in the connection map with the ssl one
+ self.connection_map[conn_obj.socket] = conn_obj
+ del self.connection_map[s]
+ s = conn_obj.socket
+
+ # SSL handshake
+ if conn_obj.ssl_must_handshake and conn_obj.connected:
+ result = self.do_handshake(s)
+ if not result:
+ continue
+
# check if we got disconnected while reading from socket
# XXX should be empty the out buffer?
if not conn_obj.connected:
@@ -158,8 +195,8 @@ class Bot(SignalDispatcher):
# per evitare i flood? ma il flood anche sticazzi,
# server *decenti* tipo inspircd non hanno piĆ¹ quel
# problema.
- except socket.error, e:
- if e[0] == errno.EAGAIN:
+ except (socket.error, ssl.SSLError) as err:
+ if err.args[0] in (errno.EAGAIN, ssl.SSL_ERROR_WANT_WRITE):
break
else:
raise
diff --git a/pinolo/irc.py b/pinolo/irc.py
index 27009fa..8eb4f3a 100644
--- a/pinolo/irc.py
+++ b/pinolo/irc.py
@@ -15,6 +15,7 @@ import socket
import errno
import time
import traceback
+import ssl
from pinolo.tasks import TestTask
from pinolo.cowsay import cowsay
from pinolo.casuale import get_random_quit, get_random_reply
@@ -150,16 +151,34 @@ class IRCConnection(object):
self.current_nickname = None
# la queue per i thread
self.coda = self.bot.coda
-
+ # SSL status
+ self.ssl_must_handshake = False
+ self.ssl_ca_path = self.config.get("ssl_ca_path")
# state
self.channels = {}
def __repr__(self):
return "<IRCConnection(%s)>" % self.name
+ def wrap_ssl(self):
+ if not self.config["ssl"]:
+ return
+
+ try:
+ self.socket = ssl.wrap_socket(self.socket,
+ cert_reqs=ssl.CERT_NONE,
+ do_handshake_on_connect=False)
+ self.ssl_must_handshake = True
+ except ssl.SSLError:
+ raise
+
def connect(self):
+ """Create a socket and connect to the remote server; at the moment
+ we have to do a blocking connect() to support SSL sockets.
+ """
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.socket.setblocking(0)
+ self.socket.setblocking(False)
+
try:
self.socket.connect((self.config['hostname'], self.config['port']))
except socket.error, e:
@@ -169,7 +188,9 @@ class IRCConnection(object):
pass
else:
raise
- self.connected = True
+ except socket.gaierror as err:
+ print "Unknown host: %s (%s)" % (self.config['hostname'], str(err))
+ raise
self.nick()
self.ident()
diff --git a/sample.cfg b/sample.cfg
index a357b77..d4c1b2e 100644
--- a/sample.cfg
+++ b/sample.cfg
@@ -21,4 +21,5 @@ channels = #test
# channels = #foo, #bar
# nickserv = my_password
# ssl = true
-# ssl_verify = true \ No newline at end of file
+# ssl_verify = true
+# ssl_certs_file = /etc/ssl/certs/ca-certificates.crt \ No newline at end of file