Exemplo n.º 1
0
    def makeTftpRetrieval(self):
        """
        """
        progresshook = Progress(self).progresshook

        if CONFIG.has_option('honeypot', 'download_limit_size'):
            self.limit_size = CONFIG.getint('honeypot', 'download_limit_size')

        self.artifactFile = Artifact(self.file_to_get)

        tclient = None
        url = ''

        try:
            tclient = tftpy.TftpClient(self.hostname, int(self.port))

            # tftpy can't handle unicode string as filename
            # so we have to convert unicode type to str type
            tclient.download(str(self.file_to_get), self.artifactFile,
                             progresshook)

            url = 'tftp://%s/%s' % (self.hostname, self.file_to_get.strip('/'))

            self.file_to_get = self.fs.resolve_path(self.file_to_get,
                                                    self.protocol.cwd)

            if hasattr(tclient.context, 'metrics'):
                self.fs.mkfile(self.file_to_get, 0, 0,
                               tclient.context.metrics.bytes, 33188)
            else:
                self.fs.mkfile(self.file_to_get, 0, 0, 0, 33188)

        except tftpy.TftpException:
            if tclient and tclient.context and not tclient.context.fileobj.closed:
                tclient.context.fileobj.close()

        if url:

            # log to cowrie.log
            log.msg(
                format=
                'Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
                url=url,
                outfile=self.artifactFile.shasumFilename,
                shasum=self.artifactFile.shasum)

            self.protocol.logDispatch(
                eventid='cowrie.session.file_download',
                format=
                'Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
                url=url,
                outfile=self.artifactFile.shasumFilename,
                shasum=self.artifactFile.shasum,
                destfile=self.file_to_get)

            # Update the honeyfs to point to downloaded file
            self.fs.update_realfile(self.fs.getfile(self.file_to_get),
                                    self.artifactFile.shasumFilename)
            self.fs.chown(self.file_to_get, self.protocol.user.uid,
                          self.protocol.user.gid)
Exemplo n.º 2
0
    def makeTftpRetrieval(self):
        progresshook = Progress(self).progresshook

        self.artifactFile = Artifact(self.file_to_get)

        tclient = None
        url = ""

        try:
            tclient = tftpy.TftpClient(self.hostname, int(self.port))

            # tftpy can't handle unicode string as filename
            # so we have to convert unicode type to str type
            tclient.download(str(self.file_to_get), self.artifactFile,
                             progresshook)

            url = "tftp://{}/{}".format(self.hostname,
                                        self.file_to_get.strip("/"))

            self.file_to_get = self.fs.resolve_path(self.file_to_get,
                                                    self.protocol.cwd)

            if hasattr(tclient.context, "metrics"):
                self.fs.mkfile(self.file_to_get, 0, 0,
                               tclient.context.metrics.bytes, 33188)
            else:
                self.fs.mkfile(self.file_to_get, 0, 0, 0, 33188)

        except tftpy.TftpException:
            if tclient and tclient.context and not tclient.context.fileobj.closed:
                tclient.context.fileobj.close()

        if url:
            # log to cowrie.log
            log.msg(
                format=
                "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
                url=url,
                outfile=self.artifactFile.shasumFilename,
                shasum=self.artifactFile.shasum,
            )

            self.protocol.logDispatch(
                eventid="cowrie.session.file_download",
                format=
                "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
                url=url,
                outfile=self.artifactFile.shasumFilename,
                shasum=self.artifactFile.shasum,
                destfile=self.file_to_get,
            )

            # Update the honeyfs to point to downloaded file
            self.fs.update_realfile(self.fs.getfile(self.file_to_get),
                                    self.artifactFile.shasumFilename)
            self.fs.chown(self.file_to_get, self.protocol.user.uid,
                          self.protocol.user.gid)
