1# Copyright (c) 2016 Cloudbase Solutions Srl
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at:
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import sys
16
17if sys.platform != 'win32':
18    raise Exception("Intended to use only on Windows")
19else:
20    import ntsecuritycon
21    import pywintypes
22    import win32con
23    import win32event
24    import win32file
25    import win32pipe
26    import win32security
27    import winerror
28
29
30def close_handle(handle, logger=None):
31    try:
32        win32file.CloseHandle(handle)
33        return None
34    except pywintypes.error as e:
35        if logger is not None:
36            logger("failed to close handle: %s" % e.strerror)
37        return e.winerror
38
39
40def windows_create_pipe(sAttrs=-1, nSize=None):
41    # Default values if parameters are not passed
42    if sAttrs == -1:
43        sAttrs = win32security.SECURITY_ATTRIBUTES()
44        sAttrs.bInheritHandle = 1
45    if nSize is None:
46        # If this parameter is zero, the system uses the default buffer size.
47        nSize = 0
48
49    try:
50        (read_pipe, write_pipe) = win32pipe.CreatePipe(sAttrs, nSize)
51    except pywintypes.error:
52        raise
53
54    return (read_pipe, write_pipe)
55
56
57def windows_read_pipe(fd, length):
58    try:
59        (error, data) = win32file.ReadFile(fd, length)
60        return error, data
61    except pywintypes.error as e:
62        return e.winerror, ""
63
64
65def create_file(filename, desiredAccess=None, shareMode=None, attributes=-1,
66                CreationDisposition=None, flagsAndAttributes=None,
67                hTemplateFile=-1):
68    # Default values if parameters are not passed
69    if desiredAccess is None:
70        desiredAccess = win32file.GENERIC_READ | win32file.GENERIC_WRITE
71    if shareMode is None:
72        shareMode = 0
73    if attributes == -1:
74        # attributes can be None
75        attributes = None
76    if CreationDisposition is None:
77        CreationDisposition = win32file.OPEN_EXISTING
78    if flagsAndAttributes is None:
79        flagsAndAttributes = (win32file.FILE_ATTRIBUTE_NORMAL |
80                              win32file.FILE_FLAG_OVERLAPPED |
81                              win32file.FILE_FLAG_NO_BUFFERING)
82    if hTemplateFile == -1:
83        hTemplateFile = None
84
85    try:
86        npipe = win32file.CreateFile(filename,
87                                     desiredAccess,
88                                     shareMode,
89                                     attributes,
90                                     CreationDisposition,
91                                     flagsAndAttributes,
92                                     hTemplateFile)
93    except pywintypes.error:
94        raise
95    return npipe
96
97
98def write_file(handle, data, overlapped=None):
99    try:
100        (errCode, nBytesWritten) = win32file.WriteFile(handle,
101                                                       data,
102                                                       overlapped)
103        # Note: win32file.WriteFile doesn't throw an exception
104        # in case it receives ERROR_IO_PENDING.
105        return (errCode, nBytesWritten)
106    except pywintypes.error as e:
107        return (e.winerror, 0)
108
109
110def read_file(handle, bufsize, overlapped=None):
111    try:
112        # Note: win32file.ReadFile doesn't throw an exception
113        # in case it receives ERROR_IO_PENDING.
114        (errCode, read_buffer) = win32file.ReadFile(
115            handle, bufsize, overlapped)
116        return (errCode, read_buffer)
117    except pywintypes.error as e:
118        return (e.winerror, "")
119
120
121def create_named_pipe(pipename, openMode=None, pipeMode=None,
122                      nMaxInstances=None, nOutBufferSize=None,
123                      nInBufferSize=None, nDefaultTimeOut=None,
124                      saAttr=-1):
125    # Default values if parameters are not passed
126    if openMode is None:
127        openMode = win32con.PIPE_ACCESS_DUPLEX | win32con.FILE_FLAG_OVERLAPPED
128    if pipeMode is None:
129        pipeMode = (win32con.PIPE_TYPE_MESSAGE |
130                    win32con.PIPE_READMODE_BYTE |
131                    win32con.PIPE_WAIT)
132    if nMaxInstances is None:
133        nMaxInstances = 64
134    if nOutBufferSize is None:
135        nOutBufferSize = 65000
136    if nInBufferSize is None:
137        nInBufferSize = 65000
138    if nDefaultTimeOut is None:
139        nDefaultTimeOut = 0
140    if saAttr == -1:
141        # saAttr can be None
142        saAttr = win32security.SECURITY_ATTRIBUTES()
143
144        # The identifier authority.
145        sia = ntsecuritycon.SECURITY_NT_AUTHORITY
146
147        # Initialize the SID.
148        remoteAccessSid = win32security.SID()
149        remoteAccessSid.Initialize(
150            sia,  # The identifier authority.
151            1)  # The number of sub authorities to allocate.
152        # Disable access over network.
153        remoteAccessSid.SetSubAuthority(
154            0,  # The index of the sub authority to set
155            ntsecuritycon.SECURITY_NETWORK_RID)
156
157        allowedPsids = []
158        # Allow Windows Services to access the Named Pipe.
159        allowedPsid_0 = win32security.SID()
160        allowedPsid_0.Initialize(
161            sia,  # The identifier authority.
162            1)  # The number of sub authorities to allocate.
163        allowedPsid_0.SetSubAuthority(
164            0,  # The index of the sub authority to set
165            ntsecuritycon.SECURITY_LOCAL_SYSTEM_RID)
166        # Allow Administrators to access the Named Pipe.
167        allowedPsid_1 = win32security.SID()
168        allowedPsid_1.Initialize(
169            sia,  # The identifier authority.
170            2)  # The number of sub authorities to allocate.
171        allowedPsid_1.SetSubAuthority(
172            0,  # The index of the sub authority to set
173            ntsecuritycon.SECURITY_BUILTIN_DOMAIN_RID)
174        allowedPsid_1.SetSubAuthority(
175            1,  # The index of the sub authority to set
176            ntsecuritycon.DOMAIN_ALIAS_RID_ADMINS)
177
178        allowedPsids.append(allowedPsid_0)
179        allowedPsids.append(allowedPsid_1)
180
181        # Initialize an ACL.
182        acl = win32security.ACL()
183        acl.Initialize()
184        # Add denied ACL.
185        acl.AddAccessDeniedAce(win32security.ACL_REVISION,
186                               ntsecuritycon.GENERIC_ALL,
187                               remoteAccessSid)
188        # Add allowed ACLs.
189        for allowedPsid in allowedPsids:
190            acl.AddAccessAllowedAce(win32security.ACL_REVISION,
191                                    ntsecuritycon.GENERIC_ALL,
192                                    allowedPsid)
193
194        # Initialize an SD.
195        sd = win32security.SECURITY_DESCRIPTOR()
196        sd.Initialize()
197        # Set DACL.
198        sd.SetSecurityDescriptorDacl(True, acl, False)
199
200        saAttr.bInheritHandle = 1
201        saAttr.SECURITY_DESCRIPTOR = sd
202
203    try:
204        npipe = win32pipe.CreateNamedPipe(pipename,
205                                          openMode,
206                                          pipeMode,
207                                          nMaxInstances,
208                                          nOutBufferSize,
209                                          nInBufferSize,
210                                          nDefaultTimeOut,
211                                          saAttr)
212
213        if npipe == win32file.INVALID_HANDLE_VALUE:
214            return None
215
216        return npipe
217    except pywintypes.error:
218        return None
219
220
221def set_pipe_mode(hPipe, mode=-1, maxCollectionCount=None,
222                  collectDataTimeout=None):
223    # Default values if parameters are not passed
224    if mode == -1:
225        mode = win32pipe.PIPE_READMODE_BYTE
226    try:
227        win32pipe.SetNamedPipeHandleState(
228            hPipe, mode, maxCollectionCount, collectDataTimeout)
229    except pywintypes.error:
230        raise
231
232
233def connect_named_pipe(pipe_handle, overlapped=None):
234    try:
235        # If the result of ConnectNamedPipe is ERROR_IO_PENDING or
236        # ERROR_PIPE_CONNECTED, then this value is returned.
237        # All other error values raise a win32 exception
238        error = win32pipe.ConnectNamedPipe(pipe_handle, overlapped)
239        return error
240    except pywintypes.error as e:
241        return e.winerror
242
243
244def get_pipe_name(name):
245    name = name.replace('/', '')
246    name = name.replace('\\', '')
247    name = "\\\\.\\pipe\\" + name
248    return name
249
250
251def get_overlapped_result(handle, overlapped=None, bWait=False):
252    try:
253        return win32file.GetOverlappedResult(handle, overlapped, bWait)
254    except pywintypes.error:
255        raise
256
257
258def get_new_event(sa=None, bManualReset=True, bInitialState=True,
259                  objectName=None):
260    return win32event.CreateEvent(sa, bManualReset, bInitialState, objectName)
261
262
263pipe_disconnected_errors = [winerror.ERROR_PIPE_NOT_CONNECTED,
264                            winerror.ERROR_BAD_PIPE,
265                            winerror.ERROR_NO_DATA,
266                            winerror.ERROR_BROKEN_PIPE]
267