[rtmpdump] r122 - in trunk: Makefile rtmpdump.c streams.c swfvfy.c

hyc subversion at mplayerhq.hu
Tue Dec 29 00:08:07 CET 2009


Author: hyc
Date: Tue Dec 29 00:08:06 2009
New Revision: 122

Log:
Automate SWF verification, use -W instead of -s

Added:
   trunk/swfvfy.c
Modified:
   trunk/Makefile
   trunk/rtmpdump.c
   trunk/streams.c

Modified: trunk/Makefile
==============================================================================
--- trunk/Makefile	Mon Dec 28 09:59:15 2009	(r121)
+++ trunk/Makefile	Tue Dec 29 00:08:06 2009	(r122)
@@ -4,7 +4,7 @@ LD=$(CROSS_COMPILE)ld
 OPT=-O2
 CFLAGS=-Wall $(XCFLAGS) $(INC) $(OPT)
 LDFLAGS=-Wall $(XLDFLAGS)
-LIBS=-lcrypto
+LIBS=-lcrypto -lcurl -lz
 THREADLIB=-lpthread
 SLIBS=$(THREADLIB) $(LIBS)
 
@@ -26,7 +26,7 @@ osx:
 	@$(MAKE) XCFLAGS="-arch ppc -arch i386" $(MAKEFLAGS) progs
 
 mingw:
-	@$(MAKE) CROSS_COMPILE=mingw32- LIBS="-lws2_32 -lwinmm -lcrypto -lgdi32" THREADLIB= EXT=.exe $(MAKEFLAGS) progs
+	@$(MAKE) CROSS_COMPILE=mingw32- LIBS="-lws2_32 -lwinmm -lgdi32 $(LIBS)" THREADLIB= EXT=.exe $(MAKEFLAGS) progs
 
 cygwin:
 	@$(MAKE) XCFLAGS=-static XLDFLAGS="-static-libgcc -static" EXT=.exe $(MAKEFLAGS) progs
@@ -37,10 +37,10 @@ arm:
 clean:
 	rm -f *.o rtmpdump$(EXT) streams$(EXT)
 
-streams: log.o rtmp.o amf.o streams.o parseurl.o
+streams: log.o rtmp.o amf.o streams.o parseurl.o swfvfy.o
 	$(CC) $(LDFLAGS) $^ -o $@$(EXT) $(SLIBS)
 
-rtmpdump: log.o rtmp.o amf.o rtmpdump.o parseurl.o
+rtmpdump: log.o rtmp.o amf.o rtmpdump.o parseurl.o swfvfy.o
 	$(CC) $(LDFLAGS) $^ -o $@$(EXT) $(LIBS)
 
 rtmpsrv: log.o rtmp.o amf.o rtmpsrv.o
@@ -48,8 +48,9 @@ rtmpsrv: log.o rtmp.o amf.o rtmpsrv.o
 
 log.o: log.c log.h Makefile
 parseurl.o: parseurl.c parseurl.h log.h Makefile
-streams.o: streams.c rtmp.h log.h Makefile
+streams.o: streams.c rtmp.h log.h swfvfy.o Makefile
 rtmp.o: rtmp.c rtmp.h handshake.h dh.h log.h amf.h Makefile
 amf.o: amf.c amf.h bytes.h log.h Makefile
 rtmpdump.o: rtmpdump.c rtmp.h log.h amf.h Makefile
 rtmpsrv.o: rtmpsrv.c rtmp.h log.h amf.h Makefile
+swfvfy.o: swfvfy.c

Modified: trunk/rtmpdump.c
==============================================================================
--- trunk/rtmpdump.c	Mon Dec 28 09:59:15 2009	(r121)
+++ trunk/rtmpdump.c	Tue Dec 29 00:08:06 2009	(r122)
@@ -45,6 +45,17 @@
 #include "log.h"
 #include "parseurl.h"
 
+#ifdef CRYPTO
+#include <curl/curl.h>
+#define HASHLEN	32
+extern int SWFVerify(const char *url, unsigned int *size, unsigned char *hash);
+#define	InitCurl()	curl_global_init(CURL_GLOBAL_ALL)
+#define FreeCurl()	curl_global_cleanup()
+#else
+#define	InitCurl()
+#define FreeCurl()
+#endif
+
 #define RTMPDUMP_VERSION	"v2.0"
 
 #define RD_SUCCESS		0
@@ -1099,6 +1110,10 @@ main(int argc, char **argv)
   AVal token = { 0, 0 };
   char *sockshost = 0;
 
+#ifdef CRYPTO
+  unsigned char hash[HASHLEN];
+#endif
+
   char *flvFile = 0;
 
 #undef OSS
@@ -1118,8 +1133,6 @@ main(int argc, char **argv)
   signal(SIGQUIT, sigIntHandler);
 #endif
 