Exemplo n.º 3
0
    def download(self, url, fakeoutfile, *args, **kwargs):
        """
        url - URL to download
        fakeoutfile - file in guest's fs that attacker wants content to be downloaded to
        """
        try:
            parsed = compat.urllib_parse.urlparse(url)
            scheme = parsed.scheme
            host = parsed.hostname.decode('utf8')
            port = parsed.port or (443 if scheme == b'https' else 80)
            if scheme != b'http' and scheme != b'https':
                raise NotImplementedError
            if not host:
                return None
        except Exception:
            self.errorWrite('%s: Unsupported scheme.\n' % (url, ))
            return None

        # File in host's fs that will hold content of the downloaded file
        # HTTPDownloader will close() the file object so need to preserve the name
        self.artifactFile = Artifact(self.outfile)

        if not self.quiet:
            self.errorWrite(
                '--%s--  %s\n' %
                (time.strftime('%Y-%m-%d %H:%M:%S'), url.decode('utf8')))
            self.errorWrite('Connecting to %s:%d... connected.\n' %
                            (host, port))
            self.errorWrite('HTTP request sent, awaiting response... ')

        factory = HTTPProgressDownloader(self, fakeoutfile, url,
                                         self.artifactFile, *args, **kwargs)

        out_addr = None
        if CowrieConfig().has_option('honeypot', 'out_addr'):
            out_addr = (CowrieConfig().get('honeypot', 'out_addr'), 0)

        if scheme == b'https':
            context_factory = ssl.optionsForClientTLS(hostname=host)
            self.connection = reactor.connectSSL(host,
                                                 port,
                                                 factory,
                                                 context_factory,
                                                 bindAddress=out_addr)

        elif scheme == b'http':
            self.connection = reactor.connectTCP(host,
                                                 port,
                                                 factory,
                                                 bindAddress=out_addr)
        else:
            raise NotImplementedError

        return factory.deferred
Exemplo n.º 4
0
    def start(self):
        try:
            optlist, args = getopt.getopt(self.args, 'cvu:p:P:')
        except getopt.GetoptError:
            self.help()
            self.exit()
            return

        if len(args) < 2:
            self.help()
            self.exit()
            return

        self.verbose = False
        self.username = ''
        self.password = ''
        self.port = 21
        self.host = ''
        self.local_file = ''
        self.remote_path = ''

        for opt in optlist:
            if opt[0] == '-v':
                self.verbose = True
            elif opt[0] == '-u':
                self.username = opt[1]
            elif opt[0] == '-p':
                self.password = opt[1]
            elif opt[0] == '-P':
                try:
                    self.port = int(opt[1])
                except ValueError:
                    pass

        if len(args) == 2:
            self.host, self.remote_path = args
        elif len(args) >= 3:
            self.host, self.local_file, self.remote_path = args[:3]

        self.remote_dir = os.path.dirname(self.remote_path)
        self.remote_file = os.path.basename(self.remote_path)
        if not self.local_file:
            self.local_file = self.remote_file

        fakeoutfile = self.fs.resolve_path(self.local_file, self.protocol.cwd)
        path = os.path.dirname(fakeoutfile)
        if not path or not self.fs.exists(path) or not self.fs.isdir(path):
            self.write(
                'ftpget: can\'t open \'%s\': No such file or directory' %
                self.local_file)
            self.exit()
            return

        self.download_path = CONFIG.get('honeypot', 'download_path')

        self.url_log = 'ftp://'
        if self.username:
            self.url_log = '{}{}'.format(self.url_log, self.username)
            if self.password:
                self.url_log = '{}:{}'.format(self.url_log, self.password)
            self.url_log = '{}@'.format(self.url_log)
        self.url_log = '{}{}'.format(self.url_log, self.host)
        if self.port != 21:
            self.url_log = '{}:{}'.format(self.url_log, self.port)
        self.url_log = '{}/{}'.format(self.url_log, self.remote_path)

        self.artifactFile = Artifact(self.local_file)

        result = self.ftp_download()

        self.artifactFile.close()

        if not result:
            # log to cowrie.log
            log.msg(
                format='Attempt to download file(s) from URL (%(url)s) failed',
                url=self.url_log)

            self.protocol.logDispatch(
                eventid='cowrie.session.file_download.failed',
                format='Attempt to download file(s) from URL (%(url)s) failed',
                url=self.url_log)
            self.exit()
            return

        # log to cowrie.log
        log.msg(
            format=
            'Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
            url=self.url_log,
            outfile=self.artifactFile.shasumFilename,
            shasum=self.artifactFile.shasum)

        self.protocol.logDispatch(
            eventid='cowrie.session.file_download',
            format=
            'Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
            url=self.url_log,
            outfile=self.artifactFile.shasumFilename,
            shasum=self.artifactFile.shasum,
            destfile=self.local_file)

        # Update the honeyfs to point to downloaded file
        self.fs.mkfile(fakeoutfile, 0, 0,
                       os.path.getsize(self.artifactFile.shasumFilename),
                       33188)
        self.fs.update_realfile(self.fs.getfile(fakeoutfile),
                                self.artifactFile.shasumFilename)
        self.fs.chown(fakeoutfile, self.protocol.user.uid,
                      self.protocol.user.gid)

        self.exit()
