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