-  /* sleep(30); */
-
   // Check for --quiet option before printing any output
   int index = 0;
   while (index < argc)
@@ -1134,6 +1147,17 @@ main(int argc, char **argv)
   LogPrintf
     ("(c) 2009 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");
 
+  if (!InitSockets())
+    {
+      Log(LOGERROR,
+	  "Couldn't load sockets support on your platform, exiting!");
+      return RD_FAILED;
+    }
+
+  InitCurl();
+
+  /* sleep(30); */
+
   int opt;
   struct option longopts[] = {
     {"help", 0, NULL, 'h'},
@@ -1151,6 +1175,7 @@ main(int argc, char **argv)
 #ifdef CRYPTO
     {"swfhash", 1, NULL, 'w'},
     {"swfsize", 1, NULL, 'x'},
+    {"swfVfy", 1, NULL, 'W'},
 #endif
     {"flashVer", 1, NULL, 'f'},
     {"live", 0, NULL, 'v'},
@@ -1172,7 +1197,7 @@ main(int argc, char **argv)
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hVveqzr:s:t:p:a:b:f:o:u:n:c:l:y:m:k:d:A:B:T:w:x:S:#",
+		      "hVveqzr:s:t:p:a:b:f:o:u:n:c:l:y:m:k:d:A:B:T:w:x:W:S:#",
 		      longopts, NULL)) != -1)
     {
       switch (opt)
@@ -1203,6 +1228,8 @@ main(int argc, char **argv)
 	    ("--swfhash|-w hexstring  SHA256 hash of the decompressed SWF file (32 bytes)\n");
 	  LogPrintf
 	    ("--swfsize|-x num        Size of the decompressed SWF file, required for SWFVerification\n");
+	  LogPrintf
+	    ("--swfVfy|-W url         URL to player swf file, compute hash/size automatically\n");
 #endif
 	  LogPrintf
 	    ("--auth|-u string        Authentication string to be appended to the connect string\n");
@@ -1246,13 +1273,13 @@ main(int argc, char **argv)
 	case 'w':
 	  {
 	    int res = hex2bin(optarg, &swfHash.av_val);
-	    if (res != 32)
+	    if (res != HASHLEN)
 	      {
 		swfHash.av_val = NULL;
 		Log(LOGWARNING,
-		    "Couldn't parse swf hash hex string, not heyxstring or not 32 bytes, ignoring!");
+		    "Couldn't parse swf hash hex string, not heyxstring or not %d bytes, ignoring!", HASHLEN);
 	      }
-	    swfHash.av_len = 32;
+	    swfHash.av_len = HASHLEN;
 	    break;
 	  }
 	case 'x':
@@ -1268,6 +1295,13 @@ main(int argc, char **argv)
 	      }
 	    break;
 	  }
+        case 'W':
+          if (SWFVerify(optarg, &swfSize, hash) == 0)
+            {
+              swfHash.av_val = (char *)hash;
+              swfHash.av_len = HASHLEN;
+            }
+          break;
 #endif
 	case 'k':
 	  nSkipKeyFrames = atoi(optarg);
@@ -1488,12 +1522,6 @@ main(int argc, char **argv)
       STR2AVAL(flashVer, DEFAULT_FLASH_VER);
     }
 
-  if (!InitSockets())
-    {
-      Log(LOGERROR,
-	  "Couldn't load sockets support on your platform, exiting!");
-      return RD_FAILED;
-    }
 
   if (tcUrl.av_len == 0 && app.av_len != 0)
     {
@@ -1689,6 +1717,8 @@ clean:
   if (file != 0)
     fclose(file);
 
+  FreeCurl();
+
   CleanupSockets();
 
 #ifdef _DEBUG

Modified: trunk/streams.c
==============================================================================
--- trunk/streams.c	Mon Dec 28 09:59:15 2009	(r121)
+++ trunk/streams.c	Tue Dec 29 00:08:06 2009	(r122)
@@ -37,6 +37,17 @@
 #include <pthread.h>
 #endif
 
+#ifdef CRYPTO
+#include <curl/curl.h>
+#define HASHLEN	32
+extern int SWFVerify(const char *url, unsigned int *size, unsigned char *hash);
+#define	InitCurl()	curl_global_init(CURL_GLOBAL_ALL)
+#define FreeCurl()	curl_global_cleanup()
+#else
+#define	InitCurl()
+#define FreeCurl()
+#endif
+
 #define RTMPDUMP_STREAMS_VERSION	"v2.0"
 
 #define RD_SUCCESS		0
@@ -915,13 +926,13 @@ ParseOption(char opt, char *arg, RTMP_RE
     case 'w':
       {
 	int res = hex2bin(arg, &req->swfHash.av_val);
-	if (!res || res != 32)
+	if (!res || res != HASHLEN)
 	  {
 	    req->swfHash.av_val = NULL;
 	    Log(LOGWARNING,
-		"Couldn't parse swf hash hex string, not heyxstring or not 32 bytes, ignoring!");
+		"Couldn't parse swf hash hex string, not hexstring or not %d bytes, ignoring!", HASHLEN);
 	  }
-	req->swfHash.av_len = 32;
+	req->swfHash.av_len = HASHLEN;
 	break;
       }
     case 'x':
@@ -937,6 +948,17 @@ ParseOption(char opt, char *arg, RTMP_RE
 	  }
 	break;
       }