Exemplo n.º 5
0
    def start(self):
        try:
            optlist, args = getopt.getopt(self.args, 'cqO:P:', 'header=')
        except getopt.GetoptError:
            self.errorWrite('Unrecognized option\n')
            self.exit()
            return

        if len(args):
            url = args[0].strip()
        else:
            self.errorWrite('wget: missing URL\n')
            self.errorWrite('Usage: wget [OPTION]... [URL]...\n\n')
            self.errorWrite('Try `wget --help\' for more options.\n')
            self.exit()
            return

        outfile = None
        self.quiet = False
        for opt in optlist:
            if opt[0] == '-O':
                outfile = opt[1]
            if opt[0] == '-q':
                self.quiet = True

        # for some reason getopt doesn't recognize "-O -"
        # use try..except for the case if passed command is malformed
        try:
            if not outfile:
                if '-O' in args:
                    outfile = args[args.index('-O') + 1]
        except Exception:
            pass

        if '://' not in url:
            url = 'http://%s' % url

        urldata = compat.urllib_parse.urlparse(url)

        url = url.encode('utf8')

        if outfile is None:
            outfile = urldata.path.split('/')[-1]
            if not len(outfile.strip()) or not urldata.path.count('/'):
                outfile = 'index.html'

        if outfile != '-':
            outfile = self.fs.resolve_path(outfile, self.protocol.cwd)
            path = os.path.dirname(outfile)
            if not path or not self.fs.exists(path) or not self.fs.isdir(path):
                self.errorWrite(
                    'wget: %s: Cannot open: No such file or directory\n' %
                    outfile)
                self.exit()
                return

        self.url = url
        self.protocol.logDispatch(eventid='cowrie.session.file_download',
                                  format='Downloaded URL (%(url)s)',
                                  url=self.url)
        self.exit()
        return

        self.artifactFile = Artifact(outfile)
        # HTTPDownloader will close() the file object so need to preserve the name

        d = self.download(url, outfile, self.artifactFile)
        if d:
            d.addCallback(self.success, outfile)
            d.addErrback(self.error, url)
        else:
            self.exit()
Exemplo n.º 6
0
    def start(self):
        try:
            optlist, args = getopt.getopt(
                self.args, "sho:O", ["help", "manual", "silent"]
            )
        except getopt.GetoptError as err:
            # TODO: should be 'unknown' instead of 'not recognized'
            self.write(f"curl: {err}\n")
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        for opt in optlist:
            if opt[0] == "-h" or opt[0] == "--help":
                self.write(CURL_HELP)
                self.exit()
                return
            elif opt[0] == "-s" or opt[0] == "--silent":
                self.silent = True

        if len(args):
            if args[0] is not None:
                url = str(args[0]).strip()
        else:
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        if "://" not in url:
            url = "http://" + url
        urldata = compat.urllib_parse.urlparse(url)

        outfile = None
        for opt in optlist:
            if opt[0] == "-o":
                outfile = opt[1]
            if opt[0] == "-O":
                outfile = urldata.path.split("/")[-1]
                if (
                    outfile is None
                    or not len(outfile.strip())
                    or not urldata.path.count("/")
                ):
                    self.write("curl: Remote file name has no length!\n")
                    self.exit()
                    return

        if outfile:
            outfile = self.fs.resolve_path(outfile, self.protocol.cwd)
            path = os.path.dirname(outfile)
            if not path or not self.fs.exists(path) or not self.fs.isdir(path):
                self.write(
                    "curl: %s: Cannot open: No such file or directory\n" % outfile
                )
                self.exit()
                return

        url = url.encode("ascii")
        self.url = url

        self.artifactFile = Artifact(outfile)
        # HTTPDownloader will close() the file object so need to preserve the name

        self.deferred = self.download(url, outfile, self.artifactFile)
        if self.deferred:
            self.deferred.addCallback(self.success, outfile)
            self.deferred.addErrback(self.error, url)
