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