+    case 'W':
+      {
+        unsigned char hash[HASHLEN];
+        if (SWFVerify(arg, &req->swfSize, hash) == 0)
+          {
+            req->swfHash.av_val = malloc(HASHLEN);
+            req->swfHash.av_len = HASHLEN;
+            memcpy(req->swfHash.av_val, hash, HASHLEN);
+          }
+      }
+      break;
     case 'b':
       {
 	int32_t bt = atol(arg);
@@ -1102,8 +1124,11 @@ main(int argc, char **argv)
     {"tcUrl", 1, NULL, 't'},
     {"pageUrl", 1, NULL, 'p'},
     {"app", 1, NULL, 'a'},
+#ifdef CRYPTO
     {"swfhash", 1, NULL, 'w'},
     {"swfsize", 1, NULL, 'x'},
+    {"swfVfy", 1, NULL, 'W'},
+#endif
     {"auth", 1, NULL, 'u'},
     {"flashVer", 1, NULL, 'f'},
     {"live", 0, NULL, 'v'},
@@ -1127,9 +1152,13 @@ main(int argc, char **argv)
   signal(SIGINT, sigIntHandler);
   signal(SIGPIPE, SIG_IGN);
 
+  InitSockets();
+
+  InitCurl();
+
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:", longopts,
+		      "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:", longopts,
 		      NULL)) != -1)
     {
       switch (opt)
@@ -1153,11 +1182,15 @@ main(int argc, char **argv)
 	    ("--tcUrl|-t url          URL to played stream (default: \"rtmp://host[:port]/app\")\n");
 	  LogPrintf("--pageUrl|-p url        Web URL of played programme\n");
 	  LogPrintf("--app|-a app            Name of player used\n");
+#ifdef CRYPTO
 	  LogPrintf
 	    ("--swfhash|-w hexstring  SHA256 hash of the decompressed SWF file (32 bytes)\n");
 	  LogPrintf
 	    ("--swfsize|-x num        Size of the decompressed SWF file, required for SWFVerification\n");
 	  LogPrintf
+	    ("--swfVfy|-W url         URL to player swf file, compute hash/size automatically\n");
+#endif
+	  LogPrintf
 	    ("--auth|-u string        Authentication string to be appended to the connect string\n");
 	  LogPrintf
 	    ("--flashVer|-f string    Flash version string (default: \"%s\")\n",
@@ -1234,8 +1267,6 @@ main(int argc, char **argv)
   netstackdump_read = fopen("netstackdump_read", "wb");
 #endif
 
-  InitSockets();
-
   // start text UI
   ThreadCreate(controlServerThread, 0);
 
@@ -1255,6 +1286,8 @@ main(int argc, char **argv)
     }
   Log(LOGDEBUG, "Done, exiting...");
 
+  FreeCurl();
+
   CleanupSockets();
 
 #ifdef _DEBUG

Added: trunk/swfvfy.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/swfvfy.c	Tue Dec 29 00:08:06 2009	(r122)
@@ -0,0 +1,226 @@
+/*
+ *  Copyright (C) 2009 Howard Chu
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with RTMPDump; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include <curl/curl.h>
+#include <zlib.h>
+
+struct info {
+  HMAC_CTX *ctx;
+  z_stream *zs;
+  char *date;
+  int first;
+  int zlib;
+  int size;
+};
+
+#define CHUNK	16384
+
+static size_t
+swfcrunch(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  struct info *i = stream;
+  char *p = ptr;
+  size_t len = size * nmemb;
+
+  if (i->first)
+    {
+      i->first = 0;
+      /* compressed? */
+      if (!strncmp(p, "CWS", 3))
+        {
+          *p = 'F';
+          i->zlib = 1;
+        }
+      HMAC_Update(i->ctx, (unsigned char *)p, 8);
+      p += 8;
+      len -= 8;
+      i->size = 8;
+    }
+
+  if (i->zlib)
+    {
+      unsigned char out[CHUNK];
+      i->zs->next_in = (unsigned char *)p;
+      i->zs->avail_in = len;
+      do
+        {
+          i->zs->avail_out = CHUNK;
+          i->zs->next_out = out;
+          inflate(i->zs, Z_NO_FLUSH);
+          len = CHUNK - i->zs->avail_out;
+          i->size += len;
+          HMAC_Update(i->ctx, out, len);
+        } while (i->zs->avail_out == 0);
+    }
+  else
+    {
+      i->size += len;
+      HMAC_Update(i->ctx, (unsigned char *)p, len);
+    }
+  return size * nmemb;
+}
+
+static size_t
+hdrcrunch(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  struct info *i = stream;
+  char *p = ptr;
+  size_t len = size * nmemb;
+
+  if (!strncmp(p, "Last-Modified: ", 15))
+    {
+      int l = len-15;
+      strncpy(i->date, p+15, l);
+      if (i->date[l-1] == '\n')
+        l--;
+      if (i->date[l-1] == '\r')
+        l--;
+      i->date[l] = '\0';
+    }
+  return len;
+}
+
+#define HEX2BIN(a)      (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
+
+int
+SWFVerify(const char *url, unsigned int *size, unsigned char *hash)
+{
+  FILE *f = NULL;
+  char *path, *home, date[64];
+  long pos = 0;
+  int i, got = 0, ret = 0;
+  unsigned int hlen;
+  CURL *c;
+  char csbuf[96];
+  struct curl_slist cs = {csbuf};
+  struct info in;
+  z_stream zs = {0};
+  HMAC_CTX ctx;
+
+  date[0] = '\0';
+  home = getenv("HOME");
+  if (!home)
+    home = ".";
+
+  path=malloc(strlen(home)+sizeof("/.swfinfo"));
+  strcpy(path, home);
+  strcat(path, "/.swfinfo");
+
+  f = fopen(path, "r+");
+  if (f)
+    {
+      char buf[4096], *file;
+
+      file = strrchr(url, '/');
+
+      while (fgets(buf, sizeof(buf), f))
+        {
+          char *r1;
+
+          got = 0;
+
+          if (strncmp(buf, "url: ", 5))
+            continue;
+          r1 = strrchr(buf, '/');
+          buf[strlen(buf)-1] = '\0';
+          if (strcmp(r1, file))
+            continue;
+          pos = ftell(f);
+          while (got < 3 && fgets(buf, sizeof(buf), f))
+            {
+              if (!strncmp(buf, "size: ", 6))
+                {
+                  *size = strtol(buf+6, NULL, 16);
+                  got++;
+                }
+              else if (!strncmp(buf, "hash: ", 6))
+                {
+                  unsigned char *ptr = hash, *in = (unsigned char *)buf+6;
+                  int l = strlen((char *)in)-1;
+                  for (i=0; i<l; i+=2)
+                    *ptr++ = (HEX2BIN(in[i]) << 4) | HEX2BIN(in[i+1]);
+                  got++;
+                }
+              else if (!strncmp(buf, "date: ", 6))
+                {
+                  buf[strlen(buf)-1] = '\0';
+                  strncpy(date, buf, sizeof(date));
+                  got++;
+                }
+              else if (!strncmp(buf, "url: ", 5))
+                break;
+            }
+          break;
+        }
+    }
+
+  in.first = 1;
+  in.date = date;
+  HMAC_CTX_init(&ctx);
+  HMAC_Init_ex(&ctx, "Genuine Adobe Flash Player 001", 30, EVP_sha256(), NULL);
+  inflateInit(&zs);
+  in.ctx = &ctx;
+  in.zs = &zs;
+
+  c = curl_easy_init();
+  curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, swfcrunch);
+  curl_easy_setopt(c, CURLOPT_WRITEDATA, &in);
+  curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, hdrcrunch);
+  curl_easy_setopt(c, CURLOPT_HEADERDATA, &in);
+  curl_easy_setopt(c, CURLOPT_URL, url);
+  if (date[0])
+    {
+      sprintf(csbuf, "If-Modified-Since: %s", date);
+      curl_easy_setopt(c, CURLOPT_HTTPHEADER, &cs);
+    }
+  ret = curl_easy_perform(c);
+  curl_easy_cleanup(c);
+
+  inflateEnd(&zs);
+
+  if (!ret && !in.first)
+    {
+      HMAC_Final(&ctx, (unsigned char *)hash, &hlen);
+      if (got && pos)
+        fseek(f, pos, SEEK_SET);
+      else
+        {
+          if (!f)
+            f = fopen(path, "w");
+          fseek(f, 0, SEEK_END);
+          fprintf(f, "url: %s\n", url);
+        }
+      fprintf(f, "date: %s\n", date);
+      fprintf(f, "size: %08x\n", in.size);
+      fprintf(f, "hash: ");
+      for (i=0; i<SHA256_DIGEST_LENGTH; i++)
+        fprintf(f, "%02x", hash[i]);
+      fprintf(f, "\n");
+      *size = in.size;
+    }
+  HMAC_CTX_cleanup(&ctx);
+  fclose(f);
+  return ret;
+}


More information about the rtmpdump mailing list