1# Copyright 2015 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import pprint
7import socket
8import time
9
10from autotest_lib.client.common_lib import error
11from autotest_lib.server.cros import stress
12from autotest_lib.server import autotest
13from autotest_lib.server import test
14
15_CLIENT_COMPLETE_FLAG = '/tmp/network_FirewallHolePunch'
16
17class network_FirewallHolePunchServer(test.test):
18    """Server test half of the FirewallHolePunch test."""
19    version = 1
20
21
22    def connect_to_dut(self):
23        """Attempts to connect to the DUT
24
25        @returns True if connection was successful; False otherwise.
26
27        """
28        clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
29        clientsocket.settimeout(5)
30
31        connected = False
32        try:
33            clientsocket.connect((self.hostname, self.port))
34            connected = True
35            logging.debug('Connected to client')
36        except socket.timeout:
37            logging.debug('Socket connection to DUT failed.')
38
39        return connected
40
41
42    def wait_for_client_test(self):
43        """Waits for the client test to complete it's task.
44
45        @returns True if the client responds to the request; False otherwise.
46
47        """
48
49        for i in range(30):
50            result = self.client.run('ls %s' %  _CLIENT_COMPLETE_FLAG,
51                                     ignore_status=True)
52            if result.exit_status == 0:
53                return True
54            time.sleep(1)
55        return False
56
57
58    def functional_test(self, test_error, test_fail, connected):
59        """Performs a functional testing of the firewall.
60
61        This performs a single test while coordinating with the client test.
62
63        @param test_error: string of the test error message
64        @param test_fail: string of the test fail message
65        @param connected: boolean test if the connection attempt should have
66                          passed or failed.
67
68        @raises: TestError if the client flag was not updated
69        @raises: TestFail if the connection expection is not met
70
71        """
72
73        self.client.run('rm %s' %  _CLIENT_COMPLETE_FLAG, ignore_status=True)
74        if self.wait_for_client_test() is False:
75            raise error.TestError(test_error)
76        if self.connect_to_dut() is connected:
77            raise error.TestFail(test_fail)
78
79
80    def perform_tests(self):
81        """Performs all of the tests in the script."""
82
83        for test in self.tests:
84            logging.debug('Performing...')
85            logging.debug(pprint.pprint(test))
86
87            self.functional_test(test['server_error'],
88                                 test['server_fail'],
89                                 test['server_connected'])
90
91
92    def run_once(self, host, port=8888):
93        """Run the test.
94
95        @param host: the host object
96        @param port: integer value for the port the client to listen on
97
98        """
99
100        # Strict ordering matters here.  If an invalid order is given
101        # below an exception will be thrown in the client test.
102        self.tests = [# Login, fail to connect
103            {'server_error': 'The client test did not login',
104             'server_fail' : 'Server was able to connect (login).',
105             'server_connected' : True,
106             'client_command' : 'login',
107             'client_error': 'Did not receive command to login (login)'
108            },
109            # Launch App, fail to connect
110            {'server_error': 'The client test did not launch the app',
111             'server_fail' : 'Server was able to connect (setup).',
112             'server_connected' : True,
113             'client_command' : 'launch app',
114             'client_error': 'Did not receive command to launch app (setup)'
115            },
116            # Start server, connect
117            {'server_error': 'The client test did not open the port. (1)',
118             'server_fail' : 'Server was unable to connect (1).',
119             'server_connected' : False,
120             'client_command' : 'start server',
121             'client_error': 'Did not receive command to start server (1)'
122            },
123            # Stop server, fail to connect
124            {'server_error' : 'The client test did not close the port',
125             'server_fail' : str('Server was able to connect to the port. (1) '
126                                '(It should not have been able to do so.)'),
127             'server_connected' : True,
128             'client_command' : 'stop server',
129             'client_error' : 'Did not receive command to stop server'
130            },
131            # Start server, connect
132            {'server_error' : 'The client test did not open the port. (2)',
133             'server_fail'  : 'Server was unable to connect (2).',
134             'server_connected'  : False,
135             'client_command' : 'start server',
136             'client_error' : 'Did not receive command to start server (2)'
137            },
138            # Quit app, fail to connect
139            {'server_error' : 'The client test did not close the app.',
140             'server_fail'  : str('Server was able to connect to the port (2). '
141                                '(It should not have been able to do so.)'),
142             'server_connected'  : True,
143             'client_command' : 'exit app',
144             'client_error' : 'Did not receive command to close app.'
145            },
146            # Telemetry cannot relaunch a closed extension; logout and back in.
147            # Logout, fail to connect
148            {'server_error' : 'The client test did not quit',
149             'server_fail' : str('Server was able to connect to the port (3). '
150                                '(It should not have been able to do so.)'),
151             'server_connected' : True,
152             'client_command' : 'logout',
153             'client_error': 'Did not receive command to exit.'
154            },
155            # Login, fail to connect
156            {'server_error': 'The client test did not login',
157             'server_fail' : 'Server was able to connect (login).',
158             'server_connected' : True,
159             'client_command' : 'login',
160             'client_error': 'Did not receive command to login (login)'
161            },
162            # Launch app, fail to connect
163            {'server_error': 'The client test did not launch the app',
164             'server_fail' : 'Server was able to connect (setup2).',
165             'server_connected' : True,
166             'client_command' : 'launch app',
167             'client_error': 'Did not receive command to launch app (setup2)'
168            },
169            # Start server, connect
170            {'server_error': 'The client test did not open the port. (1)',
171             'server_fail' : 'Server was unable to connect (1).',
172             'server_connected' : False,
173             'client_command' : 'start server',
174             'client_error': 'Did not receive command to start server (1)'
175            },
176            # Logout, fail to connect
177            {'server_error' : 'The client test did not quit',
178             'server_fail' : str('Server was able to connect to the port (3). '
179                                '(It should not have been able to do so.)'),
180             'server_connected' : True,
181             'client_command' : 'logout',
182             'client_error': 'Did not receive command to exit.'
183            }
184            ]
185
186        self.client = host
187        self.hostname = self.client.hostname
188        self.port = port
189        client_at = autotest.Autotest(self.client)
190
191        self.client.run('rm %s' %  _CLIENT_COMPLETE_FLAG, ignore_status=True)
192
193        stressor = stress.CountedStressor(self.perform_tests)
194        stressor.start(1)
195        client_at.run_test('network_FirewallHolePunch',
196                           test_sequence=self.tests,
197                           port=self.port)
198
199