Exemplo n.º 7
0
    def start(self):
        try:
            optlist, args = getopt.getopt(self.args, "cvu:p:P:")
        except getopt.GetoptError:
            self.help()
            self.exit()
            return

        if len(args) < 2:
            self.help()
            self.exit()
            return

        self.verbose = False
        self.username = ""
        self.password = ""
        self.port = 21
        self.host = ""
        self.local_file = ""
        self.remote_path = ""

        for opt in optlist:
            if opt[0] == "-v":
                self.verbose = True
            elif opt[0] == "-u":
                self.username = opt[1]
            elif opt[0] == "-p":
                self.password = opt[1]
            elif opt[0] == "-P":
                try:
                    self.port = int(opt[1])
                except ValueError:
                    pass

        if len(args) == 2:
            self.host, self.remote_path = args
        elif len(args) >= 3:
            self.host, self.local_file, self.remote_path = args[:3]

        self.remote_dir = os.path.dirname(self.remote_path)
        self.remote_file = os.path.basename(self.remote_path)
        if not self.local_file:
            self.local_file = self.remote_file

        fakeoutfile = self.fs.resolve_path(self.local_file, self.protocol.cwd)
        path = os.path.dirname(fakeoutfile)
        if not path or not self.fs.exists(path) or not self.fs.isdir(path):
            self.write("ftpget: can't open '%s': No such file or directory" %
                       self.local_file)
            self.exit()
            return

        self.url_log = "ftp://"
        if self.username:
            self.url_log = f"{self.url_log}{self.username}"
            if self.password:
                self.url_log = f"{self.url_log}:{self.password}"
            self.url_log = f"{self.url_log}@"
        self.url_log = f"{self.url_log}{self.host}"
        if self.port != 21:
            self.url_log = f"{self.url_log}:{self.port}"
        self.url_log = f"{self.url_log}/{self.remote_path}"

        self.artifactFile = Artifact(self.local_file)

        result = self.ftp_download()

        self.artifactFile.close()

        if not result:
            # log to cowrie.log
            log.msg(
                format="Attempt to download file(s) from URL (%(url)s) failed",
                url=self.url_log,
            )

            self.protocol.logDispatch(
                eventid="cowrie.session.file_download.failed",
                format="Attempt to download file(s) from URL (%(url)s) failed",
                url=self.url_log,
            )
            self.exit()
            return

        # log to cowrie.log
        log.msg(
            format=
            "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
            url=self.url_log,
            outfile=self.artifactFile.shasumFilename,
            shasum=self.artifactFile.shasum,
        )

        self.protocol.logDispatch(
            eventid="cowrie.session.file_download",
            format=
            "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
            url=self.url_log,
            outfile=self.artifactFile.shasumFilename,
            shasum=self.artifactFile.shasum,
            destfile=self.local_file,
        )

        # Update the honeyfs to point to downloaded file
        self.fs.mkfile(fakeoutfile, 0, 0,
                       os.path.getsize(self.artifactFile.shasumFilename),
                       33188)
        self.fs.update_realfile(self.fs.getfile(fakeoutfile),
                                self.artifactFile.shasumFilename)
        self.fs.chown(fakeoutfile, self.protocol.user.uid,
                      self.protocol.user.gid)

        self.exit()
