Commit 510b6a3f authored by Stephane Bortzmeyer's avatar Stephane Bortzmeyer
Browse files

Use curl's CONNECT_TO option. Closes #19

parent d9fc5482
......@@ -49,6 +49,8 @@ Possible options, besides `--dot`:
does not mention it, result is probably indefinite.
* --POST or -P: (DoH) Uses the POST HTTP method (default is GET)
* --insecure or -k: Does not check the certificate
* -4: Uses only IPv4
* -6: Uses only IPv6
### Repetition of tests
......
......@@ -43,6 +43,9 @@ vhostname = None
tests = 1 # Number of repeated tests
ifile = None # Input file
delay = None
forceIPv4 = False
forceIPv6 = False
connectTo = None
# Monitoring plugin only:
host = None
path = None
......@@ -90,8 +93,8 @@ def is_valid_ip_address(addr):
try:
baddr = netaddr.IPAddress(addr)
except netaddr.core.AddrFormatError:
return False
return True
return (False, None)
return (True, baddr.version)
def is_valid_url(url):
try:
......@@ -133,7 +136,7 @@ def match_hostname(hostname, possibleMatch):
def validate_hostname(hostname, cert):
# Complete specification is in RFC 6125. It is long and
# complicated and I'm not sure we do it perfectly.
is_addr = is_valid_ip_address(hostname)
(is_addr, family) = is_valid_ip_address(hostname)
hostname = canonicalize(hostname)
for alt_name in get_certificate_san(cert).split(", "):
if alt_name.startswith("DNS:") and not is_addr:
......@@ -164,7 +167,8 @@ def validate_hostname(hostname, cert):
return False
class Connection:
def __init__(self, server, servername=None, dot=False, verbose=verbose, insecure=insecure, post=post, head=head):
def __init__(self, server, servername=None, connect=None, forceIPv4=False, forceIPv6=False,
dot=False, verbose=verbose, insecure=insecure, post=post, head=head):
if dot and not is_valid_hostname(server):
error("DoT requires a host name or IP address, not \"%s\"" % server)
if not dot and not is_valid_url(url):
......@@ -184,8 +188,22 @@ class Connection:
self.verbose = verbose
self.insecure = insecure
if self.dot:
addrinfo = socket.getaddrinfo(server, 853)
# May be loop over the results of getaddrinfo, to test all the IP addresses? See #13
if forceIPv4 and forceIPv6:
raise Exception("Force IPv4 *or* IPv6 but not both")
(is_addr, family) = is_valid_ip_address(self.server)
if forceIPv4:
if is_addr and family == 6:
raise Exception("You cannot force IPv4 with a litteral IPv6 address (%s)" % self.server)
family = socket.AF_INET
elif forceIPv6:
if is_addr and family == 4:
raise Exception("You cannot force IPv6 with a litteral IPv4 address (%s)" % self.server)
family = socket.AF_INET6
else:
family = 0
addrinfo = socket.getaddrinfo(server, 853, family)
# May be loop over the results of getaddrinfo, to test all
# the IP addresses? See #13.
self.sock = socket.socket(addrinfo[0][0], socket.SOCK_STREAM)
if self.verbose:
print("Connecting to %s ..." % str(addrinfo[0][4]))
......@@ -239,6 +257,29 @@ class Connection:
self.curl.setopt(pycurl.NOBODY, True)
if self.post:
self.curl.setopt(pycurl.POST, True)
if forceIPv4 and forceIPv6:
raise Exception("Force IPv4 *or* IPv6 but not both")
if forceIPv4:
self.curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
if forceIPv6:
self.curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V6)
if connect is not None:
try:
binaddress = netaddr.IPAddress(connect)
if binaddress.version == 4:
if forceIPv6:
raise Exception("You cannot force the use of IPv6 with a litteral IPv4 address (%s)" % connect)
repraddress = connect
elif binaddress.version == 6:
if forceIPv4:
raise Exception("You cannot force the use of IPv6 with a litteral IPv4 address (%s)" % connect)
repraddress = "[%s]" % connect
else:
raise Exception("%s is not IPv4 and not IPv6" % connect)
except netaddr.core.AddrFormatError:
repraddress = connect
self.curl.setopt(pycurl.CONNECT_TO, ["::%s:" % repraddress,])
def __str__(self):
return self.server
def end(self):
......@@ -317,8 +358,9 @@ if not monitoring:
name = None
message = None
try:
optlist, args = getopt.getopt (sys.argv[1:], "hvPkeV:r:f:d:t",
["help", "verbose", "dot", "head", "insecure", "POST", "vhost=", "repeat=", "file=", "delay="])
optlist, args = getopt.getopt (sys.argv[1:], "hvPkeV:r:f:d:t46",
["help", "verbose", "dot", "head", "insecure", "POST", "vhost=",
"repeat=", "file=", "delay=", "v4only", "v6only"])
for option, value in optlist:
if option == "--help" or option == "-h":
usage()
......@@ -345,6 +387,10 @@ if not monitoring:
error("--delay needs a value > 0")
elif option == "--file" or option == "-f":
ifile = value
elif option == "-4" or option == "v4only":
forceIPv4 = True
elif option == "-6" or option == "v6only":
forceIPv6 = True
else:
error("Unknown option %s" % option)
except getopt.error as reason:
......@@ -373,7 +419,7 @@ else: # Monitoring plugin
dot = (me == "check_dot")
name = None
try:
optlist, args = getopt.getopt (sys.argv[1:], "H:n:p:V:t:Pih")
optlist, args = getopt.getopt (sys.argv[1:], "H:n:p:V:t:Pih46")
for option, value in optlist:
if option == "-H":
host = value
......@@ -391,6 +437,10 @@ else: # Monitoring plugin
head = True
elif option == "-i":
insecure = True
elif option == "-4":
forceIPv4 = True
elif option == "-6":
forceIPv6 = True
else:
# Should never occur, it is trapped by getopt
print("Unknown option %s" % option)
......@@ -416,10 +466,12 @@ else: # Monitoring plugin
if dot:
url = host
else:
if vhostname is None:
if vhostname is None or vhostname == host:
connectTo = None
url = "https://%s/" % host
else:
url = "https://%s/" % vhostname # host is ignored in that case
connectTo = host
url = "https://%s/" % vhostname
if path is not None:
if path.startswith("/"):
path = path[1:]
......@@ -431,7 +483,9 @@ try:
extracheck = vhostname
else:
extracheck = None
conn = Connection(url, dot=dot, servername=extracheck, verbose=verbose, insecure=insecure, post=post, head=head)
conn = Connection(url, dot=dot, servername=extracheck, connect=connectTo, verbose=verbose,
forceIPv4=forceIPv4, forceIPv6=forceIPv6,
insecure=insecure, post=post, head=head)
except TimeoutError:
error("timeout")
except ConnectionRefusedError:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment