1from __future__ import print_function
2
3import gdbremote_testcase
4import select
5import socket
6from lldbsuite.test.decorators import *
7from lldbsuite.test.lldbtest import *
8import lldbsuite.test.lldbplatformutil
9import random
10
11if lldbplatformutil.getHostPlatform() == "windows":
12    import ctypes
13    import ctypes.wintypes
14    from ctypes.wintypes import (BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID)
15
16    kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
17
18    PIPE_ACCESS_INBOUND = 1
19    FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
20    FILE_FLAG_OVERLAPPED = 0x40000000
21    PIPE_TYPE_BYTE = 0
22    PIPE_REJECT_REMOTE_CLIENTS = 8
23    INVALID_HANDLE_VALUE = -1
24    ERROR_ACCESS_DENIED = 5
25    ERROR_IO_PENDING = 997
26
27
28    class OVERLAPPED(ctypes.Structure):
29        _fields_ = [("Internal", LPVOID), ("InternalHigh", LPVOID), ("Offset",
30            DWORD), ("OffsetHigh", DWORD), ("hEvent", HANDLE)]
31
32        def __init__(self):
33            super(OVERLAPPED, self).__init__(Internal=0, InternalHigh=0,
34                Offset=0, OffsetHigh=0, hEvent=None)
35    LPOVERLAPPED = ctypes.POINTER(OVERLAPPED)
36
37    CreateNamedPipe = kernel32.CreateNamedPipeW
38    CreateNamedPipe.restype = HANDLE
39    CreateNamedPipe.argtypes = (LPCWSTR, DWORD, DWORD, DWORD, DWORD, DWORD,
40            DWORD, LPVOID)
41
42    ConnectNamedPipe = kernel32.ConnectNamedPipe
43    ConnectNamedPipe.restype = BOOL
44    ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED)
45
46    CreateEvent = kernel32.CreateEventW
47    CreateEvent.restype = HANDLE
48    CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR)
49
50    GetOverlappedResultEx = kernel32.GetOverlappedResultEx
51    GetOverlappedResultEx.restype = BOOL
52    GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD,
53        BOOL)
54
55    ReadFile = kernel32.ReadFile
56    ReadFile.restype = BOOL
57    ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)
58
59    CloseHandle = kernel32.CloseHandle
60    CloseHandle.restype = BOOL
61    CloseHandle.argtypes = (HANDLE,)
62
63    class Pipe(object):
64        def __init__(self, prefix):
65            while True:
66                self.name = "lldb-" + str(random.randrange(1e10))
67                full_name = "\\\\.\\pipe\\" + self.name
68                self._handle = CreateNamedPipe(full_name, PIPE_ACCESS_INBOUND |
69                        FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
70                        PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1, 4096,
71                        4096, 0, None)
72                if self._handle != INVALID_HANDLE_VALUE:
73                    break
74                if ctypes.get_last_error() != ERROR_ACCESS_DENIED:
75                    raise ctypes.WinError(ctypes.get_last_error())
76
77            self._overlapped = OVERLAPPED()
78            self._overlapped.hEvent = CreateEvent(None, True, False, None)
79            result = ConnectNamedPipe(self._handle, self._overlapped)
80            assert result == 0
81            if ctypes.get_last_error() != ERROR_IO_PENDING:
82                raise ctypes.WinError(ctypes.get_last_error())
83
84        def finish_connection(self, timeout):
85            if not GetOverlappedResultEx(self._handle, self._overlapped,
86                    ctypes.byref(DWORD(0)), timeout*1000, True):
87                raise ctypes.WinError(ctypes.get_last_error())
88
89        def read(self, size, timeout):
90            buf = ctypes.create_string_buffer(size)
91            if not ReadFile(self._handle, ctypes.byref(buf), size, None,
92                    self._overlapped):
93                if ctypes.get_last_error() != ERROR_IO_PENDING:
94                    raise ctypes.WinError(ctypes.get_last_error())
95            read = DWORD(0)
96            if not GetOverlappedResultEx(self._handle, self._overlapped,
97                    ctypes.byref(read), timeout*1000, True):
98                raise ctypes.WinError(ctypes.get_last_error())
99            return buf.raw[0:read.value]
100
101        def close(self):
102            CloseHandle(self._overlapped.hEvent)
103            CloseHandle(self._handle)
104
105
106else:
107    class Pipe(object):
108        def __init__(self, prefix):
109            self.name = os.path.join(prefix, "stub_port_number")
110            os.mkfifo(self.name)
111            self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK)
112
113        def finish_connection(self, timeout):
114            pass
115
116        def read(self, size, timeout):
117            (readers, _, _) = select.select([self._fd], [], [], timeout)
118            if self._fd not in readers:
119                raise TimeoutError
120            return os.read(self._fd, size)
121
122        def close(self):
123            os.close(self._fd)
124
125
126class TestGdbRemoteConnection(gdbremote_testcase.GdbRemoteTestCaseBase):
127
128    mydir = TestBase.compute_mydir(__file__)
129
130    @skipIfRemote  # reverse connect is not a supported use case for now
131    def test_reverse_connect(self):
132        # Reverse connect is the default connection method.
133        self.connect_to_debug_monitor()
134        # Verify we can do the handshake.  If that works, we'll call it good.
135        self.do_handshake(self.sock)
136
137    @skipIfRemote
138    def test_named_pipe(self):
139        family, type, proto, _, addr = socket.getaddrinfo(
140            self.stub_hostname, 0, proto=socket.IPPROTO_TCP)[0]
141        self.sock = socket.socket(family, type, proto)
142        self.sock.settimeout(self.DEFAULT_TIMEOUT)
143
144        self.addTearDownHook(lambda: self.sock.close())
145
146        pipe = Pipe(self.getBuildDir())
147
148        self.addTearDownHook(lambda: pipe.close())
149
150        args = self.debug_monitor_extra_args
151        if lldb.remote_platform:
152            args += ["*:0"]
153        else:
154            args += ["localhost:0"]
155
156        args += ["--named-pipe", pipe.name]
157
158        server = self.spawnSubprocess(
159            self.debug_monitor_exe,
160            args,
161            install_remote=False)
162
163        pipe.finish_connection(self.DEFAULT_TIMEOUT)
164        port = pipe.read(10, self.DEFAULT_TIMEOUT)
165        # Trim null byte, convert to int
166        addr = (addr[0], int(port[:-1]))
167        self.sock.connect(addr)
168
169        # Verify we can do the handshake.  If that works, we'll call it good.
170        self.do_handshake(self.sock)
171