Exemplo n.º 8
0
    def start(self) -> None:
        try:
            optlist, args = getopt.getopt(
                self.args, "sho:O", ["help", "manual", "silent"]
            )
        except getopt.GetoptError as err:
            # TODO: should be 'unknown' instead of 'not recognized'
            self.write(f"curl: {err}\n")
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        for opt in optlist:
            if opt[0] == "-h" or opt[0] == "--help":
                self.write(CURL_HELP)
                self.exit()
                return
            elif opt[0] == "-s" or opt[0] == "--silent":
                self.silent = True

        if len(args):
            if args[0] is not None:
                url = str(args[0]).strip()
        else:
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        if "://" not in url:
            url = "http://" + url
        urldata = compat.urllib_parse.urlparse(url)

        for opt in optlist:
            if opt[0] == "-o":
                self.outfile = opt[1]
            if opt[0] == "-O":
                self.outfile = urldata.path.split("/")[-1]
                if (
                    self.outfile is None
                    or not len(self.outfile.strip())
                    or not urldata.path.count("/")
                ):
                    self.write("curl: Remote file name has no length!\n")
                    self.exit()
                    return

        if self.outfile:
            self.outfile = self.fs.resolve_path(self.outfile, self.protocol.cwd)
            if self.outfile:
                path = os.path.dirname(self.outfile)
            if not path or not self.fs.exists(path) or not self.fs.isdir(path):
                self.write(
                    f"curl: {self.outfile}: Cannot open: No such file or directory\n"
                )
                self.exit()
                return

        self.url = url.encode("ascii")

        parsed = compat.urllib_parse.urlparse(url)
        if parsed.hostname:
            self.host = parsed.hostname
        if parsed.scheme:
            scheme = parsed.scheme
        # port: int = parsed.port or (443 if scheme == "https" else 80)
        if scheme != "http" and scheme != "https":
            self.errorWrite(
                f'curl: (1) Protocol "{scheme}" not supported or disabled in libcurl\n'
            )
            self.exit()
            return

        # TODO: need to do full name resolution in case someon passes DNS name pointing to local address
        try:
            if ipaddress.ip_address(self.host).is_private:
                self.errorWrite(f"curl: (6) Could not resolve host: {self.host}\n")
                self.exit()
                return None
        except ValueError:
            pass

        self.artifact = Artifact("curl-download")

        self.deferred = self.treqDownload(url)
        if self.deferred:
            self.deferred.addCallback(self.success)
            self.deferred.addErrback(self.error)
Exemplo n.º 9
0
    def start(self):
        url: str
        try:
            optlist, args = getopt.getopt(self.args, "cqO:P:", ["header="])
        except getopt.GetoptError:
            self.errorWrite("Unrecognized option\n")
            self.exit()
            return

        if len(args):
            url = args[0].strip()
        else:
            self.errorWrite("wget: missing URL\n")
            self.errorWrite("Usage: wget [OPTION]... [URL]...\n\n")
            self.errorWrite("Try `wget --help' for more options.\n")
            self.exit()
            return

        self.outfile: str = None
        self.quiet = False
        for opt in optlist:
            if opt[0] == "-O":
                self.outfile = opt[1]
            if opt[0] == "-q":
                self.quiet = True

        # for some reason getopt doesn't recognize "-O -"
        # use try..except for the case if passed command is malformed
        try:
            if not self.outfile:
                if "-O" in args:
                    self.outfile = args[args.index("-O") + 1]
        except Exception:
            pass

        if "://" not in url:
            url = f"http://{url}"

        urldata = compat.urllib_parse.urlparse(url)

        self.host = urldata.hostname

        # TODO: need to do full name resolution in case someon passes DNS name pointing to local address
        try:
            if ipaddress.ip_address(self.host).is_private:
                self.errorWrite(
                    f"curl: (6) Could not resolve host: {self.host}\n")
                self.exit()
                return None
        except ValueError:
            pass

        self.url = url.encode("utf8")

        if self.outfile is None:
            self.outfile = urldata.path.split("/")[-1]
            if not len(self.outfile.strip()) or not urldata.path.count("/"):
                self.outfile = "index.html"

        if self.outfile != "-":
            self.outfile = self.fs.resolve_path(self.outfile,
                                                self.protocol.cwd)
            path = os.path.dirname(self.outfile)
            if not path or not self.fs.exists(path) or not self.fs.isdir(path):
                self.errorWrite(
                    "wget: {}: Cannot open: No such file or directory\n".
                    format(self.outfile))
                self.exit()
                return

        self.artifact = Artifact("curl-download")

        if not self.quiet:
            tm = time.strftime("%Y-%m-%d %H:%M:%S")
            self.errorWrite(f"--{tm}--  {url}\n")
            self.errorWrite(
                f"Connecting to {self.host}:{urldata.port}... connected.\n")
            self.errorWrite("HTTP request sent, awaiting response... ")

        self.deferred = self.wgetDownload(url)
        if self.deferred:
            self.deferred.addCallback(self.success)
            self.deferred.addErrback(self.error)
