Lots of fresh code! You have been warned!!! Do pls report trouble!
[android6bed4] / src / nl / openfortress / android6bed4 / Android6bed4.java
1 package nl.openfortress.android6bed4;
2
3 import android.app.Activity;
4 import android.content.Intent;
5 import android.net.VpnService;
6 import android.net.VpnService.Builder;
7 import android.os.Bundle;
8 import android.os.ParcelFileDescriptor;
9 import android.view.View;
10 import android.text.TextWatcher;
11 import android.text.Editable;
12 import android.widget.TextView;
13 import android.widget.CheckBox;
14 import android.widget.EditText;
15 import android.widget.Switch;
16
17 import java.io.IOException;
18 import java.net.*;
19
20
21 public class Android6bed4 extends Activity implements TextWatcher {
22
23         private DatagramSocket uplink = null;
24                 
25         private boolean persist = false;
26         
27         public InetSocketAddress publicserver;
28         
29         private Intent mktun_intent;
30
31         private boolean interactive = false;
32
33         private int local_port = 0;
34         
35         
36         /*
37          * User interface, with method names as specified in the XML file layout/user_interface.xml
38          */
39         
40         /*
41          * User interface: onPersistClick is called when the checkbox is toggled that changes reboot behaviour.
42          */
43         public void onPersistClick (View vw) {
44                 persist = ((Switch) vw).isChecked ();
45                 teardown_tunnel ();
46                 if (!persist) {
47                         //
48                         // Switching off the boot persistency is done immediately
49                         //TODO// Store "persistent := false"
50                 } else {
51                         //
52                         // Skip:
53                         // Switching on boot persistency is only done after success
54                 }
55         }
56
57         /*
58          * User interface: onDefaultRouteClick is called when the switch is toggled between a /64 and a /0 prefix.
59          */
60         public void onDefaultRouteClick (View vw) {
61                 boolean defaultroute = ((Switch) vw).isChecked ();
62                 teardown_tunnel ();
63         }
64         
65         /*
66          * User interface: onEnablerClick is called when the switch is toggled that enables/disables the tunnel.
67          */
68         public void onEnablerClick (View vw) {
69                 boolean enable = ((Switch) vw).isChecked ();
70                 teardown_tunnel ();
71                 if (enable) {
72                         //
73                         // Enable the 6bed4 tunnel
74                         try {
75                                 TextView tv = (TextView) findViewById (R.id.tunserver_ip_string);
76                                 String pubsrv_ip = tv.getText ().toString ().trim ();
77                                 if (pubsrv_ip.length () == 0) {
78                                         pubsrv_ip = "145.136.0.1";
79                                 }
80                                 publicserver = new InetSocketAddress (Inet4Address.getByName (pubsrv_ip), 25788);
81                         } catch (Throwable thr) {
82                                 publicserver = null;
83                                 Switch sv = (Switch) findViewById (R.id.enable_6bed4);
84                                 sv.setChecked (false);
85                                 return;
86                         }
87                         //
88                         // Extract local UDP port from GUI (or already have it setup, for better recycling)
89                         int new_port;
90                         try {
91                                 TextView tv = (TextView) findViewById (R.id.tunclient_port_number);
92                                 new_port = new Integer (tv.getText ().toString ().trim ()).intValue () & 0xfffe;
93                         } catch (Throwable thr) {
94                                 new_port = local_port;
95                         }
96                         try {
97                                 DatagramSocket new_uplink;
98                                 if ((new_port > 0) && (new_port <= 65535) && ((new_port & 0x0001) == 0x0000)) {
99                                         new_uplink = new DatagramSocket (new_port);
100                                 } else {
101                                         new_uplink = new DatagramSocket ();
102                                         new_port = new_uplink.getPort ();
103                                 }
104                                 if (uplink != null) {
105                                         uplink.close ();
106                                 }
107                                 local_port = new_port;
108                                 uplink = new_uplink;
109                         } catch (SocketException se) {
110                                 uplink = null;
111                                 Switch sv = (Switch) findViewById (R.id.enable_6bed4);
112                                 sv.setChecked (false);
113                                 return;
114                         }
115                         try {
116                                 setup_tunnel ((Inet6Address) Inet6Address.getByName ("::1"));   /* TODO: Note here, not with a static IPv6 address */
117                                 if (!interactive) {
118                                         finish ();
119                                 }
120                         } catch (UnknownHostException uhe) {
121                                 Switch sv = (Switch) findViewById (R.id.enable_6bed4);
122                                 sv.setChecked (false);
123                                 return;
124                         }
125                         //TODO// Wait for 1s or so, reporting the state?  Or link back from tunsvc to view?
126                 }
127         }
128         
129         /*
130          * User interface: Change to either text field -- meaning, disable any current tunnel (and the enabler widget).
131          */
132         public void afterTextChanged (Editable s) { ; }
133         public void onTextChanged (CharSequence s, int start, int count, int after) { ; }
134         public void beforeTextChanged (CharSequence s, int start, int count, int after) {
135                 teardown_tunnel ();
136         }
137         
138         /*
139          * State management interface, called from Android OS.  Thee onXXX() conform to a state diagram.
140          * 
141          * @see http://developer.android.com/reference/android/app/Activity.html#SavingPersistentState
142          */
143         protected void onCreate (Bundle savedInstanceState) {
144                 super.onCreate (savedInstanceState);
145                 TunnelService tunsvc = TunnelService.theTunnelService ();
146                 if (tunsvc != null) {
147                         uplink = tunsvc.uplink;
148                 }
149                 try {
150                         uplink = new DatagramSocket (); //TODO// Remove port (to unfix it)
151                 } catch (IOException ioe) {
152                         uplink = null;
153                 }
154                 mktun_intent = VpnService.prepare (this);
155                 boolean have_tunnel = (TunnelService.theTunnelService () != null);
156                 interactive = (mktun_intent == null) || (tunsvc != null);
157                 //TODO:USE?// savedInstanceState.getBoolean ("persist_accross_reboots", false);
158                 //TODO:USE?// savedInstanceState.getBoolean ("overtake_default_route", true);
159                 //TODO:USE?// savedInstanceState.getString ("tunserver_ip", "145.136.0.1");
160                 //TODO:USE?// savedInstanceState.getInt ("tunclient_port", 0);
161                 TextView tv;
162                 Switch sv;
163                 setContentView (R.layout.user_interface);
164                 tv = (TextView) findViewById (R.id.tunserver_ip_string);
165                 tv.addTextChangedListener (this);
166                 tv = (TextView) findViewById (R.id.tunclient_port_number);
167                 tv.addTextChangedListener (this);
168                 sv = (Switch) findViewById (R.id.enable_6bed4);
169                 sv.setChecked (have_tunnel);
170                 sv = (Switch) findViewById (R.id.overtake_default_route);
171                 sv.setChecked (true);
172                 sv = (Switch) findViewById (R.id.persist_accross_reboots);
173                 sv.setChecked (false);
174         }
175         
176         protected void onStart () {
177                 super.onStart ();
178                 if (!interactive) {
179                         //
180                         // This is a new start of the VPN
181                         try {
182                                 //TODO// extract public server address from params 
183                                 publicserver = new InetSocketAddress (Inet4Address.getByName ("145.136.0.1"), 25788); /* TODO:FIXED */
184                         } catch (UnknownHostException uhe) {
185                                 publicserver = null;
186                         }
187                         try {
188                                 setup_tunnel ((Inet6Address) Inet6Address.getByName ("::1"));   /* TODO: Note here, not with a static IPv6 address */
189                         } catch (UnknownHostException uhe) {
190                                 interactive = true;
191                         }
192                 }
193         }
194         
195         protected void onResume () {
196                 super.onResume ();
197                 /* TODO: Check if the IPv6 address is still the same?  Also run after onStart! */
198         }
199
200         protected void onPause () {
201                 super.onPause ();
202                 /* TODO: Nothing to do I suppose... try to avoid loosing IPv6 address? */
203         }
204         
205         protected void onStop () {
206                 super.onStop ();
207         }
208
209         protected void onRestart () {
210                 super.onRestart ();
211         }
212         
213         protected void onDestroy () {
214                 super.onDestroy ();
215         }
216
217
218         /***                                                ***
219          ***   Internal affairs -- tunnel service setup.    ***
220          ***                                                ***/
221
222         public void setup_tunnel (Inet6Address addr6bed4) {
223                 //
224                 // Prepare the context for the VPN
225                 if (mktun_intent == null) {
226                         mktun_intent = VpnService.prepare (this);
227                 }
228                 //TODO:ALT// Intent mktun_intent = TunnelService.prepare (this);
229                 if (mktun_intent != null) {
230                         // This is a new start of the VPN
231                         this.startActivityForResult (mktun_intent, 0);
232                         mktun_intent = null;
233                 } else {
234                         // Already started, apparently OK, so proceed to startup
235                         this.onActivityResult (0, Activity.RESULT_OK, null);
236                 }
237         }
238                 
239         synchronized protected void onActivityResult (int reqcode, int resultcode, Intent data) {
240                 if (resultcode == Activity.RESULT_OK) {
241                         //
242                         // Cleanup any prior tunnel file descriptors
243                         teardown_tunnel ();
244                         //
245                         // Setup a new tunnel
246                         // TODO: Due to this statement, two tunnel interfaces get created;
247                         //       without it, none are created.  Not sure what to think
248                         //       of it... need to leave it like this for now.
249                         TunnelService downlink = new TunnelService (uplink, publicserver);
250                         if (downlink != null) {
251                                 downlink = new TunnelService (); /*TODO:HUH?*/
252                         }
253                         if (!interactive) {
254                                 finish ();
255                         }
256                 } else {
257                         //
258                         // Result is not OK -- tear down the tunnel
259                         teardown_tunnel ();
260                 }
261         }
262
263         private void teardown_tunnel () {
264                 TunnelService tunsvc = TunnelService.theTunnelService ();
265                 if (tunsvc != null) {
266                         tunsvc.teardown ();
267                         Switch enabler = (Switch) findViewById (R.id.enable_6bed4);
268                         enabler.setChecked (false);
269                 }
270         }
271         
272         /*
273         synchronized public void onRevoke () {
274                 if (downlink != null) {
275                         downlink.teardown ();
276                 }
277         }
278         */
279
280 }