forked from seba/servefile
Added IPv6 support
This commit is contained in:
parent
0f54983a63
commit
33124dfc98
76
servefile
76
servefile
|
@ -17,6 +17,7 @@ import urllib
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
|
import select
|
||||||
import SocketServer
|
import SocketServer
|
||||||
import socket
|
import socket
|
||||||
from stat import ST_SIZE
|
from stat import ST_SIZE
|
||||||
|
@ -584,8 +585,8 @@ def catchSSLErrors(BaseSSLClass):
|
||||||
|
|
||||||
|
|
||||||
class SecureThreadedHTTPServer(ThreadedHTTPServer):
|
class SecureThreadedHTTPServer(ThreadedHTTPServer):
|
||||||
def __init__(self, pubKey, privKey, *args, **kwargs):
|
def __init__(self, pubKey, privKey, server_address, RequestHandlerClass, bind_and_activate=True):
|
||||||
ThreadedHTTPServer.__init__(self, *args, **kwargs)
|
ThreadedHTTPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate)
|
||||||
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
||||||
if type(pubKey) == crypto.X509 and type(privKey) == crypto.PKey:
|
if type(pubKey) == crypto.X509 and type(privKey) == crypto.PKey:
|
||||||
ctx.use_certificate(pubKey)
|
ctx.use_certificate(pubKey)
|
||||||
|
@ -597,6 +598,7 @@ class SecureThreadedHTTPServer(ThreadedHTTPServer):
|
||||||
self.bsocket = socket.socket(self.address_family, self.socket_type)
|
self.bsocket = socket.socket(self.address_family, self.socket_type)
|
||||||
self.socket = SSL.Connection(ctx, self.bsocket)
|
self.socket = SSL.Connection(ctx, self.bsocket)
|
||||||
|
|
||||||
|
if bind_and_activate:
|
||||||
self.server_bind()
|
self.server_bind()
|
||||||
self.server_activate()
|
self.server_activate()
|
||||||
|
|
||||||
|
@ -629,11 +631,21 @@ class ServeFile():
|
||||||
self.cert = self.key = None
|
self.cert = self.key = None
|
||||||
self.auth = None
|
self.auth = None
|
||||||
self.maxUploadSize = 0
|
self.maxUploadSize = 0
|
||||||
|
self.listenIPv4 = True
|
||||||
|
self.listenIPv6 = True
|
||||||
|
|
||||||
if self.serveMode not in range(self._NUM_MODES):
|
if self.serveMode not in range(self._NUM_MODES):
|
||||||
self.serveMode = None
|
self.serveMode = None
|
||||||
raise ValueError("Unknown serve mode, needs to be MODE_SINGLE, MODE_SINGLETAR, MODE_UPLOAD or MODE_DIRLIST.")
|
raise ValueError("Unknown serve mode, needs to be MODE_SINGLE, MODE_SINGLETAR, MODE_UPLOAD or MODE_DIRLIST.")
|
||||||
|
|
||||||
|
def setIPv4(self, ipv4):
|
||||||
|
""" En- or disable ipv4 """
|
||||||
|
self.listenIPv4 = ipv4
|
||||||
|
|
||||||
|
def setIPv6(self, ipv6):
|
||||||
|
""" En- or disable ipv6 """
|
||||||
|
self.listenIPv6 = ipv6
|
||||||
|
|
||||||
def getIPs(self):
|
def getIPs(self):
|
||||||
""" Get IPs from all interfaces via ip or ifconfig. """
|
""" Get IPs from all interfaces via ip or ifconfig. """
|
||||||
# ip and ifconfig sometimes are located in /sbin/
|
# ip and ifconfig sometimes are located in /sbin/
|
||||||
|
@ -660,8 +672,13 @@ class ServeFile():
|
||||||
proc = None
|
proc = None
|
||||||
if proc:
|
if proc:
|
||||||
ips = proc.stdout.read().strip().split("\n")
|
ips = proc.stdout.read().strip().split("\n")
|
||||||
# FIXME: When BaseHTTP supports ipv6 properly, delete this line
|
|
||||||
|
# filter out ips we are not listening on
|
||||||
|
if not self.listenIPv6:
|
||||||
ips = filter(lambda ip: ip.find(":") == -1, ips)
|
ips = filter(lambda ip: ip.find(":") == -1, ips)
|
||||||
|
if not self.listenIPv4:
|
||||||
|
ips = filter(lambda ip: ip.find(".") == -1, ips)
|
||||||
|
|
||||||
return ips
|
return ips
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -697,7 +714,7 @@ class ServeFile():
|
||||||
|
|
||||||
# generate altnames
|
# generate altnames
|
||||||
altNames = []
|
altNames = []
|
||||||
for ip in self.getIPs() + ["127.0.0.1"]:
|
for ip in self.getIPs() + ["127.0.0.1", "::1"]:
|
||||||
altNames.append("IP:%s" % ip)
|
altNames.append("IP:%s" % ip)
|
||||||
altNames.append("DNS:localhost")
|
altNames.append("DNS:localhost")
|
||||||
ext = crypto.X509Extension("subjectAltName", False, ",".join(altNames))
|
ext = crypto.X509Extension("subjectAltName", False, ",".join(altNames))
|
||||||
|
@ -736,19 +753,40 @@ class ServeFile():
|
||||||
raise ServeFileException("User and password both need to be at least one character.")
|
raise ServeFileException("User and password both need to be at least one character.")
|
||||||
self.auth = base64.b64encode("%s:%s" % (user, password))
|
self.auth = base64.b64encode("%s:%s" % (user, password))
|
||||||
|
|
||||||
def _createServer(self, handler):
|
def _createServer(self, handler, withv6=False):
|
||||||
|
ThreadedHTTPServer.address_family = socket.AF_INET
|
||||||
|
SecureThreadedHTTPServer.address_family = socket.AF_INET
|
||||||
|
listenIp = ''
|
||||||
server = None
|
server = None
|
||||||
|
|
||||||
|
if withv6:
|
||||||
|
listenIp = '::'
|
||||||
|
ThreadedHTTPServer.address_family = socket.AF_INET6
|
||||||
|
SecureThreadedHTTPServer.address_family = socket.AF_INET6
|
||||||
|
|
||||||
if self.useSSL:
|
if self.useSSL:
|
||||||
if not self._getKey():
|
if not self._getKey():
|
||||||
self.genKeyPair()
|
self.genKeyPair()
|
||||||
server = SecureThreadedHTTPServer(self._getCert(), self._getKey(), ('', self.port), handler)
|
server = SecureThreadedHTTPServer(self._getCert(), self._getKey(), (listenIp, self.port), handler, False)
|
||||||
else:
|
else:
|
||||||
server = ThreadedHTTPServer(('', self.port), handler)
|
server = ThreadedHTTPServer((listenIp, self.port), handler, False)
|
||||||
|
|
||||||
|
if withv6:
|
||||||
|
server.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
|
||||||
|
|
||||||
|
server.server_bind()
|
||||||
|
server.server_activate()
|
||||||
|
|
||||||
return server
|
return server
|
||||||
|
|
||||||
def serve(self):
|
def serve(self):
|
||||||
self.handler = self._confAndFindHandler()
|
self.handler = self._confAndFindHandler()
|
||||||
self.server = self._createServer(self.handler)
|
self.server = []
|
||||||
|
|
||||||
|
if self.listenIPv4:
|
||||||
|
self.server.append(self._createServer(self.handler))
|
||||||
|
if self.listenIPv6:
|
||||||
|
self.server.append(self._createServer(self.handler, withv6=True))
|
||||||
|
|
||||||
if self.serveMode != self.MODE_UPLOAD:
|
if self.serveMode != self.MODE_UPLOAD:
|
||||||
print "Serving \"%s\" at port %d." % (self.target, self.port)
|
print "Serving \"%s\" at port %d." % (self.target, self.port)
|
||||||
|
@ -763,13 +801,19 @@ class ServeFile():
|
||||||
print "Could not find any addresses."
|
print "Could not find any addresses."
|
||||||
else:
|
else:
|
||||||
for ip in ips:
|
for ip in ips:
|
||||||
|
if ":" in ip:
|
||||||
|
ip = "[%s]" % ip
|
||||||
print "\thttp%s://%s:%d/" % (self.useSSL and "s" or "", ip, self.port)
|
print "\thttp%s://%s:%d/" % (self.useSSL and "s" or "", ip, self.port)
|
||||||
print ""
|
print ""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.server.serve_forever()
|
while True:
|
||||||
|
(servers, _, _) = select.select(self.server, [], [])
|
||||||
|
for server in servers:
|
||||||
|
server.handle_request()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.server.socket.close()
|
for server in self.server:
|
||||||
|
server.socket.close()
|
||||||
|
|
||||||
# cleanup potential upload directory
|
# cleanup potential upload directory
|
||||||
if self.dirCreated and len(os.listdir(self.target)) == 0:
|
if self.dirCreated and len(os.listdir(self.target)) == 0:
|
||||||
|
@ -888,6 +932,10 @@ def main():
|
||||||
parser.add_argument('-c', '--compression', type=str, metavar='method', \
|
parser.add_argument('-c', '--compression', type=str, metavar='method', \
|
||||||
default="none", \
|
default="none", \
|
||||||
help="Set compression method, only in combination with --tar. Can be one of %s" % ", ".join(TarFileHandler.compressionMethods))
|
help="Set compression method, only in combination with --tar. Can be one of %s" % ", ".join(TarFileHandler.compressionMethods))
|
||||||
|
parser.add_argument('-4', '--ipv4-only', action="store_true", default=False, \
|
||||||
|
help="Listen on IPv4 only")
|
||||||
|
parser.add_argument('-6', '--ipv6-only', action="store_true", default=False, \
|
||||||
|
help="Listen on IPv6 only")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
maxUploadSize = 0
|
maxUploadSize = 0
|
||||||
|
@ -948,6 +996,10 @@ def main():
|
||||||
print "Error: Compression mode '%s' is unknown." % TarFileHandler.compression
|
print "Error: Compression mode '%s' is unknown." % TarFileHandler.compression
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.ipv4_only and args.ipv6_only:
|
||||||
|
print "You can't listen both on IPv4 and IPv6 \"only\""
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
mode = None
|
mode = None
|
||||||
if args.upload:
|
if args.upload:
|
||||||
mode = ServeFile.MODE_UPLOAD
|
mode = ServeFile.MODE_UPLOAD
|
||||||
|
@ -971,6 +1023,10 @@ def main():
|
||||||
server.setAuth(user, password)
|
server.setAuth(user, password)
|
||||||
if compression and compression != "none":
|
if compression and compression != "none":
|
||||||
server.setCompression(compression)
|
server.setCompression(compression)
|
||||||
|
if args.ipv4_only:
|
||||||
|
server.setIPv6(False)
|
||||||
|
if args.ipv6_only:
|
||||||
|
server.setIPv4(False)
|
||||||
server.serve()
|
server.serve()
|
||||||
except ServeFileException, e:
|
except ServeFileException, e:
|
||||||
print e
|
print e
|
||||||
|
|
Loading…
Reference in New Issue