Browse Source

Merge pull request #343 from rod-hynes/master

Add plugin protocol hook
Rod Hynes 9 years ago
parent
commit
191a70b345
4 changed files with 109 additions and 6 deletions
  1. 66 0
      psiphon/common/pluginProtocol.go
  2. 5 5
      psiphon/common/userAgentPicker.go
  3. 18 0
      psiphon/notice.go
  4. 20 1
      psiphon/tunnel.go

+ 66 - 0
psiphon/common/pluginProtocol.go

@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package common
+
+import (
+	"io"
+	"net"
+	"sync/atomic"
+)
+
+var registeredPluginProtocolDialer atomic.Value
+
+// PluginProtocolNetDialer is a base network dialer that's used
+// by PluginProtocolDialer to make its IP network connections. This
+// is used, for example, to create TCPConns as the base TCP
+// connections used by the plugin protocol.
+type PluginProtocolNetDialer func(network, addr string) (net.Conn, error)
+
+// PluginProtocolDialer creates a connection to addr over a
+// plugin protocol. It uses netDialer to create its base network
+// connection(s) and sends its log messages to loggerOutput.
+// PluginProtocolDialer returns true if it attempts to create
+// a connection, or false if it decides not to attempt a connection.
+type PluginProtocolDialer func(
+	loggerOutput io.Writer,
+	netDialer PluginProtocolNetDialer,
+	addr string) (
+	bool, net.Conn, error)
+
+// RegisterPluginProtocol sets the current plugin protocol
+// dialer.
+func RegisterPluginProtocol(protcolDialer PluginProtocolDialer) {
+	registeredPluginProtocolDialer.Store(protcolDialer)
+}
+
+// DialPluginProtocol uses the current plugin protocol dialer,
+// if set, to connect to addr over the plugin protocol.
+func DialPluginProtocol(
+	loggerOutput io.Writer,
+	netDialer PluginProtocolNetDialer,
+	addr string) (
+	bool, net.Conn, error) {
+
+	dialer := registeredPluginProtocolDialer.Load()
+	if dialer != nil {
+		return dialer.(PluginProtocolDialer)(loggerOutput, netDialer, addr)
+	}
+	return false, nil, nil
+}

+ 5 - 5
psiphon/common/userAgentPicker.go

@@ -26,14 +26,14 @@ import (
 
 var registeredUserAgentPicker atomic.Value
 
-func RegisterUserAgentPicker(generator func() string) {
-	registeredUserAgentPicker.Store(generator)
+func RegisterUserAgentPicker(picker func() string) {
+	registeredUserAgentPicker.Store(picker)
 }
 
 func pickUserAgent() string {
-	generator := registeredUserAgentPicker.Load()
-	if generator != nil {
-		return generator.(func() string)()
+	picker := registeredUserAgentPicker.Load()
+	if picker != nil {
+		return picker.(func() string)()
 	}
 	return ""
 }

+ 18 - 0
psiphon/notice.go

@@ -501,3 +501,21 @@ func NewNoticeConsoleRewriter(writer io.Writer) *NoticeReceiver {
 			string(object.Data))
 	})
 }
+
+// NoticeWriter implements io.Writer and emits the contents of Write() calls
+// as Notices. This is to transform logger messages, if they can be redirected
+// to an io.Writer, to notices.
+type NoticeWriter struct {
+	noticeType string
+}
+
+// NewNoticeWriter initializes a new NoticeWriter
+func NewNoticeWriter(noticeType string) *NoticeWriter {
+	return &NoticeWriter{noticeType: noticeType}
+}
+
+// Write implements io.Writer.
+func (writer *NoticeWriter) Write(p []byte) (n int, err error) {
+	outputNotice(writer.noticeType, noticeIsDiagnostic, "message", string(p))
+	return len(p), nil
+}

+ 20 - 1
psiphon/tunnel.go

@@ -689,7 +689,26 @@ func dialSsh(
 			return nil, common.ContextError(err)
 		}
 	} else {
-		dialConn, err = DialTCP(directTCPDialAddress, dialConfig)
+
+		tcpDialer := func(_, addr string) (net.Conn, error) {
+			return DialTCP(addr, dialConfig)
+		}
+
+		// For some direct connect servers, DialPluginProtocol
+		// will layer on another obfuscation protocol.
+		var dialedPlugin bool
+		dialedPlugin, dialConn, err = common.DialPluginProtocol(
+			NewNoticeWriter("DialPluginProtocol"),
+			tcpDialer,
+			directTCPDialAddress)
+
+		if dialedPlugin {
+			NoticeInfo("dialed plugin protocol for %s", serverEntry.IpAddress)
+		} else {
+			// Standard direct connection.
+			dialConn, err = tcpDialer("", directTCPDialAddress)
+		}
+
 		if err != nil {
 			return nil, common.ContextError(err)
 		}