Unverified Commit 1bd2637e authored by Kali Kaneko's avatar Kali Kaneko
Browse files

[feat] expose set transport

webapi mainly for tests, but it's usable too
parent c6c0209a
......@@ -48,6 +48,11 @@ func UseTransport(transport string) {
backend.UseTransport(transport)
}
//export GetTransport
func GetTransport() *C.char {
return (*C.char)(backend.GetTransport())
}
//export Quit
func Quit() {
backend.Quit()
......
......@@ -22,6 +22,7 @@
<file>assets/icon/png/white/vpn_wait_1.png</file>
<file>assets/icon/png/white/vpn_wait_2.png</file>
<file>assets/icon/png/white/vpn_wait_3.png</file>
<file>assets/img/bird.jpg</file>
<file alias="providers.json">providers/providers.json</file>
</qresource>
......
......@@ -52,6 +52,16 @@ void Backend::useAutomaticGateway()
UseAutomaticGateway();
}
void Backend::useTransport(QString transport)
{
UseTransport(toGoStr(transport));
}
QString Backend::getTransport()
{
return QString(GetTransport());
}
void Backend::login(QString username, QString password)
{
Login(toGoStr(username), toGoStr(password));
......
......@@ -38,6 +38,8 @@ public slots:
void donateSeen();
void useLocation(QString username);
void useAutomaticGateway();
void useTransport(QString transport);
QString getTransport();
void login(QString username, QString password);
void resetError(QString errlabel);
void resetNotification(QString label);
......
......@@ -8,9 +8,9 @@ import Qt.labs.platform 1.0
ApplicationWindow {
id: app
visible: true
width: 500
width: 300
height: 600
maximumWidth: 600
maximumWidth: 300
minimumWidth: 300
maximumHeight: 500
minimumHeight: 300
......@@ -24,17 +24,20 @@ ApplicationWindow {
console.debug(msg)
}
// TODO get a nice background color for this mainwindow. It should be customizable.
// TODO refactorize all this mess into discrete components.
// TODO refactor into discrete components.
TabBar {
id: bar
width: parent.width
TabButton {
text: qsTr("Info")
text: qsTr("Status")
}
TabButton {
text: qsTr("Location")
}
TabButton {
text: qsTr("Bridges")
}
}
StackLayout {
......@@ -47,93 +50,121 @@ ApplicationWindow {
id: infoTab
anchors.centerIn: parent
Column {
Rectangle {
id: background
anchors.fill: parent;
anchors.topMargin: 40;
Image {
source: "qrc:/assets/img/bird.jpg";
fillMode: Image.PreserveAspectCrop;
anchors.fill: parent;
opacity: 0.8;
}
}
Item {
id: connBox
anchors.centerIn: parent
spacing: 5
Text {
id: mainStatus
text: "off"
font.pixelSize: 26
anchors.horizontalCenter: parent.horizontalCenter
}
width: 300
height: 300
Text {
id: mainCurrentGateway
text: ""
font.pixelSize: 20
anchors.horizontalCenter: parent.horizontalCenter
Rectangle {
anchors.fill: parent
color: "white"
opacity: 0.3
layer.enabled: true
}
SwitchDelegate {
Column {
id: vpntoggle
anchors.centerIn: parent
spacing: 5
text: qsTr("")
checked: false
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: mainStatus
text: "off"
font.pixelSize: 26
anchors.horizontalCenter: parent.horizontalCenter
}
Connections {
function onCheckedChanged() {
if (vpntoggle.checked == true
&& ctx.status == "off") {
backend.switchOn()
}
if (vpntoggle.checked === false
&& ctx.status == "on") {
backend.switchOff()
Text {
id: mainCurrentGateway
text: ""
font.pixelSize: 20
anchors.horizontalCenter: parent.horizontalCenter
}
SwitchDelegate {
id: vpntoggle
text: qsTr("")
checked: false
anchors.horizontalCenter: parent.horizontalCenter
Connections {
function onCheckedChanged() {
if (vpntoggle.checked == true
&& ctx.status == "off") {
backend.switchOn()
}
if (vpntoggle.checked === false
&& ctx.status == "on") {
backend.switchOff()
}
}
}
}
contentItem: Text {
rightPadding: vpntoggle.indicator.width + vpntoggle.spacing
text: vpntoggle.text
font: vpntoggle.font
opacity: enabled ? 1.0 : 0.3
color: vpntoggle.down ? "#17a81a" : "#21be2b"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
contentItem: Text {
rightPadding: vpntoggle.indicator.width + vpntoggle.spacing
text: vpntoggle.text
font: vpntoggle.font
opacity: enabled ? 1.0 : 0.5
color: vpntoggle.down ? "#17a81a" : "#21be2b"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
indicator: Rectangle {
implicitWidth: 48
implicitHeight: 26
x: vpntoggle.width - width - vpntoggle.rightPadding
y: parent.height / 2 - height / 2
radius: 13
color: vpntoggle.checked ? "#17a81a" : "transparent"
border.color: vpntoggle.checked ? "#17a81a" : "#cccccc"
Rectangle {
x: vpntoggle.checked ? parent.width - width : 0
width: 26
height: 26
indicator: Rectangle {
implicitWidth: 48
implicitHeight: 26
x: vpntoggle.width - width - vpntoggle.rightPadding
y: parent.height / 2 - height / 2
radius: 13
color: vpntoggle.down ? "#cccccc" : "#ffffff"
border.color: vpntoggle.checked ? (vpntoggle.down ? "#17a81a" : "#21be2b") : "#999999"
color: vpntoggle.checked ? "#17a81a" : "transparent"
border.color: vpntoggle.checked ? "#17a81a" : "#cccccc"
Rectangle {
x: vpntoggle.checked ? parent.width - width : 0
width: 26
height: 26
radius: 13
color: vpntoggle.down ? "#cccccc" : "#ffffff"
border.color: vpntoggle.checked ? (vpntoggle.down ? "#17a81a" : "#21be2b") : "#999999"
}
}
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
visible: vpntoggle.down || vpntoggle.highlighted
color: vpntoggle.down ? "#bdbebf" : "#eeeeee"
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
visible: vpntoggle.down || vpntoggle.highlighted
color: vpntoggle.down ? "#17a81a" : "#eeeeee"
}
} // end switchdelegate
Text {
id: manualOverrideWarning
font.pixelSize: 10
color: "grey"
text: qsTr("Location has been manually set.")
anchors.horizontalCenter: parent.horizontalCenter
visible: isManualLocation()
}
} // end switchdelegate
Text {
id: manualOverrideWarning
font.pixelSize: 10
color: "grey"
text: qsTr("Location has been manually set.")
anchors.horizontalCenter: parent.horizontalCenter
visible: isManualLocation()
}
}
}
} // end column
} // end inner item
} // end outer item
Item {
......@@ -148,7 +179,7 @@ ApplicationWindow {
RadioButton {
id: autoSelectionButton
checked: !isManualLocation()
text: qsTr("Best")
text: qsTr("Recommended")
onClicked: backend.useAutomaticGateway()
}
RadioButton {
......@@ -163,7 +194,7 @@ ApplicationWindow {
visible: manualSelectionButton.checked
anchors.horizontalCenter: parent.horizontalCenter
model: [qsTr("Best")]
model: [qsTr("Recommended")]
onActivated: {
console.debug("Selected gateway:", currentText)
backend.useLocation(currentText.toString())
......@@ -200,6 +231,45 @@ ApplicationWindow {
}
} // end column
} // end item
Item {
id: bridgesTab
anchors.centerIn: parent
width: parent.width
Column {
anchors.centerIn: parent
spacing: 10
width: parent.width
CheckBox {
checked: false
text: qsTr("Use obfs4 bridges")
font.pixelSize: 14
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
id: bridgesInfo
width: 250
color: "grey"
text: qsTr("Select a bridge only if you know that you need it to evade censorship in your country or local network.")
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: Text.WordWrap
}
Text {
id: bridgeReconnect
width: 250
font.pixelSize: 12
color: "red"
text: qsTr("The change will take effect next time you connect to the VPN.")
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: Text.WordWrap
visible: true
}
} // end column
} // end item
} // end stacklayout
Connections {
......
......@@ -83,6 +83,10 @@ func UseTransport(label string) {
ctx.bm.UseTransport(label)
}
func GetTransport() *C.char {
return C.CString(ctx.bm.GetTransport())
}
func Quit() {
if ctx.autostart != nil {
ctx.autostart.Disable()
......
......@@ -57,7 +57,8 @@ type connectionCtx struct {
func (c *connectionCtx) toJson() ([]byte, error) {
statusMutex.Lock()
if c.bm != nil {
c.Locations = c.bm.ListLocationFullness("openvpn")
transport := c.bm.GetTransport()
c.Locations = c.bm.ListLocationFullness(transport)
c.CurrentGateway = c.bm.GetCurrentGateway()
c.CurrentLocation = c.bm.GetCurrentLocation()
c.CurrentCountry = c.bm.GetCurrentCountry()
......
......@@ -49,7 +49,7 @@ func webGatewaySet(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "ParseForm() err: %v", err)
return
}
gwLabel := r.FormValue("gw")
gwLabel := r.FormValue("transport")
fmt.Fprintf(w, "selected gateway: %s\n", gwLabel)
ctx.bm.UseGateway(gwLabel)
// TODO make sure we don't tear the fw down on reconnect...
......@@ -63,23 +63,48 @@ func webGatewaySet(w http.ResponseWriter, r *http.Request) {
}
func webGatewayList(w http.ResponseWriter, r *http.Request) {
locationJson, err := json.Marshal(ctx.bm.ListLocationFullness("openvpn"))
transport := ctx.bm.GetTransport()
locationJson, err := json.Marshal(ctx.bm.ListLocationFullness(transport))
if err != nil {
fmt.Fprintf(w, "Error converting json: %v", err)
}
fmt.Fprintf(w, string(locationJson))
}
// TODO
func webTransportGet(w http.ResponseWriter, r *http.Request) {
t, err := json.Marshal(ctx.bm.GetTransport())
if err != nil {
fmt.Fprintf(w, "Error converting json: %v", err)
}
fmt.Fprintf(w, string(t))
}
// TODO
func webTransportSet(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ParseForm() err: %v", err)
return
}
t := r.FormValue("transport")
if isValidTransport(t) {
fmt.Fprintf(w, "Selected transport: %s\n", t)
go ctx.bm.SetTransport(string(t))
} else {
fmt.Fprintf(w, "Unknown transport: %s\n", t)
}
default:
fmt.Fprintf(w, "Only POST supported.")
}
}
// TODO
func webTransportList(w http.ResponseWriter, r *http.Request) {
t, err := json.Marshal([]string{"openvpn", "obfs4"})
if err != nil {
fmt.Fprintf(w, "Error converting json: %v", err)
}
fmt.Fprintf(w, string(t))
}
func webQuit(w http.ResponseWriter, r *http.Request) {
......@@ -97,10 +122,19 @@ func enableWebAPI(port int) {
http.Handle("/vpn/gw/get", CheckAuth(http.HandlerFunc(webGatewayGet), token))
http.Handle("/vpn/gw/set", CheckAuth(http.HandlerFunc(webGatewaySet), token))
http.Handle("/vpn/gw/list", CheckAuth(http.HandlerFunc(webGatewayList), token))
//http.Handle("/vpn/transport/get", CheckAuth(http.HandlerFunc(webTransportGet), token))
//http.Handle("/vpn/transport/set", CheckAuth(http.HandlerFunc(webTransportSet), token))
//http.Handle("/vpn/transport/list", CheckAuth(http.HandlerFunc(webTransportList), token))
http.Handle("/vpn/transport/get", CheckAuth(http.HandlerFunc(webTransportGet), token))
http.Handle("/vpn/transport/set", CheckAuth(http.HandlerFunc(webTransportSet), token))
http.Handle("/vpn/transport/list", CheckAuth(http.HandlerFunc(webTransportList), token))
http.Handle("/vpn/status", CheckAuth(http.HandlerFunc(webStatus), token))
http.Handle("/vpn/quit", CheckAuth(http.HandlerFunc(webQuit), token))
http.ListenAndServe(":"+strconv.Itoa(port), nil)
}
func isValidTransport(t string) bool {
for _, b := range []string{"openvpn", "obfs4"} {
if b == t {
return true
}
}
return false
}
......@@ -30,6 +30,8 @@ type Bitmask interface {
ListLocationFullness(protocol string) map[string]float64
UseGateway(name string)
UseAutomaticGateway()
GetTransport() string
SetTransport(string) error
GetCurrentGateway() string
GetCurrentLocation() string
GetCurrentCountry() string
......
......@@ -121,26 +121,8 @@ func (b *Bitmask) listenShapeErr() {
func (b *Bitmask) startOpenVPN() error {
arg := []string{}
// Empty transport means we get only the openvpn gateways
if b.transport == "" {
arg = b.openvpnArgs
gateways, err := b.bonafide.GetGateways("openvpn")
if err != nil {
return err
}
err = b.launch.firewallStart(gateways)
if err != nil {
return err
}
for _, gw := range gateways {
for _, port := range gw.Ports {
arg = append(arg, "--remote", gw.IPAddress, port, "tcp4")
}
}
} else {
// For now, obf4 is the only supported Pluggable Transport
gateways, err := b.bonafide.GetGateways(b.transport)
if b.GetTransport() == "obfs4" {
gateways, err := b.bonafide.GetGateways("obfs4")
if err != nil {
return err
}
......@@ -164,6 +146,22 @@ func (b *Bitmask) startOpenVPN() error {
proxyArgs := strings.Split(proxy, ":")
arg = append(arg, "--remote", proxyArgs[0], proxyArgs[1], "tcp4")
arg = append(arg, "--route", gw.IPAddress, "255.255.255.255", "net_gateway")
} else {
arg = b.openvpnArgs
gateways, err := b.bonafide.GetGateways("openvpn")
if err != nil {
return err
}
err = b.launch.firewallStart(gateways)
if err != nil {
return err
}
for _, gw := range gateways {
for _, port := range gw.Ports {
arg = append(arg, "--remote", gw.IPAddress, port, "tcp4")
}
}
}
arg = append(arg,
"--verb", "3",
......@@ -171,8 +169,8 @@ func (b *Bitmask) startOpenVPN() error {
"--management", openvpnManagementAddr, openvpnManagementPort,
"--ca", b.getTempCaCertPath(),
"--cert", b.certPemPath,
"--key", b.certPemPath,
"--persist-tun")
"--key", b.certPemPath)
//"--persist-tun")
return b.launch.openvpnStart(arg...)
}
......@@ -330,6 +328,23 @@ func (b *Bitmask) UseTransport(transport string) error {
return nil
}
func (b *Bitmask) GetTransport() string {
if b.transport == "obfs4" {
return "obfs4"
} else {
return "openvpn"
}
}
func (b *Bitmask) SetTransport(t string) error {
if t != "openvpn" && t != "obfs4" {
return errors.New("Transport not supported: " + t)
}
log.Println("Setting transport to", t)
b.transport = t
return nil
}
func (b *Bitmask) getTempCertPemPath() string {
return path.Join(b.tempdir, "openvpn.pem")
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment