UNPKG

hostapd_switch

Version:

Utility to easy switch between client mode and ap mode.

340 lines (270 loc) 9.6 kB
import * as Promise from "bluebird"; import * as pathExists from "path-exists"; import * as dnsmasqconf from "dnsmasq-conf"; import merge from "json-add"; import testinternet from 'promise-test-connection'; import wpamanager from 'wpasupplicant-manager'; import listwificlients from "listwificlients" import netw from "netw"; const verb = require('verbo'); const exec = require('promised-exec'); import hostapdconf from "hostapdjs"; interface IScan { essid: string; mac: string; signal: string; } type INetworkType = 'wifi' | 'wired' interface INetwork { type: INetworkType; mac: string; interface: string; essid?: string; scan?: IScan[]; ip?: string; gateway?: string; } type Iwifimode = 'ap' | 'host' | 'client' | 'unmanaged' interface IWifiClient { mac: string; signal: string; signalMin?: string; signalMax?: string; } function testconn(d: string, testint?: boolean) { return new Promise<boolean>(function (resolve, reject) { netw().then(function (n) { let dev: any = false; let ip: any = false; for (let ns = 0; ns < n.length; ns++) { if (n[ns].interface == d) { dev = d; if (n[ns].ip) { ip = n[ns].ip } // if (n.networks[ns].gateway) { // gw = n.networks[ns].gateway // } } } if (!dev) { reject('no interface'); } else if (!ip) { reject(dev + ' can\'t get an ip address'); // } else if (!gw) { // reject(dev + ' has no gateway') } else { if (testint) { testinternet().then(function () { resolve(true); }).catch(function (err) { reject(err); }) } else { console.log("no internet test"); resolve(true); } } }).catch(function (err) { reject('netw' + err); }) }) } interface IHostapd { interface: string; ssid: string; wpa_passphrase: any; driver?: string; }; interface IHostapdCf { driver?: string; ssid?: string; wpa_passphrase?: string; interface?: string; }; interface IDnsmasq { interface: string; }; interface IDnsmasqCf { interface?: string; }; interface IClassOpt { interface?: string; wpasupplicant_path?: string; hostapd?: IHostapdCf; redirect?: boolean; dnsmasq?: IDnsmasqCf; }; interface IClassConf { interface: string; wpasupplicant_path: string; hostapd: IHostapd; dnsmasq: IDnsmasq; init: boolean; redirect: boolean; }; interface IDnsModes { ap: IDnsMode; link: IDnsMode; host: IDnsMode }; interface IDnsMode { noresolv: boolean, dns: [string], dhcp: { stop: number; start: number; lease: string; }; hostIp: string, test: boolean, interface: any, address?: string } interface IDns { modes: IDnsModes; mode?: string; path: string; interface: any; test: boolean; dhcp: { stop: number; start: number; lease: string; }; dns: [string]; hostIp: string; ap: Function; host: Function; link: Function; setmode(string); } export default class HostapdSwitch extends wpamanager { config: IClassConf; dnsmasq: IDns; wifimode: Iwifimode; constructor(options: IClassOpt, init?: boolean) { const config: IClassConf = { interface: "wlan0", wpasupplicant_path: "/etc/wpa_supplicant/wpa_supplicant.conf", redirect: true, hostapd: { interface: "wlan0", wpa_passphrase: false, ssid: "hapd111" }, dnsmasq: { interface: "wlan0" }, init: false }; merge(config, options) if (config.interface !== 'auto' && (!options || !options.hostapd || !options.hostapd.interface)) { config.hostapd.interface = config.interface } if (config.interface !== 'auto' && (!options || !options.dnsmasq || !options.dnsmasq.interface)) { config.dnsmasq.interface = config.interface } if (!pathExists.sync('/etc/default/hostapd')) { throw Error('no default conf file was founded for hostapd') } if (!config.hostapd.ssid) { throw Error('No ssid was provided') } if (!config.hostapd.wpa_passphrase) { throw Error('No wpa_passphrase was provided') } super(config.wpasupplicant_path) this.config = config; this.dnsmasq = new dnsmasqconf(config.dnsmasq); if (init) { hostapdconf(config.hostapd).then(function () { console.log('hostapd is now configured') }); }; }; host(e?: any) { const that = this let dnsmasq = this.dnsmasq; let hostIp = dnsmasq.hostIp; let cmd = 'ifconfig ' + this.config.interface + ' down && sleep 2 ; pkill wpa_supplicant && systemctl restart hostapd ; systemctl restart dnsmasq && ifconfig ' + this.config.interface + ' ' + hostIp + ' netmask 255.255.255.0 up && sleep 5'; return new Promise<boolean>(function (resolve, reject) { dnsmasq.host().then(function () { exec(cmd).then(function () { exec('iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination ' + hostIp + ':80 && iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination ' + hostIp + ':80').then(function () { that.wifimode = "host"; resolve(true) }).catch(function (err) { verb(err, 'error', 'hostapd_switch ipfilter host switch') }) }).catch(function (err) { verb(err, 'error', 'hostapd_switch executing host switch') }) }).catch(function (err) { verb(err, 'error', 'hostapd_switch executing dnsmasq host switch') }) }) }; ap(e?: any) { const that = this let dnsmasq = this.dnsmasq; let hostIp = dnsmasq.hostIp; let cmd = 'ifconfig ' + this.config.interface + ' down && sleep 2 ; pkill wpa_supplicant && systemctl restart hostapd ; systemctl restart dnsmasq && ifconfig ' + this.config.interface + ' ' + hostIp + ' netmask 255.255.255.0 up && for i in $( iptables -t nat --line-numbers -L | grep ^[0-9] | awk \'{ print $1 }\' | tac ); do iptables -t nat -D PREROUTING $i; done' return new Promise<boolean>(function (resolve, reject) { dnsmasq.ap().then(function () { exec(cmd).then(function () { that.wifimode = "ap"; resolve(true) }).catch(function (err) { verb(err, 'error', 'hostapd_switch executing ap switch') }) }).catch(function (err) { verb(err, 'error', 'hostapd_switch executing dnsmasq before ap switch') }) }) }; client(testnetw?: boolean, testint?: boolean) { const that = this const dev = this.config.interface; let driver: string; if (this.config.hostapd.driver === 'nl80211') { driver = 'nl80211'; } else { driver = 'wext'; } let cmd = 'ifconfig ' + dev + ' down && sleep 2 ; pkill wpa_supplicant ; dhclient -r ' + dev + ' ; systemctl stop hostapd ; systemctl stop dnsmasq ; sleep 2; ifconfig ' + dev + ' up && wpa_supplicant -B -i ' + dev + ' -c ' + this.config.wpasupplicant_path + ' -D ' + driver + ' && dhclient ' + dev + ' && for i in $( iptables -t nat --line-numbers -L | grep ^[0-9] | awk \'{ print $1 }\' | tac ); do iptables -t nat -D PREROUTING $i; done; sleep 10'; return new Promise<boolean>(function (resolve, reject) { exec(cmd).then(function () { that.wifimode = "client"; if (testnetw) { testconn(dev, testint).then(function (answer) { resolve(answer) }).catch(function (err) { reject(err) }) } else { resolve(true) } }).catch(function (err) { verb(err, 'warn', 'hostapd_switch exec') if (testnetw) { testconn(dev, testint).then(function (answer) { resolve(answer) }).catch(function (err) { reject(err) }) } else { resolve(true) } }) }) }; listwificlients(): Promise<IWifiClient[]> { const that = this return new Promise<IWifiClient[]>(function (resolve, reject) { if (that.wifimode === 'host' || that.wifimode === 'ap') { listwificlients(that.config.interface).then((a) => { resolve(a) }).catch((err) => { reject(err) }) } else { reject('wifimode is ' + that.wifimode) } }) } }