xpra icon
Bug tracker and wiki

Ticket #1174: named-pipes-ctypes.patch

File named-pipes-ctypes.patch, 11.4 KB (added by Antoine Martin, 3 years ago)

ctypes implementation that doesn't crash! (also doesn't work..)

  • xpra/log.py

     
    237237                ("mmap"         , "mmap transfers"),
    238238                ("protocol"     , "Packet input and output (formatting, parsing, sending and receiving)"),
    239239                ("websocket"    , "Websocket layer"),
     240                ("named-pipe"   , "Named pipe"),
    240241                ("crypto"       , "Encryption"),
    241242                ("auth"         , "Authentication"),
    242243                ])),
  • xpra/platform/win32/namedpipes/connection.py

     
    66
    77#@PydevCodeAnalysisIgnore
    88
    9 import ctypes
    109import binascii
     10from ctypes import windll, create_string_buffer, byref, c_ulong, c_char_p
    1111
    1212from xpra.net.bytestreams import Connection
    1313
    1414from xpra.log import Logger
    15 log = Logger("network", "win32")
     15log = Logger("network", "named-pipe", "win32")
    1616
    17 kernel32 = ctypes.windll.kernel32
     17kernel32 = windll.kernel32
     18CreateFileA = kernel32.CreateFileA
    1819ReadFile = kernel32.ReadFile
    1920WriteFile = kernel32.WriteFile
    2021CloseHandle = kernel32.CloseHandle
    2122DisconnectNamedPipe = kernel32.DisconnectNamedPipe
    2223FlushFileBuffers = kernel32.FlushFileBuffers
     24WaitNamedPipeA = kernel32.WaitNamedPipeA
     25GetLastError = kernel32.GetLastError
     26SetNamedPipeHandleState = kernel32.SetNamedPipeHandleState
    2327
    2428ERROR_PIPE_NOT_CONNECTED = 233
    2529ERROR_MORE_DATA = 234
     
    3438        try:
    3539            return Connection.untilConcludes(self, *args)
    3640        except Exception as e:
    37             code = ctypes.get_last_error()
     41            code = GetLastError()
    3842            if code==ERROR_PIPE_NOT_CONNECTED:
    3943                return None
    4044            raise IOError("%s: %s" % (e, code))
     
    4347        return self._read(self._pipe_read, n)
    4448
    4549    def _pipe_read(self, buf):
     50        BUFSIZE = 4096
     51        buf = create_string_buffer(BUFSIZE)
     52        read = c_ulong(0)
     53        hr = ERROR_MORE_DATA
    4654        data = []
    47         hr = ERROR_MORE_DATA
    4855        while hr==ERROR_MORE_DATA:
    49             hr, d = ReadFile(self.pipe_handle, 65536)
    50             data.append(d)
     56            r = ReadFile(self.pipe_handle, buf, BUFSIZE, byref(read), None)
     57            if r==1 or read.value!=0:
     58                data.append(buf.value)
    5159        s = b"".join(data)
    5260        log("pipe_read: %i / %s", hr, binascii.hexlify(s))
    5361        return s
     
    5765
    5866    def _pipe_write(self, buf):
    5967        log("pipe_write: %s", binascii.hexlify(buf))
    60         WriteFile(self.pipe_handle, buf)
     68        while buf:
     69            written = c_ulong(0)
     70            r = WriteFile(self.pipe_handle, c_char_p(buf), len(buf), byref(written), None)
     71            if not r:
     72                raise Exception("failed to write buffer to named pipe handle %i" % self.pipe_handle)
     73            if len(buf)==written.value:
     74                break
     75            buf = buf[written.value:]
    6176        FlushFileBuffers(self.pipe_handle)
    6277        #SetFilePointer(self.pipe_handle, 0, FILE_BEGIN)
    6378        return len(buf)
     
    7085                l = log.debug
    7186            l("Error: %s(%s) %i: %s", fn, self.pipe_handle, code, e)
    7287        try:
     88            FlushFileBuffers(self.pipe_handle)
     89        except Exception as e:
     90            _close_err("FlushFileBuffers", e)
     91        try:
    7392            DisconnectNamedPipe(self.pipe_handle)
    7493        except Exception as e:
    7594            _close_err("DisconnectNamedPipe", e)
     
    87106        d["type"] = "named-pipe"
    88107        d["closed"] = self.pipe_handle is None
    89108        return d
     109
     110
     111GENERIC_READ = 0x80000000
     112GENERIC_WRITE = 0x40000000
     113OPEN_EXISTING = 0x3
     114INVALID_HANDLE_VALUE = -1
     115ERROR_PIPE_BUSY = 231
     116PIPE_READMODE_MESSAGE = 0x2
     117
     118def connect_to_namedpipe(pipe_name, timeout=10):
     119    log("connect_to_namedpipe(%s, %i)", pipe_name, timeout)
     120    import time
     121    start = time.time()
     122    while True:
     123        if time.time()-start>=timeout:
     124            raise Exception("timeout waiting for named pipe '%s'" % pipe_name)
     125        pipe_handle = CreateFileA(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, 0, None)
     126        log("CreateFileA(%s)=%s", pipe_name, pipe_handle)
     127        if pipe_handle!=INVALID_HANDLE_VALUE:
     128            break
     129        if GetLastError()!=ERROR_PIPE_BUSY:
     130            raise Exception("cannot open named pipe '%s'" % pipe_name)
     131        if WaitNamedPipeA(pipe_name, timeout*10000)==0:
     132            raise Exception("timeout waiting for named pipe '%s'" % pipe_name)
     133    #we have a valid handle!
     134    dwMode = c_ulong(PIPE_READMODE_MESSAGE)
     135    r = SetNamedPipeHandleState(pipe_handle, byref(dwMode), None, None);
     136    log("SetNamedPipeHandleState(..)=%i", r)
     137    if not r:
     138        log.warn("Warning: SetNamedPipeHandleState failed")
     139    return pipe_handle
  • xpra/platform/win32/namedpipes/listener.py

     
    1010from threading import Thread
    1111
    1212from xpra.log import Logger
    13 log = Logger("network", "win32")
     13log = Logger("network", "named-pipe", "win32")
    1414
    1515
    1616kernel32 = ctypes.windll.kernel32
     
    1717ReadFile = kernel32.ReadFile
    1818WriteFile = kernel32.WriteFile
    1919CloseHandle = kernel32.CloseHandle
    20 CreateNamedPipe = kernel32.CreateNamedPipeW
     20CreateNamedPipeA = kernel32.CreateNamedPipeA
    2121ConnectNamedPipe = kernel32.ConnectNamedPipe
    2222DisconnectNamedPipe = kernel32.DisconnectNamedPipe
    2323FlushFileBuffers = kernel32.FlushFileBuffers
     24GetLastError = kernel32.GetLastError
    2425
    2526FILE_GENERIC_READ = 0x120089
    2627FILE_GENERIC_WRITE = 0x120116
     
    3031PIPE_ACCESS_DUPLEX = 0x3
    3132PIPE_READMODE_BYTE = 0
    3233PIPE_UNLIMITED_INSTANCES = 0xff
    33 
     34PIPE_ACCESS_DUPLEX = 0x3
     35PIPE_TYPE_MESSAGE = 0x4
     36PIPE_READMODE_MESSAGE = 0x2
     37PIPE_WAIT = 0
     38PIPE_UNLIMITED_INSTANCES = 255
     39NMPWAIT_USE_DEFAULT_WAIT = 0
     40INVALID_HANDLE_VALUE = -1
    3441ERROR_PIPE_CONNECTED = 535
    3542
    36 SECURITY_CREATOR_SID_AUTHORITY = (0, 0, 0, 0, 0, 3)
    37 SECURITY_WORLD_SID_AUTHORITY = (0, 0, 0, 0, 0, 1)
    38 SECURITY_WORLD_RID = 0
    39 SECURITY_CREATOR_OWNER_RID = 0
    40 
    4143TIMEOUT = 6000
    4244MAX_INSTANCES = PIPE_UNLIMITED_INSTANCES
    4345
    44 class SECURITY_ATTRIBUTES(ctypes.Structure):
    45     _fields_ = [
    46         ("nLength", ctypes.c_int),
    47         ("lpSecurityDescriptor", ctypes.c_void_p),
    48         ("bInheritHandle", ctypes.c_int),
    49         ]
    5046
    51 
    5247class NamedPipeListener(Thread):
    5348    def __init__(self, pipe_name, new_connection_cb=None):
    5449        self.pipe_name = pipe_name
    5550        self.new_connection_cb = new_connection_cb
    5651        self.exit_loop = False
    57         Thread.__init__(self, name="NamedPipeListener")
     52        Thread.__init__(self, name="NamedPipeListener-%s" % pipe_name)
    5853        self.daemon = True
    5954
    6055    def __repr__(self):
     
    6560        self.exit_loop = True
    6661
    6762    def run(self):
     63        log("%s.run()", self)
    6864        try:
    6965            self.do_run()
    7066        except Exception:
     
    7268
    7369    def do_run(self):
    7470        while not self.exit_loop:
    75             pipe_handle = self.CreatePipeHandle()
     71            pipe_handle = None
    7672            try:
    77                 hr = ConnectNamedPipe(pipe_handle)
    78                 assert hr in (0, ERROR_PIPE_CONNECTED), "ConnectNamedPipe returned %i" % hr
    79             except Exception as e:
    80                 log.error("Error: connecting pipe handle %s:", pipe_handle)
    81                 log.error(" %s", e)
     73                pipe_handle = self.CreatePipeHandle()
     74            except Exception:
     75                log.error("Error: failed to create named pipe '%s'", self.pipe_name)
     76                return
     77            log("CreatePipeHandle()=%s", pipe_handle)
     78            if pipe_handle==INVALID_HANDLE_VALUE:
     79                log.error("Error: invalid handle for named pipe '%s'", self.pipe_name)
     80                return
     81            hr = ConnectNamedPipe(pipe_handle, None)
     82            log("ConnectNamedPipe()=%s", hr)
     83            if self.exit_loop:
    8284                CloseHandle(pipe_handle)
    8385                break
    84             log("new client connected to pipe: %s", hr)
    85             if self.exit_loop:
    86                 break
    87             if self.new_connection_cb:
    88                 self.new_connection_cb(self, pipe_handle)
    89             else:
    90                 log.warn("Warning: no callback defined for new named pipe connection on %s", self.pipe_name)
     86            if hr==0 and GetLastError()==ERROR_PIPE_CONNECTED:
     87                hr = 1
     88            if hr==0:
     89                log.error("Error: cannot connect to named pipe '%s'", self.pipe_name)
    9190                CloseHandle(pipe_handle)
     91                continue
     92            #from now on, the pipe_handle will be managed elsewhere:
     93            self.new_connection_cb(self, pipe_handle)
    9294
    9395    def CreatePipeHandle(self):
     96        BUFSIZE = 4096
    9497        sa = self.CreatePipeSecurityObject()
    95         try:
    96             return CreateNamedPipe(self.pipe_name,
    97                     PIPE_ACCESS_DUPLEX| FILE_FLAG_OVERLAPPED,
    98                     PIPE_READMODE_BYTE,
    99                     MAX_INSTANCES,
    100                     0, 0, TIMEOUT, sa)
    101         except Exception:
    102             log("failed to create named pipe '%s'", self.pipe_name)
    103             raise
     98        return CreateNamedPipeA(self.pipe_name, PIPE_ACCESS_DUPLEX,
     99                                PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
     100                                PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, NMPWAIT_USE_DEFAULT_WAIT, sa)
    104101
    105102    def CreatePipeSecurityObject(self):
     103        #TODO: re-implement using ctypes
    106104        return None
    107         # Create a security object giving World read/write access,
    108         # but only "Owner" modify access.
    109         #sa = SECURITY_ATTRIBUTES()
    110         #sidEveryone = pywintypes.SID()
    111         #sidEveryone.Initialize(SECURITY_WORLD_SID_AUTHORITY,1)
    112         #sidEveryone.SetSubAuthority(0, SECURITY_WORLD_RID)
    113         #sidCreator = pywintypes.SID()
    114         #sidCreator.Initialize(SECURITY_CREATOR_SID_AUTHORITY,1)
    115         #sidCreator.SetSubAuthority(0, SECURITY_CREATOR_OWNER_RID)
    116         #acl = pywintypes.ACL()
    117         #acl.AddAccessAllowedAce(FILE_GENERIC_READ|FILE_GENERIC_WRITE, sidEveryone)
    118         #acl.AddAccessAllowedAce(FILE_ALL_ACCESS, sidCreator)
    119         #sa.SetSecurityDescriptorDacl(1, acl, 0)
    120         #return sa
  • xpra/scripts/main.py

     
    17561756            raise InitException("named pipes are only supported on MS Windows")
    17571757        import errno
    17581758        from xpra.platform.win32.dotxpra import PIPE_PATH
    1759         from xpra.platform.win32.namedpipes.connection import NamedPipeConnection
    1760         from win32file import CreateFile, GENERIC_READ, GENERIC_WRITE, OPEN_EXISTING    #@UnresolvedImport
     1759        from xpra.platform.win32.namedpipes.connection import NamedPipeConnection, connect_to_namedpipe
    17611760        path = PIPE_PATH+pipe_name
    17621761        try:
    1763             pipe_handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, 0, None)
     1762            pipe_handle = connect_to_namedpipe(path)
    17641763        except Exception as e:
    17651764            if e[0]==errno.ENOENT:
    17661765                raise InitException("the named pipe '%s' does not exist" % pipe_name)