From 7fc2736a4f445a82dcf0e84d8426b6e6615cd4eb Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Thu, 28 Dec 2017 19:32:58 +1300 Subject: [PATCH 1/7] Inital proof of concept for (libassuane #1) --- npiperelay.go | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/npiperelay.go b/npiperelay.go index 08cbaab..4bdf6ca 100644 --- a/npiperelay.go +++ b/npiperelay.go @@ -1,10 +1,13 @@ package main import ( + "errors" "flag" "io" "log" "os" + "strconv" + "strings" "sync" "syscall" "time" @@ -40,6 +43,49 @@ func dialPipe(p string, poll bool) (*overlappedFile, error) { } } +func dialPort(p int, poll bool) (*overlappedFile, error) { + if p < 0 || p > 65535 { + return nil, errors.New("Invalid port value") + } + + for { + h, err := windows.Socket(windows.AF_INET, windows.SOCK_STREAM, 0) + if err != nil { + return nil, err + } + + // Create a SockaddrInet4 for connecting to + sa := &windows.SockaddrInet4{Addr: [4]byte{0x7F, 0x00, 0x00, 0x01}, Port: p} + if err != nil { + return nil, err + } + + // Bind to a randomly assigned port + err = windows.Bind(h, &windows.SockaddrInet4{}) + if err != nil { + return nil, err + } + + conn := newOverlappedFile(h) + + _, err = conn.asyncIo(func(h windows.Handle, n *uint32, o *windows.Overlapped) error { + return windows.ConnectEx(h, sa, nil, 0, nil, o) + }) + err = os.NewSyscallError("connectEx", err) + + if err == nil { + return conn, nil + } + + if poll && os.IsNotExist(err) { + time.Sleep(200 * time.Millisecond) + continue + } + + return nil, err + } +} + func underlyingError(err error) error { if serr, ok := err.(*os.SyscallError); ok { return serr.Err @@ -64,6 +110,39 @@ func main() { log.Fatalln(err) } + if !strings.HasPrefix("//./", args[0]) { + tmp := make([]byte, 22) // 5 bytes for ascii port number, 1 for newline, 16 for nonce + + var port int + nonce := make([]byte, 16) + + // Check if file is a LibAssuane socket + _, err := conn.Read(tmp) + if err != nil { + log.Fatalln("Could not open socket", err) + } + + for i, c := range tmp { + // Find the new line + if c == 0x0A { + port, _ = strconv.Atoi(string(tmp[:i])) + copy(nonce, tmp[i+1:]) + + log.Printf("Port: %d, Nonce: %X", port, nonce) + break + } + } + + _ = conn.Close() + + conn, err = dialPort(port, *poll) + + _, err = conn.Write(nonce) + if err != nil { + log.Fatal(err) + } + } + if *verbose { log.Println("connected") } From 6048153a0f79711ce7b408a071d6226d5f2e8549 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Thu, 28 Dec 2017 19:48:35 +1300 Subject: [PATCH 2/7] gpg4win relay example --- scripts/gpg-relay | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 scripts/gpg-relay diff --git a/scripts/gpg-relay b/scripts/gpg-relay new file mode 100644 index 0000000..d85ac95 --- /dev/null +++ b/scripts/gpg-relay @@ -0,0 +1,3 @@ +#!/bin/sh + +exec socat UNIX-LISTEN:/home//.gnupg/S.gpg-agent,fork, EXEC:'npiperelay.exe -ep -s "C:/Users//AppData/Roaming/gnupg/S.gpg-agent.extra"',nofork From a3c44478565c357d025eac5fd7192eed0ee51c6b Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Thu, 28 Dec 2017 19:58:48 +1300 Subject: [PATCH 3/7] Reduce verbosity unless requested --- npiperelay.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/npiperelay.go b/npiperelay.go index 4bdf6ca..8d1033a 100644 --- a/npiperelay.go +++ b/npiperelay.go @@ -128,7 +128,9 @@ func main() { port, _ = strconv.Atoi(string(tmp[:i])) copy(nonce, tmp[i+1:]) - log.Printf("Port: %d, Nonce: %X", port, nonce) + if *verbose { + log.Printf("Port: %d, Nonce: %X", port, nonce) + } break } } From 0015687c47ec6747f2c73a7a19b1dae479e34a21 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Thu, 28 Dec 2017 21:42:20 +1300 Subject: [PATCH 4/7] terminate when pipe or stdin closes in example --- scripts/gpg-relay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gpg-relay b/scripts/gpg-relay index d85ac95..166dd17 100644 --- a/scripts/gpg-relay +++ b/scripts/gpg-relay @@ -1,3 +1,3 @@ #!/bin/sh -exec socat UNIX-LISTEN:/home//.gnupg/S.gpg-agent,fork, EXEC:'npiperelay.exe -ep -s "C:/Users//AppData/Roaming/gnupg/S.gpg-agent.extra"',nofork +exec socat UNIX-LISTEN:/home//.gnupg/S.gpg-agent,fork, EXEC:'npiperelay.exe -ei -ep -s "C:/Users//AppData/Roaming/gnupg/S.gpg-agent"',nofork From 06bad53d17d957fcd42dbc649fc1cec02557b63a Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Fri, 29 Dec 2017 15:01:25 +1300 Subject: [PATCH 5/7] Support polling sockets and general house-keeping --- npiperelay.go | 152 ++++++++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 66 deletions(-) diff --git a/npiperelay.go b/npiperelay.go index 8d1033a..886cfcd 100644 --- a/npiperelay.go +++ b/npiperelay.go @@ -17,6 +17,11 @@ import ( const cERROR_PIPE_NOT_CONNECTED syscall.Errno = 233 +const WSAECONNREFUSED syscall.Errno = 10061 +const WSAENETUNREACH syscall.Errno = 10051 +const WSAETIMEDOUT syscall.Errno = 10060 +const ERROR_CONNECTION_REFUSED syscall.Errno = 1225 + var ( poll = flag.Bool("p", false, "poll until the the named pipe exists") closeWrite = flag.Bool("s", false, "send a 0-byte message to the pipe after EOF on stdin") @@ -30,17 +35,13 @@ func dialPipe(p string, poll bool) (*overlappedFile, error) { if err != nil { return nil, err } - for { - h, err := windows.CreateFile(&p16[0], windows.GENERIC_READ|windows.GENERIC_WRITE, 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_OVERLAPPED, 0) - if err == nil { - return newOverlappedFile(h), nil - } - if poll && os.IsNotExist(err) { - time.Sleep(200 * time.Millisecond) - continue - } - return nil, &os.PathError{Path: p, Op: "open", Err: err} + + h, err := windows.CreateFile(&p16[0], windows.GENERIC_READ|windows.GENERIC_WRITE, 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_OVERLAPPED, 0) + if err == nil { + return newOverlappedFile(h), nil } + + return nil, err } func dialPort(p int, poll bool) (*overlappedFile, error) { @@ -48,42 +49,32 @@ func dialPort(p int, poll bool) (*overlappedFile, error) { return nil, errors.New("Invalid port value") } - for { - h, err := windows.Socket(windows.AF_INET, windows.SOCK_STREAM, 0) - if err != nil { - return nil, err - } - - // Create a SockaddrInet4 for connecting to - sa := &windows.SockaddrInet4{Addr: [4]byte{0x7F, 0x00, 0x00, 0x01}, Port: p} - if err != nil { - return nil, err - } - - // Bind to a randomly assigned port - err = windows.Bind(h, &windows.SockaddrInet4{}) - if err != nil { - return nil, err - } - - conn := newOverlappedFile(h) + h, err := windows.Socket(windows.AF_INET, windows.SOCK_STREAM, 0) + if err != nil { + return nil, err + } - _, err = conn.asyncIo(func(h windows.Handle, n *uint32, o *windows.Overlapped) error { - return windows.ConnectEx(h, sa, nil, 0, nil, o) - }) - err = os.NewSyscallError("connectEx", err) + // Create a SockaddrInet4 for connecting to + sa := &windows.SockaddrInet4{Addr: [4]byte{0x7F, 0x00, 0x00, 0x01}, Port: p} - if err == nil { - return conn, nil - } + // Bind to a randomly assigned local port + err = windows.Bind(h, &windows.SockaddrInet4{}) + if err != nil { + return nil, err + } - if poll && os.IsNotExist(err) { - time.Sleep(200 * time.Millisecond) - continue - } + // Wrap our socket up to be properly handled + conn := newOverlappedFile(h) - return nil, err + // Connect to the LibAssuan socket using overlapped ConnectEx operation + _, err = conn.asyncIo(func(h windows.Handle, n *uint32, o *windows.Overlapped) error { + return windows.ConnectEx(h, sa, nil, 0, nil, o) + }) + if err == nil { + return conn, nil } + + return nil, err } func underlyingError(err error) error { @@ -105,44 +96,73 @@ func main() { log.Println("connecting to", args[0]) } - conn, err := dialPipe(args[0], *poll) - if err != nil { - log.Fatalln(err) - } + var conn *overlappedFile + var err error - if !strings.HasPrefix("//./", args[0]) { - tmp := make([]byte, 22) // 5 bytes for ascii port number, 1 for newline, 16 for nonce + // Loop only if we're polling the named pipe or socket + for { + conn, err = dialPipe(args[0], *poll) - var port int - nonce := make([]byte, 16) + if *poll && os.IsNotExist(err) { + time.Sleep(200 * time.Millisecond) + continue + } - // Check if file is a LibAssuane socket - _, err := conn.Read(tmp) if err != nil { - log.Fatalln("Could not open socket", err) + err = &os.PathError{Path: args[0], Op: "open", Err: err} + log.Fatalln(err) } - for i, c := range tmp { - // Find the new line - if c == 0x0A { - port, _ = strconv.Atoi(string(tmp[:i])) - copy(nonce, tmp[i+1:]) + // Not a named pipe, so attempt to read contents and connect to a TCP port for LibAssaaun + if !strings.HasPrefix("//./", args[0]) { + tmp := make([]byte, 22) // 5 bytes for ascii port number, 1 for newline, 16 for nonce + + var port int + var nonce [16]byte + + _, err := conn.Read(tmp) + if err != nil { + log.Fatalln("Could not open file", err) + } + + for i, c := range tmp { + // Find the new line + if c == 0x0A { + port, err = strconv.Atoi(string(tmp[:i])) + if err != nil { + log.Fatalln(err) + } + + copy(nonce[:], tmp[i+1:]) - if *verbose { - log.Printf("Port: %d, Nonce: %X", port, nonce) + if *verbose { + log.Printf("Port: %d, Nonce: %X", port, nonce) + } + break } - break } - } - _ = conn.Close() + _ = conn.Close() - conn, err = dialPort(port, *poll) + conn, err = dialPort(port, *poll) - _, err = conn.Write(nonce) - if err != nil { - log.Fatal(err) + if *poll && (err == WSAETIMEDOUT || err == WSAECONNREFUSED || err == WSAENETUNREACH || err == ERROR_CONNECTION_REFUSED) { + time.Sleep(200 * time.Millisecond) + continue + } + + err = os.NewSyscallError("ConnectEx", err) + + if err != nil { + log.Fatal(err) + } + + _, err = conn.Write(nonce[:]) + if err != nil { + log.Fatal(err) + } } + break } if *verbose { From f0ef6555b2053a6be34b48cb96e4ee4c974cdc19 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Thu, 4 Jan 2018 05:06:46 +1300 Subject: [PATCH 6/7] Use bufio for reading libassuan files (see #2) --- npiperelay.go | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/npiperelay.go b/npiperelay.go index 886cfcd..3ceefa7 100644 --- a/npiperelay.go +++ b/npiperelay.go @@ -1,13 +1,13 @@ package main import ( + "bufio" "errors" "flag" "io" "log" "os" "strconv" - "strings" "sync" "syscall" "time" @@ -28,6 +28,7 @@ var ( closeOnEOF = flag.Bool("ep", false, "terminate on EOF reading from the pipe, even if there is more data to write") closeOnStdinEOF = flag.Bool("ei", false, "terminate on EOF reading from stdin, even if there is more data to write") verbose = flag.Bool("v", false, "verbose output on stderr") + assuan = flag.Bool("a", false, "treat the target as a libassuan file socket (Used by GnuPG)") ) func dialPipe(p string, poll bool) (*overlappedFile, error) { @@ -113,37 +114,37 @@ func main() { log.Fatalln(err) } - // Not a named pipe, so attempt to read contents and connect to a TCP port for LibAssaaun - if !strings.HasPrefix("//./", args[0]) { - tmp := make([]byte, 22) // 5 bytes for ascii port number, 1 for newline, 16 for nonce - + // LibAssaaun file socket: Attempt to read contents of the target file and connect to a TCP port + if *assuan { var port int var nonce [16]byte - _, err := conn.Read(tmp) + reader := bufio.NewReader(conn) + + // Read the target port number from the first line + tmp, _, err := reader.ReadLine() + port, err = strconv.Atoi(string(tmp)) if err != nil { - log.Fatalln("Could not open file", err) + log.Fatalln(err) } - for i, c := range tmp { - // Find the new line - if c == 0x0A { - port, err = strconv.Atoi(string(tmp[:i])) - if err != nil { - log.Fatalln(err) - } + // Read the rest of the nonce from the file + n, err := reader.Read(nonce[:]) + if err != nil { + log.Fatalln(err) + } - copy(nonce[:], tmp[i+1:]) + if n != 16 { + log.Fatalf("Read incorrect number of bytes for nonce. Expected 16, got %d (0x%X)", n, nonce) + } - if *verbose { - log.Printf("Port: %d, Nonce: %X", port, nonce) - } - break - } + if *verbose { + log.Printf("Port: %d, Nonce: %X", port, nonce) } _ = conn.Close() + // Try to connect to the libassaun TCP socket hosted on localhost conn, err = dialPort(port, *poll) if *poll && (err == WSAETIMEDOUT || err == WSAECONNREFUSED || err == WSAENETUNREACH || err == ERROR_CONNECTION_REFUSED) { From b7795c8bf6897d17962d70f8c2e0424af7a112c2 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Thu, 4 Jan 2018 05:50:58 +1300 Subject: [PATCH 7/7] add flag to gpg-relay example script --- scripts/gpg-relay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gpg-relay b/scripts/gpg-relay index 166dd17..70603af 100644 --- a/scripts/gpg-relay +++ b/scripts/gpg-relay @@ -1,3 +1,3 @@ #!/bin/sh -exec socat UNIX-LISTEN:/home//.gnupg/S.gpg-agent,fork, EXEC:'npiperelay.exe -ei -ep -s "C:/Users//AppData/Roaming/gnupg/S.gpg-agent"',nofork +exec socat UNIX-LISTEN:/home//.gnupg/S.gpg-agent,fork, EXEC:'npiperelay.exe -ei -ep -s -a "C:/Users//AppData/Roaming/gnupg/S.gpg-agent"',nofork