blob: 8cd5f6e9296f30a0862dda0a04a148be6e2a8693 [file]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
#the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This is the TCP/UDP Socket Library for Apache Warble (incubating)
"""
# Socket imports
import select
import socket
import ssl
import struct
from socket import AF_INET, SOCK_DGRAM
import time
class tcp():
def __init__(self, testParameters, report):
self.report = report
self.iptype = socket.AF_INET6 if (testParameters.get('ipv6', False) == True) else socket.AF_INET
self.host = testParameters.get('host')
self.port = int(testParameters.get('port', 80))
self.error = None
self.socket = socket.socket(self.iptype, socket.SOCK_STREAM)
self.socket.settimeout(3)
self.bytes = 0
self.report.debug("Initialising socket")
self.report.timer('init')
self.status_code = None
self.location = None
self.server = None
self.realip = None
self.cert = None
try:
self.report.debug("Looking up hostname %s..." % self.host)
af, socktype, proto, canonname, sa = socket.getaddrinfo(self.host, self.port, self.iptype, socket.SOCK_STREAM)[0]
self.report.timer('dns')
self.sa = sa
except Exception as err:
raise Exception("Could not resolve hostname: %s" % err)
self.realip = sa[0];
if not self.realip:
self.report.error('dns', "Could not resolve host %s" % self.host)
return None
if self.realip and self.realip == '127.0.0.1' or self.realip == '::1':
self.report.error('dns', "Hostname %s points to localhost!" % self.host)
return None
self.report.debug("Connecting to %s:%u" % (self.realip, self.port))
self.socket = socket.socket(af, socktype, proto)
def __del__(self):
# Close socket if not already closed
try:
self.socket.close()
except:
pass
def secure(self, SNI = None, verify = False):
""" Wrap socket in OpenSSL """
self.report.debug("Wrapping socket for TLS")
if SNI:
self.report.debug("Using SNI extension for %s" % SNI)
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) # SSL, TLS1, TLS1.1 is largely deprecated now.
context.verify_mode = ssl.CERT_OPTIONAL
# Are we going to test the certificate for validity?
if verify == True:
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True
context.load_default_certs()
self.socket = context.wrap_socket(self.socket, server_hostname = SNI)
while True:
try:
self.socket.do_handshake()
break
except ssl.SSLWantReadError:
select.select([self.socket], [], [])
except ssl.SSLWantWriteError:
select.select([], [self.socket], [])
self.report.debug("Shook hands, TLS ready")
return context
else:
self.socket = ssl.wrap_socket(self.socket)
def connect(self):
try:
self.socket.connect(self.sa)
self.report.timer('connect')
except Exception as err:
print("Connection to %s failed" % self.realip)
raise Exception("Could not connect to host: %s" % str(err))
def send(self, b):
""" Send bytes (or convert string to bytes) to socket """
if type(b) is str:
self.socket.send(b.encode('ascii', errors = 'replace'))
else:
self.socket.send(b)
def readline(self, recv_buffer=256, delim=b'\n'):
""" Reads a line from a TCP (SSL?) socket, if presented within 60 seconds """
buffer = b''
data = True
self.socket.setblocking(0)
while data:
try:
data = self.socket.recv(recv_buffer)
buffer += data
self.bytes += len(data)
while delim in buffer:
line, buffer = buffer.split(delim, 1)
yield line
except BlockingIOError as err:
ready = select.select([self.socket], [], [], 60)
if ready[0]:
continue
else:
raise Exception("Socket timeout after 60 seconds")
except ssl.SSLWantReadError as err:
ready = select.select([self.socket], [], [], 60)
if ready[0]:
continue
else:
raise Exception("Socket timeout after 60 seconds")
except Exception as err:
print(type(err))
raise err