Advertisement
malsbury

recM1_IP.pl

Jan 10th, 2016
340
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 8.87 KB | None | 0 0
  1. #!/usr/bin/perl
  2. # This script was originally for Slingbox 350/500 models
  3. # Modified to work with remote M1 with non-static IP
  4. # Adapted "Grab Slingbox" function from Solo/Pro HD script to work with M1
  5. # Modified "Sleep" function so it pauses after channel change instead of the beginning of the script
  6. # 20160110-0945
  7.  
  8. use IO::Select;
  9. use IO::Socket::INET;
  10. use Crypt::Tea_JS;
  11. use Getopt::Long;
  12. use LWP::Simple;
  13. use JSON qw( decode_json );
  14.  
  15. BEGIN { $| = 1 } # Flush stdout right away
  16.  
  17. ### START  MAIN ###
  18. $finderId = 'FINDER_ID';       # change to your Slingbox finder ID
  19. $user = 'admin';               # set to 'admin' or 'guest' as desired
  20. $pass = 'PASSWORD';            # change this to your slingbox password
  21. $vbw = 3500;                   # video bandwidth, kbps
  22. $vsm = 32;                     # smoothness, 1 .. 64
  23. $vs = 12;                      # video size, 3 for QVGA, 4 for 640x240, 5 for VGA, 12 for 1280x544/720, 15 for 1920x544, 16 for 1920x1080
  24. $fr = 30;                      # frame rate, fps, must be 10, 15, 20, 30 or 60
  25. $sleep = 5;                    # sleep time in seconds before starting capture
  26. $chan = '';                    # tune to a channel before capture
  27. $dur = 0;                      # duration in seconds you want to capture
  28. $output = "recM1.asf";         # output file (use -stdout option for stdout)
  29. $stdout = 0;
  30.  
  31. # Parse command line arguments
  32. my $ok = GetOptions(
  33.    "chan=s"   => sub {$chan = $_[1];},
  34.    "dur=s"    => sub {$dur = $_[1];},
  35.    "fr=s"     => sub {$fr = $_[1];},
  36.    "output=s" => sub {$output = $_[1];},
  37.    "pass=s"   => sub {$pass = $_[1];},
  38.    "h"        => sub {&useage(); exit(0);},
  39.    "sleep=s"  => sub {$sleep = $_[1];},
  40.    "stdout"   => sub {$stdout = 1;},
  41.    "user=s"   => sub {$user = $_[1];},
  42.    "vbw=s"    => sub {$vbw = $_[1];},
  43.    "vsm=s"    => sub {$vsm = $_[1];},
  44.    "vs=s"     => sub {$vs = $_[1];},
  45. );
  46. if (! $ok) {
  47.    &useage();
  48.    exit(1);
  49. }
  50.  
  51. #use Finder ID to detected current remote IP and port
  52. $json = get("http://newwatch.slingbox.com/watch/slingAccounts/get_slingbox_sparcs_info/?finderId=$finderId");
  53. (@split_json) = split("=", $json, 2);
  54. $decoded = decode_json($split_json[1]);
  55. $slingip = $decoded->{'deviceAddress'} . ":" . $decoded->{'devicePort'};
  56. print STDERR "Slingbox Located: " .$slingip."\n";
  57. #end of IP/Port search
  58.  
  59. START:
  60. $skey = pack("H*", 'AAAADEBCBABBFB87FACFCC7CBCAADDDD'); # start with fixed key,
  61. $smode = 0x2000;                # basic cipher mode,
  62. $sid = $seq = 0;                # no session ID, seq 0
  63. $s_ctrl = sling_open("Control"); # open control connection to SB
  64. $sel = new IO::Select($s_ctrl);  # to test
  65. sling_cmd(0x67, pack("V a32 a32 x132", 0, futf16($user), futf16($pass))); # log in to SB
  66. $rand = pack("H*", 'feedfacedeadbeef1111222233334444'); # 'random' challenge
  67. sling_cmd(0xc6, pack("V a16 x36", 1, $rand)); # setup dynamic key on SB
  68. $skey = dynk($rand, $sid, 2, 3) ^ dynk(substr($dbuf, 0, 16), $sid, -1, -4);
  69. $smode = 0x8000;                # use dynamic key from now on
  70. sling_cmd(0x7e, pack("V V", 1, 0)); # stream control
  71. if ($stat) {                        # box in use
  72.     print STDERR "Another authorized user is viewing this Slingbox.\n";
  73.     print STDERR "Taking control of Slingbox.\n";
  74.     sling_cmd(0x93, pack("a32 a32 x8", futf16("admin"), futf16($pass))); # grab SB
  75.     sling_cmd(0x66, '');                # wait for grab?
  76.     sling_cmd(0x6a, pack("V x172", 1)); # unk fn
  77.     sling_cmd(0x7e, pack("V V", 1, 0)); # stream control
  78.     goto START if $stat;
  79. }
  80. die "Could not grab box." if $stat;
  81. sling_cmd(0xa6, pack("v10 x76", 0x1cf, 0, 0x40, 0x10, 0x5000, 0x180, 1, 0x1e,
  82.                       0, 0x3d));
  83. if ($chan) {         # want to tune to a channel
  84.     $ircmds = '';
  85.     for $chdigit (split(//, sprintf("%04d", $chan))) {
  86.         $ircmds .= pack("v2", $chdigit ? $chdigit + 8 : 18, 500);
  87.     }
  88.     sling_cmd(0x87, $ircmds . pack("x456 v4", 3, 0, 0, 0));
  89. }
  90. sleep($sleep) if $sleep; #allow short pause before stream to allow channel change if needed
  91. sling_cmd(0xb5, pack("V11 a16 V2 x92", 0xff, 0xff, $vs, 1,
  92.                      0x05000000 + ($fr << 16) + $vbw, 0x10001 + ($vsm << 8), 3, 1,
  93.                      0x40, 0x4f, 1, $rand, 0x1012020, 1)); # set stream params
  94. $s_stream = sling_open("Stream"); # open stream connection to SB
  95. $s_stream->sockopt(SO_RCVBUF, 524288) or die "Sockopt: $!\n";
  96.  
  97. $pktsiz = 3072;         # SB 350 always? uses 3072 byte packets
  98. read($s_stream, $ibuf, $pktsiz) == $pktsiz or die; # read Header Object and data header
  99. ($pos = index($ibuf, pack("H*", "3626b2758e66cf11a6d900aa0062ce6c"))) > 0 or die; # find data hdr
  100. $pos += 50;                     # end of data hdr
  101. print STDERR "Stream started\n";
  102. my $out;
  103. if ($stdout) {
  104.    $out = *STDOUT
  105. } else {
  106.    open($out, ">$output") or die "failed to write to output file: $output\n";
  107. }
  108. binmode($out);
  109. print $out substr($ibuf, 0, $pos); # write headers to file
  110. read($s_stream, $tbuf, $pos);     # get rest of 1st data packet
  111. $ibuf = substr($ibuf, $pos) . $tbuf; # assemble 1st data packet
  112. # now, read data packets from SB, decrypt as needed, write to output file
  113. $| = 1;
  114. $pktn = $runt = 0; # no packets, runtime
  115. while (1) {
  116.     print $out $ibuf;    # output the whole buffer
  117.     ($pmode, $pad, $pktt, $pad) = unpack("N x V2 x2 C", $ibuf);
  118.     last if ($pmode & ~1) != 0x82000018; # unexpected or corrupted data
  119.     if (++$pktn % 1000 == 0) {  # every 1000 packets
  120.         $runt = int($pktt / 1000); # convert ms to seconds
  121.         if ($sel->can_read(0)) {   # control socket has data
  122.             last unless sysread($s_ctrl, $hbuf, 999); # read but ignore replies
  123.         }
  124.         sling_cmd(0x66, ''); # send a keepalive
  125.         printf(STDERR "\r%02d:%02d:%02d pkts:%d", int($runt / 3600), int($runt / 60) % 60, $runt % 60, $pktn); # show stats
  126.         last if $dur && $runt > $dur;
  127.     }
  128.     last unless read($s_stream, $ibuf, $pktsiz) == $pktsiz; # get next packet from stream
  129. }
  130. ### END MAIN ###
  131.  
  132. sub tea {                       # en(de)crypt buf, calling Tea_JS for each 8-byte block
  133.     my ($buf, $key, $enc) = @_;
  134.     my @keys = unpack("V*", $key);
  135.     my @longs = unpack("V*", $buf);
  136.     my $sub = $enc ? \&Crypt::Tea_JS::oldtea_code : \&Crypt::Tea_JS::oldtea_decode;
  137.     for (my $j = 0; $j < @longs; $j += 2) {
  138.         ($longs[$j], $longs[$j+1]) = &$sub($longs[$j], $longs[$j+1], @keys);
  139.     }
  140.     return pack("V*", @longs);
  141. }
  142.  
  143. sub dynk {                      # hash function for dynamic key
  144.     my ($t, $s, $v, $r) = (unpack("b*", $_[0]), unpack("b*", pack("v",$_[1])));
  145.     $v^=$t=substr($t,$r=$_*$_[($_[1]>>$_-1&1)+2]).substr($t,0,$r)^$s for (1..16);
  146.     pack("b*", $v);
  147. }
  148.  
  149. sub sling_cmd {                 # send message to SB, check response
  150.     my ($opcode, $data) = @_;
  151.     my $parity = 0;
  152.     if ($smode == 0x8000) {     # using a dynamic key
  153.         $parity ^= $_ for (unpack("C*", $data)); # compute integrity check
  154.     }
  155.     print $s_ctrl pack("v5 x6 v2 x4 v x6", 0x101, $sid, $opcode, 0, ++$seq, length($data),
  156.                        $smode, $parity) . tea($data, $skey, 1); # encrypt body
  157.     return if $opcode == 0x66;                                  # don't wait for keepalive result
  158.     sysread($s_ctrl, $hbuf, 32) == 32 or die "missing or bad response header";
  159.     ($sid, $stat, $dlen) = unpack("x2 v x8 v x2 v", $hbuf);
  160.     die "cmd: $opcode err: $stat $dlen" if $stat && $stat != 0x0d && $stat != 0x13; # error code from SB
  161.     if ($dlen) {
  162.         sysread($s_ctrl, $dbuf, 512) == $dlen or die "missing or bad response data";
  163.         $dbuf = tea($dbuf, $skey, 0); # decrypt body
  164.     }
  165. }
  166.  
  167. sub sling_open {                # open a connection to SB, send HTTP header
  168.     my $sock = IO::Socket::INET->new($slingip) or die $!; # open socket
  169.     print $sock "GET /stream.asf HTTP/1.1\r\nAccept: */*\r\n" # send HTTP header
  170.         . "Pragma: Sling-Connection-Type=$_[0], Session-Id=$sid\r\n\r\n";
  171.     return $sock;
  172. }
  173.  
  174. sub futf16 { pack("v*", unpack("C*", $_[0])) } # fake UTF-16
  175.  
  176. sub useage {
  177.    print "$0 [options]\n";
  178.    print "OPTIONS:\n";
  179.    print "-h                    print program useage/options\n";
  180.    print "-output file          output to named file (defaults to rec350.asf)\n";
  181.    print "-stdout               output to stdout instead of a file\n";
  182.    print "-user name            slingbox username to use (admin or guest)\n";
  183.    print "-pass password        slingbox password\n";
  184.    print "-vbw rate             video bit rate (defaults to 3500)\n";
  185.    print "-vsm smoothness       video smoothness (1:64, defaults to 32)\n";
  186.    print "-vs size              video resolution(3=QVGA,4=640x240,5=640x480,16=1920x1080)\n";
  187.    print "-fr fps               video frame rate (10,15,20,30,60, defaults to 30 fps)\n";
  188.    print "-sleep secs           wait this many seconds before starting capture(defaults to 5)\n";
  189.    print "-dur secs             capture only this many seconds (defaults to unlimited)\n";
  190.    print "-chan channelNum      tune to this channel before capturing\n";
  191. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement