├── extension ├── img │ └── logos │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 19.png │ │ ├── 38.png │ │ ├── 48.png │ │ ├── bw-128.png │ │ ├── bw-19.png │ │ └── bw-38.png ├── fonts │ ├── fonts │ │ ├── icomoon.eot │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ └── icomoon.svg │ └── style.css ├── options │ ├── options.html │ └── options.js ├── logs │ ├── logs.html │ └── logs.js ├── popup │ ├── popup.html │ ├── popup.css │ └── popup.js ├── manifest.json └── background.js ├── tools ├── uninstall_host.bat ├── install_host.bat ├── uninstall_host.sh └── install_host.sh ├── .gitignore ├── host ├── ca.psiphon.chrome.json ├── ca.psiphon.chrome-win.json ├── embedded_values.go.stub ├── README.md ├── Dockerfile ├── make.bash └── psiphon-native-messaging-host.go └── README.md /extension/img/logos/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/128.png -------------------------------------------------------------------------------- /extension/img/logos/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/16.png -------------------------------------------------------------------------------- /extension/img/logos/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/19.png -------------------------------------------------------------------------------- /extension/img/logos/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/38.png -------------------------------------------------------------------------------- /extension/img/logos/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/48.png -------------------------------------------------------------------------------- /extension/img/logos/bw-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/bw-128.png -------------------------------------------------------------------------------- /extension/img/logos/bw-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/bw-19.png -------------------------------------------------------------------------------- /extension/img/logos/bw-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/img/logos/bw-38.png -------------------------------------------------------------------------------- /extension/fonts/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/fonts/fonts/icomoon.eot -------------------------------------------------------------------------------- /extension/fonts/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/fonts/fonts/icomoon.ttf -------------------------------------------------------------------------------- /extension/fonts/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psiphon-Labs/psiphon-chrome/HEAD/extension/fonts/fonts/icomoon.woff -------------------------------------------------------------------------------- /tools/uninstall_host.bat: -------------------------------------------------------------------------------- 1 | :: Deletes the entry created by install_host.bat 2 | REG DELETE "HKCU\Software\Google\Chrome\NativeMessagingHosts\ca.psiphon.chrome" /f 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | 4 | *.crx 5 | *.pem 6 | *.zip 7 | 8 | dist 9 | host/bin 10 | host/embedded_values.go 11 | host/psiphon-native-messaging-host 12 | host/psiphon-native-messaging-host.exe 13 | -------------------------------------------------------------------------------- /tools/install_host.bat: -------------------------------------------------------------------------------- 1 | :: Change HKCU to HKLM if you want to install globally. 2 | :: %~dp0 is the directory containing this bat script and ends with a backslash. 3 | REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\ca.psiphon.chrome" /ve /t REG_SZ /d "%~dp0..\host\ca.psiphon.chrome.json" /f 4 | -------------------------------------------------------------------------------- /extension/options/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /host/ca.psiphon.chrome.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ca.psiphon.chrome", 3 | "description": "Psiphon Censorship Circumvention System", 4 | "path": "HOST_PATH", 5 | "type": "stdio", 6 | "allowed_origins": [ 7 | "chrome-extension://gnalljkfdmkhinjcipgjjehclbpagega/", 8 | "chrome-extension://gmjcfdpikhccadldjdagaehblacjchnd/" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /host/ca.psiphon.chrome-win.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ca.psiphon.chrome", 3 | "description": "Psiphon Censorship Circumvention System", 4 | "path": "psiphon-native-messaging-host.exe", 5 | "type": "stdio", 6 | "allowed_origins": [ 7 | "chrome-extension://gnalljkfdmkhinjcipgjjehclbpagega/", 8 | "chrome-extension://gmjcfdpikhccadldjdagaehblacjchnd/" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /extension/logs/logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |' + bgPage.logs[i] + '
'; 9 | } 10 | 11 | document.getElementById('select-response-text').addEventListener('click', function() { 12 | var range = document.createRange(); 13 | 14 | range.selectNodeContents(logViewer); 15 | 16 | var selection = window.getSelection(); 17 | selection.removeAllRanges(); 18 | selection.addRange(range); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Psiphon", 4 | "short_name": "Psiphon", 5 | "description": "Psiphon Circumvention System", 6 | "version": "0.0.2", 7 | "minimum_chrome_version": "40", 8 | "background": { 9 | "scripts": ["background.js"] 10 | }, 11 | "browser_action": { 12 | "default_title": "Psiphon", 13 | "default_popup": "popup/popup.html", 14 | "default_icon": { 15 | "19": "img/logos/bw-19.png", 16 | "38": "img/logos/bw-38.png" 17 | } 18 | }, 19 | "options_ui": { 20 | "page": "options/options.html", 21 | "chrome_style": true 22 | }, 23 | "icons": { 24 | "16": "img/logos/16.png", 25 | "48": "img/logos/48.png", 26 | "128": "img/logos/128.png" 27 | }, 28 | "permissions": [ 29 | "storage", 30 | "tabs", 31 | "nativeMessaging", 32 | "proxy" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /extension/fonts/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icomoon'; 3 | src:url('fonts/icomoon.eot?-jepcyq'); 4 | src:url('fonts/icomoon.eot?#iefix-jepcyq') format('embedded-opentype'), 5 | url('fonts/icomoon.ttf?-jepcyq') format('truetype'), 6 | url('fonts/icomoon.woff?-jepcyq') format('woff'), 7 | url('fonts/icomoon.svg?-jepcyq#icomoon') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | [class^="icon-"], [class*=" icon-"] { 13 | font-family: 'icomoon'; 14 | speak: none; 15 | font-style: normal; 16 | font-weight: normal; 17 | font-variant: normal; 18 | text-transform: none; 19 | line-height: 1; 20 | 21 | /* Better Font Rendering =========== */ 22 | -webkit-font-smoothing: antialiased; 23 | -moz-osx-font-smoothing: grayscale; 24 | } 25 | 26 | .icon-lock:before { 27 | content: "\e602"; 28 | } 29 | .icon-unlocked:before { 30 | content: "\e603"; 31 | } 32 | .icon-cog:before { 33 | content: "\e600"; 34 | } 35 | .icon-eye:before { 36 | content: "\e604"; 37 | } 38 | .icon-eye-blocked:before { 39 | content: "\e605"; 40 | } 41 | -------------------------------------------------------------------------------- /host/README.md: -------------------------------------------------------------------------------- 1 | ##Psiphon Chrome Native Messaging Host 2 | 3 | ###Building with Docker 4 | 5 | Note that you may need to use `sudo docker` below, depending on your OS. 6 | 7 | Create the build image: 8 | 1. Change to the directory containing the `Dockerfile` 9 | 2. Run the command: `docker build --no-cache=true -t psichrome .` (this may take some time to complete) 10 | 3. Once completed, verify that you see an image named `psichrome` when running: `docker images` 11 | 12 | Run the build: 13 | 14 | ```bash 15 | docker run --rm -v $(pwd):/go/src/github.com/Psiphon-Labs/psiphon-chrome psichrome /bin/bash -c 'cd /go/src/github.com/Psiphon-Labs/psiphon-chrome && ./make.bash' 16 | ``` 17 | 18 | When that command completes, the compiled binaries will be located in the current directory. The files will be named: 19 | - `psiphon-native-messaging-host.exe` (for Windows) 20 | - `psiphon-native-messaging-host` (for Linux) 21 | 22 | If attempting to install the extension along with the now built native messaging host, return to the instructions in the [main README](../README.md)) 23 | -------------------------------------------------------------------------------- /extension/popup/popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | min-width: 200px; 3 | background-color: #eeeeee; 4 | } 5 | 6 | #logo-status { 7 | height: 128px; 8 | width: 128px; 9 | display: block; 10 | margin: 0.5em auto; 11 | } 12 | 13 | #settings-button { 14 | position: absolute; 15 | top: 5px; 16 | right: 5px; 17 | font-size: 1.3em; 18 | cursor: pointer; 19 | } 20 | 21 | .controls { 22 | margin: 1.8em; 23 | text-align: center; 24 | } 25 | 26 | .btn { 27 | background-color: #16627f; 28 | -moz-border-radius: 5px; 29 | -webkit-border-radius: 5px; 30 | border-radius: 5px; 31 | display: inline-block; 32 | cursor: pointer; 33 | color: #eeeeee; 34 | font-family: Arial; 35 | font-size: 1.25em; 36 | padding: 12px 30px; 37 | text-decoration: none; 38 | text-shadow: 0px 1px 0px #2f6627; 39 | } 40 | 41 | .btn:hover { 42 | background-color: #bcdae4; 43 | } 44 | 45 | .btn:active { 46 | position: relative; 47 | top: 1px; 48 | } 49 | 50 | #connect-button { 51 | background-color: #51a253; 52 | } 53 | 54 | #disconnect-button { 55 | background-color: #c25350; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /host/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile to build an image with the local version of psiphon-tunnel-core. 2 | # 3 | # See README.md for usage instructions. 4 | 5 | FROM ubuntu:15.04 6 | 7 | ENV GOVERSION=go1.4.2 8 | 9 | # Install system-level dependencies. 10 | ENV DEBIAN_FRONTEND=noninteractive 11 | RUN apt-get update && apt-get -y install build-essential curl git mercurial upx gcc-mingw-w64-i686 gcc-mingw-w64-x86-64 gcc-multilib 12 | 13 | # Install Go. 14 | ENV GOROOT=/usr/local/go GOPATH=/go 15 | ENV PATH=$PATH:$GOROOT/bin:$GOPATH/bin 16 | 17 | RUN echo "INSTALLING GO" && \ 18 | curl -L https://storage.googleapis.com/golang/$GOVERSION.linux-amd64.tar.gz -o /tmp/go.tar.gz && \ 19 | tar -C /usr/local -xzf /tmp/go.tar.gz && \ 20 | rm /tmp/go.tar.gz && \ 21 | echo $GOVERSION > $GOROOT/VERSION && \ 22 | echo "GO INSTALLED" 23 | 24 | ENV CGO_ENABLED=1 25 | 26 | RUN go get github.com/mitchellh/gox && go get github.com/inconshreveable/gonative && go get github.com/pwaller/goupx 27 | 28 | RUN mkdir -p /usr/local/gonative && cd /usr/local/gonative && gonative build 29 | 30 | ENV PATH=/usr/local/gonative/go/bin:$PATH 31 | 32 | WORKDIR $GOPATH/src 33 | -------------------------------------------------------------------------------- /extension/options/options.js: -------------------------------------------------------------------------------- 1 | var bgPage = chrome.extension.getBackgroundPage(); 2 | 3 | function saveOptions () { 4 | chrome.storage.local.set({ 5 | // Settings object to save 6 | }, function() { 7 | if (!chrome.runtime.lastError) { 8 | // Successful settings save 9 | } else { 10 | // Error updating settings 11 | } 12 | }); 13 | } 14 | 15 | function restoreOptions () { 16 | chrome.storage.local.get({ 17 | // Default object to populate if none is found 18 | }, function(items) { 19 | // Items is an object keyed to the previously stored values/default object above 20 | // Set options DOM elements based on retrieved settings 21 | if (!chrome.runtime.lastError) { 22 | // Successful settings retrieval 23 | } else { 24 | // Error retrieving settings 25 | } 26 | }); 27 | } 28 | 29 | document.addEventListener('DOMContentLoaded', function() { 30 | //restoreOptions(); 31 | //document.getElementById('save-button').addEventListener('click', saveOptions); 32 | document.getElementById('view-logs-button').addEventListener('click', bgPage.viewLogs); 33 | document.getElementById('force-system-proxy-button').addEventListener('click', bgPage.proxySettings.forceToSystemProxy); 34 | }); 35 | -------------------------------------------------------------------------------- /tools/install_host.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd $(dirname $(pwd)) 5 | 6 | DIR="$(dirname $(find $(pwd) -type f -name 'ca.psiphon.chrome.json'))" 7 | 8 | if [ "$(uname -s)" == "Darwin" ]; then 9 | if [ "$(whoami)" == "root" ]; then 10 | TARGET_DIR="/Library/Google/Chrome/NativeMessagingHosts" 11 | else 12 | TARGET_DIR="$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts" 13 | fi 14 | else 15 | if [ "$(whoami)" == "root" ]; then 16 | TARGET_DIR="/etc/opt/chrome/native-messaging-hosts" 17 | else 18 | TARGET_DIR="$HOME/.config/google-chrome/NativeMessagingHosts" 19 | fi 20 | fi 21 | 22 | HOST_NAME=ca.psiphon.chrome 23 | EXECUTABLE=psiphon-native-messaging-host 24 | 25 | # Create directory to store native messaging host. 26 | mkdir -p "$TARGET_DIR" 27 | 28 | # Copy native messaging host manifest. 29 | cp "$DIR/$HOST_NAME.json" "$TARGET_DIR" 30 | 31 | # Update host path in the manifest. 32 | HOST_PATH=$DIR/$EXECUTABLE 33 | ESCAPED_HOST_PATH=${HOST_PATH////\\/} 34 | sed -i -e "s/HOST_PATH/$ESCAPED_HOST_PATH/" "$TARGET_DIR/$HOST_NAME.json" 35 | 36 | # Set permissions for the manifest so that all users can read it. 37 | chmod a+r "$TARGET_DIR/$HOST_NAME.json" 38 | 39 | echo "Native messaging host $HOST_NAME has been installed." 40 | -------------------------------------------------------------------------------- /host/make.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | set -exv # verbose output for testing 5 | 6 | if [ ! -f make.bash ]; then 7 | echo 'make.bash must be run from $GOPATH/src/github.com/Psiphon-Labs/psiphon-chrome/host' 8 | exit 1 9 | fi 10 | 11 | CGO_ENABLED=1 12 | 13 | # Make sure we have our dependencies 14 | echo -e "go-getting dependencies...\n" 15 | GOOS=linux go get -d -v ./... 16 | GOOS=windows go get -d -v ./... 17 | 18 | EXE_BASENAME="psiphon-native-messaging-host" 19 | BUILDDATE=$(date --iso-8601=seconds) 20 | #BUILDREPO=$(git config --get remote.origin.url) 21 | #BUILDREV=$(git rev-parse HEAD) 22 | BUILDREPO="dev_build" 23 | BUILDREV="Experimental_Dev_Build" 24 | LDFLAGS="\ 25 | -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon.buildDate $BUILDDATE \ 26 | -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon.buildRepo $BUILDREPO \ 27 | -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon.buildRev $BUILDREV \ 28 | " 29 | echo -e "LDFLAGS=$LDFLAGS\n" 30 | 31 | echo -e "\nBuilding windows-386..." 32 | CC=/usr/bin/i686-w64-mingw32-gcc \ 33 | gox -verbose -ldflags "$LDFLAGS" -osarch windows/386 -output ${EXE_BASENAME} 34 | upx --best ${EXE_BASENAME}.exe 35 | 36 | #echo -e "\nBuilding windows-amd64..." 37 | #CC=/usr/bin/x86_64-w64-mingw32-gcc \ 38 | # gox -verbose -ldflags "$LDFLAGS" -osarch windows/amd64 -output windows_amd64_${EXE_BASENAME} 39 | #upx --best windows_amd64_${EXE_BASENAME}.exe 40 | 41 | #echo -e "\nBuilding linux-amd64..." 42 | #gox -verbose -ldflags "$LDFLAGS" -osarch linux/amd64 -output linux_amd64_${EXE_BASENAME} 43 | #goupx --best linux_amd64_${EXE_BASENAME} 44 | 45 | echo -e "\nBuilding linux-386..." 46 | CFLAGS=-m32 \ 47 | gox -verbose -ldflags "$LDFLAGS" -osarch linux/386 -output ${EXE_BASENAME} 48 | goupx --best ${EXE_BASENAME} 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##Psiphon Chrome 2 | This is a Chrome app using native messaging 3 | 4 | It currently: 5 | - Instantiates the tunnel core (with values from `host/embedded_values.go`) 6 | - Manages the Chrome proxy settings 7 | 8 | In order to use this app (until cross platform installers are created) you must: 9 | 1. Copy `host/embedded_values.go.stub` to `host/embedded_values.go` and populate the configuration file appropriately 10 | 2. Build the native messaging host (instructions in [host/README.md](host/README.md)) 11 | 3. Install the native messaging host from the host directory (instructions below) 12 | 4. Load the app from the `app`directory via `chrome://extensions` -> `Load unpacked extension...` 13 | 14 | ###To install the host: 15 | ####On Windows: 16 | Run the `install_host.bat` script in the `tools` directory 17 | 18 | This script installs the native messaging host for the current user 19 | It creates a registry key: `HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\ca.psiphon.chrome`and sets its default value to the full path of `host\ca.psiphon.chrome-win.json` 20 | 21 | If you want to install the native messaging host for all users, change `HKEY_CURRENT_USER` to `HKEY_LOCAL_MACHINE` when creating the registry key 22 | 23 | ####On Mac and Linux: 24 | Run the `install_host.sh` script in the `tools` directory 25 | 26 | By default the host is installed only for the user who runs the script, but if you run it with admin privileges (i.e. `sudo tools/install_host.sh`), then the host will be installed for all users 27 | 28 | 29 | ###To uninstall: 30 | ####Uninstalling the host: 31 | 1. Run the `tools/uninstall_host.bat` or `tools/uninstall_host.sh` script to uninstall the host from Windows or Mac/Linux respectively 32 | 33 | ####Uninstalling the extension: 34 | 1. Navigate to `chrome://extensions` 35 | 2. Click the trash can icon on the right hand side of the `Psiphon` entry 36 | -------------------------------------------------------------------------------- /extension/popup/popup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isConnected() { 4 | logoStatus.element.src = logoStatus.connectedSource; 5 | document.getElementById('connect-button').style.display = 'none'; 6 | document.getElementById('disconnect-button').style.display = 'initial'; 7 | } 8 | 9 | function isDisconnected() { 10 | logoStatus.element.src = logoStatus.disconnectedSource; 11 | document.getElementById('connect-button').style.display = 'initial'; 12 | document.getElementById('disconnect-button').style.display = 'none'; 13 | } 14 | 15 | function uiStateFromBackgroundPage () { 16 | if (bgPage.connectionState.active) { 17 | isConnected(); 18 | } else { 19 | isDisconnected(); 20 | } 21 | } 22 | 23 | var bgPage = chrome.extension.getBackgroundPage(); 24 | var logoStatus = { 25 | element: null, 26 | connectedSource: '../img/logos/128.png', 27 | disconnectedSource: '../img/logos/bw-128.png' 28 | }; 29 | 30 | chrome.runtime.onMessage.addListener(function (message) { 31 | if (typeof message !== 'undefined' && message) { 32 | if (message.type === 'status') { 33 | switch (message.status) { 34 | case 'connecting': 35 | console.log('connecting'); 36 | break; 37 | case 'disconnecting': 38 | console.log('disconnecting'); 39 | break; 40 | case 'connected': 41 | isConnected(); 42 | break; 43 | case 'disconnected': 44 | isDisconnected(); 45 | break; 46 | default: 47 | console.error('Unhandled "status" message of type "%s"', message.status); 48 | break; 49 | } 50 | } 51 | } 52 | }); 53 | 54 | document.addEventListener('DOMContentLoaded', function () { 55 | logoStatus.element = document.getElementById('logo-status'); 56 | uiStateFromBackgroundPage(); 57 | 58 | document.getElementById('connect-button').addEventListener('click', bgPage.psiphon.connect); 59 | document.getElementById('disconnect-button').addEventListener('click', bgPage.psiphon.disconnect); 60 | document.getElementById('settings-button').addEventListener('click', function () { chrome.runtime.openOptionsPage(); }); 61 | }); 62 | -------------------------------------------------------------------------------- /extension/fonts/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /host/psiphon-native-messaging-host.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "encoding/json" 8 | "fmt" 9 | "io" 10 | "os" 11 | "sync" 12 | 13 | "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon" 14 | ) 15 | 16 | type PsiphonPipe struct { 17 | r *io.PipeReader 18 | w *io.PipeWriter 19 | } 20 | 21 | var psiphonPipe PsiphonPipe 22 | 23 | var stdoutWriter io.Writer 24 | var stderrWriter io.Writer 25 | var psiphonOutputWriter io.Writer 26 | var shutdownBroadcast chan struct{} 27 | var controllerWaitGroup *sync.WaitGroup 28 | 29 | func sendMessage(message []byte) { 30 | length := make([]byte, 4) 31 | binary.LittleEndian.PutUint32(length, uint32(len(message))) 32 | 33 | stdoutWriter.Write(length) 34 | stdoutWriter.Write(message) 35 | } 36 | 37 | func main() { 38 | canExit := make(chan bool) 39 | 40 | psiphonPipe.r, psiphonPipe.w = io.Pipe() 41 | 42 | stderrWriter = os.Stderr 43 | stdoutWriter = os.Stdout 44 | 45 | mainStdin := bufio.NewReader(os.Stdin) 46 | 47 | go func() { 48 | scanner := bufio.NewScanner(psiphonPipe.r) 49 | for scanner.Scan() { 50 | sendMessage(scanner.Bytes()) 51 | } 52 | if err := scanner.Err(); err != nil { 53 | fmt.Fprintf(stderrWriter, "error scanning lines from psiphonPipe.r: %s\n", err) 54 | } 55 | }() 56 | go func() { 57 | for { 58 | messageLengthBytes := make([]byte, 4) 59 | bytesRead, err := mainStdin.Read(messageLengthBytes) 60 | if err != nil { 61 | fmt.Fprintf(stderrWriter, "error reading message length: %s\n", err) 62 | break 63 | } 64 | if bytesRead == 0 { 65 | fmt.Fprintf(stderrWriter, "error reading byte array with message: array is empty\n") 66 | break 67 | } 68 | 69 | var messageLength int32 70 | tempBuffer := bytes.NewBuffer(messageLengthBytes) 71 | err = binary.Read(tempBuffer, binary.LittleEndian, &messageLength) 72 | if err != nil { 73 | fmt.Fprintf(stderrWriter, "error converting message length from bytes to int : %s\n", err) 74 | break 75 | } 76 | 77 | messageBytes := make([]byte, messageLength) 78 | bytesRead, err = mainStdin.Read(messageBytes) 79 | if err != nil { 80 | fmt.Fprintf(stderrWriter, "error reading message length: %s\n", err) 81 | break 82 | } 83 | 84 | var parsed map[string]interface{} 85 | err = json.Unmarshal(messageBytes, &parsed) 86 | if err != nil { 87 | fmt.Fprintf(stderrWriter, "error unmarshalling json: %s\n", err) 88 | break 89 | } 90 | 91 | if parsed["psi"] == "connect" { 92 | runPsiphon() 93 | } 94 | 95 | if parsed["psi"] == "disconnect" { 96 | close(shutdownBroadcast) 97 | controllerWaitGroup.Wait() 98 | } 99 | 100 | } 101 | 102 | // allow main to finish 103 | canExit <- true 104 | }() 105 | 106 | <-canExit 107 | } 108 | 109 | // {{{ Run Psiphon 110 | func runPsiphon() { 111 | // Initialize default Notice output 112 | psiphon.SetNoticeOutput(psiphonPipe.w) 113 | 114 | //TODO: Embedded server entries 115 | 116 | // {{{ Load and parse config file 117 | config, err := psiphon.LoadConfig(PSIPHON_CONFIG) 118 | if err != nil { 119 | psiphon.NoticeError("error processing configuration file: %s", err) 120 | os.Exit(1) 121 | } 122 | // }}} 123 | 124 | // {{{ Initialize data store 125 | 126 | err = psiphon.InitDataStore(config) 127 | if err != nil { 128 | psiphon.NoticeError("error initializing datastore: %s", err) 129 | os.Exit(1) 130 | } 131 | // }}} 132 | 133 | controller, err := psiphon.NewController(config) 134 | if err != nil { 135 | psiphon.NoticeError("error creating controller: %s", err) 136 | os.Exit(1) 137 | } 138 | 139 | shutdownBroadcast = make(chan struct{}) 140 | controllerWaitGroup = new(sync.WaitGroup) 141 | controllerWaitGroup.Add(1) 142 | go func() { 143 | defer controllerWaitGroup.Done() 144 | controller.Run(shutdownBroadcast) 145 | }() 146 | } 147 | 148 | // }}} 149 | -------------------------------------------------------------------------------- /extension/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var logs = []; 4 | function logMessage (message) { 5 | logs.push(message); 6 | } 7 | 8 | function viewLogs () { 9 | chrome.tabs.create({'url': chrome.extension.getURL('logs/logs.html')}); 10 | } 11 | 12 | var nativeHost = null; 13 | 14 | var connectionState = { 15 | active: false, 16 | tunnels: 0, 17 | egressRegions: [], 18 | homepage: { 19 | url: null, 20 | shown: false 21 | }, 22 | proxies: { 23 | socks: null, 24 | http: null 25 | } 26 | }; 27 | 28 | var proxySettings = { 29 | initial: null, 30 | storeCurrent: function () { 31 | chrome.proxy.settings.get({'incognito': false}, function(config) { 32 | proxySettings.initial = config.value; 33 | console.log(JSON.stringify(config)); 34 | }); 35 | }, 36 | restoreInitial: function () { 37 | if (proxySettings.initial) { 38 | chrome.proxy.settings.set({value: proxySettings.initial, scope: 'regular'}, function() {}); 39 | } else { 40 | console.warn('No initial settings to restore'); 41 | } 42 | }, 43 | forceToSystemProxy: function () { 44 | chrome.proxy.settings.set({value: {mode: "system"}}, function() {}); 45 | proxySettings.storeCurrent() 46 | }, 47 | set: function (port, bypassList) { 48 | if (typeof port === 'undefined') { 49 | console.warn('proxySettings.set - port was undefined, defaulting to 1080'); 50 | port = 1080; 51 | } 52 | if (typeof bypassList === 'undefined') { 53 | console.warn('proxySettings.set - bypassList was undefined, defaulting to []'); 54 | bypassList = []; 55 | } 56 | 57 | chrome.proxy.settings.set({value: { 58 | mode: 'fixed_servers', 59 | rules: { 60 | singleProxy: { 61 | scheme: 'socks5', 62 | host: 'localhost', 63 | port: port 64 | }, 65 | bypassList: bypassList 66 | } 67 | }, scope: 'regular'}, function() {}); 68 | } 69 | }; 70 | 71 | var psiphon = { 72 | connect: function () { 73 | nativeHost.postMessage({'psi':'connect'}); 74 | }, 75 | disconnect: function () { 76 | nativeHost.postMessage({'psi':'disconnect'}); 77 | } 78 | }; 79 | 80 | function onNativeMessage(message) { 81 | if (message.noticeType) { 82 | switch (message.noticeType) { 83 | case 'AvailableEgressRegions': 84 | connectionState.egressRegions = message.data.regions; 85 | break; 86 | case 'ListeningHttpProxyPort': 87 | connectionState.proxies.http = message.data.port; 88 | break; 89 | case 'ListeningSocksProxyPort': 90 | connectionState.proxies.socks = message.data.port; 91 | break; 92 | case 'Tunnels': 93 | connectionState.tunnels = message.data.count; 94 | break; 95 | case 'Homepage': 96 | connectionState.homepage.url = message.data.url; 97 | break; 98 | default: 99 | break; 100 | } 101 | 102 | if (!connectionState.active && connectionState.tunnels > 0 && connectionState.proxies.socks) { 103 | connectionState.active = true; 104 | tunnelAvailable(); 105 | } else if (connectionState.active && (connectionState.tunnels < 1 || !connectionState.proxies.socks)) { 106 | connectionState.active = false; 107 | noTunnelAvailable(); 108 | } 109 | } 110 | 111 | logMessage(JSON.stringify(message)); 112 | } 113 | 114 | function tunnelAvailable() { 115 | console.log('A tunnel is available, setting proxy'); 116 | 117 | proxySettings.set(connectionState.proxies.socks, ['localhost']); 118 | chrome.runtime.sendMessage({type: 'status', status: 'connected'}); 119 | chrome.browserAction.setIcon({path: 'img/logos/38.png'}); 120 | 121 | if (connectionState.homepage.url && connectionState.homepage.shown === false) { 122 | connectionState.homepage.shown = true; 123 | window.setTimeout(function () { 124 | window.open(connectionState.homepage.url); 125 | }, 1000); 126 | } 127 | } 128 | 129 | function noTunnelAvailable() { 130 | console.log('No tunnel is available, resetting proxy'); 131 | 132 | proxySettings.restoreInitial(); 133 | chrome.runtime.sendMessage({type: 'status', status: 'disconnected'}); 134 | chrome.browserAction.setIcon({path: 'img/logos/bw-38.png'}); 135 | } 136 | 137 | // Store proxy settings prior to beginning managing them in the extension 138 | proxySettings.storeCurrent(); 139 | 140 | // Initialize connection to native host 141 | nativeHost = chrome.runtime.connectNative('ca.psiphon.chrome'); 142 | nativeHost.onMessage.addListener(onNativeMessage); 143 | nativeHost.onDisconnect.addListener(function() { 144 | logMessage('failed to connect to native host'); 145 | nativeHost = null; 146 | }); 147 | 148 | chrome.proxy.onProxyError.addListener(function(error) { 149 | logMessage('proxy error: ' + JSON.stringify(error)); 150 | }); 151 | --------------------------------------------------------------------------------