xref: /aosp_15_r20/external/curl/tests/smbserver.py (revision 6236dae45794135f37c4eb022389c904c8b0090d)
1*6236dae4SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6236dae4SAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
3*6236dae4SAndroid Build Coastguard Worker#
4*6236dae4SAndroid Build Coastguard Worker#  Project                     ___| | | |  _ \| |
5*6236dae4SAndroid Build Coastguard Worker#                             / __| | | | |_) | |
6*6236dae4SAndroid Build Coastguard Worker#                            | (__| |_| |  _ <| |___
7*6236dae4SAndroid Build Coastguard Worker#                             \___|\___/|_| \_\_____|
8*6236dae4SAndroid Build Coastguard Worker#
9*6236dae4SAndroid Build Coastguard Worker# Copyright (C) Daniel Stenberg, <[email protected]>, et al.
10*6236dae4SAndroid Build Coastguard Worker#
11*6236dae4SAndroid Build Coastguard Worker# This software is licensed as described in the file COPYING, which
12*6236dae4SAndroid Build Coastguard Worker# you should have received as part of this distribution. The terms
13*6236dae4SAndroid Build Coastguard Worker# are also available at https://curl.se/docs/copyright.html.
14*6236dae4SAndroid Build Coastguard Worker#
15*6236dae4SAndroid Build Coastguard Worker# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16*6236dae4SAndroid Build Coastguard Worker# copies of the Software, and permit persons to whom the Software is
17*6236dae4SAndroid Build Coastguard Worker# furnished to do so, under the terms of the COPYING file.
18*6236dae4SAndroid Build Coastguard Worker#
19*6236dae4SAndroid Build Coastguard Worker# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20*6236dae4SAndroid Build Coastguard Worker# KIND, either express or implied.
21*6236dae4SAndroid Build Coastguard Worker#
22*6236dae4SAndroid Build Coastguard Worker# SPDX-License-Identifier: curl
23*6236dae4SAndroid Build Coastguard Worker#
24*6236dae4SAndroid Build Coastguard Worker"""Server for testing SMB."""
25*6236dae4SAndroid Build Coastguard Worker
26*6236dae4SAndroid Build Coastguard Workerfrom __future__ import (absolute_import, division, print_function,
27*6236dae4SAndroid Build Coastguard Worker                        unicode_literals)
28*6236dae4SAndroid Build Coastguard Worker
29*6236dae4SAndroid Build Coastguard Workerimport argparse
30*6236dae4SAndroid Build Coastguard Workerimport logging
31*6236dae4SAndroid Build Coastguard Workerimport os
32*6236dae4SAndroid Build Coastguard Workerimport signal
33*6236dae4SAndroid Build Coastguard Workerimport sys
34*6236dae4SAndroid Build Coastguard Workerimport tempfile
35*6236dae4SAndroid Build Coastguard Workerimport threading
36*6236dae4SAndroid Build Coastguard Worker
37*6236dae4SAndroid Build Coastguard Worker# Import our curl test data helper
38*6236dae4SAndroid Build Coastguard Workerfrom util import ClosingFileHandler, TestData
39*6236dae4SAndroid Build Coastguard Worker
40*6236dae4SAndroid Build Coastguard Workerif sys.version_info.major >= 3:
41*6236dae4SAndroid Build Coastguard Worker    import configparser
42*6236dae4SAndroid Build Coastguard Workerelse:
43*6236dae4SAndroid Build Coastguard Worker    import ConfigParser as configparser
44*6236dae4SAndroid Build Coastguard Worker
45*6236dae4SAndroid Build Coastguard Worker# impacket needs to be installed in the Python environment
46*6236dae4SAndroid Build Coastguard Workertry:
47*6236dae4SAndroid Build Coastguard Worker    import impacket  # noqa: F401
48*6236dae4SAndroid Build Coastguard Workerexcept ImportError:
49*6236dae4SAndroid Build Coastguard Worker    sys.stderr.write(
50*6236dae4SAndroid Build Coastguard Worker        'Warning: Python package impacket is required for smb testing; '
51*6236dae4SAndroid Build Coastguard Worker        'use pip or your package manager to install it\n')
52*6236dae4SAndroid Build Coastguard Worker    sys.exit(1)
53*6236dae4SAndroid Build Coastguard Workerfrom impacket import smb as imp_smb
54*6236dae4SAndroid Build Coastguard Workerfrom impacket import smbserver as imp_smbserver
55*6236dae4SAndroid Build Coastguard Workerfrom impacket.nt_errors import (STATUS_ACCESS_DENIED, STATUS_NO_SUCH_FILE,
56*6236dae4SAndroid Build Coastguard Worker                                STATUS_SUCCESS)
57*6236dae4SAndroid Build Coastguard Worker
58*6236dae4SAndroid Build Coastguard Workerlog = logging.getLogger(__name__)
59*6236dae4SAndroid Build Coastguard WorkerSERVER_MAGIC = "SERVER_MAGIC"
60*6236dae4SAndroid Build Coastguard WorkerTESTS_MAGIC = "TESTS_MAGIC"
61*6236dae4SAndroid Build Coastguard WorkerVERIFIED_REQ = "verifiedserver"
62*6236dae4SAndroid Build Coastguard WorkerVERIFIED_RSP = "WE ROOLZ: {pid}\n"
63*6236dae4SAndroid Build Coastguard Worker
64*6236dae4SAndroid Build Coastguard Worker
65*6236dae4SAndroid Build Coastguard Workerclass ShutdownHandler(threading.Thread):
66*6236dae4SAndroid Build Coastguard Worker    """
67*6236dae4SAndroid Build Coastguard Worker    Cleanly shut down the SMB server.
68*6236dae4SAndroid Build Coastguard Worker
69*6236dae4SAndroid Build Coastguard Worker    This can only be done from another thread while the server is in
70*6236dae4SAndroid Build Coastguard Worker    serve_forever(), so a thread is spawned here that waits for a shutdown
71*6236dae4SAndroid Build Coastguard Worker    signal before doing its thing. Use in a with statement around the
72*6236dae4SAndroid Build Coastguard Worker    serve_forever() call.
73*6236dae4SAndroid Build Coastguard Worker    """
74*6236dae4SAndroid Build Coastguard Worker
75*6236dae4SAndroid Build Coastguard Worker    def __init__(self, server):
76*6236dae4SAndroid Build Coastguard Worker        super(ShutdownHandler, self).__init__()
77*6236dae4SAndroid Build Coastguard Worker        self.server = server
78*6236dae4SAndroid Build Coastguard Worker        self.shutdown_event = threading.Event()
79*6236dae4SAndroid Build Coastguard Worker
80*6236dae4SAndroid Build Coastguard Worker    def __enter__(self):
81*6236dae4SAndroid Build Coastguard Worker        self.start()
82*6236dae4SAndroid Build Coastguard Worker        signal.signal(signal.SIGINT, self._sighandler)
83*6236dae4SAndroid Build Coastguard Worker        signal.signal(signal.SIGTERM, self._sighandler)
84*6236dae4SAndroid Build Coastguard Worker
85*6236dae4SAndroid Build Coastguard Worker    def __exit__(self, *_):
86*6236dae4SAndroid Build Coastguard Worker        # Call for shutdown just in case it wasn't done already
87*6236dae4SAndroid Build Coastguard Worker        self.shutdown_event.set()
88*6236dae4SAndroid Build Coastguard Worker        # Wait for thread, and therefore also the server, to finish
89*6236dae4SAndroid Build Coastguard Worker        self.join()
90*6236dae4SAndroid Build Coastguard Worker        # Uninstall our signal handlers
91*6236dae4SAndroid Build Coastguard Worker        signal.signal(signal.SIGINT, signal.SIG_DFL)
92*6236dae4SAndroid Build Coastguard Worker        signal.signal(signal.SIGTERM, signal.SIG_DFL)
93*6236dae4SAndroid Build Coastguard Worker        # Delete any temporary files created by the server during its run
94*6236dae4SAndroid Build Coastguard Worker        log.info("Deleting %d temporary file(s)", len(self.server.tmpfiles))
95*6236dae4SAndroid Build Coastguard Worker        for f in self.server.tmpfiles:
96*6236dae4SAndroid Build Coastguard Worker            os.unlink(f)
97*6236dae4SAndroid Build Coastguard Worker
98*6236dae4SAndroid Build Coastguard Worker    def _sighandler(self, _signum, _frame):
99*6236dae4SAndroid Build Coastguard Worker        # Wake up the cleanup task
100*6236dae4SAndroid Build Coastguard Worker        self.shutdown_event.set()
101*6236dae4SAndroid Build Coastguard Worker
102*6236dae4SAndroid Build Coastguard Worker    def run(self):
103*6236dae4SAndroid Build Coastguard Worker        # Wait for shutdown signal
104*6236dae4SAndroid Build Coastguard Worker        self.shutdown_event.wait()
105*6236dae4SAndroid Build Coastguard Worker        # Notify the server to shut down
106*6236dae4SAndroid Build Coastguard Worker        self.server.shutdown()
107*6236dae4SAndroid Build Coastguard Worker
108*6236dae4SAndroid Build Coastguard Worker
109*6236dae4SAndroid Build Coastguard Workerdef smbserver(options):
110*6236dae4SAndroid Build Coastguard Worker    """Start up a TCP SMB server that serves forever."""
111*6236dae4SAndroid Build Coastguard Worker    if options.pidfile:
112*6236dae4SAndroid Build Coastguard Worker        pid = os.getpid()
113*6236dae4SAndroid Build Coastguard Worker        # see tests/server/util.c function write_pidfile
114*6236dae4SAndroid Build Coastguard Worker        if os.name == "nt":
115*6236dae4SAndroid Build Coastguard Worker            pid += 65536
116*6236dae4SAndroid Build Coastguard Worker        with open(options.pidfile, "w") as f:
117*6236dae4SAndroid Build Coastguard Worker            f.write(str(pid))
118*6236dae4SAndroid Build Coastguard Worker
119*6236dae4SAndroid Build Coastguard Worker    # Here we write a mini config for the server
120*6236dae4SAndroid Build Coastguard Worker    smb_config = configparser.ConfigParser()
121*6236dae4SAndroid Build Coastguard Worker    smb_config.add_section("global")
122*6236dae4SAndroid Build Coastguard Worker    smb_config.set("global", "server_name", "SERVICE")
123*6236dae4SAndroid Build Coastguard Worker    smb_config.set("global", "server_os", "UNIX")
124*6236dae4SAndroid Build Coastguard Worker    smb_config.set("global", "server_domain", "WORKGROUP")
125*6236dae4SAndroid Build Coastguard Worker    smb_config.set("global", "log_file", "None")
126*6236dae4SAndroid Build Coastguard Worker    smb_config.set("global", "credentials_file", "")
127*6236dae4SAndroid Build Coastguard Worker
128*6236dae4SAndroid Build Coastguard Worker    # We need a share which allows us to test that the server is running
129*6236dae4SAndroid Build Coastguard Worker    smb_config.add_section("SERVER")
130*6236dae4SAndroid Build Coastguard Worker    smb_config.set("SERVER", "comment", "server function")
131*6236dae4SAndroid Build Coastguard Worker    smb_config.set("SERVER", "read only", "yes")
132*6236dae4SAndroid Build Coastguard Worker    smb_config.set("SERVER", "share type", "0")
133*6236dae4SAndroid Build Coastguard Worker    smb_config.set("SERVER", "path", SERVER_MAGIC)
134*6236dae4SAndroid Build Coastguard Worker
135*6236dae4SAndroid Build Coastguard Worker    # Have a share for tests.  These files will be autogenerated from the
136*6236dae4SAndroid Build Coastguard Worker    # test input.
137*6236dae4SAndroid Build Coastguard Worker    smb_config.add_section("TESTS")
138*6236dae4SAndroid Build Coastguard Worker    smb_config.set("TESTS", "comment", "tests")
139*6236dae4SAndroid Build Coastguard Worker    smb_config.set("TESTS", "read only", "yes")
140*6236dae4SAndroid Build Coastguard Worker    smb_config.set("TESTS", "share type", "0")
141*6236dae4SAndroid Build Coastguard Worker    smb_config.set("TESTS", "path", TESTS_MAGIC)
142*6236dae4SAndroid Build Coastguard Worker
143*6236dae4SAndroid Build Coastguard Worker    if not options.srcdir or not os.path.isdir(options.srcdir):
144*6236dae4SAndroid Build Coastguard Worker        raise ScriptError("--srcdir is mandatory")
145*6236dae4SAndroid Build Coastguard Worker
146*6236dae4SAndroid Build Coastguard Worker    test_data_dir = os.path.join(options.srcdir, "data")
147*6236dae4SAndroid Build Coastguard Worker
148*6236dae4SAndroid Build Coastguard Worker    smb_server = TestSmbServer((options.host, options.port),
149*6236dae4SAndroid Build Coastguard Worker                               config_parser=smb_config,
150*6236dae4SAndroid Build Coastguard Worker                               test_data_directory=test_data_dir)
151*6236dae4SAndroid Build Coastguard Worker    log.info("[SMB] setting up SMB server on port %s", options.port)
152*6236dae4SAndroid Build Coastguard Worker    smb_server.processConfigFile()
153*6236dae4SAndroid Build Coastguard Worker
154*6236dae4SAndroid Build Coastguard Worker    # Start a thread that cleanly shuts down the server on a signal
155*6236dae4SAndroid Build Coastguard Worker    with ShutdownHandler(smb_server):
156*6236dae4SAndroid Build Coastguard Worker        # This will block until smb_server.shutdown() is called
157*6236dae4SAndroid Build Coastguard Worker        smb_server.serve_forever()
158*6236dae4SAndroid Build Coastguard Worker
159*6236dae4SAndroid Build Coastguard Worker    return 0
160*6236dae4SAndroid Build Coastguard Worker
161*6236dae4SAndroid Build Coastguard Worker
162*6236dae4SAndroid Build Coastguard Workerclass TestSmbServer(imp_smbserver.SMBSERVER):
163*6236dae4SAndroid Build Coastguard Worker    """
164*6236dae4SAndroid Build Coastguard Worker    Test server for SMB which subclasses the impacket SMBSERVER and provides
165*6236dae4SAndroid Build Coastguard Worker    test functionality.
166*6236dae4SAndroid Build Coastguard Worker    """
167*6236dae4SAndroid Build Coastguard Worker
168*6236dae4SAndroid Build Coastguard Worker    def __init__(self,
169*6236dae4SAndroid Build Coastguard Worker                 address,
170*6236dae4SAndroid Build Coastguard Worker                 config_parser=None,
171*6236dae4SAndroid Build Coastguard Worker                 test_data_directory=None):
172*6236dae4SAndroid Build Coastguard Worker        imp_smbserver.SMBSERVER.__init__(self,
173*6236dae4SAndroid Build Coastguard Worker                                         address,
174*6236dae4SAndroid Build Coastguard Worker                                         config_parser=config_parser)
175*6236dae4SAndroid Build Coastguard Worker        self.tmpfiles = []
176*6236dae4SAndroid Build Coastguard Worker
177*6236dae4SAndroid Build Coastguard Worker        # Set up a test data object so we can get test data later.
178*6236dae4SAndroid Build Coastguard Worker        self.ctd = TestData(test_data_directory)
179*6236dae4SAndroid Build Coastguard Worker
180*6236dae4SAndroid Build Coastguard Worker        # Override smbComNtCreateAndX so we can pretend to have files which
181*6236dae4SAndroid Build Coastguard Worker        # don't exist.
182*6236dae4SAndroid Build Coastguard Worker        self.hookSmbCommand(imp_smb.SMB.SMB_COM_NT_CREATE_ANDX,
183*6236dae4SAndroid Build Coastguard Worker                            self.create_and_x)
184*6236dae4SAndroid Build Coastguard Worker
185*6236dae4SAndroid Build Coastguard Worker    def create_and_x(self, conn_id, smb_server, smb_command, recv_packet):
186*6236dae4SAndroid Build Coastguard Worker        """
187*6236dae4SAndroid Build Coastguard Worker        Our version of smbComNtCreateAndX looks for special test files and
188*6236dae4SAndroid Build Coastguard Worker        fools the rest of the framework into opening them as if they were
189*6236dae4SAndroid Build Coastguard Worker        normal files.
190*6236dae4SAndroid Build Coastguard Worker        """
191*6236dae4SAndroid Build Coastguard Worker        conn_data = smb_server.getConnectionData(conn_id)
192*6236dae4SAndroid Build Coastguard Worker
193*6236dae4SAndroid Build Coastguard Worker        # Wrap processing in a try block which allows us to throw SmbError
194*6236dae4SAndroid Build Coastguard Worker        # to control the flow.
195*6236dae4SAndroid Build Coastguard Worker        try:
196*6236dae4SAndroid Build Coastguard Worker            ncax_parms = imp_smb.SMBNtCreateAndX_Parameters(
197*6236dae4SAndroid Build Coastguard Worker                smb_command["Parameters"])
198*6236dae4SAndroid Build Coastguard Worker
199*6236dae4SAndroid Build Coastguard Worker            path = self.get_share_path(conn_data,
200*6236dae4SAndroid Build Coastguard Worker                                       ncax_parms["RootFid"],
201*6236dae4SAndroid Build Coastguard Worker                                       recv_packet["Tid"])
202*6236dae4SAndroid Build Coastguard Worker            log.info("[SMB] Requested share path: %s", path)
203*6236dae4SAndroid Build Coastguard Worker
204*6236dae4SAndroid Build Coastguard Worker            disposition = ncax_parms["Disposition"]
205*6236dae4SAndroid Build Coastguard Worker            log.debug("[SMB] Requested disposition: %s", disposition)
206*6236dae4SAndroid Build Coastguard Worker
207*6236dae4SAndroid Build Coastguard Worker            # Currently we only support reading files.
208*6236dae4SAndroid Build Coastguard Worker            if disposition != imp_smb.FILE_OPEN:
209*6236dae4SAndroid Build Coastguard Worker                raise SmbError(STATUS_ACCESS_DENIED,
210*6236dae4SAndroid Build Coastguard Worker                                   "Only support reading files")
211*6236dae4SAndroid Build Coastguard Worker
212*6236dae4SAndroid Build Coastguard Worker            # Check to see if the path we were given is actually a
213*6236dae4SAndroid Build Coastguard Worker            # magic path which needs generating on the fly.
214*6236dae4SAndroid Build Coastguard Worker            if path not in [SERVER_MAGIC, TESTS_MAGIC]:
215*6236dae4SAndroid Build Coastguard Worker                # Pass the command onto the original handler.
216*6236dae4SAndroid Build Coastguard Worker                return imp_smbserver.SMBCommands.smbComNtCreateAndX(conn_id,
217*6236dae4SAndroid Build Coastguard Worker                                                                    smb_server,
218*6236dae4SAndroid Build Coastguard Worker                                                                    smb_command,
219*6236dae4SAndroid Build Coastguard Worker                                                                    recv_packet)
220*6236dae4SAndroid Build Coastguard Worker
221*6236dae4SAndroid Build Coastguard Worker            flags2 = recv_packet["Flags2"]
222*6236dae4SAndroid Build Coastguard Worker            ncax_data = imp_smb.SMBNtCreateAndX_Data(flags=flags2,
223*6236dae4SAndroid Build Coastguard Worker                                                     data=smb_command[
224*6236dae4SAndroid Build Coastguard Worker                                                         "Data"])
225*6236dae4SAndroid Build Coastguard Worker            requested_file = imp_smbserver.decodeSMBString(
226*6236dae4SAndroid Build Coastguard Worker                flags2,
227*6236dae4SAndroid Build Coastguard Worker                ncax_data["FileName"])
228*6236dae4SAndroid Build Coastguard Worker            log.debug("[SMB] User requested file '%s'", requested_file)
229*6236dae4SAndroid Build Coastguard Worker
230*6236dae4SAndroid Build Coastguard Worker            if path == SERVER_MAGIC:
231*6236dae4SAndroid Build Coastguard Worker                fid, full_path = self.get_server_path(requested_file)
232*6236dae4SAndroid Build Coastguard Worker            else:
233*6236dae4SAndroid Build Coastguard Worker                assert path == TESTS_MAGIC
234*6236dae4SAndroid Build Coastguard Worker                fid, full_path = self.get_test_path(requested_file)
235*6236dae4SAndroid Build Coastguard Worker
236*6236dae4SAndroid Build Coastguard Worker            self.tmpfiles.append(full_path)
237*6236dae4SAndroid Build Coastguard Worker
238*6236dae4SAndroid Build Coastguard Worker            resp_parms = imp_smb.SMBNtCreateAndXResponse_Parameters()
239*6236dae4SAndroid Build Coastguard Worker            resp_data = ""
240*6236dae4SAndroid Build Coastguard Worker
241*6236dae4SAndroid Build Coastguard Worker            # Simple way to generate a fid
242*6236dae4SAndroid Build Coastguard Worker            if len(conn_data["OpenedFiles"]) == 0:
243*6236dae4SAndroid Build Coastguard Worker                fakefid = 1
244*6236dae4SAndroid Build Coastguard Worker            else:
245*6236dae4SAndroid Build Coastguard Worker                fakefid = conn_data["OpenedFiles"].keys()[-1] + 1
246*6236dae4SAndroid Build Coastguard Worker            resp_parms["Fid"] = fakefid
247*6236dae4SAndroid Build Coastguard Worker            resp_parms["CreateAction"] = disposition
248*6236dae4SAndroid Build Coastguard Worker
249*6236dae4SAndroid Build Coastguard Worker            if os.path.isdir(path):
250*6236dae4SAndroid Build Coastguard Worker                resp_parms[
251*6236dae4SAndroid Build Coastguard Worker                    "FileAttributes"] = imp_smb.SMB_FILE_ATTRIBUTE_DIRECTORY
252*6236dae4SAndroid Build Coastguard Worker                resp_parms["IsDirectory"] = 1
253*6236dae4SAndroid Build Coastguard Worker            else:
254*6236dae4SAndroid Build Coastguard Worker                resp_parms["IsDirectory"] = 0
255*6236dae4SAndroid Build Coastguard Worker                resp_parms["FileAttributes"] = ncax_parms["FileAttributes"]
256*6236dae4SAndroid Build Coastguard Worker
257*6236dae4SAndroid Build Coastguard Worker            # Get this file's information
258*6236dae4SAndroid Build Coastguard Worker            resp_info, error_code = imp_smbserver.queryPathInformation(
259*6236dae4SAndroid Build Coastguard Worker                os.path.dirname(full_path), os.path.basename(full_path),
260*6236dae4SAndroid Build Coastguard Worker                level=imp_smb.SMB_QUERY_FILE_ALL_INFO)
261*6236dae4SAndroid Build Coastguard Worker
262*6236dae4SAndroid Build Coastguard Worker            if error_code != STATUS_SUCCESS:
263*6236dae4SAndroid Build Coastguard Worker                raise SmbError(error_code, "Failed to query path info")
264*6236dae4SAndroid Build Coastguard Worker
265*6236dae4SAndroid Build Coastguard Worker            resp_parms["CreateTime"] = resp_info["CreationTime"]
266*6236dae4SAndroid Build Coastguard Worker            resp_parms["LastAccessTime"] = resp_info[
267*6236dae4SAndroid Build Coastguard Worker                "LastAccessTime"]
268*6236dae4SAndroid Build Coastguard Worker            resp_parms["LastWriteTime"] = resp_info["LastWriteTime"]
269*6236dae4SAndroid Build Coastguard Worker            resp_parms["LastChangeTime"] = resp_info[
270*6236dae4SAndroid Build Coastguard Worker                "LastChangeTime"]
271*6236dae4SAndroid Build Coastguard Worker            resp_parms["FileAttributes"] = resp_info[
272*6236dae4SAndroid Build Coastguard Worker                "ExtFileAttributes"]
273*6236dae4SAndroid Build Coastguard Worker            resp_parms["AllocationSize"] = resp_info[
274*6236dae4SAndroid Build Coastguard Worker                "AllocationSize"]
275*6236dae4SAndroid Build Coastguard Worker            resp_parms["EndOfFile"] = resp_info["EndOfFile"]
276*6236dae4SAndroid Build Coastguard Worker
277*6236dae4SAndroid Build Coastguard Worker            # Let's store the fid for the connection
278*6236dae4SAndroid Build Coastguard Worker            # smbServer.log("Create file %s, mode:0x%x" % (pathName, mode))
279*6236dae4SAndroid Build Coastguard Worker            conn_data["OpenedFiles"][fakefid] = {}
280*6236dae4SAndroid Build Coastguard Worker            conn_data["OpenedFiles"][fakefid]["FileHandle"] = fid
281*6236dae4SAndroid Build Coastguard Worker            conn_data["OpenedFiles"][fakefid]["FileName"] = path
282*6236dae4SAndroid Build Coastguard Worker            conn_data["OpenedFiles"][fakefid]["DeleteOnClose"] = False
283*6236dae4SAndroid Build Coastguard Worker
284*6236dae4SAndroid Build Coastguard Worker        except SmbError as s:
285*6236dae4SAndroid Build Coastguard Worker            log.debug("[SMB] SmbError hit: %s", s)
286*6236dae4SAndroid Build Coastguard Worker            error_code = s.error_code
287*6236dae4SAndroid Build Coastguard Worker            resp_parms = ""
288*6236dae4SAndroid Build Coastguard Worker            resp_data = ""
289*6236dae4SAndroid Build Coastguard Worker
290*6236dae4SAndroid Build Coastguard Worker        resp_cmd = imp_smb.SMBCommand(imp_smb.SMB.SMB_COM_NT_CREATE_ANDX)
291*6236dae4SAndroid Build Coastguard Worker        resp_cmd["Parameters"] = resp_parms
292*6236dae4SAndroid Build Coastguard Worker        resp_cmd["Data"] = resp_data
293*6236dae4SAndroid Build Coastguard Worker        smb_server.setConnectionData(conn_id, conn_data)
294*6236dae4SAndroid Build Coastguard Worker
295*6236dae4SAndroid Build Coastguard Worker        return [resp_cmd], None, error_code
296*6236dae4SAndroid Build Coastguard Worker
297*6236dae4SAndroid Build Coastguard Worker    def get_share_path(self, conn_data, root_fid, tid):
298*6236dae4SAndroid Build Coastguard Worker        conn_shares = conn_data["ConnectedShares"]
299*6236dae4SAndroid Build Coastguard Worker
300*6236dae4SAndroid Build Coastguard Worker        if tid in conn_shares:
301*6236dae4SAndroid Build Coastguard Worker            if root_fid > 0:
302*6236dae4SAndroid Build Coastguard Worker                # If we have a rootFid, the path is relative to that fid
303*6236dae4SAndroid Build Coastguard Worker                path = conn_data["OpenedFiles"][root_fid]["FileName"]
304*6236dae4SAndroid Build Coastguard Worker                log.debug("RootFid present %s!" % path)
305*6236dae4SAndroid Build Coastguard Worker            else:
306*6236dae4SAndroid Build Coastguard Worker                if "path" in conn_shares[tid]:
307*6236dae4SAndroid Build Coastguard Worker                    path = conn_shares[tid]["path"]
308*6236dae4SAndroid Build Coastguard Worker                else:
309*6236dae4SAndroid Build Coastguard Worker                    raise SmbError(STATUS_ACCESS_DENIED,
310*6236dae4SAndroid Build Coastguard Worker                                       "Connection share had no path")
311*6236dae4SAndroid Build Coastguard Worker        else:
312*6236dae4SAndroid Build Coastguard Worker            raise SmbError(imp_smbserver.STATUS_SMB_BAD_TID,
313*6236dae4SAndroid Build Coastguard Worker                               "TID was invalid")
314*6236dae4SAndroid Build Coastguard Worker
315*6236dae4SAndroid Build Coastguard Worker        return path
316*6236dae4SAndroid Build Coastguard Worker
317*6236dae4SAndroid Build Coastguard Worker    def get_server_path(self, requested_filename):
318*6236dae4SAndroid Build Coastguard Worker        log.debug("[SMB] Get server path '%s'", requested_filename)
319*6236dae4SAndroid Build Coastguard Worker
320*6236dae4SAndroid Build Coastguard Worker        if requested_filename not in [VERIFIED_REQ]:
321*6236dae4SAndroid Build Coastguard Worker            raise SmbError(STATUS_NO_SUCH_FILE, "Couldn't find the file")
322*6236dae4SAndroid Build Coastguard Worker
323*6236dae4SAndroid Build Coastguard Worker        fid, filename = tempfile.mkstemp()
324*6236dae4SAndroid Build Coastguard Worker        log.debug("[SMB] Created %s (%d) for storing '%s'",
325*6236dae4SAndroid Build Coastguard Worker                  filename, fid, requested_filename)
326*6236dae4SAndroid Build Coastguard Worker
327*6236dae4SAndroid Build Coastguard Worker        contents = ""
328*6236dae4SAndroid Build Coastguard Worker
329*6236dae4SAndroid Build Coastguard Worker        if requested_filename == VERIFIED_REQ:
330*6236dae4SAndroid Build Coastguard Worker            log.debug("[SMB] Verifying server is alive")
331*6236dae4SAndroid Build Coastguard Worker            pid = os.getpid()
332*6236dae4SAndroid Build Coastguard Worker            # see tests/server/util.c function write_pidfile
333*6236dae4SAndroid Build Coastguard Worker            if os.name == "nt":
334*6236dae4SAndroid Build Coastguard Worker                pid += 65536
335*6236dae4SAndroid Build Coastguard Worker            contents = VERIFIED_RSP.format(pid=pid).encode('utf-8')
336*6236dae4SAndroid Build Coastguard Worker
337*6236dae4SAndroid Build Coastguard Worker        self.write_to_fid(fid, contents)
338*6236dae4SAndroid Build Coastguard Worker        return fid, filename
339*6236dae4SAndroid Build Coastguard Worker
340*6236dae4SAndroid Build Coastguard Worker    def write_to_fid(self, fid, contents):
341*6236dae4SAndroid Build Coastguard Worker        # Write the contents to file descriptor
342*6236dae4SAndroid Build Coastguard Worker        os.write(fid, contents)
343*6236dae4SAndroid Build Coastguard Worker        os.fsync(fid)
344*6236dae4SAndroid Build Coastguard Worker
345*6236dae4SAndroid Build Coastguard Worker        # Rewind the file to the beginning so a read gets us the contents
346*6236dae4SAndroid Build Coastguard Worker        os.lseek(fid, 0, os.SEEK_SET)
347*6236dae4SAndroid Build Coastguard Worker
348*6236dae4SAndroid Build Coastguard Worker    def get_test_path(self, requested_filename):
349*6236dae4SAndroid Build Coastguard Worker        log.info("[SMB] Get reply data from 'test%s'", requested_filename)
350*6236dae4SAndroid Build Coastguard Worker
351*6236dae4SAndroid Build Coastguard Worker        fid, filename = tempfile.mkstemp()
352*6236dae4SAndroid Build Coastguard Worker        log.debug("[SMB] Created %s (%d) for storing test '%s'",
353*6236dae4SAndroid Build Coastguard Worker                  filename, fid, requested_filename)
354*6236dae4SAndroid Build Coastguard Worker
355*6236dae4SAndroid Build Coastguard Worker        try:
356*6236dae4SAndroid Build Coastguard Worker            contents = self.ctd.get_test_data(requested_filename).encode('utf-8')
357*6236dae4SAndroid Build Coastguard Worker            self.write_to_fid(fid, contents)
358*6236dae4SAndroid Build Coastguard Worker            return fid, filename
359*6236dae4SAndroid Build Coastguard Worker
360*6236dae4SAndroid Build Coastguard Worker        except Exception:
361*6236dae4SAndroid Build Coastguard Worker            log.exception("Failed to make test file")
362*6236dae4SAndroid Build Coastguard Worker            raise SmbError(STATUS_NO_SUCH_FILE, "Failed to make test file")
363*6236dae4SAndroid Build Coastguard Worker
364*6236dae4SAndroid Build Coastguard Worker
365*6236dae4SAndroid Build Coastguard Workerclass SmbError(Exception):
366*6236dae4SAndroid Build Coastguard Worker    def __init__(self, error_code, error_message):
367*6236dae4SAndroid Build Coastguard Worker        super(SmbError, self).__init__(error_message)
368*6236dae4SAndroid Build Coastguard Worker        self.error_code = error_code
369*6236dae4SAndroid Build Coastguard Worker
370*6236dae4SAndroid Build Coastguard Worker
371*6236dae4SAndroid Build Coastguard Workerclass ScriptRC(object):
372*6236dae4SAndroid Build Coastguard Worker    """Enum for script return codes."""
373*6236dae4SAndroid Build Coastguard Worker
374*6236dae4SAndroid Build Coastguard Worker    SUCCESS = 0
375*6236dae4SAndroid Build Coastguard Worker    FAILURE = 1
376*6236dae4SAndroid Build Coastguard Worker    EXCEPTION = 2
377*6236dae4SAndroid Build Coastguard Worker
378*6236dae4SAndroid Build Coastguard Worker
379*6236dae4SAndroid Build Coastguard Workerclass ScriptError(Exception):
380*6236dae4SAndroid Build Coastguard Worker    pass
381*6236dae4SAndroid Build Coastguard Worker
382*6236dae4SAndroid Build Coastguard Worker
383*6236dae4SAndroid Build Coastguard Workerdef get_options():
384*6236dae4SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
385*6236dae4SAndroid Build Coastguard Worker
386*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--port", action="store", default=9017,
387*6236dae4SAndroid Build Coastguard Worker                      type=int, help="port to listen on")
388*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--host", action="store", default="127.0.0.1",
389*6236dae4SAndroid Build Coastguard Worker                      help="host to listen on")
390*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--verbose", action="store", type=int, default=0,
391*6236dae4SAndroid Build Coastguard Worker                        help="verbose output")
392*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--pidfile", action="store",
393*6236dae4SAndroid Build Coastguard Worker                        help="file name for the PID")
394*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--logfile", action="store",
395*6236dae4SAndroid Build Coastguard Worker                        help="file name for the log")
396*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--srcdir", action="store", help="test directory")
397*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--id", action="store", help="server ID")
398*6236dae4SAndroid Build Coastguard Worker    parser.add_argument("--ipv4", action="store_true", default=0,
399*6236dae4SAndroid Build Coastguard Worker                        help="IPv4 flag")
400*6236dae4SAndroid Build Coastguard Worker
401*6236dae4SAndroid Build Coastguard Worker    return parser.parse_args()
402*6236dae4SAndroid Build Coastguard Worker
403*6236dae4SAndroid Build Coastguard Worker
404*6236dae4SAndroid Build Coastguard Workerdef setup_logging(options):
405*6236dae4SAndroid Build Coastguard Worker    """Set up logging from the command line options."""
406*6236dae4SAndroid Build Coastguard Worker    root_logger = logging.getLogger()
407*6236dae4SAndroid Build Coastguard Worker    add_stdout = False
408*6236dae4SAndroid Build Coastguard Worker
409*6236dae4SAndroid Build Coastguard Worker    formatter = logging.Formatter("%(asctime)s %(levelname)-5.5s %(message)s")
410*6236dae4SAndroid Build Coastguard Worker
411*6236dae4SAndroid Build Coastguard Worker    # Write out to a logfile
412*6236dae4SAndroid Build Coastguard Worker    if options.logfile:
413*6236dae4SAndroid Build Coastguard Worker        handler = ClosingFileHandler(options.logfile)
414*6236dae4SAndroid Build Coastguard Worker        handler.setFormatter(formatter)
415*6236dae4SAndroid Build Coastguard Worker        handler.setLevel(logging.DEBUG)
416*6236dae4SAndroid Build Coastguard Worker        root_logger.addHandler(handler)
417*6236dae4SAndroid Build Coastguard Worker    else:
418*6236dae4SAndroid Build Coastguard Worker        # The logfile wasn't specified. Add a stdout logger.
419*6236dae4SAndroid Build Coastguard Worker        add_stdout = True
420*6236dae4SAndroid Build Coastguard Worker
421*6236dae4SAndroid Build Coastguard Worker    if options.verbose:
422*6236dae4SAndroid Build Coastguard Worker        # Add a stdout logger as well in verbose mode
423*6236dae4SAndroid Build Coastguard Worker        root_logger.setLevel(logging.DEBUG)
424*6236dae4SAndroid Build Coastguard Worker        add_stdout = True
425*6236dae4SAndroid Build Coastguard Worker    else:
426*6236dae4SAndroid Build Coastguard Worker        root_logger.setLevel(logging.WARNING)
427*6236dae4SAndroid Build Coastguard Worker
428*6236dae4SAndroid Build Coastguard Worker    if add_stdout:
429*6236dae4SAndroid Build Coastguard Worker        stdout_handler = logging.StreamHandler(sys.stdout)
430*6236dae4SAndroid Build Coastguard Worker        stdout_handler.setFormatter(formatter)
431*6236dae4SAndroid Build Coastguard Worker        stdout_handler.setLevel(logging.DEBUG)
432*6236dae4SAndroid Build Coastguard Worker        root_logger.addHandler(stdout_handler)
433*6236dae4SAndroid Build Coastguard Worker
434*6236dae4SAndroid Build Coastguard Worker
435*6236dae4SAndroid Build Coastguard Workerif __name__ == '__main__':
436*6236dae4SAndroid Build Coastguard Worker    # Get the options from the user.
437*6236dae4SAndroid Build Coastguard Worker    options = get_options()
438*6236dae4SAndroid Build Coastguard Worker
439*6236dae4SAndroid Build Coastguard Worker    # Setup logging using the user options
440*6236dae4SAndroid Build Coastguard Worker    setup_logging(options)
441*6236dae4SAndroid Build Coastguard Worker
442*6236dae4SAndroid Build Coastguard Worker    # Run main script.
443*6236dae4SAndroid Build Coastguard Worker    try:
444*6236dae4SAndroid Build Coastguard Worker        rc = smbserver(options)
445*6236dae4SAndroid Build Coastguard Worker    except Exception:
446*6236dae4SAndroid Build Coastguard Worker        log.exception('Error in SMB server')
447*6236dae4SAndroid Build Coastguard Worker        rc = ScriptRC.EXCEPTION
448*6236dae4SAndroid Build Coastguard Worker
449*6236dae4SAndroid Build Coastguard Worker    if options.pidfile and os.path.isfile(options.pidfile):
450*6236dae4SAndroid Build Coastguard Worker        os.unlink(options.pidfile)
451*6236dae4SAndroid Build Coastguard Worker
452*6236dae4SAndroid Build Coastguard Worker    log.info("[SMB] Returning %d", rc)
453*6236dae4SAndroid Build Coastguard Worker    sys.exit(rc)
454