Exemplo n.º 10
0
    def download(self, url, fakeoutfile, *args, **kwargs):
        """
        url - URL to download
        fakeoutfile - file in guest's fs that attacker wants content to be downloaded to
        """
        try:
            parsed = compat.urllib_parse.urlparse(url)
            scheme = parsed.scheme
            host = parsed.hostname.decode("utf8")
            port = parsed.port or (443 if scheme == b"https" else 80)
            if scheme != b"http" and scheme != b"https":
                raise NotImplementedError
            if not host:
                return None
        except Exception:
            self.errorWrite(f"{url}: Unsupported scheme.\n")
            return None

        if not self.quiet:
            self.errorWrite(
                "--{}--  {}\n".format(
                    time.strftime("%Y-%m-%d %H:%M:%S"), url.decode("utf8")
                )
            )
            self.errorWrite(f"Connecting to {host}:{port}... connected.\n")
            self.errorWrite("HTTP request sent, awaiting response... ")

        # TODO: need to do full name resolution.
        try:
            if ipaddress.ip_address(host).is_private:
                self.errorWrite(
                    "Resolving {} ({})... failed: nodename nor servname provided, or not known.\n".format(
                        host, host
                    )
                )
                self.errorWrite(f"wget: unable to resolve host address ‘{host}’\n")
                return None
        except ValueError:
            pass

        # File in host's fs that will hold content of the downloaded file
        # HTTPDownloader will close() the file object so need to preserve the name
        self.artifactFile = Artifact(self.outfile)

        factory = HTTPProgressDownloader(
            self, fakeoutfile, url, self.artifactFile, *args, **kwargs
        )

        out_addr = None
        if CowrieConfig.has_option("honeypot", "out_addr"):
            out_addr = (CowrieConfig.get("honeypot", "out_addr"), 0)

        if scheme == b"https":
            context_factory = ssl.optionsForClientTLS(hostname=host)
            self.connection = reactor.connectSSL(
                host, port, factory, context_factory, bindAddress=out_addr
            )

        elif scheme == b"http":
            self.connection = reactor.connectTCP(
                host, port, factory, bindAddress=out_addr
            )
        else:
            raise NotImplementedError

        return factory.deferred
Exemplo n.º 11
0
    def start(self):
        try:
            optlist, args = getopt.getopt(self.args, 'sho:O',
                                          ['help', 'manual', 'silent'])
        except getopt.GetoptError as err:
            # TODO: should be 'unknown' instead of 'not recognized'
            self.write("curl: {}\n".format(err))
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        for opt in optlist:
            if opt[0] == '-h' or opt[0] == '--help':
                self.curl_help()
                return
            elif opt[0] == '-s' or opt[0] == '--silent':
                self.silent = True

        if len(args):
            if args[0] is not None:
                url = str(args[0]).strip()
        else:
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        if '://' not in url:
            url = 'http://' + url
        urldata = compat.urllib_parse.urlparse(url)

        outfile = None
        for opt in optlist:
            if opt[0] == '-o':
                outfile = opt[1]
            if opt[0] == '-O':
                outfile = urldata.path.split('/')[-1]
                if outfile is None or not len(
                        outfile.strip()) or not urldata.path.count('/'):
                    self.write('curl: Remote file name has no length!\n')
                    self.exit()
                    return

        if outfile:
            outfile = self.fs.resolve_path(outfile, self.protocol.cwd)
            path = os.path.dirname(outfile)
            if not path or \
                    not self.fs.exists(path) or \
                    not self.fs.isdir(path):
                self.write(
                    'curl: %s: Cannot open: No such file or directory\n' %
                    outfile)
                self.exit()
                return

        url = url.encode('ascii')
        self.url = url

        self.artifactFile = Artifact(outfile)
        # HTTPDownloader will close() the file object so need to preserve the name

        self.deferred = self.download(url, outfile, self.artifactFile)
        if self.deferred:
            self.deferred.addCallback(self.success, outfile)
            self.deferred.addErrback(self.error, url)