namedpipe.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. // Copyright 2021 The Go Authors. All rights reserved.
  2. // Copyright 2015 Microsoft
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. //go:build windows
  6. // +build windows
  7. // Package namedpipe implements a net.Conn and net.Listener around Windows named pipes.
  8. package namedpipe
  9. import (
  10. "context"
  11. "io"
  12. "net"
  13. "os"
  14. "runtime"
  15. "sync/atomic"
  16. "time"
  17. "unsafe"
  18. "golang.org/x/sys/windows"
  19. )
  20. type pipe struct {
  21. *file
  22. path string
  23. }
  24. type messageBytePipe struct {
  25. pipe
  26. writeClosed int32
  27. readEOF bool
  28. }
  29. type pipeAddress string
  30. func (f *pipe) LocalAddr() net.Addr {
  31. return pipeAddress(f.path)
  32. }
  33. func (f *pipe) RemoteAddr() net.Addr {
  34. return pipeAddress(f.path)
  35. }
  36. func (f *pipe) SetDeadline(t time.Time) error {
  37. f.SetReadDeadline(t)
  38. f.SetWriteDeadline(t)
  39. return nil
  40. }
  41. // CloseWrite closes the write side of a message pipe in byte mode.
  42. func (f *messageBytePipe) CloseWrite() error {
  43. if !atomic.CompareAndSwapInt32(&f.writeClosed, 0, 1) {
  44. return io.ErrClosedPipe
  45. }
  46. err := f.file.Flush()
  47. if err != nil {
  48. atomic.StoreInt32(&f.writeClosed, 0)
  49. return err
  50. }
  51. _, err = f.file.Write(nil)
  52. if err != nil {
  53. atomic.StoreInt32(&f.writeClosed, 0)
  54. return err
  55. }
  56. return nil
  57. }
  58. // Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
  59. // they are used to implement CloseWrite.
  60. func (f *messageBytePipe) Write(b []byte) (int, error) {
  61. if atomic.LoadInt32(&f.writeClosed) != 0 {
  62. return 0, io.ErrClosedPipe
  63. }
  64. if len(b) == 0 {
  65. return 0, nil
  66. }
  67. return f.file.Write(b)
  68. }
  69. // Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
  70. // mode pipe will return io.EOF, as will all subsequent reads.
  71. func (f *messageBytePipe) Read(b []byte) (int, error) {
  72. if f.readEOF {
  73. return 0, io.EOF
  74. }
  75. n, err := f.file.Read(b)
  76. if err == io.EOF {
  77. // If this was the result of a zero-byte read, then
  78. // it is possible that the read was due to a zero-size
  79. // message. Since we are simulating CloseWrite with a
  80. // zero-byte message, ensure that all future Read calls
  81. // also return EOF.
  82. f.readEOF = true
  83. } else if err == windows.ERROR_MORE_DATA {
  84. // ERROR_MORE_DATA indicates that the pipe's read mode is message mode
  85. // and the message still has more bytes. Treat this as a success, since
  86. // this package presents all named pipes as byte streams.
  87. err = nil
  88. }
  89. return n, err
  90. }
  91. func (f *pipe) Handle() windows.Handle {
  92. return f.handle
  93. }
  94. func (s pipeAddress) Network() string {
  95. return "pipe"
  96. }
  97. func (s pipeAddress) String() string {
  98. return string(s)
  99. }
  100. // tryDialPipe attempts to dial the specified pipe until cancellation or timeout.
  101. func tryDialPipe(ctx context.Context, path *string) (windows.Handle, error) {
  102. for {
  103. select {
  104. case <-ctx.Done():
  105. return 0, ctx.Err()
  106. default:
  107. path16, err := windows.UTF16PtrFromString(*path)
  108. if err != nil {
  109. return 0, err
  110. }
  111. h, err := windows.CreateFile(path16, windows.GENERIC_READ|windows.GENERIC_WRITE, 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_OVERLAPPED|windows.SECURITY_SQOS_PRESENT|windows.SECURITY_ANONYMOUS, 0)
  112. if err == nil {
  113. return h, nil
  114. }
  115. if err != windows.ERROR_PIPE_BUSY {
  116. return h, &os.PathError{Err: err, Op: "open", Path: *path}
  117. }
  118. // Wait 10 msec and try again. This is a rather simplistic
  119. // view, as we always try each 10 milliseconds.
  120. time.Sleep(10 * time.Millisecond)
  121. }
  122. }
  123. }
  124. // DialConfig exposes various options for use in Dial and DialContext.
  125. type DialConfig struct {
  126. ExpectedOwner *windows.SID // If non-nil, the pipe is verified to be owned by this SID.
  127. }
  128. // DialTimeout connects to the specified named pipe by path, timing out if the
  129. // connection takes longer than the specified duration. If timeout is zero, then
  130. // we use a default timeout of 2 seconds.
  131. func (config *DialConfig) DialTimeout(path string, timeout time.Duration) (net.Conn, error) {
  132. if timeout == 0 {
  133. timeout = time.Second * 2
  134. }
  135. absTimeout := time.Now().Add(timeout)
  136. ctx, _ := context.WithDeadline(context.Background(), absTimeout)
  137. conn, err := config.DialContext(ctx, path)
  138. if err == context.DeadlineExceeded {
  139. return nil, os.ErrDeadlineExceeded
  140. }
  141. return conn, err
  142. }
  143. // DialContext attempts to connect to the specified named pipe by path.
  144. func (config *DialConfig) DialContext(ctx context.Context, path string) (net.Conn, error) {
  145. var err error
  146. var h windows.Handle
  147. h, err = tryDialPipe(ctx, &path)
  148. if err != nil {
  149. return nil, err
  150. }
  151. if config.ExpectedOwner != nil {
  152. sd, err := windows.GetSecurityInfo(h, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
  153. if err != nil {
  154. windows.Close(h)
  155. return nil, err
  156. }
  157. realOwner, _, err := sd.Owner()
  158. if err != nil {
  159. windows.Close(h)
  160. return nil, err
  161. }
  162. if !realOwner.Equals(config.ExpectedOwner) {
  163. windows.Close(h)
  164. return nil, windows.ERROR_ACCESS_DENIED
  165. }
  166. }
  167. var flags uint32
  168. err = windows.GetNamedPipeInfo(h, &flags, nil, nil, nil)
  169. if err != nil {
  170. windows.Close(h)
  171. return nil, err
  172. }
  173. f, err := makeFile(h)
  174. if err != nil {
  175. windows.Close(h)
  176. return nil, err
  177. }
  178. // If the pipe is in message mode, return a message byte pipe, which
  179. // supports CloseWrite.
  180. if flags&windows.PIPE_TYPE_MESSAGE != 0 {
  181. return &messageBytePipe{
  182. pipe: pipe{file: f, path: path},
  183. }, nil
  184. }
  185. return &pipe{file: f, path: path}, nil
  186. }
  187. var defaultDialer DialConfig
  188. // DialTimeout calls DialConfig.DialTimeout using an empty configuration.
  189. func DialTimeout(path string, timeout time.Duration) (net.Conn, error) {
  190. return defaultDialer.DialTimeout(path, timeout)
  191. }
  192. // DialContext calls DialConfig.DialContext using an empty configuration.
  193. func DialContext(ctx context.Context, path string) (net.Conn, error) {
  194. return defaultDialer.DialContext(ctx, path)
  195. }
  196. type acceptResponse struct {
  197. f *file
  198. err error
  199. }
  200. type pipeListener struct {
  201. firstHandle windows.Handle
  202. path string
  203. config ListenConfig
  204. acceptCh chan chan acceptResponse
  205. closeCh chan int
  206. doneCh chan int
  207. }
  208. func makeServerPipeHandle(path string, sd *windows.SECURITY_DESCRIPTOR, c *ListenConfig, isFirstPipe bool) (windows.Handle, error) {
  209. path16, err := windows.UTF16PtrFromString(path)
  210. if err != nil {
  211. return 0, &os.PathError{Op: "open", Path: path, Err: err}
  212. }
  213. var oa windows.OBJECT_ATTRIBUTES
  214. oa.Length = uint32(unsafe.Sizeof(oa))
  215. var ntPath windows.NTUnicodeString
  216. if err := windows.RtlDosPathNameToNtPathName(path16, &ntPath, nil, nil); err != nil {
  217. if ntstatus, ok := err.(windows.NTStatus); ok {
  218. err = ntstatus.Errno()
  219. }
  220. return 0, &os.PathError{Op: "open", Path: path, Err: err}
  221. }
  222. defer windows.LocalFree(windows.Handle(unsafe.Pointer(ntPath.Buffer)))
  223. oa.ObjectName = &ntPath
  224. // The security descriptor is only needed for the first pipe.
  225. if isFirstPipe {
  226. if sd != nil {
  227. oa.SecurityDescriptor = sd
  228. } else {
  229. // Construct the default named pipe security descriptor.
  230. var acl *windows.ACL
  231. if err := windows.RtlDefaultNpAcl(&acl); err != nil {
  232. return 0, err
  233. }
  234. defer windows.LocalFree(windows.Handle(unsafe.Pointer(acl)))
  235. sd, err = windows.NewSecurityDescriptor()
  236. if err != nil {
  237. return 0, err
  238. }
  239. if err = sd.SetDACL(acl, true, false); err != nil {
  240. return 0, err
  241. }
  242. oa.SecurityDescriptor = sd
  243. }
  244. }
  245. typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS)
  246. if c.MessageMode {
  247. typ |= windows.FILE_PIPE_MESSAGE_TYPE
  248. }
  249. disposition := uint32(windows.FILE_OPEN)
  250. access := uint32(windows.GENERIC_READ | windows.GENERIC_WRITE | windows.SYNCHRONIZE)
  251. if isFirstPipe {
  252. disposition = windows.FILE_CREATE
  253. // By not asking for read or write access, the named pipe file system
  254. // will put this pipe into an initially disconnected state, blocking
  255. // client connections until the next call with isFirstPipe == false.
  256. access = windows.SYNCHRONIZE
  257. }
  258. timeout := int64(-50 * 10000) // 50ms
  259. var (
  260. h windows.Handle
  261. iosb windows.IO_STATUS_BLOCK
  262. )
  263. err = windows.NtCreateNamedPipeFile(&h, access, &oa, &iosb, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout)
  264. if err != nil {
  265. if ntstatus, ok := err.(windows.NTStatus); ok {
  266. err = ntstatus.Errno()
  267. }
  268. return 0, &os.PathError{Op: "open", Path: path, Err: err}
  269. }
  270. runtime.KeepAlive(ntPath)
  271. return h, nil
  272. }
  273. func (l *pipeListener) makeServerPipe() (*file, error) {
  274. h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
  275. if err != nil {
  276. return nil, err
  277. }
  278. f, err := makeFile(h)
  279. if err != nil {
  280. windows.Close(h)
  281. return nil, err
  282. }
  283. return f, nil
  284. }
  285. func (l *pipeListener) makeConnectedServerPipe() (*file, error) {
  286. p, err := l.makeServerPipe()
  287. if err != nil {
  288. return nil, err
  289. }
  290. // Wait for the client to connect.
  291. ch := make(chan error)
  292. go func(p *file) {
  293. ch <- connectPipe(p)
  294. }(p)
  295. select {
  296. case err = <-ch:
  297. if err != nil {
  298. p.Close()
  299. p = nil
  300. }
  301. case <-l.closeCh:
  302. // Abort the connect request by closing the handle.
  303. p.Close()
  304. p = nil
  305. err = <-ch
  306. if err == nil || err == os.ErrClosed {
  307. err = net.ErrClosed
  308. }
  309. }
  310. return p, err
  311. }
  312. func (l *pipeListener) listenerRoutine() {
  313. closed := false
  314. for !closed {
  315. select {
  316. case <-l.closeCh:
  317. closed = true
  318. case responseCh := <-l.acceptCh:
  319. var (
  320. p *file
  321. err error
  322. )
  323. for {
  324. p, err = l.makeConnectedServerPipe()
  325. // If the connection was immediately closed by the client, try
  326. // again.
  327. if err != windows.ERROR_NO_DATA {
  328. break
  329. }
  330. }
  331. responseCh <- acceptResponse{p, err}
  332. closed = err == net.ErrClosed
  333. }
  334. }
  335. windows.Close(l.firstHandle)
  336. l.firstHandle = 0
  337. // Notify Close and Accept callers that the handle has been closed.
  338. close(l.doneCh)
  339. }
  340. // ListenConfig contains configuration for the pipe listener.
  341. type ListenConfig struct {
  342. // SecurityDescriptor contains a Windows security descriptor. If nil, the default from RtlDefaultNpAcl is used.
  343. SecurityDescriptor *windows.SECURITY_DESCRIPTOR
  344. // MessageMode determines whether the pipe is in byte or message mode. In either
  345. // case the pipe is read in byte mode by default. The only practical difference in
  346. // this implementation is that CloseWrite is only supported for message mode pipes;
  347. // CloseWrite is implemented as a zero-byte write, but zero-byte writes are only
  348. // transferred to the reader (and returned as io.EOF in this implementation)
  349. // when the pipe is in message mode.
  350. MessageMode bool
  351. // InputBufferSize specifies the initial size of the input buffer, in bytes, which the OS will grow as needed.
  352. InputBufferSize int32
  353. // OutputBufferSize specifies the initial size of the output buffer, in bytes, which the OS will grow as needed.
  354. OutputBufferSize int32
  355. }
  356. // Listen creates a listener on a Windows named pipe path,such as \\.\pipe\mypipe.
  357. // The pipe must not already exist.
  358. func (c *ListenConfig) Listen(path string) (net.Listener, error) {
  359. h, err := makeServerPipeHandle(path, c.SecurityDescriptor, c, true)
  360. if err != nil {
  361. return nil, err
  362. }
  363. l := &pipeListener{
  364. firstHandle: h,
  365. path: path,
  366. config: *c,
  367. acceptCh: make(chan chan acceptResponse),
  368. closeCh: make(chan int),
  369. doneCh: make(chan int),
  370. }
  371. // The first connection is swallowed on Windows 7 & 8, so synthesize it.
  372. if maj, min, _ := windows.RtlGetNtVersionNumbers(); maj < 6 || (maj == 6 && min < 4) {
  373. path16, err := windows.UTF16PtrFromString(path)
  374. if err == nil {
  375. h, err = windows.CreateFile(path16, 0, 0, nil, windows.OPEN_EXISTING, windows.SECURITY_SQOS_PRESENT|windows.SECURITY_ANONYMOUS, 0)
  376. if err == nil {
  377. windows.CloseHandle(h)
  378. }
  379. }
  380. }
  381. go l.listenerRoutine()
  382. return l, nil
  383. }
  384. var defaultListener ListenConfig
  385. // Listen calls ListenConfig.Listen using an empty configuration.
  386. func Listen(path string) (net.Listener, error) {
  387. return defaultListener.Listen(path)
  388. }
  389. func connectPipe(p *file) error {
  390. c, err := p.prepareIo()
  391. if err != nil {
  392. return err
  393. }
  394. defer p.wg.Done()
  395. err = windows.ConnectNamedPipe(p.handle, &c.o)
  396. _, err = p.asyncIo(c, nil, 0, err)
  397. if err != nil && err != windows.ERROR_PIPE_CONNECTED {
  398. return err
  399. }
  400. return nil
  401. }
  402. func (l *pipeListener) Accept() (net.Conn, error) {
  403. ch := make(chan acceptResponse)
  404. select {
  405. case l.acceptCh <- ch:
  406. response := <-ch
  407. err := response.err
  408. if err != nil {
  409. return nil, err
  410. }
  411. if l.config.MessageMode {
  412. return &messageBytePipe{
  413. pipe: pipe{file: response.f, path: l.path},
  414. }, nil
  415. }
  416. return &pipe{file: response.f, path: l.path}, nil
  417. case <-l.doneCh:
  418. return nil, net.ErrClosed
  419. }
  420. }
  421. func (l *pipeListener) Close() error {
  422. select {
  423. case l.closeCh <- 1:
  424. <-l.doneCh
  425. case <-l.doneCh:
  426. }
  427. return nil
  428. }
  429. func (l *pipeListener) Addr() net.Addr {
  430. return pipeAddress(l.path)
  431. }