github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/rpc/comms/ipc_windows.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // +build windows
    18  
    19  package comms
    20  
    21  import (
    22  	"fmt"
    23  	"io"
    24  	"net"
    25  	"os"
    26  	"sync"
    27  	"syscall"
    28  	"time"
    29  	"unsafe"
    30  
    31  	"github.com/ethereum/go-ethereum/logger"
    32  	"github.com/ethereum/go-ethereum/logger/glog"
    33  	"github.com/ethereum/go-ethereum/rpc/codec"
    34  	"github.com/ethereum/go-ethereum/rpc/shared"
    35  	"github.com/ethereum/go-ethereum/rpc/useragent"
    36  )
    37  
    38  var (
    39  	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
    40  
    41  	procCreateNamedPipeW    = modkernel32.NewProc("CreateNamedPipeW")
    42  	procConnectNamedPipe    = modkernel32.NewProc("ConnectNamedPipe")
    43  	procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
    44  	procWaitNamedPipeW      = modkernel32.NewProc("WaitNamedPipeW")
    45  	procCreateEventW        = modkernel32.NewProc("CreateEventW")
    46  	procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult")
    47  	procCancelIoEx          = modkernel32.NewProc("CancelIoEx")
    48  )
    49  
    50  func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
    51  	r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
    52  	handle = syscall.Handle(r0)
    53  	if handle == syscall.InvalidHandle {
    54  		if e1 != 0 {
    55  			err = error(e1)
    56  		} else {
    57  			err = syscall.EINVAL
    58  		}
    59  	}
    60  	return
    61  }
    62  
    63  func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) {
    64  	r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0)
    65  	if r1 == 0 {
    66  		if e1 != 0 {
    67  			err = error(e1)
    68  		} else {
    69  			err = syscall.EINVAL
    70  		}
    71  	}
    72  	return
    73  }
    74  
    75  func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) {
    76  	r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0)
    77  	if r1 == 0 {
    78  		if e1 != 0 {
    79  			err = error(e1)
    80  		} else {
    81  			err = syscall.EINVAL
    82  		}
    83  	}
    84  	return
    85  }
    86  
    87  func disconnectNamedPipe(handle syscall.Handle) (err error) {
    88  	r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0)
    89  	if r1 == 0 {
    90  		if e1 != 0 {
    91  			err = error(e1)
    92  		} else {
    93  			err = syscall.EINVAL
    94  		}
    95  	}
    96  	return
    97  }
    98  
    99  func waitNamedPipe(name *uint16, timeout uint32) (err error) {
   100  	r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0)
   101  	if r1 == 0 {
   102  		if e1 != 0 {
   103  			err = error(e1)
   104  		} else {
   105  			err = syscall.EINVAL
   106  		}
   107  	}
   108  	return
   109  }
   110  
   111  func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) {
   112  	var _p0 uint32
   113  	if manualReset {
   114  		_p0 = 1
   115  	} else {
   116  		_p0 = 0
   117  	}
   118  	var _p1 uint32
   119  	if initialState {
   120  		_p1 = 1
   121  	} else {
   122  		_p1 = 0
   123  	}
   124  	r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0)
   125  	handle = syscall.Handle(r0)
   126  	if handle == syscall.InvalidHandle {
   127  		if e1 != 0 {
   128  			err = error(e1)
   129  		} else {
   130  			err = syscall.EINVAL
   131  		}
   132  	}
   133  	return
   134  }
   135  
   136  func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) {
   137  	var _p0 uint32
   138  	if wait {
   139  		_p0 = 1
   140  	} else {
   141  		_p0 = 0
   142  	}
   143  	r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0)
   144  	if r1 == 0 {
   145  		if e1 != 0 {
   146  			err = error(e1)
   147  		} else {
   148  			err = syscall.EINVAL
   149  		}
   150  	}
   151  	return
   152  }
   153  
   154  const (
   155  	// openMode
   156  	pipe_access_duplex   = 0x3
   157  	pipe_access_inbound  = 0x1
   158  	pipe_access_outbound = 0x2
   159  
   160  	// openMode write flags
   161  	file_flag_first_pipe_instance = 0x00080000
   162  	file_flag_write_through       = 0x80000000
   163  	file_flag_overlapped          = 0x40000000
   164  
   165  	// openMode ACL flags
   166  	write_dac              = 0x00040000
   167  	write_owner            = 0x00080000
   168  	access_system_security = 0x01000000
   169  
   170  	// pipeMode
   171  	pipe_type_byte    = 0x0
   172  	pipe_type_message = 0x4
   173  
   174  	// pipeMode read mode flags
   175  	pipe_readmode_byte    = 0x0
   176  	pipe_readmode_message = 0x2
   177  
   178  	// pipeMode wait mode flags
   179  	pipe_wait   = 0x0
   180  	pipe_nowait = 0x1
   181  
   182  	// pipeMode remote-client mode flags
   183  	pipe_accept_remote_clients = 0x0
   184  	pipe_reject_remote_clients = 0x8
   185  
   186  	pipe_unlimited_instances = 255
   187  
   188  	nmpwait_wait_forever = 0xFFFFFFFF
   189  
   190  	// the two not-an-errors below occur if a client connects to the pipe between
   191  	// the server's CreateNamedPipe and ConnectNamedPipe calls.
   192  	error_no_data        syscall.Errno = 0xE8
   193  	error_pipe_connected syscall.Errno = 0x217
   194  	error_pipe_busy      syscall.Errno = 0xE7
   195  	error_sem_timeout    syscall.Errno = 0x79
   196  
   197  	error_bad_pathname syscall.Errno = 0xA1
   198  	error_invalid_name syscall.Errno = 0x7B
   199  
   200  	error_io_incomplete syscall.Errno = 0x3e4
   201  )
   202  
   203  var _ net.Conn = (*PipeConn)(nil)
   204  var _ net.Listener = (*PipeListener)(nil)
   205  
   206  // ErrClosed is the error returned by PipeListener.Accept when Close is called
   207  // on the PipeListener.
   208  var ErrClosed = PipeError{"Pipe has been closed.", false}
   209  
   210  // PipeError is an error related to a call to a pipe
   211  type PipeError struct {
   212  	msg     string
   213  	timeout bool
   214  }
   215  
   216  // Error implements the error interface
   217  func (e PipeError) Error() string {
   218  	return e.msg
   219  }
   220  
   221  // Timeout implements net.AddrError.Timeout()
   222  func (e PipeError) Timeout() bool {
   223  	return e.timeout
   224  }
   225  
   226  // Temporary implements net.AddrError.Temporary()
   227  func (e PipeError) Temporary() bool {
   228  	return false
   229  }
   230  
   231  // Dial connects to a named pipe with the given address. If the specified pipe is not available,
   232  // it will wait indefinitely for the pipe to become available.
   233  //
   234  // The address must be of the form \\.\\pipe\<name> for local pipes and \\<computer>\pipe\<name>
   235  // for remote pipes.
   236  //
   237  // Dial will return a PipeError if you pass in a badly formatted pipe name.
   238  //
   239  // Examples:
   240  //   // local pipe
   241  //   conn, err := Dial(`\\.\pipe\mypipename`)
   242  //
   243  //   // remote pipe
   244  //   conn, err := Dial(`\\othercomp\pipe\mypipename`)
   245  func Dial(address string) (*PipeConn, error) {
   246  	for {
   247  		conn, err := dial(address, nmpwait_wait_forever)
   248  		if err == nil {
   249  			return conn, nil
   250  		}
   251  		if isPipeNotReady(err) {
   252  			<-time.After(100 * time.Millisecond)
   253  			continue
   254  		}
   255  		return nil, err
   256  	}
   257  }
   258  
   259  // DialTimeout acts like Dial, but will time out after the duration of timeout
   260  func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) {
   261  	deadline := time.Now().Add(timeout)
   262  
   263  	now := time.Now()
   264  	for now.Before(deadline) {
   265  		millis := uint32(deadline.Sub(now) / time.Millisecond)
   266  		conn, err := dial(address, millis)
   267  		if err == nil {
   268  			return conn, nil
   269  		}
   270  		if err == error_sem_timeout {
   271  			// This is WaitNamedPipe's timeout error, so we know we're done
   272  			return nil, PipeError{fmt.Sprintf(
   273  				"Timed out waiting for pipe '%s' to come available", address), true}
   274  		}
   275  		if isPipeNotReady(err) {
   276  			left := deadline.Sub(time.Now())
   277  			retry := 100 * time.Millisecond
   278  			if left > retry {
   279  				<-time.After(retry)
   280  			} else {
   281  				<-time.After(left - time.Millisecond)
   282  			}
   283  			now = time.Now()
   284  			continue
   285  		}
   286  		return nil, err
   287  	}
   288  	return nil, PipeError{fmt.Sprintf(
   289  		"Timed out waiting for pipe '%s' to come available", address), true}
   290  }
   291  
   292  // isPipeNotReady checks the error to see if it indicates the pipe is not ready
   293  func isPipeNotReady(err error) bool {
   294  	// Pipe Busy means another client just grabbed the open pipe end,
   295  	// and the server hasn't made a new one yet.
   296  	// File Not Found means the server hasn't created the pipe yet.
   297  	// Neither is a fatal error.
   298  
   299  	return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy
   300  }
   301  
   302  // newOverlapped creates a structure used to track asynchronous
   303  // I/O requests that have been issued.
   304  func newOverlapped() (*syscall.Overlapped, error) {
   305  	event, err := createEvent(nil, true, true, nil)
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  	return &syscall.Overlapped{HEvent: event}, nil
   310  }
   311  
   312  // waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete.
   313  // This function returns the number of bytes transferred by the operation and an error code if
   314  // applicable (nil otherwise).
   315  func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) {
   316  	_, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE)
   317  	if err != nil {
   318  		return 0, err
   319  	}
   320  	var transferred uint32
   321  	err = getOverlappedResult(handle, overlapped, &transferred, true)
   322  	return transferred, err
   323  }
   324  
   325  // dial is a helper to initiate a connection to a named pipe that has been started by a server.
   326  // The timeout is only enforced if the pipe server has already created the pipe, otherwise
   327  // this function will return immediately.
   328  func dial(address string, timeout uint32) (*PipeConn, error) {
   329  	name, err := syscall.UTF16PtrFromString(string(address))
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	// If at least one instance of the pipe has been created, this function
   334  	// will wait timeout milliseconds for it to become available.
   335  	// It will return immediately regardless of timeout, if no instances
   336  	// of the named pipe have been created yet.
   337  	// If this returns with no error, there is a pipe available.
   338  	if err := waitNamedPipe(name, timeout); err != nil {
   339  		if err == error_bad_pathname {
   340  			// badly formatted pipe name
   341  			return nil, badAddr(address)
   342  		}
   343  		return nil, err
   344  	}
   345  	pathp, err := syscall.UTF16PtrFromString(address)
   346  	if err != nil {
   347  		return nil, err
   348  	}
   349  	handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE,
   350  		uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING,
   351  		syscall.FILE_FLAG_OVERLAPPED, 0)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  	return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil
   356  }
   357  
   358  // Listen returns a new PipeListener that will listen on a pipe with the given
   359  // address. The address must be of the form \\.\pipe\<name>
   360  //
   361  // Listen will return a PipeError for an incorrectly formatted pipe name.
   362  func Listen(address string) (*PipeListener, error) {
   363  	handle, err := createPipe(address, true)
   364  	if err == error_invalid_name {
   365  		return nil, badAddr(address)
   366  	}
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	return &PipeListener{
   371  		addr:   PipeAddr(address),
   372  		handle: handle,
   373  	}, nil
   374  }
   375  
   376  // PipeListener is a named pipe listener. Clients should typically
   377  // use variables of type net.Listener instead of assuming named pipe.
   378  type PipeListener struct {
   379  	addr   PipeAddr
   380  	handle syscall.Handle
   381  	closed bool
   382  
   383  	// acceptHandle contains the current handle waiting for
   384  	// an incoming connection or nil.
   385  	acceptHandle syscall.Handle
   386  	// acceptOverlapped is set before waiting on a connection.
   387  	// If not waiting, it is nil.
   388  	acceptOverlapped *syscall.Overlapped
   389  	// acceptMutex protects the handle and overlapped structure.
   390  	acceptMutex sync.Mutex
   391  }
   392  
   393  // Accept implements the Accept method in the net.Listener interface; it
   394  // waits for the next call and returns a generic net.Conn.
   395  func (l *PipeListener) Accept() (net.Conn, error) {
   396  	c, err := l.AcceptPipe()
   397  	for err == error_no_data {
   398  		// Ignore clients that connect and immediately disconnect.
   399  		c, err = l.AcceptPipe()
   400  	}
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  	return c, nil
   405  }
   406  
   407  // AcceptPipe accepts the next incoming call and returns the new connection.
   408  // It might return an error if a client connected and immediately cancelled
   409  // the connection.
   410  func (l *PipeListener) AcceptPipe() (*PipeConn, error) {
   411  	if l == nil || l.addr == "" || l.closed {
   412  		return nil, syscall.EINVAL
   413  	}
   414  
   415  	// the first time we call accept, the handle will have been created by the Listen
   416  	// call. This is to prevent race conditions where the client thinks the server
   417  	// isn't listening because it hasn't actually called create yet. After the first time, we'll
   418  	// have to create a new handle each time
   419  	handle := l.handle
   420  	if handle == 0 {
   421  		var err error
   422  		handle, err = createPipe(string(l.addr), false)
   423  		if err != nil {
   424  			return nil, err
   425  		}
   426  	} else {
   427  		l.handle = 0
   428  	}
   429  
   430  	overlapped, err := newOverlapped()
   431  	if err != nil {
   432  		return nil, err
   433  	}
   434  	defer syscall.CloseHandle(overlapped.HEvent)
   435  	if err := connectNamedPipe(handle, overlapped); err != nil && err != error_pipe_connected {
   436  		if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING {
   437  			l.acceptMutex.Lock()
   438  			l.acceptOverlapped = overlapped
   439  			l.acceptHandle = handle
   440  			l.acceptMutex.Unlock()
   441  			defer func() {
   442  				l.acceptMutex.Lock()
   443  				l.acceptOverlapped = nil
   444  				l.acceptHandle = 0
   445  				l.acceptMutex.Unlock()
   446  			}()
   447  
   448  			_, err = waitForCompletion(handle, overlapped)
   449  		}
   450  		if err == syscall.ERROR_OPERATION_ABORTED {
   451  			// Return error compatible to net.Listener.Accept() in case the
   452  			// listener was closed.
   453  			return nil, ErrClosed
   454  		}
   455  		if err != nil {
   456  			return nil, err
   457  		}
   458  	}
   459  	return &PipeConn{handle: handle, addr: l.addr}, nil
   460  }
   461  
   462  // Close stops listening on the address.
   463  // Already Accepted connections are not closed.
   464  func (l *PipeListener) Close() error {
   465  	if l.closed {
   466  		return nil
   467  	}
   468  	l.closed = true
   469  	if l.handle != 0 {
   470  		err := disconnectNamedPipe(l.handle)
   471  		if err != nil {
   472  			return err
   473  		}
   474  		err = syscall.CloseHandle(l.handle)
   475  		if err != nil {
   476  			return err
   477  		}
   478  		l.handle = 0
   479  	}
   480  	l.acceptMutex.Lock()
   481  	defer l.acceptMutex.Unlock()
   482  	if l.acceptOverlapped != nil && l.acceptHandle != 0 {
   483  		// Cancel the pending IO. This call does not block, so it is safe
   484  		// to hold onto the mutex above.
   485  		if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil {
   486  			return err
   487  		}
   488  		err := syscall.CloseHandle(l.acceptOverlapped.HEvent)
   489  		if err != nil {
   490  			return err
   491  		}
   492  		l.acceptOverlapped.HEvent = 0
   493  		err = syscall.CloseHandle(l.acceptHandle)
   494  		if err != nil {
   495  			return err
   496  		}
   497  		l.acceptHandle = 0
   498  	}
   499  	return nil
   500  }
   501  
   502  // Addr returns the listener's network address, a PipeAddr.
   503  func (l *PipeListener) Addr() net.Addr { return l.addr }
   504  
   505  // PipeConn is the implementation of the net.Conn interface for named pipe connections.
   506  type PipeConn struct {
   507  	handle syscall.Handle
   508  	addr   PipeAddr
   509  
   510  	// these aren't actually used yet
   511  	readDeadline  *time.Time
   512  	writeDeadline *time.Time
   513  }
   514  
   515  type iodata struct {
   516  	n   uint32
   517  	err error
   518  }
   519  
   520  // completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to
   521  // abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending,
   522  // the content of iodata is returned.
   523  func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) {
   524  	if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING {
   525  		var timer <-chan time.Time
   526  		if deadline != nil {
   527  			if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 {
   528  				timer = time.After(timeDiff)
   529  			}
   530  		}
   531  		done := make(chan iodata)
   532  		go func() {
   533  			n, err := waitForCompletion(c.handle, overlapped)
   534  			done <- iodata{n, err}
   535  		}()
   536  		select {
   537  		case data = <-done:
   538  		case <-timer:
   539  			syscall.CancelIoEx(c.handle, overlapped)
   540  			data = iodata{0, timeout(c.addr.String())}
   541  		}
   542  	}
   543  	// Windows will produce ERROR_BROKEN_PIPE upon closing
   544  	// a handle on the other end of a connection. Go RPC
   545  	// expects an io.EOF error in this case.
   546  	if data.err == syscall.ERROR_BROKEN_PIPE {
   547  		data.err = io.EOF
   548  	}
   549  	return int(data.n), data.err
   550  }
   551  
   552  // Read implements the net.Conn Read method.
   553  func (c *PipeConn) Read(b []byte) (int, error) {
   554  	// Use ReadFile() rather than Read() because the latter
   555  	// contains a workaround that eats ERROR_BROKEN_PIPE.
   556  	overlapped, err := newOverlapped()
   557  	if err != nil {
   558  		return 0, err
   559  	}
   560  	defer syscall.CloseHandle(overlapped.HEvent)
   561  	var n uint32
   562  	err = syscall.ReadFile(c.handle, b, &n, overlapped)
   563  	return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped)
   564  }
   565  
   566  // Write implements the net.Conn Write method.
   567  func (c *PipeConn) Write(b []byte) (int, error) {
   568  	overlapped, err := newOverlapped()
   569  	if err != nil {
   570  		return 0, err
   571  	}
   572  	defer syscall.CloseHandle(overlapped.HEvent)
   573  	var n uint32
   574  	err = syscall.WriteFile(c.handle, b, &n, overlapped)
   575  	return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped)
   576  }
   577  
   578  // Close closes the connection.
   579  func (c *PipeConn) Close() error {
   580  	return syscall.CloseHandle(c.handle)
   581  }
   582  
   583  // LocalAddr returns the local network address.
   584  func (c *PipeConn) LocalAddr() net.Addr {
   585  	return c.addr
   586  }
   587  
   588  // RemoteAddr returns the remote network address.
   589  func (c *PipeConn) RemoteAddr() net.Addr {
   590  	// not sure what to do here, we don't have remote addr....
   591  	return c.addr
   592  }
   593  
   594  // SetDeadline implements the net.Conn SetDeadline method.
   595  // Note that timeouts are only supported on Windows Vista/Server 2008 and above
   596  func (c *PipeConn) SetDeadline(t time.Time) error {
   597  	c.SetReadDeadline(t)
   598  	c.SetWriteDeadline(t)
   599  	return nil
   600  }
   601  
   602  // SetReadDeadline implements the net.Conn SetReadDeadline method.
   603  // Note that timeouts are only supported on Windows Vista/Server 2008 and above
   604  func (c *PipeConn) SetReadDeadline(t time.Time) error {
   605  	c.readDeadline = &t
   606  	return nil
   607  }
   608  
   609  // SetWriteDeadline implements the net.Conn SetWriteDeadline method.
   610  // Note that timeouts are only supported on Windows Vista/Server 2008 and above
   611  func (c *PipeConn) SetWriteDeadline(t time.Time) error {
   612  	c.writeDeadline = &t
   613  	return nil
   614  }
   615  
   616  // PipeAddr represents the address of a named pipe.
   617  type PipeAddr string
   618  
   619  // Network returns the address's network name, "pipe".
   620  func (a PipeAddr) Network() string { return "pipe" }
   621  
   622  // String returns the address of the pipe
   623  func (a PipeAddr) String() string {
   624  	return string(a)
   625  }
   626  
   627  // createPipe is a helper function to make sure we always create pipes
   628  // with the same arguments, since subsequent calls to create pipe need
   629  // to use the same arguments as the first one. If first is set, fail
   630  // if the pipe already exists.
   631  func createPipe(address string, first bool) (syscall.Handle, error) {
   632  	n, err := syscall.UTF16PtrFromString(address)
   633  	if err != nil {
   634  		return 0, err
   635  	}
   636  	mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED)
   637  	if first {
   638  		mode |= file_flag_first_pipe_instance
   639  	}
   640  	return createNamedPipe(n,
   641  		mode,
   642  		pipe_type_byte,
   643  		pipe_unlimited_instances,
   644  		512, 512, 0, nil)
   645  }
   646  
   647  func badAddr(addr string) PipeError {
   648  	return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false}
   649  }
   650  func timeout(addr string) PipeError {
   651  	return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true}
   652  }
   653  
   654  func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
   655  	c, err := Dial(cfg.Endpoint)
   656  	if err != nil {
   657  		return nil, err
   658  	}
   659  
   660  	coder := codec.New(c)
   661  	msg := shared.Request{
   662  		Id:      0,
   663  		Method:  useragent.EnableUserAgentMethod,
   664  		Jsonrpc: shared.JsonRpcVersion,
   665  		Params:  []byte("[]"),
   666  	}
   667  
   668  	coder.WriteResponse(msg)
   669  	coder.Recv()
   670  
   671  	return &ipcClient{cfg.Endpoint, c, codec, coder}, nil
   672  }
   673  
   674  func (self *ipcClient) reconnect() error {
   675  	c, err := Dial(self.endpoint)
   676  	if err == nil {
   677  		self.coder = self.codec.New(c)
   678  
   679  		req := shared.Request{
   680  			Id:      0,
   681  			Method:  useragent.EnableUserAgentMethod,
   682  			Jsonrpc: shared.JsonRpcVersion,
   683  			Params:  []byte("[]"),
   684  		}
   685  		self.coder.WriteResponse(req)
   686  		self.coder.Recv()
   687  	}
   688  	return err
   689  }
   690  
   691  func startIpc(cfg IpcConfig, codec codec.Codec, initializer func(conn net.Conn) (shared.EthereumApi, error)) error {
   692  	os.Remove(cfg.Endpoint) // in case it still exists from a previous run
   693  
   694  	l, err := Listen(cfg.Endpoint)
   695  	if err != nil {
   696  		return err
   697  	}
   698  	os.Chmod(cfg.Endpoint, 0600)
   699  
   700  	go func() {
   701  		for {
   702  			conn, err := l.Accept()
   703  			if err != nil {
   704  				glog.V(logger.Error).Infof("Error accepting ipc connection - %v\n", err)
   705  				continue
   706  			}
   707  
   708  			id := newIpcConnId()
   709  			glog.V(logger.Debug).Infof("New IPC connection with id %06d started\n", id)
   710  
   711  			api, err := initializer(conn)
   712  			if err != nil {
   713  				glog.V(logger.Error).Infof("Unable to initialize IPC connection - %v\n", err)
   714  				conn.Close()
   715  				continue
   716  			}
   717  
   718  			go handle(id, conn, api, codec)
   719  		}
   720  
   721  		os.Remove(cfg.Endpoint)
   722  	}()
   723  
   724  	glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint)
   725  
   726  	return nil
   727  }