/** * Created : Apr 30, 2012 * * @author pquiring */ import java.io.*; import java.util.*; import javaforce.*; public class VPNConnection extends Thread implements ShellProcessListener { public String name; //friendly name (not username) public String pack, id; public String host, user, domain, pass; public String caps, capsOpts, routes, routeOpts; public String domainsearch; private ShellProcess sp; private String fcaps[], fcapsOpts[], froutes[], frouteOpts[]; private boolean connected, failed, routingApplied, DNSApplied; private String local_ip, remote_ip, dns; private File passwdFile; private boolean contains(String array[], String value) { for(int a=0;a<array.length;a++) { if (array[a].equals(value)) return true; } return false; } private boolean startsWith(String array[], String value) { for(int a=0;a<array.length;a++) { if (array[a].startsWith(value)) return true; } return false; } private String get(String array[], String value) { for(int a=0;a<array.length;a++) { if (array[a].startsWith(value)) return array[a].substring(value.length()); } return null; } private String quote(String in) { return "\"" + in + "\""; } public void run() { passwdFile = new File("/etc/ppp/peers/vpn"+id); try { fcaps = caps.split(","); fcapsOpts = capsOpts.split(","); froutes = routes.split(";"); //split again with "," frouteOpts = routeOpts.split(","); FileOutputStream fos = new FileOutputStream(passwdFile); Runtime.getRuntime().exec(new String[] {"chmod","600",passwdFile.getAbsolutePath()}); //only root can read fos.write(("password " + pass).getBytes()); fos.close(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("pppd"); cmd.add("call"); cmd.add("vpn" + id); cmd.add("pty"); cmd.add("pptp " + host + " --nolaunchpppd --loglevel 0 --logstring jflinux-" + id); // cmd.add("ipparam"); // cmd.add("jflinux-" + id); cmd.add("nodetach"); cmd.add("lock"); cmd.add("usepeerdns"); cmd.add("noipdefault"); //get local IP from server cmd.add("nodefaultroute"); cmd.add("noauth"); cmd.add("user"); if (domain == null) domain = ""; cmd.add((domain.length() > 0 ? domain + "\\\\" : "") + user); if (contains(fcaps, "windows")) { //setup windows caps fcaps = new String[] {"chap", "mschap", "mschapv2"}; fcapsOpts = new String[] {"mppe-all", "mppe-stateful", "echo", "bsd", "deflate", "tcp"}; } if (!contains(fcaps, "eap")) { cmd.add("refuse-eap"); } if (!contains(fcaps, "pap")) { cmd.add("refuse-pap"); } if (!contains(fcaps, "chap")) { cmd.add("refuse-chap"); } if (!contains(fcaps, "mschap")) { cmd.add("refuse-mschap"); } if (!contains(fcaps, "mschapv2")) { cmd.add("refuse-mschap-v2"); } boolean mppe = false; if (contains(fcapsOpts, "mppe-all")) { cmd.add("require-mppe"); mppe = true; } if (contains(fcapsOpts, "mppe-40")) { cmd.add("require-mppe-40"); mppe = true; } if (contains(fcapsOpts, "mppe-128")) { cmd.add("require-mppe-128"); mppe = true; } if ((mppe) && (contains(fcapsOpts, "mppe-stateful"))) { cmd.add("mppe-stateful"); } if (contains(fcapsOpts, "echo")) { cmd.add("lcp-echo-failure"); cmd.add("0"); cmd.add("lcp-echo-interval"); cmd.add("0"); } if (!contains(fcapsOpts, "bsd")) { cmd.add("nobsdcomp"); } if (!contains(fcapsOpts, "deflate")) { cmd.add("nodeflate"); } if (!contains(fcapsOpts, "tcp")) { cmd.add("novj"); } sp = new ShellProcess(); sp.addListener(this); sp.keepOutput(false); //test //String tmp = ""; //for(int a=0;a<cmd.size();a++) tmp += cmd.get(a) + " "; //JFLog.log("Execute:" + tmp); //test sp.run(cmd.toArray(new String[0]), true); } catch (Exception e) { Server.This.jbusClient.call(pack, "vpnFailed", quote(id)); Server.This.pendingVPN = null; JFLog.log(e); } } public void close() { passwdFile.delete(); sp.destroy(); if (routingApplied) { delRouting(); } JF.sleep(500); //this fixes a bug where DNS fails to failback to lower interface //something else is writting to /etc/resolv.conf if (DNSApplied) { delDNS(); } } /* Sample output: failure: MS-CHAP authentication failed: E=691 Authentication failure CHAP authentication failed Connection terminated. success: CHAP authentication succeeded MPPE 128-bit stateless compression enabled local IP address 192.168.0.205 remote IP address 192.168.0.200 primary DNS address 192.168.0.3 */ public void shellProcessOutput(String string) { JFLog.log(string); //test if (failed) return; String lns[] = string.split("\n"); for(int a=0;a<lns.length;a++) { if (lns[a].indexOf("failed") != -1) { passwdFile.delete(); failed = true; Server.This.jbusClient.call(pack, "vpnFailed", quote(id)); Server.This.pendingVPN = null; } if (lns[a].indexOf("succeeded") != -1) { passwdFile.delete(); connected = true; Server.This.jbusClient.call(pack, "vpnSuccess", quote(id)); Server.This.pendingVPN = null; } if (lns[a].startsWith("local IP address ")) { local_ip = lns[a].substring(18); } if (lns[a].startsWith("remote IP address ")) { remote_ip = lns[a].substring(18); } if (lns[a].startsWith("primary DNS address ")) { dns = lns[a].substring(22); } if ((!routingApplied) && (local_ip != null) && (remote_ip != null) && (dns != null)) { applyRouting(); routingApplied = true; applyDNS(); DNSApplied = true; } } } private void applyRouting() { ShellProcess sp = new ShellProcess(); String output; try { if (startsWith(frouteOpts, "network=")) { String netmask = get(frouteOpts, "network="); output = sp.run(new String[] { "route", "add", "-net", Server.mask(remote_ip, netmask), "netmask", netmask, "gw", local_ip },true); if (sp.getErrorLevel() != 0) { JFLog.log("VPN:Route Add Failed:" + sp.command); JFLog.log(output); } } for(int a=0;a<froutes.length;a++) { String rf[] = froutes[a].split(","); if (rf.length != 4) continue; //address netmask gateway metric if (rf[0].length() == 0) continue; if (rf[1].length() == 0) continue; if (rf[2].length() == 0) continue; if (rf[3].length() == 0) rf[3] = "1"; output = sp.run(new String[] { "route", "add", "-net", rf[0], "netmask", rf[1], "gw", rf[2], "metric", rf[3] }, true); if (sp.getErrorLevel() != 0) { JFLog.log("VPN:Route Add Failed:" + sp.command); JFLog.log(output); } } } catch (Exception e) { JFLog.log(e); } } private void delRouting() { ShellProcess sp = new ShellProcess(); String output; try { if (startsWith(frouteOpts, "network=")) { String netmask = get(frouteOpts, "network="); output = sp.run(new String[] { "route", "del", Server.mask(remote_ip, netmask) }, true); if (sp.getErrorLevel() != 0) { JFLog.log("VPN:Route Delete Failed:" + sp.command); JFLog.log(output); } } for(int a=0;a<froutes.length;a++) { String rf[] = froutes[a].split(","); if (rf.length != 4) continue; //address netmask gateway metric if (rf[0].length() == 0) continue; if (rf[1].length() == 0) continue; if (rf[2].length() == 0) continue; if (rf[3].length() == 0) rf[3] = "1"; output = sp.run(new String[] { "route", "del", rf[0] }, true); if (sp.getErrorLevel() != 0) { JFLog.log("VPN:Route Delete Failed:" + sp.command); JFLog.log(output); } } } catch (Exception e) { JFLog.log(e); } } Interface iface = new Interface(); private void applyDNS() { iface.dev = "vpn" + Server.This.vpnConnections.size(); if (domainsearch.length() > 0) { if (!domainsearch.endsWith(".")) domainsearch += "."; } else { domainsearch = "localdomain."; } iface.domain_name = domainsearch; iface.domain_name_servers = dns; Server.This.doDNS(iface); } private void delDNS() { Server.This.undoDNS(iface, true); } }