• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 9051612299

12 May 2024 01:04PM UTC coverage: 74.05% (-0.04%) from 74.089%
9051612299

Pull #2405

github

web-flow
Merge 8c883f9a1 into e92a30bbf
Pull Request #2405: Add "none" ssh authentication method

4464 of 7249 branches covered (61.58%)

0 of 4 new or added lines in 1 file covered. (0.0%)

4 existing lines in 2 files now uncovered.

13072 of 17653 relevant lines covered (74.05%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.57
/pwnlib/tubes/tube.py
1
# -*- coding: utf-8 -*-
2
from __future__ import absolute_import
1✔
3
from __future__ import division
1✔
4

5
import abc
1✔
6
import logging
1✔
7
import os
1✔
8
import re
1✔
9
import six
1✔
10
import string
1✔
11
import subprocess
1✔
12
import sys
1✔
13
import threading
1✔
14
import time
1✔
15

16
from six.moves import range
1✔
17

18
from pwnlib import atexit
1✔
19
from pwnlib import term
1✔
20
from pwnlib.context import context
1✔
21
from pwnlib.log import Logger
1✔
22
from pwnlib.timeout import Timeout
1✔
23
from pwnlib.tubes.buffer import Buffer
1✔
24
from pwnlib.util import misc
1✔
25
from pwnlib.util import packing
1✔
26

27

28
class tube(Timeout, Logger):
1✔
29
    """
30
    Container of all the tube functions common to sockets, TTYs and SSH connetions.
31
    """
32

33
    default = Timeout.default
1✔
34
    forever = Timeout.forever
1✔
35

36
    def __init__(self, timeout = default, level = None, *a, **kw):
1✔
37
        super(tube, self).__init__(timeout)
1✔
38

39
        Logger.__init__(self, None)
1✔
40
        if level is not None:
1✔
41
            self.setLevel(level)
1✔
42

43
        self.buffer = Buffer(*a, **kw)
1✔
44
        self._newline = None
1✔
45
        atexit.register(self.close)
1✔
46

47
    @property
1✔
48
    def newline(self):
1✔
49
        r'''Character sent with methods like sendline() or used for recvline().
50

51
            >>> t = tube()
52
            >>> t.newline = b'X'
53
            >>> t.unrecv(b'A\nB\nCX')
54
            >>> t.recvline()
55
            b'A\nB\nCX'
56

57
            >>> t = tube()
58
            >>> context.newline = b'\r\n'
59
            >>> t.newline
60
            b'\r\n'
61

62
            # Clean up
63
            >>> context.clear()
64
        '''
65
        if self._newline is not None:
1✔
66
            return self._newline
1✔
67
        return context.newline
1✔
68

69
    @newline.setter
1✔
70
    def newline(self, newline):
1✔
71
        self._newline = packing._need_bytes(newline)
1✔
72

73
    # Functions based on functions from subclasses
74
    def recv(self, numb = None, timeout = default):
1✔
75
        r"""recv(numb = 4096, timeout = default) -> bytes
76

77
        Receives up to `numb` bytes of data from the tube, and returns
78
        as soon as any quantity of data is available.
79

80
        If the request is not satisfied before ``timeout`` seconds pass,
81
        all data is buffered and an empty string (``''``) is returned.
82

83
        Raises:
84
            exceptions.EOFError: The connection is closed
85

86
        Returns:
87
            A bytes object containing bytes received from the socket,
88
            or ``''`` if a timeout occurred while waiting.
89

90
        Examples:
91

92
            >>> t = tube()
93
            >>> # Fake a data source
94
            >>> t.recv_raw = lambda n: b'Hello, world'
95
            >>> t.recv() == b'Hello, world'
96
            True
97
            >>> t.unrecv(b'Woohoo')
98
            >>> t.recv() == b'Woohoo'
99
            True
100
            >>> with context.local(log_level='debug'):
101
            ...    _ = t.recv() # doctest: +ELLIPSIS
102
            [...] Received 0xc bytes:
103
                b'Hello, world'
104
        """
105
        numb = self.buffer.get_fill_size(numb)
1✔
106
        return self._recv(numb, timeout) or b''
1✔
107

108
    def unrecv(self, data):
1✔
109
        """unrecv(data)
110

111
        Puts the specified data back at the beginning of the receive
112
        buffer.
113

114
        Examples:
115

116
            >>> t = tube()
117
            >>> t.recv_raw = lambda n: b'hello'
118
            >>> t.recv()
119
            b'hello'
120
            >>> t.recv()
121
            b'hello'
122
            >>> t.unrecv(b'world')
123
            >>> t.recv()
124
            b'world'
125
            >>> t.recv()
126
            b'hello'
127
        """
128
        data = packing._need_bytes(data)
1✔
129
        self.buffer.unget(data)
1✔
130

131
    def _fillbuffer(self, timeout = default):
1✔
132
        """_fillbuffer(timeout = default)
133

134
        Fills the internal buffer from the pipe, by calling
135
        :meth:`recv_raw` exactly once.
136

137
        Returns:
138

139
            The bytes of data received, or ``''`` if no data was received.
140

141
        Examples:
142

143
            >>> t = tube()
144
            >>> t.recv_raw = lambda *a: b'abc'
145
            >>> len(t.buffer)
146
            0
147
            >>> t._fillbuffer()
148
            b'abc'
149
            >>> len(t.buffer)
150
            3
151
        """
152
        data = b''
1✔
153

154
        with self.local(timeout):
1✔
155
            data = self.recv_raw(self.buffer.get_fill_size())
1✔
156

157
        if data and self.isEnabledFor(logging.DEBUG):
1✔
158
            self.debug('Received %#x bytes:' % len(data))
1✔
159
            self.maybe_hexdump(data, level=logging.DEBUG)
1✔
160
        if data:
1✔
161
            self.buffer.add(data)
1✔
162

163
        return data
1✔
164

165

166
    def _recv(self, numb = None, timeout = default):
1✔
167
        """_recv(numb = 4096, timeout = default) -> str
168

169
        Receives one chunk of from the internal buffer or from the OS if the
170
        buffer is empty.
171
        """
172
        numb = self.buffer.get_fill_size(numb)
1✔
173

174
        # No buffered data, could not put anything in the buffer
175
        # before timeout.
176
        if not self.buffer and not self._fillbuffer(timeout):
1✔
177
            return b''
1✔
178

179
        return self.buffer.get(numb)
1✔
180

181
    def recvpred(self, pred, timeout = default):
1✔
182
        """recvpred(pred, timeout = default) -> bytes
183

184
        Receives one byte at a time from the tube, until ``pred(all_bytes)``
185
        evaluates to True.
186

187
        If the request is not satisfied before ``timeout`` seconds pass,
188
        all data is buffered and an empty string (``''``) is returned.
189

190
        Arguments:
191
            pred(callable): Function to call, with the currently-accumulated data.
192
            timeout(int): Timeout for the operation
193

194
        Raises:
195
            exceptions.EOFError: The connection is closed
196

197
        Returns:
198
            A bytes object containing bytes received from the socket,
199
            or ``''`` if a timeout occurred while waiting.
200

201
        Examples:
202

203
            >>> t = tube()
204
            >>> t.recv_raw = lambda n: b'abbbaccc'
205
            >>> pred = lambda p: p.count(b'a') == 2
206
            >>> t.recvpred(pred)
207
            b'abbba'
208
            >>> pred = lambda p: p.count(b'd') > 0
209
            >>> t.recvpred(pred, timeout=0.05)
210
            b''
211
        """
212

213
        data = b''
1✔
214

215
        with self.countdown(timeout):
1✔
216
            while not pred(data):
1✔
217
                if not self.countdown_active():
1✔
218
                    self.unrecv(data)
1✔
219
                    return b''
1✔
220

221
                try:
1✔
222
                    res = self.recv(1, timeout=timeout)
1✔
223
                except Exception:
×
224
                    self.unrecv(data)
×
225
                    return b''
×
226

227
                if res:
1!
228
                    data += res
1✔
229
                else:
230
                    self.unrecv(data)
×
231
                    return b''
×
232

233
        return data
1✔
234

235
    def recvn(self, numb, timeout = default):
1✔
236
        """recvn(numb, timeout = default) -> bytes
237

238
        Receives exactly `n` bytes.
239

240
        If the request is not satisfied before ``timeout`` seconds pass,
241
        all data is buffered and an empty string (``''``) is returned.
242

243
        Raises:
244
            exceptions.EOFError: The connection closed before the request could be satisfied
245

246
        Returns:
247
            A string containing bytes received from the socket,
248
            or ``''`` if a timeout occurred while waiting.
249

250
        Examples:
251

252
            >>> t = tube()
253
            >>> data = b'hello world'
254
            >>> t.recv_raw = lambda *a: data
255
            >>> t.recvn(len(data)) == data
256
            True
257
            >>> t.recvn(len(data)+1) == data + data[:1]
258
            True
259
            >>> t.recv_raw = lambda *a: None
260
            >>> # The remaining data is buffered
261
            >>> t.recv() == data[1:]
262
            True
263
            >>> t.recv_raw = lambda *a: time.sleep(0.01) or b'a'
264
            >>> t.recvn(10, timeout=0.05)
265
            b''
266
            >>> t.recvn(10, timeout=0.06) # doctest: +ELLIPSIS
267
            b'aaaaaa...'
268
        """
269
        # Keep track of how much data has been received
270
        # It will be pasted together at the end if a
271
        # timeout does not occur, or put into the tube buffer.
272
        with self.countdown(timeout):
1✔
273
            while self.countdown_active() and len(self.buffer) < numb and self._fillbuffer(self.timeout):
1✔
274
                pass
1✔
275

276
        if len(self.buffer) < numb:
1✔
277
            return b''
1✔
278

279
        return self.buffer.get(numb)
1✔
280

281
    def recvuntil(self, delims, drop=False, timeout=default):
1✔
282
        """recvuntil(delims, drop=False, timeout=default) -> bytes
283

284
        Receive data until one of `delims` is encountered.
285

286
        If the request is not satisfied before ``timeout`` seconds pass,
287
        all data is buffered and an empty string (``''``) is returned.
288

289
        arguments:
290
            delims(bytes,tuple): Byte-string of delimiters characters, or list of delimiter byte-strings.
291
            drop(bool): Drop the ending.  If :const:`True` it is removed from the end of the return value.
292

293
        Raises:
294
            exceptions.EOFError: The connection closed before the request could be satisfied
295

296
        Returns:
297
            A string containing bytes received from the socket,
298
            or ``''`` if a timeout occurred while waiting.
299

300
        Examples:
301

302
            >>> t = tube()
303
            >>> t.recv_raw = lambda n: b"Hello World!"
304
            >>> t.recvuntil(b' ')
305
            b'Hello '
306
            >>> _=t.clean(0)
307
            >>> # Matches on 'o' in 'Hello'
308
            >>> t.recvuntil((b' ',b'W',b'o',b'r'))
309
            b'Hello'
310
            >>> _=t.clean(0)
311
            >>> # Matches expressly full string
312
            >>> t.recvuntil(b' Wor')
313
            b'Hello Wor'
314
            >>> _=t.clean(0)
315
            >>> # Matches on full string, drops match
316
            >>> t.recvuntil(b' Wor', drop=True)
317
            b'Hello'
318

319
            >>> # Try with regex special characters
320
            >>> t = tube()
321
            >>> t.recv_raw = lambda n: b"Hello|World"
322
            >>> t.recvuntil(b'|', drop=True)
323
            b'Hello'
324

325
        """
326
        # Convert string into singleton tupple
327
        if isinstance(delims, (bytes, bytearray, six.text_type)):
1✔
328
            delims = (delims,)
1✔
329
        delims = tuple(map(packing._need_bytes, delims))
1✔
330

331
        # Longest delimiter for tracking purposes
332
        longest = max(map(len, delims))
1✔
333

334
        # Cumulative data to search
335
        data = []
1✔
336
        top = b''
1✔
337

338
        with self.countdown(timeout):
1!
339
            while self.countdown_active():
1!
340
                try:
1✔
341
                    res = self.recv(timeout=self.timeout)
1✔
342
                except Exception:
1✔
343
                    self.unrecv(b''.join(data) + top)
1✔
344
                    raise
1✔
345

346
                if not res:
1✔
347
                    self.unrecv(b''.join(data) + top)
1✔
348
                    return b''
1✔
349

350
                top += res
1✔
351
                start = len(top)
1✔
352
                for d in delims:
1✔
353
                    j = top.find(d)
1✔
354
                    if start > j > -1:
1✔
355
                        start = j
1✔
356
                        end = j + len(d)
1✔
357
                if start < len(top):
1✔
358
                    self.unrecv(top[end:])
1✔
359
                    if drop:
1✔
360
                        top = top[:start]
1✔
361
                    else:
362
                        top = top[:end]
1✔
363
                    return b''.join(data) + top
1✔
364
                if len(top) > longest:
1✔
365
                    i = -longest - 1
1✔
366
                    data.append(top[:i])
1✔
367
                    top = top[i:]
1✔
368

UNCOV
369
        return b''
×
370

371
    def recvlines(self, numlines=2**20, keepends=False, timeout=default):
1✔
372
        r"""recvlines(numlines, keepends=False, timeout=default) -> list of bytes objects
373

374
        Receive up to ``numlines`` lines.
375

376
        A "line" is any sequence of bytes terminated by the byte sequence
377
        set by :attr:`newline`, which defaults to ``'\n'``.
378

379
        If the request is not satisfied before ``timeout`` seconds pass,
380
        all data is buffered and an empty string (``''``) is returned.
381

382
        Arguments:
383
            numlines(int): Maximum number of lines to receive
384
            keepends(bool): Keep newlines at the end of each line (:const:`False`).
385
            timeout(int): Maximum timeout
386

387
        Raises:
388
            exceptions.EOFError: The connection closed before the request could be satisfied
389

390
        Returns:
391
            A string containing bytes received from the socket,
392
            or ``''`` if a timeout occurred while waiting.
393

394
        Examples:
395

396
            >>> t = tube()
397
            >>> t.recv_raw = lambda n: b'\n'
398
            >>> t.recvlines(3)
399
            [b'', b'', b'']
400
            >>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\n'
401
            >>> t.recvlines(3)
402
            [b'Foo', b'Bar', b'Baz']
403
            >>> t.recvlines(3, True)
404
            [b'Foo\n', b'Bar\n', b'Baz\n']
405
        """
406
        lines = []
1✔
407
        with self.countdown(timeout):
1✔
408
            for _ in range(numlines):
1✔
409
                try:
1✔
410
                    # We must set 'keepends' to True here so that we can
411
                    # restore the original, unmodified data to the buffer
412
                    # in the event of a timeout.
413
                    res = self.recvline(keepends=True, timeout=timeout)
1✔
414
                except Exception:
×
415
                    self.unrecv(b''.join(lines))
×
416
                    raise
×
417

418
                if res:
1!
419
                    lines.append(res)
1✔
420
                else:
421
                    break
×
422

423
        if not keepends:
1✔
424
            lines = [line.rstrip(self.newline) for line in lines]
1✔
425

426
        return lines
1✔
427

428
    def recvlinesS(self, numlines=2**20, keepends=False, timeout=default):
1✔
429
        r"""recvlinesS(numlines, keepends=False, timeout=default) -> str list
430

431
        This function is identical to :meth:`recvlines`, but decodes
432
        the received bytes into string using :func:`context.encoding`.
433
        You should use :meth:`recvlines` whenever possible for better performance.
434

435
        Examples:
436

437
            >>> t = tube()
438
            >>> t.recv_raw = lambda n: b'\n'
439
            >>> t.recvlinesS(3)
440
            ['', '', '']
441
            >>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\n'
442
            >>> t.recvlinesS(3)
443
            ['Foo', 'Bar', 'Baz']
444
        """
445
        return [packing._decode(x) for x in self.recvlines(numlines, keepends, timeout)]
1✔
446

447
    def recvlinesb(self, numlines=2**20, keepends=False, timeout=default):
1✔
448
        r"""recvlinesb(numlines, keepends=False, timeout=default) -> bytearray list
449

450
        This function is identical to :meth:`recvlines`, but returns a bytearray.
451

452
        Examples:
453

454
            >>> t = tube()
455
            >>> t.recv_raw = lambda n: b'\n'
456
            >>> t.recvlinesb(3)
457
            [bytearray(b''), bytearray(b''), bytearray(b'')]
458
            >>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\n'
459
            >>> t.recvlinesb(3)
460
            [bytearray(b'Foo'), bytearray(b'Bar'), bytearray(b'Baz')]
461
        """
462
        return [bytearray(x) for x in self.recvlines(numlines, keepends, timeout)]
1✔
463

464
    def recvline(self, keepends=True, timeout=default):
1✔
465
        r"""recvline(keepends=True, timeout=default) -> bytes
466

467
        Receive a single line from the tube.
468

469
        A "line" is any sequence of bytes terminated by the byte sequence
470
        set in :attr:`newline`, which defaults to ``b'\n'``.
471

472
        If the connection is closed (:class:`EOFError`) before a newline
473
        is received, the buffered data is returned by default and a warning
474
        is logged. If the buffer is empty, an :class:`EOFError` is raised.
475
        This behavior can be changed by setting :meth:`pwnlib.context.ContextType.throw_eof_on_incomplete_line`.
476

477
        If the request is not satisfied before ``timeout`` seconds pass,
478
        all data is buffered and an empty byte string (``b''``) is returned.
479

480
        Arguments:
481
            keepends(bool): Keep the line ending (:const:`True`).
482
            timeout(int): Timeout
483

484
        Raises:
485
            :class:`EOFError`: The connection closed before the request
486
                                 could be satisfied and the buffer is empty
487

488
        Return:
489
            All bytes received over the tube until the first
490
            newline ``'\n'`` is received.  Optionally retains
491
            the ending. If the connection is closed before a newline
492
            is received, the remaining data received up to this point
493
            is returned.
494

495

496
        Examples:
497

498
            >>> t = tube()
499
            >>> t.recv_raw = lambda n: b'Foo\nBar\r\nBaz\n'
500
            >>> t.recvline()
501
            b'Foo\n'
502
            >>> t.recvline()
503
            b'Bar\r\n'
504
            >>> t.recvline(keepends = False)
505
            b'Baz'
506
            >>> t.newline = b'\r\n'
507
            >>> t.recvline(keepends = False)
508
            b'Foo\nBar'
509
            >>> t = tube()
510
            >>> def _recv_eof(n):
511
            ...     if not _recv_eof.throw:
512
            ...         _recv_eof.throw = True
513
            ...         return b'real line\ntrailing data'
514
            ...     raise EOFError
515
            >>> _recv_eof.throw = False
516
            >>> t.recv_raw = _recv_eof
517
            >>> t.recvline()
518
            b'real line\n'
519
            >>> t.recvline()
520
            b'trailing data'
521
            >>> t.recvline() # doctest: +ELLIPSIS
522
            Traceback (most recent call last):
523
            ...
524
            EOFError
525
        """
526
        try:
1✔
527
            return self.recvuntil(self.newline, drop = not keepends, timeout = timeout)
1✔
528
        except EOFError:
1✔
529
            if not context.throw_eof_on_incomplete_line and self.buffer.size > 0:
1✔
530
                if context.throw_eof_on_incomplete_line is None:
1!
531
                    self.warn_once('EOFError during recvline. Returning buffered data without trailing newline.')
1✔
532
                return self.buffer.get()
1✔
533
            raise
1✔
534

535
    def recvline_pred(self, pred, keepends=False, timeout=default):
1✔
536
        r"""recvline_pred(pred, keepends=False) -> bytes
537

538
        Receive data until ``pred(line)`` returns a truthy value.
539
        Drop all other data.
540

541
        If the request is not satisfied before ``timeout`` seconds pass,
542
        all data is buffered and an empty string (``''``) is returned.
543

544
        Arguments:
545
            pred(callable): Function to call.  Returns the line for which
546
                this function returns :const:`True`.
547

548
        Examples:
549

550
            >>> t = tube()
551
            >>> t.recv_raw = lambda n: b"Foo\nBar\nBaz\n"
552
            >>> t.recvline_pred(lambda line: line == b"Bar\n")
553
            b'Bar'
554
            >>> t.recvline_pred(lambda line: line == b"Bar\n", keepends=True)
555
            b'Bar\n'
556
            >>> t.recvline_pred(lambda line: line == b'Nope!', timeout=0.1)
557
            b''
558
        """
559

560
        tmpbuf = Buffer()
1✔
561
        line   = b''
1✔
562
        with self.countdown(timeout):
1✔
563
            while self.countdown_active():
1✔
564
                try:
1✔
565
                    line = self.recvline(keepends=True)
1✔
566
                except Exception:
×
567
                    self.buffer.unget(tmpbuf)
×
568
                    raise
×
569

570
                if not line:
1✔
571
                    self.buffer.unget(tmpbuf)
1✔
572
                    return b''
1✔
573

574
                if pred(line):
1✔
575
                    if not keepends:
1✔
576
                        line = line[:-len(self.newline)]
1✔
577
                    return line
1✔
578
                else:
579
                    tmpbuf.add(line)
1✔
580

581
        return b''
1✔
582

583
    def recvline_contains(self, items, keepends = False, timeout = default):
1✔
584
        r"""
585
        Receive lines until one line is found which contains at least
586
        one of `items`.
587

588
        Arguments:
589
            items(str,tuple): List of strings to search for, or a single string.
590
            keepends(bool): Return lines with newlines if :const:`True`
591
            timeout(int): Timeout, in seconds
592

593
        Examples:
594

595
            >>> t = tube()
596
            >>> t.recv_raw = lambda n: b"Hello\nWorld\nXylophone\n"
597
            >>> t.recvline_contains(b'r')
598
            b'World'
599
            >>> f = lambda n: b"cat dog bird\napple pear orange\nbicycle car train\n"
600
            >>> t = tube()
601
            >>> t.recv_raw = f
602
            >>> t.recvline_contains(b'pear')
603
            b'apple pear orange'
604
            >>> t = tube()
605
            >>> t.recv_raw = f
606
            >>> t.recvline_contains((b'car', b'train'))
607
            b'bicycle car train'
608
        """
609
        if isinstance(items, (bytes, bytearray, six.text_type)):
1✔
610
            items = (items,)
1✔
611
        items = tuple(map(packing._need_bytes, items))
1✔
612

613
        def pred(line):
1✔
614
            return any(d in line for d in items)
1✔
615

616
        return self.recvline_pred(pred, keepends, timeout)
1✔
617

618
    def recvline_startswith(self, delims, keepends=False, timeout=default):
1✔
619
        r"""recvline_startswith(delims, keepends=False, timeout=default) -> bytes
620

621
        Keep receiving lines until one is found that starts with one of
622
        `delims`.  Returns the last line received.
623

624
        If the request is not satisfied before ``timeout`` seconds pass,
625
        all data is buffered and an empty string (``''``) is returned.
626

627
        Arguments:
628
            delims(str,tuple): List of strings to search for, or string of single characters
629
            keepends(bool): Return lines with newlines if :const:`True`
630
            timeout(int): Timeout, in seconds
631

632
        Returns:
633
            The first line received which starts with a delimiter in ``delims``.
634

635
        Examples:
636

637
            >>> t = tube()
638
            >>> t.recv_raw = lambda n: b"Hello\nWorld\nXylophone\n"
639
            >>> t.recvline_startswith((b'W',b'X',b'Y',b'Z'))
640
            b'World'
641
            >>> t.recvline_startswith((b'W',b'X',b'Y',b'Z'), True)
642
            b'Xylophone\n'
643
            >>> t.recvline_startswith(b'Wo')
644
            b'World'
645
        """
646
        # Convert string into singleton tupple
647
        if isinstance(delims, (bytes, bytearray, six.text_type)):
1✔
648
            delims = (delims,)
1✔
649
        delims = tuple(map(packing._need_bytes, delims))
1✔
650

651
        return self.recvline_pred(lambda line: any(map(line.startswith, delims)),
1✔
652
                                  keepends=keepends,
653
                                  timeout=timeout)
654

655
    def recvline_endswith(self, delims, keepends=False, timeout=default):
1✔
656
        r"""recvline_endswith(delims, keepends=False, timeout=default) -> bytes
657

658
        Keep receiving lines until one is found that ends with one of
659
        `delims`.  Returns the last line received.
660

661
        If the request is not satisfied before ``timeout`` seconds pass,
662
        all data is buffered and an empty string (``''``) is returned.
663

664
        See :meth:`recvline_startswith` for more details.
665

666
        Examples:
667

668
            >>> t = tube()
669
            >>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\nKaboodle\n'
670
            >>> t.recvline_endswith(b'r')
671
            b'Bar'
672
            >>> t.recvline_endswith((b'a',b'b',b'c',b'd',b'e'), True)
673
            b'Kaboodle\n'
674
            >>> t.recvline_endswith(b'oodle')
675
            b'Kaboodle'
676
        """
677
        # Convert string into singleton tupple
678
        if isinstance(delims, (bytes, bytearray, six.text_type)):
1✔
679
            delims = (delims,)
1✔
680

681
        delims = tuple(packing._need_bytes(delim) + self.newline for delim in delims)
1✔
682

683
        return self.recvline_pred(lambda line: any(map(line.endswith, delims)),
1✔
684
                                  keepends=keepends,
685
                                  timeout=timeout)
686

687
    def recvregex(self, regex, exact=False, timeout=default, capture=False):
1✔
688
        r"""recvregex(regex, exact=False, timeout=default, capture=False) -> bytes
689

690
        Wrapper around :func:`recvpred`, which will return when a regex
691
        matches the string in the buffer.
692

693
        Returns all received data up until the regex matched. If `capture` is
694
        set to True, a :class:`re.Match` object is returned instead.
695

696
        By default :func:`re.RegexObject.search` is used, but if `exact` is
697
        set to True, then :func:`re.RegexObject.match` will be used instead.
698

699
        If the request is not satisfied before ``timeout`` seconds pass,
700
        all data is buffered and an empty string (``''``) is returned.
701

702
        Examples:
703

704
            >>> t = tube()
705
            >>> t.recv_raw = lambda n: b'The lucky number is 1337 as always\nBla blubb blargh\n'
706
            >>> m = t.recvregex(br'number is ([0-9]+) as always\n', capture=True)
707
            >>> m.group(1)
708
            b'1337'
709
            >>> t.recvregex(br'Bla .* blargh\n')
710
            b'Bla blubb blargh\n'
711
        """
712

713
        if isinstance(regex, (bytes, bytearray, six.text_type)):
1!
714
            regex = packing._need_bytes(regex)
1✔
715
            regex = re.compile(regex)
1✔
716

717
        if exact:
1!
718
            pred = regex.match
×
719
        else:
720
            pred = regex.search
1✔
721

722
        if capture:
1✔
723
            return pred(self.recvpred(pred, timeout = timeout))
1✔
724
        else:
725
            return self.recvpred(pred, timeout = timeout)
1✔
726

727
    def recvline_regex(self, regex, exact=False, keepends=False, timeout=default):
1✔
728
        """recvline_regex(regex, exact=False, keepends=False, timeout=default) -> bytes
729

730
        Wrapper around :func:`recvline_pred`, which will return when a regex
731
        matches a line.
732

733
        By default :func:`re.RegexObject.search` is used, but if `exact` is
734
        set to True, then :func:`re.RegexObject.match` will be used instead.
735

736
        If the request is not satisfied before ``timeout`` seconds pass,
737
        all data is buffered and an empty string (``''``) is returned.
738
        """
739

740
        if isinstance(regex, (bytes, bytearray, six.text_type)):
1!
741
            regex = packing._need_bytes(regex)
1✔
742
            regex = re.compile(regex)
1✔
743

744
        if exact:
1!
745
            pred = regex.match
×
746
        else:
747
            pred = regex.search
1✔
748

749
        return self.recvline_pred(pred, keepends = keepends, timeout = timeout)
1✔
750

751
    def recvrepeat(self, timeout=default):
1✔
752
        """recvrepeat(timeout=default) -> bytes
753

754
        Receives data until a timeout or EOF is reached.
755

756
        Examples:
757

758
            >>> data = [
759
            ... b'd',
760
            ... b'', # simulate timeout
761
            ... b'c',
762
            ... b'b',
763
            ... b'a',
764
            ... ]
765
            >>> def delayrecv(n, data=data):
766
            ...     return data.pop()
767
            >>> t = tube()
768
            >>> t.recv_raw = delayrecv
769
            >>> t.recvrepeat(0.2)
770
            b'abc'
771
            >>> t.recv()
772
            b'd'
773
        """
774

775
        try:
1✔
776
            while self._fillbuffer(timeout=timeout):
1✔
777
                pass
1✔
778
        except EOFError:
×
779
            pass
×
780

781
        return self.buffer.get()
1✔
782

783
    def recvall(self, timeout=Timeout.forever):
1✔
784
        """recvall(timeout=Timeout.forever) -> bytes
785

786
        Receives data until EOF is reached and closes the tube.
787
        """
788

789
        with self.waitfor('Receiving all data') as h:
1✔
790
            l = len(self.buffer)
1✔
791
            with self.local(timeout):
1✔
792
                try:
1✔
793
                    while True:
1✔
794
                        l = misc.size(len(self.buffer))
1✔
795
                        h.status(l)
1✔
796
                        if not self._fillbuffer():
1✔
797
                            break
1✔
798
                except EOFError:
1✔
799
                    pass
1✔
800
            h.success("Done (%s)" % l)
1✔
801
        self.close()
1✔
802

803
        return self.buffer.get()
1✔
804

805
    def send(self, data):
1✔
806
        """send(data)
807

808
        Sends data.
809

810
        If log level ``DEBUG`` is enabled, also prints out the data
811
        received.
812

813
        If it is not possible to send anymore because of a closed
814
        connection, it raises ``exceptions.EOFError``
815

816
        Examples:
817

818
            >>> def p(x): print(repr(x))
819
            >>> t = tube()
820
            >>> t.send_raw = p
821
            >>> t.send(b'hello')
822
            b'hello'
823
        """
824

825
        data = packing._need_bytes(data)
1✔
826

827
        if self.isEnabledFor(logging.DEBUG):
1!
828
            self.debug('Sent %#x bytes:' % len(data))
×
829
            self.maybe_hexdump(data, level=logging.DEBUG)
×
830

831
        self.send_raw(data)
1✔
832

833
    def sendline(self, line=b''):
1✔
834
        r"""sendline(data)
835

836
        Shorthand for ``t.send(data + t.newline)``.
837

838
        Examples:
839

840
            >>> def p(x): print(repr(x))
841
            >>> t = tube()
842
            >>> t.send_raw = p
843
            >>> t.sendline(b'hello')
844
            b'hello\n'
845
            >>> t.newline = b'\r\n'
846
            >>> t.sendline(b'hello')
847
            b'hello\r\n'
848
        """
849

850
        line = packing._need_bytes(line)
1✔
851

852
        self.send(line + self.newline)
1✔
853

854
    def sendlines(self, lines=[]):
1✔
855
        for line in lines:
×
856
            line = packing._need_bytes(line)
×
857
            self.sendline(line)
×
858

859
    def sendafter(self, delim, data, timeout = default):
1✔
860
        """sendafter(delim, data, timeout = default) -> str
861

862
        A combination of ``recvuntil(delim, timeout=timeout)`` and ``send(data)``.
863
        """
864

865
        data = packing._need_bytes(data)
×
866
        res = self.recvuntil(delim, timeout=timeout)
×
867
        self.send(data)
×
868
        return res
×
869

870
    def sendlineafter(self, delim, data, timeout = default):
1✔
871
        """sendlineafter(delim, data, timeout = default) -> str
872

873
        A combination of ``recvuntil(delim, timeout=timeout)`` and ``sendline(data)``."""
874

875
        data = packing._need_bytes(data)
×
876
        res = self.recvuntil(delim, timeout=timeout)
×
877
        self.sendline(data)
×
878
        return res
×
879

880
    def sendthen(self, delim, data, timeout = default):
1✔
881
        """sendthen(delim, data, timeout = default) -> str
882

883
        A combination of ``send(data)`` and ``recvuntil(delim, timeout=timeout)``."""
884

885
        data = packing._need_bytes(data)
×
886
        self.send(data)
×
887
        return self.recvuntil(delim, timeout=timeout)
×
888

889
    def sendlinethen(self, delim, data, timeout = default):
1✔
890
        """sendlinethen(delim, data, timeout = default) -> str
891

892
        A combination of ``sendline(data)`` and ``recvuntil(delim, timeout=timeout)``."""
893

894
        data = packing._need_bytes(data)
×
895
        self.sendline(data)
×
896
        return self.recvuntil(delim, timeout=timeout)
×
897

898
    def interactive(self, prompt = term.text.bold_red('$') + ' '):
1✔
899
        """interactive(prompt = pwnlib.term.text.bold_red('$') + ' ')
900

901
        Does simultaneous reading and writing to the tube. In principle this just
902
        connects the tube to standard in and standard out, but in practice this
903
        is much more usable, since we are using :mod:`pwnlib.term` to print a
904
        floating prompt.
905

906
        Thus it only works while in :data:`pwnlib.term.term_mode`.
907
        """
908

909
        self.info('Switching to interactive mode')
1✔
910

911
        go = threading.Event()
1✔
912
        def recv_thread():
1✔
913
            while not go.is_set():
1✔
914
                try:
1✔
915
                    cur = self.recv(timeout = 0.05)
1✔
916
                    cur = cur.replace(self.newline, b'\n')
1✔
917
                    if cur:
1✔
918
                        stdout = sys.stdout
1✔
919
                        if not term.term_mode:
1!
920
                            stdout = getattr(stdout, 'buffer', stdout)
1✔
921
                        stdout.write(cur)
1✔
922
                        stdout.flush()
1✔
923
                except EOFError:
×
924
                    self.info('Got EOF while reading in interactive')
×
925
                    break
×
926

927
        t = context.Thread(target = recv_thread)
1✔
928
        t.daemon = True
1✔
929
        t.start()
1✔
930

931
        from pwnlib.args import term_mode
1✔
932
        try:
1✔
933
            os_linesep = os.linesep.encode()
1✔
934
            to_skip = b''
1✔
935
            while not go.is_set():
1✔
936
                if term.term_mode:
1!
937
                    data = term.readline.readline(prompt = prompt, float = True)
×
938
                    if data.endswith(b'\n') and self.newline != b'\n':
×
939
                        data = data[:-1] + self.newline
×
940
                else:
941
                    stdin = getattr(sys.stdin, 'buffer', sys.stdin)
1✔
942
                    data = stdin.read(1)
1✔
943
                    # Keep OS's line separator if NOTERM is set and
944
                    # the user did not specify a custom newline
945
                    # even if stdin is a tty.
946
                    if sys.stdin.isatty() and (
1!
947
                        term_mode
948
                        or context.newline != b"\n"
949
                        or self._newline is not None
950
                    ):
951
                        if to_skip:
×
952
                            if to_skip[:1] != data:
×
953
                                data = os_linesep[: -len(to_skip)] + data
×
954
                            else:
955
                                to_skip = to_skip[1:]
×
956
                                if to_skip:
×
957
                                    continue
×
958
                                data = self.newline
×
959
                        # If we observe a prefix of the line separator in a tty,
960
                        # assume we'll see the rest of it immediately after.
961
                        # This could stall until the next character is seen if
962
                        # the line separator is started but never finished, but
963
                        # that is unlikely to happen in a dynamic tty.
964
                        elif data and os_linesep.startswith(data):
×
965
                            if len(os_linesep) > 1:
×
966
                                to_skip = os_linesep[1:]
×
967
                                continue
×
968
                            data = self.newline
×
969

970
                if data:
1!
971
                    try:
×
972
                        self.send(data)
×
973
                    except EOFError:
×
974
                        go.set()
×
975
                        self.info('Got EOF while sending in interactive')
×
976
                else:
977
                    go.set()
1✔
978
        except KeyboardInterrupt:
×
979
            self.info('Interrupted')
×
980
            go.set()
×
981

982
        while t.is_alive():
1✔
983
            t.join(timeout = 0.1)
1✔
984

985
    def stream(self, line_mode=True):
1✔
986
        """stream()
987

988
        Receive data until the tube exits, and print it to stdout.
989

990
        Similar to :func:`interactive`, except that no input is sent.
991

992
        Similar to ``print(tube.recvall())`` except that data is printed
993
        as it is received, rather than after all data is received.
994

995
        Arguments:
996
            line_mode(bool): Whether to receive line-by-line or raw data.
997

998
        Returns:
999
            All data printed.
1000
        """
1001
        buf = Buffer()
×
1002
        function = self.recvline if line_mode else self.recv
×
1003
        try:
×
1004
            while True:
×
1005
                buf.add(function())
×
1006
                stdout = sys.stdout
×
1007
                if not term.term_mode:
×
1008
                    stdout = getattr(stdout, 'buffer', stdout)
×
1009
                stdout.write(buf.data[-1])
×
1010
        except KeyboardInterrupt:
×
1011
            pass
×
1012
        except EOFError:
×
1013
            pass
×
1014

1015
        return buf.get()
×
1016

1017
    def clean(self, timeout = 0.05):
1✔
1018
        """clean(timeout = 0.05)
1019

1020
        Removes all the buffered data from a tube by calling
1021
        :meth:`pwnlib.tubes.tube.tube.recv` with a low timeout until it fails.
1022

1023
        If ``timeout`` is zero, only cached data will be cleared.
1024

1025
        Note: If timeout is set to zero, the underlying network is
1026
        not actually polled; only the internal buffer is cleared.
1027

1028
        Returns:
1029

1030
            All data received
1031

1032
        Examples:
1033

1034
            >>> t = tube()
1035
            >>> t.unrecv(b'clean me up')
1036
            >>> t.clean(0)
1037
            b'clean me up'
1038
            >>> len(t.buffer)
1039
            0
1040
        """
1041
        if timeout == 0:
1✔
1042
            return self.buffer.get()
1✔
1043

1044
        return self.recvrepeat(timeout)
1✔
1045

1046
    def clean_and_log(self, timeout = 0.05):
1✔
1047
        r"""clean_and_log(timeout = 0.05)
1048

1049
        Works exactly as :meth:`pwnlib.tubes.tube.tube.clean`, but logs received
1050
        data with :meth:`pwnlib.self.info`.
1051

1052
        Returns:
1053

1054
            All data received
1055

1056
        Examples:
1057

1058
            >>> def recv(n, data=[b'', b'hooray_data']):
1059
            ...     while data: return data.pop()
1060
            >>> t = tube()
1061
            >>> t.recv_raw      = recv
1062
            >>> t.connected_raw = lambda d: True
1063
            >>> t.fileno        = lambda: 1234
1064
            >>> with context.local(log_level='info'):
1065
            ...     data = t.clean_and_log() #doctest: +ELLIPSIS
1066
            [DEBUG] Received 0xb bytes:
1067
                b'hooray_data'
1068
            >>> data
1069
            b'hooray_data'
1070
            >>> context.clear()
1071
        """
1072
        cached_data = self.buffer.get()
1✔
1073
        if cached_data and not self.isEnabledFor(logging.DEBUG):
1!
1074
            with context.local(log_level='debug'):
×
1075
                self.debug('Received %#x bytes:' % len(cached_data))
×
1076
                self.maybe_hexdump(cached_data, level=logging.DEBUG)
×
1077
        with context.local(log_level='debug'):
1✔
1078
            return cached_data + self.clean(timeout)
1✔
1079

1080
    def connect_input(self, other):
1✔
1081
        """connect_input(other)
1082

1083
        Connects the input of this tube to the output of another tube object.
1084

1085

1086
        Examples:
1087

1088
            >>> def p(x): print(x.decode())
1089
            >>> def recvone(n, data=[b'data']):
1090
            ...     while data: return data.pop()
1091
            ...     raise EOFError
1092
            >>> a = tube()
1093
            >>> b = tube()
1094
            >>> a.recv_raw = recvone
1095
            >>> b.send_raw = p
1096
            >>> a.connected_raw = lambda d: True
1097
            >>> b.connected_raw = lambda d: True
1098
            >>> a.shutdown      = lambda d: True
1099
            >>> b.shutdown      = lambda d: True
1100
            >>> import time
1101
            >>> _=(b.connect_input(a), time.sleep(0.1))
1102
            data
1103
        """
1104

1105
        def pump():
1✔
1106
            import sys as _sys
1✔
1107
            while self.countdown_active():
1!
1108
                if not (self.connected('send') and other.connected('recv')):
1!
UNCOV
1109
                    break
×
1110

1111
                try:
1✔
1112
                    data = other.recv(timeout = 0.05)
1✔
1113
                except EOFError:
1✔
1114
                    break
1✔
1115

1116
                if not _sys:
1!
1117
                    return
×
1118

1119
                if not data:
1✔
1120
                    continue
1✔
1121

1122
                try:
1✔
1123
                    self.send(data)
1✔
1124
                except EOFError:
×
1125
                    break
×
1126

1127
                if not _sys:
1!
1128
                    return
×
1129

1130
            self.shutdown('send')
1✔
1131
            other.shutdown('recv')
1✔
1132

1133
        t = context.Thread(target = pump)
1✔
1134
        t.daemon = True
1✔
1135
        t.start()
1✔
1136

1137
    def connect_output(self, other):
1✔
1138
        """connect_output(other)
1139

1140
        Connects the output of this tube to the input of another tube object.
1141

1142
        Examples:
1143

1144
            >>> def p(x): print(repr(x))
1145
            >>> def recvone(n, data=[b'data']):
1146
            ...     while data: return data.pop()
1147
            ...     raise EOFError
1148
            >>> a = tube()
1149
            >>> b = tube()
1150
            >>> a.recv_raw = recvone
1151
            >>> b.send_raw = p
1152
            >>> a.connected_raw = lambda d: True
1153
            >>> b.connected_raw = lambda d: True
1154
            >>> a.shutdown      = lambda d: True
1155
            >>> b.shutdown      = lambda d: True
1156
            >>> _=(a.connect_output(b), time.sleep(0.1))
1157
            b'data'
1158
        """
1159

1160
        other.connect_input(self)
1✔
1161

1162
    def connect_both(self, other):
1✔
1163
        """connect_both(other)
1164

1165
        Connects the both ends of this tube object with another tube object."""
1166

1167
        self.connect_input(other)
1✔
1168
        self.connect_output(other)
1✔
1169

1170
    def spawn_process(self, *args, **kwargs):
1✔
1171
        """Spawns a new process having this tube as stdin, stdout and stderr.
1172

1173
        Takes the same arguments as :class:`subprocess.Popen`."""
1174

1175
        return subprocess.Popen(
1✔
1176
            *args,
1177
            stdin = self.fileno(),
1178
            stdout = self.fileno(),
1179
            stderr = self.fileno(),
1180
            **kwargs
1181
        )
1182

1183
    def __lshift__(self, other):
1✔
1184
        """
1185
        Shorthand for connecting multiple tubes.
1186

1187
        See :meth:`connect_input` for more information.
1188

1189
        Examples:
1190

1191
            The following are equivalent ::
1192

1193
                tube_a >> tube.b
1194
                tube_a.connect_input(tube_b)
1195

1196
            This is useful when chaining multiple tubes ::
1197

1198
                tube_a >> tube_b >> tube_a
1199
                tube_a.connect_input(tube_b)
1200
                tube_b.connect_input(tube_a)
1201
        """
1202
        self.connect_input(other)
×
1203
        return other
×
1204

1205
    def __rshift__(self, other):
1✔
1206
        """
1207
        Inverse of the ``<<`` operator.  See :meth:`__lshift__`.
1208

1209
        See :meth:`connect_input` for more information.
1210
        """
1211
        self.connect_output(other)
×
1212
        return other
×
1213

1214
    def __ne__(self, other):
1✔
1215
        """
1216
        Shorthand for connecting tubes to eachother.
1217

1218
        The following are equivalent ::
1219

1220
            a >> b >> a
1221
            a <> b
1222

1223
        See :meth:`connect_input` for more information.
1224
        """
1225
        self << other << self
×
1226

1227
    def wait_for_close(self, timeout=default):
1✔
1228
        """Waits until the tube is closed."""
1229

1230
        with self.countdown(timeout):
1✔
1231
            while self.countdown_active():
1!
1232
                if not self.connected():
1✔
1233
                    return
1✔
1234
                time.sleep(min(self.timeout, 0.05))
1✔
1235

1236
    wait = wait_for_close
1✔
1237

1238
    def can_recv(self, timeout = 0):
1✔
1239
        """can_recv(timeout = 0) -> bool
1240

1241
        Returns True, if there is data available within `timeout` seconds.
1242

1243
        Examples:
1244

1245
            >>> import time
1246
            >>> t = tube()
1247
            >>> t.can_recv_raw = lambda *a: False
1248
            >>> t.can_recv()
1249
            False
1250
            >>> _=t.unrecv(b'data')
1251
            >>> t.can_recv()
1252
            True
1253
            >>> _=t.recv()
1254
            >>> t.can_recv()
1255
            False
1256
        """
1257

1258
        return bool(self.buffer or self.can_recv_raw(timeout))
1✔
1259

1260
    def settimeout(self, timeout):
1✔
1261
        """settimeout(timeout)
1262

1263
        Set the timeout for receiving operations. If the string "default"
1264
        is given, then :data:`context.timeout` will be used. If None is given,
1265
        then there will be no timeout.
1266

1267
        Examples:
1268

1269
            >>> t = tube()
1270
            >>> t.settimeout_raw = lambda t: None
1271
            >>> t.settimeout(3)
1272
            >>> t.timeout == 3
1273
            True
1274
        """
1275

1276
        self.timeout = timeout
1✔
1277

1278

1279
    shutdown_directions = {
1✔
1280
        'in':    'recv',
1281
        'read':  'recv',
1282
        'recv':  'recv',
1283
        'out':   'send',
1284
        'write': 'send',
1285
        'send':  'send',
1286
    }
1287

1288
    connected_directions = shutdown_directions.copy()
1✔
1289
    connected_directions['any'] = 'any'
1✔
1290

1291
    def shutdown(self, direction = "send"):
1✔
1292
        """shutdown(direction = "send")
1293

1294
        Closes the tube for futher reading or writing depending on `direction`.
1295

1296
        Arguments:
1297
          direction(str): Which direction to close; "in", "read" or "recv"
1298
            closes the tube in the ingoing direction, "out", "write" or "send"
1299
            closes it in the outgoing direction.
1300

1301
        Returns:
1302
          :const:`None`
1303

1304
        Examples:
1305

1306
            >>> def p(x): print(x)
1307
            >>> t = tube()
1308
            >>> t.shutdown_raw = p
1309
            >>> _=list(map(t.shutdown, ('in', 'read', 'recv', 'out', 'write', 'send')))
1310
            recv
1311
            recv
1312
            recv
1313
            send
1314
            send
1315
            send
1316
            >>> t.shutdown('bad_value') #doctest: +ELLIPSIS
1317
            Traceback (most recent call last):
1318
            ...
1319
            KeyError: "direction must be in ['in', 'out', 'read', 'recv', 'send', 'write']"
1320
        """
1321
        try:
1✔
1322
            direction = self.shutdown_directions[direction]
1✔
1323
        except KeyError:
1✔
1324
            raise KeyError('direction must be in %r' % sorted(self.shutdown_directions))
1✔
1325
        else:
1326
            self.shutdown_raw(self.shutdown_directions[direction])
1✔
1327

1328
    def connected(self, direction = 'any'):
1✔
1329
        """connected(direction = 'any') -> bool
1330

1331
        Returns True if the tube is connected in the specified direction.
1332

1333
        Arguments:
1334
          direction(str): Can be the string 'any', 'in', 'read', 'recv',
1335
                          'out', 'write', 'send'.
1336

1337
        Doctest:
1338

1339
            >>> def p(x): print(x)
1340
            >>> t = tube()
1341
            >>> t.connected_raw = p
1342
            >>> _=list(map(t.connected, ('any', 'in', 'read', 'recv', 'out', 'write', 'send')))
1343
            any
1344
            recv
1345
            recv
1346
            recv
1347
            send
1348
            send
1349
            send
1350
            >>> t.connected('bad_value') #doctest: +ELLIPSIS
1351
            Traceback (most recent call last):
1352
            ...
1353
            KeyError: "direction must be in ['any', 'in', 'out', 'read', 'recv', 'send', 'write']"
1354
        """
1355
        try:
1✔
1356
            direction = self.connected_directions[direction]
1✔
1357
        except KeyError:
1✔
1358
            raise KeyError('direction must be in %r' % sorted(self.connected_directions))
1✔
1359
        else:
1360
            return self.connected_raw(direction)
1✔
1361

1362
    def __enter__(self):
1✔
1363
        """Permit use of 'with' to control scoping and closing sessions.
1364

1365
        Examples:
1366

1367
            >>> t = tube()
1368
            >>> def p(x): print(x)
1369
            >>> t.close = lambda: p("Closed!")
1370
            >>> with t: pass
1371
            Closed!
1372
        """
1373
        return self
1✔
1374

1375
    def __exit__(self, type, value, traceback):
1✔
1376
        """Handles closing for 'with' statement
1377

1378
        See :meth:`__enter__`
1379
        """
1380
        self.close()
1✔
1381

1382
    # The minimal interface to be implemented by a child
1383
    @abc.abstractmethod
1✔
1384
    def recv_raw(self, numb):
1✔
1385
        """recv_raw(numb) -> str
1386

1387
        Should not be called directly. Receives data without using the buffer
1388
        on the object.
1389

1390
        Unless there is a timeout or closed connection, this should always
1391
        return data. In case of a timeout, it should return None, in case
1392
        of a closed connection it should raise an ``exceptions.EOFError``.
1393
        """
1394

1395
        raise EOFError('Not implemented')
1✔
1396

1397
    @abc.abstractmethod
1✔
1398
    def send_raw(self, data):
1✔
1399
        """send_raw(data)
1400

1401
        Should not be called directly. Sends data to the tube.
1402

1403
        Should return ``exceptions.EOFError``, if it is unable to send any
1404
        more, because of a closed tube.
1405
        """
1406

1407
        raise EOFError('Not implemented')
×
1408

1409
    def settimeout_raw(self, timeout):
1✔
1410
        """settimeout_raw(timeout)
1411

1412
        Should not be called directly. Sets the timeout for
1413
        the tube.
1414
        """
1415

1416
        raise NotImplementedError()
1✔
1417

1418
    def timeout_change(self):
1✔
1419
        """
1420
        Should not be called directly. Informs the raw layer of the tube that the timeout has changed.
1421

1422

1423
        Inherited from :class:`Timeout`.
1424
        """
1425
        try:
1✔
1426
            self.settimeout_raw(self.timeout)
1✔
1427
        except NotImplementedError:
1✔
1428
            pass
1✔
1429

1430
    def can_recv_raw(self, timeout):
1✔
1431
        """can_recv_raw(timeout) -> bool
1432

1433
        Should not be called directly. Returns True, if
1434
        there is data available within the timeout, but
1435
        ignores the buffer on the object.
1436
        """
1437

1438
        raise NotImplementedError()
×
1439

1440
    def connected_raw(self, direction):
1✔
1441
        """connected(direction = 'any') -> bool
1442

1443
        Should not be called directly.  Returns True iff the
1444
        tube is connected in the given direction.
1445
        """
1446

1447
        raise NotImplementedError()
×
1448

1449
    def close(self):
1✔
1450
        """close()
1451

1452
        Closes the tube.
1453
        """
1454
        pass
×
1455
        # Ideally we could:
1456
        # raise NotImplementedError()
1457
        # But this causes issues with the unit tests.
1458

1459
    def fileno(self):
1✔
1460
        """fileno() -> int
1461

1462
        Returns the file number used for reading.
1463
        """
1464

1465
        raise NotImplementedError()
×
1466

1467
    def shutdown_raw(self, direction):
1✔
1468
        """shutdown_raw(direction)
1469

1470
        Should not be called directly.  Closes the tube for further reading or
1471
        writing.
1472
        """
1473

1474
        raise NotImplementedError()
×
1475

1476

1477
    def p64(self, *a, **kw):        return self.send(packing.p64(*a, **kw))
1!
1478
    def p32(self, *a, **kw):        return self.send(packing.p32(*a, **kw))
1!
1479
    def p16(self, *a, **kw):        return self.send(packing.p16(*a, **kw))
1!
1480
    def p8(self, *a, **kw):         return self.send(packing.p8(*a, **kw))
1!
1481
    def pack(self, *a, **kw):       return self.send(packing.pack(*a, **kw))
1✔
1482

1483
    def u64(self, *a, **kw):        return packing.u64(self.recvn(8), *a, **kw)
1!
1484
    def u32(self, *a, **kw):        return packing.u32(self.recvn(4), *a, **kw)
1!
1485
    def u16(self, *a, **kw):        return packing.u16(self.recvn(2), *a, **kw)
1!
1486
    def u8(self, *a, **kw):         return packing.u8(self.recvn(1), *a, **kw)
1!
1487
    def unpack(self, *a, **kw):     return packing.unpack(self.recvn(context.bytes), *a, **kw)
1✔
1488

1489
    def flat(self, *a, **kw):       return self.send(packing.flat(*a,**kw))
1!
1490
    def fit(self, *a, **kw):        return self.send(packing.fit(*a, **kw))
1!
1491

1492
    # Dynamic functions
1493

1494
    def make_wrapper(func):
1✔
1495
        def wrapperb(self, *a, **kw):
1✔
1496
            return bytearray(func(self, *a, **kw))
×
1497
        def wrapperS(self, *a, **kw):
1✔
1498
            return packing._decode(func(self, *a, **kw))
1✔
1499
        wrapperb.__doc__ = 'Same as :meth:`{func.__name__}`, but returns a bytearray'.format(func=func)
1✔
1500
        wrapperb.__name__ = func.__name__ + 'b'
1✔
1501
        wrapperS.__doc__ = 'Same as :meth:`{func.__name__}`, but returns a str, ' \
1✔
1502
                           'decoding the result using `context.encoding`. ' \
1503
                           '(note that the binary versions are way faster)'.format(func=func)
1504
        wrapperS.__name__ = func.__name__ + 'S'
1✔
1505
        return wrapperb, wrapperS
1✔
1506

1507
    for func in [recv,
1✔
1508
                 recvn,
1509
                 recvall,
1510
                 recvrepeat,
1511
                 recvuntil,
1512
                 recvpred,
1513
                 recvregex,
1514
                 recvline,
1515
                 recvline_contains,
1516
                 recvline_startswith,
1517
                 recvline_endswith,
1518
                 recvline_regex]:
1519
        for wrapper in make_wrapper(func):
1✔
1520
            locals()[wrapper.__name__] = wrapper
1✔
1521

1522
    def make_wrapper(func, alias):
1✔
1523
        def wrapper(self, *a, **kw):
1✔
1524
            return func(self, *a, **kw)
1✔
1525
        wrapper.__doc__ = 'Alias for :meth:`{func.__name__}`'.format(func=func)
1✔
1526
        wrapper.__name__ = alias
1✔
1527
        return wrapper
1✔
1528

1529
    for _name in list(locals()):
1✔
1530
        if 'recv' in _name:
1✔
1531
            _name2 = _name.replace('recv', 'read')
1✔
1532
        elif 'send' in _name:
1✔
1533
            _name2 = _name.replace('send', 'write')
1✔
1534
        else:
1535
            continue
1✔
1536
        locals()[_name2] = make_wrapper(locals()[_name], _name2)
1✔
1537

1538
    # Clean up the scope
1539
    del wrapper, func, make_wrapper, _name, _name2
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc