|
|
@ -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,8 +598,9 @@ 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)
|
|
|
|
|
|
|
|
|
|
|
|
self.server_bind()
|
|
|
|
if bind_and_activate:
|
|
|
|
self.server_activate()
|
|
|
|
self.server_bind()
|
|
|
|
|
|
|
|
self.server_activate()
|
|
|
|
|
|
|
|
|
|
|
|
def shutdown_request(self, request):
|
|
|
|
def shutdown_request(self, request):
|
|
|
|
request.shutdown()
|
|
|
|
request.shutdown()
|
|
|
@ -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
|
|
|
|
|
|
|
|
ips = filter(lambda ip: ip.find(":") == -1, ips)
|
|
|
|
# filter out ips we are not listening on
|
|
|
|
|
|
|
|
if not self.listenIPv6:
|
|
|
|
|
|
|
|
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
|
|
|
|