Skip to content

Instantly share code, notes, and snippets.

@1060460048
Forked from msmuenchen/kdg-bot.php
Created December 14, 2016 07:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1060460048/5a40f95ce4eb31ee7d5c942ef006a896 to your computer and use it in GitHub Desktop.
Save 1060460048/5a40f95ce4eb31ee7d5c942ef006a896 to your computer and use it in GitHub Desktop.
Automatic login bot for Kabel Deutschland 30-Min Free Wifi Hotspot
<?php
/*
Kabel Deutschland 30-Min Free Wifi Hotspot Auto-Login bot for Linux and Windows
According to the ToC, there is nothing which forbids the usage of this tool, but you have to accept and act by them.
See https://www.hotspot.kabeldeutschland.de/portal/nutzungsbedingungen.html?profile=2 for details.
Windows Install:
1) Install PHP
1.1) Grab and unpack http://windows.php.net/downloads/releases/php-5.5.3-nts-Win32-VC11-x86.zip
1.2) If you did not install the VS2012 CPP runtimes, grab and install www.microsoft.com/de-de/download/details.aspx?id=30679
2) Copy this script to the directory where you unpacked php, so that php.exe and kdg-bot.php are in the same directory
3) Rename php.ini-development to php.ini
4) Uncomment the following lines in php.ini:
4.1) extension_dir = "ext"
4.2) extension=php_curl.dll
4.3) extension=php_openssl.dll
Linux install:
0) Doesn't work on Linux for now...
1) Using the package manager of your OS, install the latest php-cli package (it's called php5-cli on Debian/Ubuntu, no idea for other *nix)
2) Install the php-curl and php-openssl extensions and enable them
Usage:
1) Run "php kdg-bot.php" in the shell as soon as you have connected to one of the Kabel Deutschland hotspots
2) Keep it open until you're done surfing, terminate with Ctrl-C
*/
error_reporting(E_ALL);
echo "kdg wlan auto-relogin\n\n";
class conio {
public static function beep($a = 3) {
for ($i = 0; $i < $a; $i++)
echo chr(7);
}
//printf with timestamp
public static function pft() {
$a = func_get_args();
echo date("d.m.Y H:i:s") . " " . call_user_func_array("sprintf", $a) . "\n";
}
public static function exec($cmd) {
$ret = array();
$rc = exec($cmd, $ret);
if ($rc != 0) {
conio::beep(5);
printf("command %s returned rc %d:\n%s", $cmd, $rc, print_r($rc, true));
}
return $ret;
}
}
interface OSNetworkInterface {
public function getConnectedNetwork();
public function getAvailableNetworks();
public function connectToNet($ssid, $mac);
}
class netsh implements OSNetworkInterface {
public function getConnectedNetwork() {
$ret = array(
"mac" => "",
"ssid" => ""
);
$c = conio::exec("netsh wlan show interfaces");
foreach ($c as $l) {
$l = trim($l);
if ($l == "")
continue;
list($k, $v) = explode(":", $l, 2);
$k = trim($k);
$v = trim($v);
if ($k == "SSID")
$ret["ssid"] = $v;
else if ($k == "BSSID")
$ret["mac"] = $v;
}
return $ret;
}
public function getAvailableNetworks() {
$ret = array();
$c = conio::exec("netsh wlan show networks mode=bssid");
$mode = 0; //1: netsh preamble, 2: expect network info
$currentNet = array();
foreach ($c as $l) {
$l = trim($l);
if ($l == "") {
if ($mode == 0) {
$mode = 1;
} else if ($mode == 1) {
$mode = 2;
} else if ($mode == 2) { //start new network
$ret[] = $currentNet;
$currentNet = array(
"ssid" => "",
"mac" => array()
);
}
continue;
}
if ($mode != 2)
continue;
list($k, $v) = explode(":", $l, 2);
$k = trim($k);
$v = trim($v);
if (substr($k, 0, 4) == "SSID")
$currentNet["ssid"] = $v;
else if (substr($k, 0, 6) == "BSSIDD")
$currentNet["mac"][] = $v;
}
return $ret;
}
public function connectToNet($ssid, $mac) {
$c = conio::exec("netsh wlan connect name=\"" . $ssid . "\"");
}
public function disconnect() {
$c = conio::exec("netsh wlan disconnect");
}
}
interface WifiProvider {
//is this class able to establish a connection with this AP?
public function isApplicable($networkName, $macAddr);
//are we logged in into this AP?
public function getState();
//login at the AP
public function connect();
}
class Provider_KDG30MinFree implements WifiProvider {
private $apibase = "https://www.hotspot.kabeldeutschland.de/api/v4";
private $sessionObj = null;
public function isApplicable($networkName, $macAddr) {
if ($networkName == "30 Min Free WIFI" || $networkName == "KD WLAN Hotspot+")
return true;
return false;
}
public function getState() {
$ret["res"] = false;
$ret["time"] = 0;
$ret["noconnect"] = false;
$this->sessionObj = null;
try {
conio::pft("getting %s", "{$this->apibase}/session");
$content = CURL::get("{$this->apibase}/session");
//conio::pft("got session object:\n%s",$content);
$obj = json_decode($content);
if ($obj == false)
throw new Exception("could not decode JSON");
if (!property_exists($obj, "currentLoginProfile"))
throw new Exception("session object did not include currentLoginProfile");
$profileIdx = $obj->currentLoginProfile;
if (!property_exists($obj, "loginProfiles"))
throw new Exception("session object did not include loginProfiles");
if (!property_exists($obj, "session"))
throw new Exception("session object did not include session key");
$this->sessionObj = $obj;
if ($profileIdx === null) //we are not connected at all
return $ret;
if (!property_exists($obj->loginProfiles, $profileIdx))
throw new Exception("session object did not include loginProfiles->$profileIdx");
$profile = $obj->loginProfiles->$profileIdx;
if (!property_exists($profile, "time") || !property_exists($profile->time, "left") || !property_exists($profile->time->left, "daily"))
throw new Exception("session profile did not include left time");
$ret["time"] = $profile->time->left->daily;
$ret["res"] = true;
}
catch (Exception $e) {
conio::pft("got exception %s: '%s'", get_class($e), $e->getMessage());
conio::beep(2);
$ret["noconnect"] = true;
return $ret;
}
return $ret;
}
public function connect() {
try {
if ($this->sessionObj == null)
throw new Exception("session object is null, cannot connect");
//find the key of the 30_min_relogin profile
$profiles = get_object_vars($this->sessionObj->loginProfiles);
$found = -1;
foreach ($profiles as $k => $p) {
if ($p->displayName == "30_min_relogin") {
$found = $k;
}
}
if ($found == -1) {
throw new Exception("session profiles do not include 30_min_relogin, cannot connect for free");
}
$profile = $profiles[$found];
$sk = $this->sessionObj->session;
conio::pft("SK is %s, profile key is %d, accessType is %s", $sk, $found, $profile->accessType);
$ret = CURL::post("{$this->apibase}/login", array(
"loginProfile" => $found,
"session" => $sk,
"accessType" => $profile->accessType
));
$obj = json_decode($ret);
if ($obj === false)
throw new Exception("JSON decode failed");
if (!property_exists($obj, "success"))
throw new Exception("login object did not include success");
if (!property_exists($obj, "errorCode"))
throw new Exception("login object did not include errorCode");
if ($obj->success !== true || $obj->errorCode != 0)
throw new Exception("login object returned error %d", $obj->errorCode);
conio::pft("login went OK");
return true;
}
catch (Exception $e) {
conio::pft("got exception %s: '%s'", get_class($e), $e->getMessage());
conio::beep(2);
return false;
}
}
}
//cURL exception
class CURLException extends Exception {
function __construct($msg, $c) {
$msg .= ": " . curl_error($c);
parent::__construct($msg);
}
}
//cURL server exception
class CURLDownloadException extends Exception {
public $rc;
function __construct($rc) {
$this->rc = $rc;
parent::__construct("HTTP return code: $rc");
}
}
class CURL {
private $c;
public function __construct() {
$this->c = curl_init();
curl_setopt($this->c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->c, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($this->c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($this->c, CURLOPT_SSL_VERIFYHOST, 0); //2?
curl_setopt($this->c, CURLOPT_USERAGENT, "Public WiFi Login Bot");
}
public static function get($url) {
$inst = new static();
$r = curl_setopt($inst->c, CURLOPT_HTTPGET, true);
if ($r === false)
throw new CURLException("curl_setopt(HTTPGET) failed", $inst->c);
$r = curl_setopt($inst->c, CURLOPT_URL, $url);
if ($r === false)
throw new CURLException("curl_setopt(URL) failed", $inst->c);
$r = curl_setopt($inst->c, CURLOPT_HEADER, false);
if ($r === false)
throw new CURLException("curl_setopt(HEADER) failed", $inst->c);
$ret = curl_exec($inst->c);
if ($ret === false)
throw new CURLException("curl_exec failed", $inst->c);
$rc = curl_getinfo($inst->c, CURLINFO_HTTP_CODE);
if ($rc === false)
throw new CURLException("curl_getinfo(HTTPCODE) failed", $inst->c);
if ($rc != 200)
throw new CURLDownloadException($rc);
curl_close($inst->c);
return $ret;
}
public static function post($url, $fields) {
$inst = new static();
$fields_string = "";
foreach ($fields as $key => $value) {
$fields_string .= $key . '=' . urlencode($value) . '&';
}
$fields_string = rtrim($fields_string, '&');
$r = curl_setopt($inst->c, CURLOPT_URL, $url);
if ($r === false)
throw new CURLException("curl_setopt(URL) failed", $inst->c);
$r = curl_setopt($inst->c, CURLOPT_POST, count($fields));
if ($r === false)
throw new CURLException("curl_setopt(POST) failed", $inst->c);
$r = curl_setopt($inst->c, CURLOPT_POSTFIELDS, $fields_string);
if ($r === false)
throw new CURLException("curl_setopt(POSTFIELDS) failed", $inst->c);
$ret = curl_exec($inst->c);
if ($ret === false)
throw new CURLException("curl_exec failed", $inst->c);
$rc = curl_getinfo($inst->c, CURLINFO_HTTP_CODE);
if ($rc === false)
throw new CURLException("curl_getinfo(HTTPCODE) failed", $inst->c);
if ($rc != 200)
throw new CURLDownloadException($rc);
curl_close($inst->c);
return $ret;
}
}
$providers = array(
new Provider_KDG30MinFree()
);
$netsh = new netsh();
$failCount = 0;
while (true) {
if ($failCount > 3) {
conio::pft("fail count %d, disconnecting from possibly dead wifi", $failCount);
$failCount = 0;
$netsh->disconnect();
sleep(20);
}
conio::pft("checking state");
$net = $netsh->getConnectedNetwork();
if ($net["ssid"] == "") {
conio::pft("not connected");
//check if we have any provider applicable for one of the WiFis
$nwl = $netsh->getAvailableNetworks();
$found = array();
foreach ($nwl as $net) {
foreach ($providers as $p) {
foreach ($net["mac"] as $mac) {
if (!$p->isApplicable($net["ssid"], $mac))
continue;
$found[] = array(
"ssid" => $net["ssid"],
"mac" => $mac,
"provider" => $p
);
}
}
}
if (sizeof($found) > 0) {
foreach ($found as $net) {
conio::pft("associating with WLAN '%s' (MAC %s)", $net["ssid"], $net["mac"]);
$netsh->connectToNet($net["ssid"], $net["mac"]);
sleep(30); //allow for some time for DHCP
$state = $p->getState();
if ($state["noconnect"] == true) {
conio::pft("can't connect, disconnecting and trying the next one");
$netsh->disconnect();
continue;
}
if ($state["res"] == true) {
conio::pft("somehow the script still thinks it's logged in...");
break;
}
conio::pft("executing provider connect");
$p->connect();
}
}
sleep(30);
continue;
}
conio::pft("AP associated: '%s' (MAC %s)", $net["ssid"], $net["mac"]);
$found = false;
foreach ($providers as $p) {
conio::pft("Checking if %s is applicable", get_class($p));
if (!$p->isApplicable($net["ssid"], $net["mac"]))
continue;
$found = true;
break;
}
if (!$found) {
conio::pft("did not find a provider for AP '%s'", $net["ssid"]);
sleep(30);
continue;
}
conio::pft("found provider %s", get_class($p));
$state = $p->getState();
if ($state["res"] == true) {
//cap re-check at 60 seconds
if ($state["time"] > 60)
$st = 50;
else
$st = $state["time"] - 10;
//wait min. 10 seconds before reconnect!
if ($st < 10)
$st = 10;
//under 60 seconds remaining? beep to alert the user
if ($state["time"] < 60)
conio::beep(3);
conio::pft("still connected for %d seconds, re-check in %d seconds", $state["time"], $st);
$failCount = 0;
sleep($st);
continue;
} else {
if ($state["noconnect"] == true) {
conio::pft("could not get state, will not try to connect!");
$failCount++;
sleep(30);
continue;
}
conio::pft("(re)connecting");
$res = $p->connect();
if ($res === false) {
conio::pft("connect failed");
$failCount++;
sleep(10);
continue;
}
conio::pft("connect OK");
}
sleep(60);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment