1*f6aab3d8Srobertimport ctypes
2*f6aab3d8Srobertimport errno
3*f6aab3d8Srobertimport io
4*f6aab3d8Srobertimport threading
5*f6aab3d8Srobertimport socket
6*f6aab3d8Srobertimport traceback
7*f6aab3d8Srobertfrom lldbsuite.support import seven
8*f6aab3d8Srobert
9*f6aab3d8Srobertdef checksum(message):
10*f6aab3d8Srobert    """
11*f6aab3d8Srobert    Calculate the GDB server protocol checksum of the message.
12*f6aab3d8Srobert
13*f6aab3d8Srobert    The GDB server protocol uses a simple modulo 256 sum.
14*f6aab3d8Srobert    """
15*f6aab3d8Srobert    check = 0
16*f6aab3d8Srobert    for c in message:
17*f6aab3d8Srobert        check += ord(c)
18*f6aab3d8Srobert    return check % 256
19*f6aab3d8Srobert
20*f6aab3d8Srobert
21*f6aab3d8Srobertdef frame_packet(message):
22*f6aab3d8Srobert    """
23*f6aab3d8Srobert    Create a framed packet that's ready to send over the GDB connection
24*f6aab3d8Srobert    channel.
25*f6aab3d8Srobert
26*f6aab3d8Srobert    Framing includes surrounding the message between $ and #, and appending
27*f6aab3d8Srobert    a two character hex checksum.
28*f6aab3d8Srobert    """
29*f6aab3d8Srobert    return "$%s#%02x" % (message, checksum(message))
30*f6aab3d8Srobert
31*f6aab3d8Srobert
32*f6aab3d8Srobertdef escape_binary(message):
33*f6aab3d8Srobert    """
34*f6aab3d8Srobert    Escape the binary message using the process described in the GDB server
35*f6aab3d8Srobert    protocol documentation.
36*f6aab3d8Srobert
37*f6aab3d8Srobert    Most bytes are sent through as-is, but $, #, and { are escaped by writing
38*f6aab3d8Srobert    a { followed by the original byte mod 0x20.
39*f6aab3d8Srobert    """
40*f6aab3d8Srobert    out = ""
41*f6aab3d8Srobert    for c in message:
42*f6aab3d8Srobert        d = ord(c)
43*f6aab3d8Srobert        if d in (0x23, 0x24, 0x7d):
44*f6aab3d8Srobert            out += chr(0x7d)
45*f6aab3d8Srobert            out += chr(d ^ 0x20)
46*f6aab3d8Srobert        else:
47*f6aab3d8Srobert            out += c
48*f6aab3d8Srobert    return out
49*f6aab3d8Srobert
50*f6aab3d8Srobert
51*f6aab3d8Srobertdef hex_encode_bytes(message):
52*f6aab3d8Srobert    """
53*f6aab3d8Srobert    Encode the binary message by converting each byte into a two-character
54*f6aab3d8Srobert    hex string.
55*f6aab3d8Srobert    """
56*f6aab3d8Srobert    out = ""
57*f6aab3d8Srobert    for c in message:
58*f6aab3d8Srobert        out += "%02x" % ord(c)
59*f6aab3d8Srobert    return out
60*f6aab3d8Srobert
61*f6aab3d8Srobert
62*f6aab3d8Srobertdef hex_decode_bytes(hex_bytes):
63*f6aab3d8Srobert    """
64*f6aab3d8Srobert    Decode the hex string into a binary message by converting each two-character
65*f6aab3d8Srobert    hex string into a single output byte.
66*f6aab3d8Srobert    """
67*f6aab3d8Srobert    out = ""
68*f6aab3d8Srobert    hex_len = len(hex_bytes)
69*f6aab3d8Srobert    i = 0
70*f6aab3d8Srobert    while i < hex_len - 1:
71*f6aab3d8Srobert        out += chr(int(hex_bytes[i:i + 2], 16))
72*f6aab3d8Srobert        i += 2
73*f6aab3d8Srobert    return out
74*f6aab3d8Srobert
75*f6aab3d8Srobert
76*f6aab3d8Srobertclass MockGDBServerResponder:
77*f6aab3d8Srobert    """
78*f6aab3d8Srobert    A base class for handling client packets and issuing server responses for
79*f6aab3d8Srobert    GDB tests.
80*f6aab3d8Srobert
81*f6aab3d8Srobert    This handles many typical situations, while still allowing subclasses to
82*f6aab3d8Srobert    completely customize their responses.
83*f6aab3d8Srobert
84*f6aab3d8Srobert    Most subclasses will be interested in overriding the other() method, which
85*f6aab3d8Srobert    handles any packet not recognized in the common packet handling code.
86*f6aab3d8Srobert    """
87*f6aab3d8Srobert
88*f6aab3d8Srobert    registerCount = 40
89*f6aab3d8Srobert    packetLog = None
90*f6aab3d8Srobert    class RESPONSE_DISCONNECT: pass
91*f6aab3d8Srobert
92*f6aab3d8Srobert    def __init__(self):
93*f6aab3d8Srobert        self.packetLog = []
94*f6aab3d8Srobert
95*f6aab3d8Srobert    def respond(self, packet):
96*f6aab3d8Srobert        """
97*f6aab3d8Srobert        Return the unframed packet data that the server should issue in response
98*f6aab3d8Srobert        to the given packet received from the client.
99*f6aab3d8Srobert        """
100*f6aab3d8Srobert        self.packetLog.append(packet)
101*f6aab3d8Srobert        if packet is MockGDBServer.PACKET_INTERRUPT:
102*f6aab3d8Srobert            return self.interrupt()
103*f6aab3d8Srobert        if packet == "c":
104*f6aab3d8Srobert            return self.cont()
105*f6aab3d8Srobert        if packet.startswith("vCont;c"):
106*f6aab3d8Srobert            return self.vCont(packet)
107*f6aab3d8Srobert        if packet[0] == "A":
108*f6aab3d8Srobert            return self.A(packet)
109*f6aab3d8Srobert        if packet[0] == "D":
110*f6aab3d8Srobert            return self.D(packet)
111*f6aab3d8Srobert        if packet[0] == "g":
112*f6aab3d8Srobert            return self.readRegisters()
113*f6aab3d8Srobert        if packet[0] == "G":
114*f6aab3d8Srobert            # Gxxxxxxxxxxx
115*f6aab3d8Srobert            # Gxxxxxxxxxxx;thread:1234;
116*f6aab3d8Srobert            return self.writeRegisters(packet[1:].split(';')[0])
117*f6aab3d8Srobert        if packet[0] == "p":
118*f6aab3d8Srobert            regnum = packet[1:].split(';')[0]
119*f6aab3d8Srobert            return self.readRegister(int(regnum, 16))
120*f6aab3d8Srobert        if packet[0] == "P":
121*f6aab3d8Srobert            register, value = packet[1:].split("=")
122*f6aab3d8Srobert            return self.writeRegister(int(register, 16), value)
123*f6aab3d8Srobert        if packet[0] == "m":
124*f6aab3d8Srobert            addr, length = [int(x, 16) for x in packet[1:].split(',')]
125*f6aab3d8Srobert            return self.readMemory(addr, length)
126*f6aab3d8Srobert        if packet[0] == "M":
127*f6aab3d8Srobert            location, encoded_data = packet[1:].split(":")
128*f6aab3d8Srobert            addr, length = [int(x, 16) for x in location.split(',')]
129*f6aab3d8Srobert            return self.writeMemory(addr, encoded_data)
130*f6aab3d8Srobert        if packet[0:7] == "qSymbol":
131*f6aab3d8Srobert            return self.qSymbol(packet[8:])
132*f6aab3d8Srobert        if packet[0:10] == "qSupported":
133*f6aab3d8Srobert            return self.qSupported(packet[11:].split(";"))
134*f6aab3d8Srobert        if packet == "qfThreadInfo":
135*f6aab3d8Srobert            return self.qfThreadInfo()
136*f6aab3d8Srobert        if packet == "qsThreadInfo":
137*f6aab3d8Srobert            return self.qsThreadInfo()
138*f6aab3d8Srobert        if packet == "qC":
139*f6aab3d8Srobert            return self.qC()
140*f6aab3d8Srobert        if packet == "QEnableErrorStrings":
141*f6aab3d8Srobert            return self.QEnableErrorStrings()
142*f6aab3d8Srobert        if packet == "?":
143*f6aab3d8Srobert            return self.haltReason()
144*f6aab3d8Srobert        if packet == "s":
145*f6aab3d8Srobert            return self.haltReason()
146*f6aab3d8Srobert        if packet[0] == "H":
147*f6aab3d8Srobert            tid = packet[2:]
148*f6aab3d8Srobert            if "." in tid:
149*f6aab3d8Srobert                assert tid.startswith("p")
150*f6aab3d8Srobert                # TODO: do we want to do anything with PID?
151*f6aab3d8Srobert                tid = tid.split(".", 1)[1]
152*f6aab3d8Srobert            return self.selectThread(packet[1], int(tid, 16))
153*f6aab3d8Srobert        if packet[0:6] == "qXfer:":
154*f6aab3d8Srobert            obj, read, annex, location = packet[6:].split(":")
155*f6aab3d8Srobert            offset, length = [int(x, 16) for x in location.split(',')]
156*f6aab3d8Srobert            data, has_more = self.qXferRead(obj, annex, offset, length)
157*f6aab3d8Srobert            if data is not None:
158*f6aab3d8Srobert                return self._qXferResponse(data, has_more)
159*f6aab3d8Srobert            return ""
160*f6aab3d8Srobert        if packet.startswith("vAttach;"):
161*f6aab3d8Srobert            pid = packet.partition(';')[2]
162*f6aab3d8Srobert            return self.vAttach(int(pid, 16))
163*f6aab3d8Srobert        if packet[0] == "Z":
164*f6aab3d8Srobert            return self.setBreakpoint(packet)
165*f6aab3d8Srobert        if packet.startswith("qThreadStopInfo"):
166*f6aab3d8Srobert            threadnum = int (packet[15:], 16)
167*f6aab3d8Srobert            return self.threadStopInfo(threadnum)
168*f6aab3d8Srobert        if packet == "QThreadSuffixSupported":
169*f6aab3d8Srobert            return self.QThreadSuffixSupported()
170*f6aab3d8Srobert        if packet == "QListThreadsInStopReply":
171*f6aab3d8Srobert            return self.QListThreadsInStopReply()
172*f6aab3d8Srobert        if packet.startswith("qMemoryRegionInfo:"):
173*f6aab3d8Srobert            return self.qMemoryRegionInfo(int(packet.split(':')[1], 16))
174*f6aab3d8Srobert        if packet == "qQueryGDBServer":
175*f6aab3d8Srobert            return self.qQueryGDBServer()
176*f6aab3d8Srobert        if packet == "qHostInfo":
177*f6aab3d8Srobert            return self.qHostInfo()
178*f6aab3d8Srobert        if packet == "qGetWorkingDir":
179*f6aab3d8Srobert            return self.qGetWorkingDir()
180*f6aab3d8Srobert        if packet == "qOffsets":
181*f6aab3d8Srobert            return self.qOffsets();
182*f6aab3d8Srobert        if packet == "qProcessInfo":
183*f6aab3d8Srobert            return self.qProcessInfo()
184*f6aab3d8Srobert        if packet == "qsProcessInfo":
185*f6aab3d8Srobert            return self.qsProcessInfo()
186*f6aab3d8Srobert        if packet.startswith("qfProcessInfo"):
187*f6aab3d8Srobert            return self.qfProcessInfo(packet)
188*f6aab3d8Srobert        if packet.startswith("jGetLoadedDynamicLibrariesInfos"):
189*f6aab3d8Srobert            return self.jGetLoadedDynamicLibrariesInfos(packet)
190*f6aab3d8Srobert        if packet.startswith("qPathComplete:"):
191*f6aab3d8Srobert            return self.qPathComplete()
192*f6aab3d8Srobert        if packet.startswith("vFile:"):
193*f6aab3d8Srobert            return self.vFile(packet)
194*f6aab3d8Srobert        if packet.startswith("vRun;"):
195*f6aab3d8Srobert            return self.vRun(packet)
196*f6aab3d8Srobert        if packet.startswith("qLaunchSuccess"):
197*f6aab3d8Srobert            return self.qLaunchSuccess()
198*f6aab3d8Srobert        if packet.startswith("QEnvironment:"):
199*f6aab3d8Srobert            return self.QEnvironment(packet)
200*f6aab3d8Srobert        if packet.startswith("QEnvironmentHexEncoded:"):
201*f6aab3d8Srobert            return self.QEnvironmentHexEncoded(packet)
202*f6aab3d8Srobert        if packet.startswith("qRegisterInfo"):
203*f6aab3d8Srobert            regnum = int(packet[len("qRegisterInfo"):], 16)
204*f6aab3d8Srobert            return self.qRegisterInfo(regnum)
205*f6aab3d8Srobert        if packet == "k":
206*f6aab3d8Srobert            return self.k()
207*f6aab3d8Srobert
208*f6aab3d8Srobert        return self.other(packet)
209*f6aab3d8Srobert
210*f6aab3d8Srobert    def qsProcessInfo(self):
211*f6aab3d8Srobert        return "E04"
212*f6aab3d8Srobert
213*f6aab3d8Srobert    def qfProcessInfo(self, packet):
214*f6aab3d8Srobert        return "E04"
215*f6aab3d8Srobert
216*f6aab3d8Srobert    def jGetLoadedDynamicLibrariesInfos(self, packet):
217*f6aab3d8Srobert        return ""
218*f6aab3d8Srobert
219*f6aab3d8Srobert    def qGetWorkingDir(self):
220*f6aab3d8Srobert        return "2f"
221*f6aab3d8Srobert
222*f6aab3d8Srobert    def qOffsets(self):
223*f6aab3d8Srobert        return ""
224*f6aab3d8Srobert
225*f6aab3d8Srobert    def qProcessInfo(self):
226*f6aab3d8Srobert        return ""
227*f6aab3d8Srobert
228*f6aab3d8Srobert    def qHostInfo(self):
229*f6aab3d8Srobert        return "ptrsize:8;endian:little;"
230*f6aab3d8Srobert
231*f6aab3d8Srobert    def qQueryGDBServer(self):
232*f6aab3d8Srobert        return "E04"
233*f6aab3d8Srobert
234*f6aab3d8Srobert    def interrupt(self):
235*f6aab3d8Srobert        raise self.UnexpectedPacketException()
236*f6aab3d8Srobert
237*f6aab3d8Srobert    def cont(self):
238*f6aab3d8Srobert        raise self.UnexpectedPacketException()
239*f6aab3d8Srobert
240*f6aab3d8Srobert    def vCont(self, packet):
241*f6aab3d8Srobert        raise self.UnexpectedPacketException()
242*f6aab3d8Srobert
243*f6aab3d8Srobert    def A(self, packet):
244*f6aab3d8Srobert        return ""
245*f6aab3d8Srobert
246*f6aab3d8Srobert    def D(self, packet):
247*f6aab3d8Srobert        return "OK"
248*f6aab3d8Srobert
249*f6aab3d8Srobert    def readRegisters(self):
250*f6aab3d8Srobert        return "00000000" * self.registerCount
251*f6aab3d8Srobert
252*f6aab3d8Srobert    def readRegister(self, register):
253*f6aab3d8Srobert        return "00000000"
254*f6aab3d8Srobert
255*f6aab3d8Srobert    def writeRegisters(self, registers_hex):
256*f6aab3d8Srobert        return "OK"
257*f6aab3d8Srobert
258*f6aab3d8Srobert    def writeRegister(self, register, value_hex):
259*f6aab3d8Srobert        return "OK"
260*f6aab3d8Srobert
261*f6aab3d8Srobert    def readMemory(self, addr, length):
262*f6aab3d8Srobert        return "00" * length
263*f6aab3d8Srobert
264*f6aab3d8Srobert    def writeMemory(self, addr, data_hex):
265*f6aab3d8Srobert        return "OK"
266*f6aab3d8Srobert
267*f6aab3d8Srobert    def qSymbol(self, symbol_args):
268*f6aab3d8Srobert        return "OK"
269*f6aab3d8Srobert
270*f6aab3d8Srobert    def qSupported(self, client_supported):
271*f6aab3d8Srobert        return "qXfer:features:read+;PacketSize=3fff;QStartNoAckMode+"
272*f6aab3d8Srobert
273*f6aab3d8Srobert    def qfThreadInfo(self):
274*f6aab3d8Srobert        return "l"
275*f6aab3d8Srobert
276*f6aab3d8Srobert    def qsThreadInfo(self):
277*f6aab3d8Srobert        return "l"
278*f6aab3d8Srobert
279*f6aab3d8Srobert    def qC(self):
280*f6aab3d8Srobert        return "QC0"
281*f6aab3d8Srobert
282*f6aab3d8Srobert    def QEnableErrorStrings(self):
283*f6aab3d8Srobert        return "OK"
284*f6aab3d8Srobert
285*f6aab3d8Srobert    def haltReason(self):
286*f6aab3d8Srobert        # SIGINT is 2, return type is 2 digit hex string
287*f6aab3d8Srobert        return "S02"
288*f6aab3d8Srobert
289*f6aab3d8Srobert    def qXferRead(self, obj, annex, offset, length):
290*f6aab3d8Srobert        return None, False
291*f6aab3d8Srobert
292*f6aab3d8Srobert    def _qXferResponse(self, data, has_more):
293*f6aab3d8Srobert        return "%s%s" % ("m" if has_more else "l", escape_binary(data))
294*f6aab3d8Srobert
295*f6aab3d8Srobert    def vAttach(self, pid):
296*f6aab3d8Srobert        raise self.UnexpectedPacketException()
297*f6aab3d8Srobert
298*f6aab3d8Srobert    def selectThread(self, op, thread_id):
299*f6aab3d8Srobert        return "OK"
300*f6aab3d8Srobert
301*f6aab3d8Srobert    def setBreakpoint(self, packet):
302*f6aab3d8Srobert        raise self.UnexpectedPacketException()
303*f6aab3d8Srobert
304*f6aab3d8Srobert    def threadStopInfo(self, threadnum):
305*f6aab3d8Srobert        return ""
306*f6aab3d8Srobert
307*f6aab3d8Srobert    def other(self, packet):
308*f6aab3d8Srobert        # empty string means unsupported
309*f6aab3d8Srobert        return ""
310*f6aab3d8Srobert
311*f6aab3d8Srobert    def QThreadSuffixSupported(self):
312*f6aab3d8Srobert        return ""
313*f6aab3d8Srobert
314*f6aab3d8Srobert    def QListThreadsInStopReply(self):
315*f6aab3d8Srobert        return ""
316*f6aab3d8Srobert
317*f6aab3d8Srobert    def qMemoryRegionInfo(self, addr):
318*f6aab3d8Srobert        return ""
319*f6aab3d8Srobert
320*f6aab3d8Srobert    def qPathComplete(self):
321*f6aab3d8Srobert        return ""
322*f6aab3d8Srobert
323*f6aab3d8Srobert    def vFile(self, packet):
324*f6aab3d8Srobert        return ""
325*f6aab3d8Srobert
326*f6aab3d8Srobert    def vRun(self, packet):
327*f6aab3d8Srobert        return ""
328*f6aab3d8Srobert
329*f6aab3d8Srobert    def qLaunchSuccess(self):
330*f6aab3d8Srobert        return ""
331*f6aab3d8Srobert
332*f6aab3d8Srobert    def QEnvironment(self, packet):
333*f6aab3d8Srobert        return "OK"
334*f6aab3d8Srobert
335*f6aab3d8Srobert    def QEnvironmentHexEncoded(self, packet):
336*f6aab3d8Srobert        return "OK"
337*f6aab3d8Srobert
338*f6aab3d8Srobert    def qRegisterInfo(self, num):
339*f6aab3d8Srobert        return ""
340*f6aab3d8Srobert
341*f6aab3d8Srobert    def k(self):
342*f6aab3d8Srobert        return ["W01", self.RESPONSE_DISCONNECT]
343*f6aab3d8Srobert
344*f6aab3d8Srobert    """
345*f6aab3d8Srobert    Raised when we receive a packet for which there is no default action.
346*f6aab3d8Srobert    Override the responder class to implement behavior suitable for the test at
347*f6aab3d8Srobert    hand.
348*f6aab3d8Srobert    """
349*f6aab3d8Srobert    class UnexpectedPacketException(Exception):
350*f6aab3d8Srobert        pass
351*f6aab3d8Srobert
352*f6aab3d8Srobert
353*f6aab3d8Srobertclass ServerChannel:
354*f6aab3d8Srobert    """
355*f6aab3d8Srobert    A wrapper class for TCP or pty-based server.
356*f6aab3d8Srobert    """
357*f6aab3d8Srobert
358*f6aab3d8Srobert    def get_connect_address(self):
359*f6aab3d8Srobert        """Get address for the client to connect to."""
360*f6aab3d8Srobert
361*f6aab3d8Srobert    def get_connect_url(self):
362*f6aab3d8Srobert        """Get URL suitable for process connect command."""
363*f6aab3d8Srobert
364*f6aab3d8Srobert    def close_server(self):
365*f6aab3d8Srobert        """Close all resources used by the server."""
366*f6aab3d8Srobert
367*f6aab3d8Srobert    def accept(self):
368*f6aab3d8Srobert        """Accept a single client connection to the server."""
369*f6aab3d8Srobert
370*f6aab3d8Srobert    def close_connection(self):
371*f6aab3d8Srobert        """Close all resources used by the accepted connection."""
372*f6aab3d8Srobert
373*f6aab3d8Srobert    def recv(self):
374*f6aab3d8Srobert        """Receive a data packet from the connected client."""
375*f6aab3d8Srobert
376*f6aab3d8Srobert    def sendall(self, data):
377*f6aab3d8Srobert        """Send the data to the connected client."""
378*f6aab3d8Srobert
379*f6aab3d8Srobert
380*f6aab3d8Srobertclass ServerSocket(ServerChannel):
381*f6aab3d8Srobert    def __init__(self, family, type, proto, addr):
382*f6aab3d8Srobert        self._server_socket = socket.socket(family, type, proto)
383*f6aab3d8Srobert        self._connection = None
384*f6aab3d8Srobert
385*f6aab3d8Srobert        self._server_socket.bind(addr)
386*f6aab3d8Srobert        self._server_socket.listen(1)
387*f6aab3d8Srobert
388*f6aab3d8Srobert    def close_server(self):
389*f6aab3d8Srobert        self._server_socket.close()
390*f6aab3d8Srobert
391*f6aab3d8Srobert    def accept(self):
392*f6aab3d8Srobert        assert self._connection is None
393*f6aab3d8Srobert        # accept() is stubborn and won't fail even when the socket is
394*f6aab3d8Srobert        # shutdown, so we'll use a timeout
395*f6aab3d8Srobert        self._server_socket.settimeout(30.0)
396*f6aab3d8Srobert        client, client_addr = self._server_socket.accept()
397*f6aab3d8Srobert        # The connected client inherits its timeout from self._socket,
398*f6aab3d8Srobert        # but we'll use a blocking socket for the client
399*f6aab3d8Srobert        client.settimeout(None)
400*f6aab3d8Srobert        self._connection = client
401*f6aab3d8Srobert
402*f6aab3d8Srobert    def close_connection(self):
403*f6aab3d8Srobert        assert self._connection is not None
404*f6aab3d8Srobert        self._connection.close()
405*f6aab3d8Srobert        self._connection = None
406*f6aab3d8Srobert
407*f6aab3d8Srobert    def recv(self):
408*f6aab3d8Srobert        assert self._connection is not None
409*f6aab3d8Srobert        return self._connection.recv(4096)
410*f6aab3d8Srobert
411*f6aab3d8Srobert    def sendall(self, data):
412*f6aab3d8Srobert        assert self._connection is not None
413*f6aab3d8Srobert        return self._connection.sendall(data)
414*f6aab3d8Srobert
415*f6aab3d8Srobert
416*f6aab3d8Srobertclass TCPServerSocket(ServerSocket):
417*f6aab3d8Srobert    def __init__(self):
418*f6aab3d8Srobert        family, type, proto, _, addr = socket.getaddrinfo(
419*f6aab3d8Srobert                "localhost", 0, proto=socket.IPPROTO_TCP)[0]
420*f6aab3d8Srobert        super().__init__(family, type, proto, addr)
421*f6aab3d8Srobert
422*f6aab3d8Srobert    def get_connect_address(self):
423*f6aab3d8Srobert        return "[{}]:{}".format(*self._server_socket.getsockname())
424*f6aab3d8Srobert
425*f6aab3d8Srobert    def get_connect_url(self):
426*f6aab3d8Srobert        return "connect://" + self.get_connect_address()
427*f6aab3d8Srobert
428*f6aab3d8Srobert
429*f6aab3d8Srobertclass UnixServerSocket(ServerSocket):
430*f6aab3d8Srobert    def __init__(self, addr):
431*f6aab3d8Srobert        super().__init__(socket.AF_UNIX, socket.SOCK_STREAM, 0, addr)
432*f6aab3d8Srobert
433*f6aab3d8Srobert    def get_connect_address(self):
434*f6aab3d8Srobert        return self._server_socket.getsockname()
435*f6aab3d8Srobert
436*f6aab3d8Srobert    def get_connect_url(self):
437*f6aab3d8Srobert        return "unix-connect://" + self.get_connect_address()
438*f6aab3d8Srobert
439*f6aab3d8Srobert
440*f6aab3d8Srobertclass PtyServerSocket(ServerChannel):
441*f6aab3d8Srobert    def __init__(self):
442*f6aab3d8Srobert        import pty
443*f6aab3d8Srobert        import tty
444*f6aab3d8Srobert        primary, secondary = pty.openpty()
445*f6aab3d8Srobert        tty.setraw(primary)
446*f6aab3d8Srobert        self._primary = io.FileIO(primary, 'r+b')
447*f6aab3d8Srobert        self._secondary = io.FileIO(secondary, 'r+b')
448*f6aab3d8Srobert
449*f6aab3d8Srobert    def get_connect_address(self):
450*f6aab3d8Srobert        libc = ctypes.CDLL(None)
451*f6aab3d8Srobert        libc.ptsname.argtypes = (ctypes.c_int,)
452*f6aab3d8Srobert        libc.ptsname.restype = ctypes.c_char_p
453*f6aab3d8Srobert        return libc.ptsname(self._primary.fileno()).decode()
454*f6aab3d8Srobert
455*f6aab3d8Srobert    def get_connect_url(self):
456*f6aab3d8Srobert        return "serial://" + self.get_connect_address()
457*f6aab3d8Srobert
458*f6aab3d8Srobert    def close_server(self):
459*f6aab3d8Srobert        self._secondary.close()
460*f6aab3d8Srobert        self._primary.close()
461*f6aab3d8Srobert
462*f6aab3d8Srobert    def recv(self):
463*f6aab3d8Srobert        try:
464*f6aab3d8Srobert            return self._primary.read(4096)
465*f6aab3d8Srobert        except OSError as e:
466*f6aab3d8Srobert            # closing the pty results in EIO on Linux, convert it to EOF
467*f6aab3d8Srobert            if e.errno == errno.EIO:
468*f6aab3d8Srobert                return b''
469*f6aab3d8Srobert            raise
470*f6aab3d8Srobert
471*f6aab3d8Srobert    def sendall(self, data):
472*f6aab3d8Srobert        return self._primary.write(data)
473*f6aab3d8Srobert
474*f6aab3d8Srobert
475*f6aab3d8Srobertclass MockGDBServer:
476*f6aab3d8Srobert    """
477*f6aab3d8Srobert    A simple TCP-based GDB server that can test client behavior by receiving
478*f6aab3d8Srobert    commands and issuing custom-tailored responses.
479*f6aab3d8Srobert
480*f6aab3d8Srobert    Responses are generated via the .responder property, which should be an
481*f6aab3d8Srobert    instance of a class based on MockGDBServerResponder.
482*f6aab3d8Srobert    """
483*f6aab3d8Srobert
484*f6aab3d8Srobert    responder = None
485*f6aab3d8Srobert    _socket = None
486*f6aab3d8Srobert    _thread = None
487*f6aab3d8Srobert    _receivedData = None
488*f6aab3d8Srobert    _receivedDataOffset = None
489*f6aab3d8Srobert    _shouldSendAck = True
490*f6aab3d8Srobert
491*f6aab3d8Srobert    def __init__(self, socket):
492*f6aab3d8Srobert        self._socket = socket
493*f6aab3d8Srobert        self.responder = MockGDBServerResponder()
494*f6aab3d8Srobert
495*f6aab3d8Srobert    def start(self):
496*f6aab3d8Srobert        # Start a thread that waits for a client connection.
497*f6aab3d8Srobert        self._thread = threading.Thread(target=self.run)
498*f6aab3d8Srobert        self._thread.start()
499*f6aab3d8Srobert
500*f6aab3d8Srobert    def stop(self):
501*f6aab3d8Srobert        self._thread.join()
502*f6aab3d8Srobert        self._thread = None
503*f6aab3d8Srobert
504*f6aab3d8Srobert    def get_connect_address(self):
505*f6aab3d8Srobert        return self._socket.get_connect_address()
506*f6aab3d8Srobert
507*f6aab3d8Srobert    def get_connect_url(self):
508*f6aab3d8Srobert        return self._socket.get_connect_url()
509*f6aab3d8Srobert
510*f6aab3d8Srobert    def run(self):
511*f6aab3d8Srobert        # For testing purposes, we only need to worry about one client
512*f6aab3d8Srobert        # connecting just one time.
513*f6aab3d8Srobert        try:
514*f6aab3d8Srobert            self._socket.accept()
515*f6aab3d8Srobert        except:
516*f6aab3d8Srobert            traceback.print_exc()
517*f6aab3d8Srobert            return
518*f6aab3d8Srobert        self._shouldSendAck = True
519*f6aab3d8Srobert        self._receivedData = ""
520*f6aab3d8Srobert        self._receivedDataOffset = 0
521*f6aab3d8Srobert        data = None
522*f6aab3d8Srobert        try:
523*f6aab3d8Srobert            while True:
524*f6aab3d8Srobert                data = seven.bitcast_to_string(self._socket.recv())
525*f6aab3d8Srobert                if data is None or len(data) == 0:
526*f6aab3d8Srobert                    break
527*f6aab3d8Srobert                self._receive(data)
528*f6aab3d8Srobert        except self.TerminateConnectionException:
529*f6aab3d8Srobert            pass
530*f6aab3d8Srobert        except Exception as e:
531*f6aab3d8Srobert            print("An exception happened when receiving the response from the gdb server. Closing the client...")
532*f6aab3d8Srobert            traceback.print_exc()
533*f6aab3d8Srobert        finally:
534*f6aab3d8Srobert            self._socket.close_connection()
535*f6aab3d8Srobert            self._socket.close_server()
536*f6aab3d8Srobert
537*f6aab3d8Srobert    def _receive(self, data):
538*f6aab3d8Srobert        """
539*f6aab3d8Srobert        Collects data, parses and responds to as many packets as exist.
540*f6aab3d8Srobert        Any leftover data is kept for parsing the next time around.
541*f6aab3d8Srobert        """
542*f6aab3d8Srobert        self._receivedData += data
543*f6aab3d8Srobert        packet = self._parsePacket()
544*f6aab3d8Srobert        while packet is not None:
545*f6aab3d8Srobert            self._handlePacket(packet)
546*f6aab3d8Srobert            packet = self._parsePacket()
547*f6aab3d8Srobert
548*f6aab3d8Srobert    def _parsePacket(self):
549*f6aab3d8Srobert        """
550*f6aab3d8Srobert        Reads bytes from self._receivedData, returning:
551*f6aab3d8Srobert        - a packet's contents if a valid packet is found
552*f6aab3d8Srobert        - the PACKET_ACK unique object if we got an ack
553*f6aab3d8Srobert        - None if we only have a partial packet
554*f6aab3d8Srobert
555*f6aab3d8Srobert        Raises an InvalidPacketException if unexpected data is received
556*f6aab3d8Srobert        or if checksums fail.
557*f6aab3d8Srobert
558*f6aab3d8Srobert        Once a complete packet is found at the front of self._receivedData,
559*f6aab3d8Srobert        its data is removed form self._receivedData.
560*f6aab3d8Srobert        """
561*f6aab3d8Srobert        data = self._receivedData
562*f6aab3d8Srobert        i = self._receivedDataOffset
563*f6aab3d8Srobert        data_len = len(data)
564*f6aab3d8Srobert        if data_len == 0:
565*f6aab3d8Srobert            return None
566*f6aab3d8Srobert        if i == 0:
567*f6aab3d8Srobert            # If we're looking at the start of the received data, that means
568*f6aab3d8Srobert            # we're looking for the start of a new packet, denoted by a $.
569*f6aab3d8Srobert            # It's also possible we'll see an ACK here, denoted by a +
570*f6aab3d8Srobert            if data[0] == '+':
571*f6aab3d8Srobert                self._receivedData = data[1:]
572*f6aab3d8Srobert                return self.PACKET_ACK
573*f6aab3d8Srobert            if ord(data[0]) == 3:
574*f6aab3d8Srobert                self._receivedData = data[1:]
575*f6aab3d8Srobert                return self.PACKET_INTERRUPT
576*f6aab3d8Srobert            if data[0] == '$':
577*f6aab3d8Srobert                i += 1
578*f6aab3d8Srobert            else:
579*f6aab3d8Srobert                raise self.InvalidPacketException(
580*f6aab3d8Srobert                        "Unexpected leading byte: %s" % data[0])
581*f6aab3d8Srobert
582*f6aab3d8Srobert        # If we're looking beyond the start of the received data, then we're
583*f6aab3d8Srobert        # looking for the end of the packet content, denoted by a #.
584*f6aab3d8Srobert        # Note that we pick up searching from where we left off last time
585*f6aab3d8Srobert        while i < data_len and data[i] != '#':
586*f6aab3d8Srobert            i += 1
587*f6aab3d8Srobert
588*f6aab3d8Srobert        # If there isn't enough data left for a checksum, just remember where
589*f6aab3d8Srobert        # we left off so we can pick up there the next time around
590*f6aab3d8Srobert        if i > data_len - 3:
591*f6aab3d8Srobert            self._receivedDataOffset = i
592*f6aab3d8Srobert            return None
593*f6aab3d8Srobert
594*f6aab3d8Srobert        # If we have enough data remaining for the checksum, extract it and
595*f6aab3d8Srobert        # compare to the packet contents
596*f6aab3d8Srobert        packet = data[1:i]
597*f6aab3d8Srobert        i += 1
598*f6aab3d8Srobert        try:
599*f6aab3d8Srobert            check = int(data[i:i + 2], 16)
600*f6aab3d8Srobert        except ValueError:
601*f6aab3d8Srobert            raise self.InvalidPacketException("Checksum is not valid hex")
602*f6aab3d8Srobert        i += 2
603*f6aab3d8Srobert        if check != checksum(packet):
604*f6aab3d8Srobert            raise self.InvalidPacketException(
605*f6aab3d8Srobert                    "Checksum %02x does not match content %02x" %
606*f6aab3d8Srobert                    (check, checksum(packet)))
607*f6aab3d8Srobert        # remove parsed bytes from _receivedData and reset offset so parsing
608*f6aab3d8Srobert        # can start on the next packet the next time around
609*f6aab3d8Srobert        self._receivedData = data[i:]
610*f6aab3d8Srobert        self._receivedDataOffset = 0
611*f6aab3d8Srobert        return packet
612*f6aab3d8Srobert
613*f6aab3d8Srobert    def _sendPacket(self, packet):
614*f6aab3d8Srobert        self._socket.sendall(seven.bitcast_to_bytes(frame_packet(packet)))
615*f6aab3d8Srobert
616*f6aab3d8Srobert    def _handlePacket(self, packet):
617*f6aab3d8Srobert        if packet is self.PACKET_ACK:
618*f6aab3d8Srobert            # Ignore ACKs from the client. For the future, we can consider
619*f6aab3d8Srobert            # adding validation code to make sure the client only sends ACKs
620*f6aab3d8Srobert            # when it's supposed to.
621*f6aab3d8Srobert            return
622*f6aab3d8Srobert        response = ""
623*f6aab3d8Srobert        # We'll handle the ack stuff here since it's not something any of the
624*f6aab3d8Srobert        # tests will be concerned about, and it'll get turned off quickly anyway.
625*f6aab3d8Srobert        if self._shouldSendAck:
626*f6aab3d8Srobert            self._socket.sendall(seven.bitcast_to_bytes('+'))
627*f6aab3d8Srobert        if packet == "QStartNoAckMode":
628*f6aab3d8Srobert            self._shouldSendAck = False
629*f6aab3d8Srobert            response = "OK"
630*f6aab3d8Srobert        elif self.responder is not None:
631*f6aab3d8Srobert            # Delegate everything else to our responder
632*f6aab3d8Srobert            response = self.responder.respond(packet)
633*f6aab3d8Srobert        if not isinstance(response, list):
634*f6aab3d8Srobert            response = [response]
635*f6aab3d8Srobert        for part in response:
636*f6aab3d8Srobert            if part is MockGDBServerResponder.RESPONSE_DISCONNECT:
637*f6aab3d8Srobert                raise self.TerminateConnectionException()
638*f6aab3d8Srobert            self._sendPacket(part)
639*f6aab3d8Srobert
640*f6aab3d8Srobert    PACKET_ACK = object()
641*f6aab3d8Srobert    PACKET_INTERRUPT = object()
642*f6aab3d8Srobert
643*f6aab3d8Srobert    class TerminateConnectionException(Exception):
644*f6aab3d8Srobert        pass
645*f6aab3d8Srobert
646*f6aab3d8Srobert    class InvalidPacketException(Exception):
647*f6aab3d8Srobert        pass
648