]> granicus.if.org Git - rtmpdump/commitdiff
Add ctim to ~/.swfinfo, recording the last time we checked on the
authorhyc <hyc@400ebc74-4327-4243-bc38-086b20814532>
Tue, 19 Jan 2010 00:58:21 +0000 (00:58 +0000)
committerhyc <hyc@400ebc74-4327-4243-bc38-086b20814532>
Tue, 19 Jan 2010 00:58:21 +0000 (00:58 +0000)
info for a given SWF. Use Age parameter to control how often we check.

git-svn-id: svn://svn.mplayerhq.hu/rtmpdump/trunk@239 400ebc74-4327-4243-bc38-086b20814532

hashswf.c
rtmp.h
rtmpdump.c
rtmpsuck.c
streams.c

index 9cf7a0b13aefc3ba3f2cc6e147d0ebe0c83408f9..3a5b86a04b8a08b0882003131839238300121f15 100644 (file)
--- a/hashswf.c
+++ b/hashswf.c
@@ -20,6 +20,8 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
+#include <time.h>
 
 #include "rtmp.h"
 
@@ -234,14 +236,98 @@ leave:
   return ret;
 }
 
+static const char *monthtab[12] = {"Jan", "Feb", "Mar",
+                               "Apr", "May", "Jun",
+                               "Jul", "Aug", "Sep",
+                               "Oct", "Nov", "Dec"};
+static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+/* Parse an HTTP datestamp into Unix time */
+static time_t
+make_unix_time(char *s)
+{
+    struct tm       time;
+    int             i, ysub = 1900, fmt = 0;
+    char           *month;
+    char           *n;
+    time_t res;
+
+    if (s[3] != ' ')
+    {
+       fmt = 1;
+       if (s[3] != ',')
+           ysub = 0;
+    }
+    for (n = s; *n; ++n)
+       if (*n == '-' || *n == ':')
+           *n = ' ';
+
+    time.tm_mon = 0;
+    n = strchr(s, ' ');
+    if (fmt)
+    {
+       /* Day, DD-MMM-YYYY HH:MM:SS GMT */
+       time.tm_mday = strtol(n+1, &n, 0);
+       month = n+1;
+       n = strchr(month, ' ');
+       time.tm_year = strtol(n+1, &n, 0);
+       time.tm_hour = strtol(n+1, &n, 0);
+       time.tm_min = strtol(n+1, &n, 0);
+       time.tm_sec = strtol(n+1, NULL, 0);
+    } else
+    {
+       /* Unix ctime() format. Does not conform to HTTP spec. */
+       /* Day MMM DD HH:MM:SS YYYY */
+       month = n+1;
+       n = strchr(month, ' ');
+       while (isspace(*n)) n++;
+       time.tm_mday = strtol(n, &n, 0);
+       time.tm_hour = strtol(n+1, &n, 0);
+       time.tm_min = strtol(n+1, &n, 0);
+       time.tm_sec = strtol(n+1, &n, 0);
+       time.tm_year = strtol(n+1, NULL, 0);
+    }
+    if (time.tm_year > 100)
+       time.tm_year -= ysub;
+
+    for (i = 0; i < 12; i++)
+       if (!strncasecmp(month, monthtab[i], 3))
+       {
+           time.tm_mon = i;
+           break;
+       }
+    time.tm_isdst = 0;         /* daylight saving is never in effect in GMT */
+    res = mktime(&time);
+    /* Unfortunately, mktime() assumes the input is in local time,
+     * not GMT, so we have to correct it here.
+     */
+    if (res != -1)
+       res += timezone;
+    return res;
+}
+
+/* Convert a Unix time to a network time string
+ * Weekday, DD-MMM-YYYY HH:MM:SS GMT
+ */
+void strtime(time_t *t, char *s)
+{
+    struct tm *tm;
+
+    tm = gmtime((time_t *)t);
+    sprintf(s, "%s, %02d %s %d %02d:%02d:%02d GMT",
+       days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon],
+       tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
 #define HEX2BIN(a)      (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
 
 int
-RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int ask)
+RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int age)
 {
   FILE *f = NULL;
-  char *path, *home, date[64];
+  char *path, *home, date[64], cctim[64];
   long pos = 0;
+  time_t ctim = 0, cnow;
   int i, got = 0, ret = 0;
   unsigned int hlen;
   struct info in = {0};
@@ -253,6 +339,16 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int ask)
   if (!home)
     home = ".";
 
+  /* SWF hash info is cached in a fixed-format file.
+   * url: <url of SWF file>
+   * ctim: HTTP datestamp of when we last checked it.
+   * date: HTTP datestamp of the SWF's last modification.
+   * size: SWF size in hex
+   * hash: SWF hash in hex
+   *
+   * These fields must be present in this order. All fields
+   * besides URL are fixed size.
+   */
   path=malloc(strlen(home)+sizeof("/.swfinfo"));
   strcpy(path, home);
   strcat(path, "/.swfinfo");
@@ -314,6 +410,12 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int ask)
                   strncpy(date, buf+6, sizeof(date));
                   got++;
                 }
+              else if (!strncmp(buf, "ctim: ", 6))
+                {
+                  buf[strlen(buf)-1] = '\0';
+                 ctim = make_unix_time(buf+6);
+                  got++;
+                }
               else if (!strncmp(buf, "url: ", 5))
                 break;
             }
@@ -322,8 +424,15 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int ask)
       break;
     }
 
-  if (got && !ask)
-    goto out;
+  cnow = time(NULL);
+  /* If we got a cache time, see if it's young enough to use directly */
+  if (got && ctim)
+    {
+      ctim = cnow - ctim;
+      ctim /= 3600 * 24; /* seconds to days */
+      if (ctim < age)  /* ok, it's new enough */
+        goto out;
+    }
 
   in.first = 1;
   in.date = date;
@@ -342,11 +451,8 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int ask)
       Log(LOGERROR, "%s: couldn't contact swfurl %s",
         __FUNCTION__, url);
     }
-  else if (!in.first)
+  else
     {
-      HMAC_Final(&ctx, (unsigned char *)hash, &hlen);
-      *size = in.size;
-
       if (got && pos)
         fseek(f, pos, SEEK_SET);
       else
@@ -371,12 +477,21 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int ask)
 
           fprintf(f, "url: %.*s\n", i, 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");
+      strtime(&cnow, cctim);
+      fprintf(f, "ctim: %s\n", cctim);
+
+      if (!in.first)
+        {
+          HMAC_Final(&ctx, (unsigned char *)hash, &hlen);
+          *size = in.size;
+
+          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");
+        }
     }
   HMAC_CTX_cleanup(&ctx);
 out:
diff --git a/rtmp.h b/rtmp.h
index 976507713e437849b27855ea2a6b8c8b2d6ba564..e11a455c02865ddd6b9e4d711aa9d217e9b8161e 100644 (file)
--- a/rtmp.h
+++ b/rtmp.h
@@ -252,7 +252,7 @@ bool RTMPSockBuf_Fill(RTMPSockBuf *sb);
 /* hashswf.c */
 #define HASHLEN        32
 
-int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int ask);
+int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, int age);
 #endif
 
 #endif
index 88cf177776a6a9e7a2aeed50345ce7ef3b308033..8cd057f7d363c9b2e33d50bd11b1ca29cb791e56 100644 (file)
@@ -1197,6 +1197,8 @@ main(int argc, char **argv)
   int edepth = 0;
 
 #ifdef CRYPTO
+  int swfAge = 30;     /* 30 days for SWF cache by default */
+  int swfVfy = 0;
   unsigned char hash[HASHLEN];
 #endif
 
@@ -1261,6 +1263,7 @@ main(int argc, char **argv)
     {"swfhash", 1, NULL, 'w'},
     {"swfsize", 1, NULL, 'x'},
     {"swfVfy", 1, NULL, 'W'},
+    {"swfAge", 1, NULL, 'X'},
 #endif
     {"flashVer", 1, NULL, 'f'},
     {"live", 0, NULL, 'v'},
@@ -1282,7 +1285,7 @@ main(int argc, char **argv)
 
   while ((opt =
          getopt_long(argc, argv,
-                     "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:m:k:d:A:B:T:w:x:W:S:#",
+                     "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:m:k:d:A:B:T:w:x:W:X:S:#",
                      longopts, NULL)) != -1)
     {
       switch (opt)
@@ -1315,6 +1318,8 @@ main(int argc, char **argv)
            ("--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");
+         LogPrintf
+           ("--swfAge|-X days        Number of days to use cached SWF hash before refreshing\n");
 #endif
          LogPrintf
            ("--auth|-u string        Authentication string to be appended to the connect string\n");
@@ -1388,11 +1393,20 @@ main(int argc, char **argv)
          }
         case 'W':
          STR2AVAL(swfUrl, optarg);
-          if (RTMP_HashSWF(optarg, &swfSize, hash, 1) == 0)
-            {
-              swfHash.av_val = (char *)hash;
-              swfHash.av_len = HASHLEN;
-            }
+         swfVfy = 1;
+          break;
+        case 'X':
+         {
+           int num = atoi(optarg);
+           if (num < 1)
+             {
+               Log(LOGERROR, "SWF Age must be at least 1, ignoring\n");
+             }
+           else
+             {
+               swfAge = num;
+             }
+         }
           break;
 #endif
        case 'k':
@@ -1601,6 +1615,16 @@ main(int argc, char **argv)
       bResume = false;
     }
 
+#ifdef CRYPTO
+  if (swfVfy)
+    {
+      if (RTMP_HashSWF(swfUrl.av_val, &swfSize, hash, swfAge) == 0)
+        {
+          swfHash.av_val = (char *)hash;
+          swfHash.av_len = HASHLEN;
+        }
+    }
+
   if (swfHash.av_len == 0 && swfSize > 0)
     {
       Log(LOGWARNING,
@@ -1615,6 +1639,7 @@ main(int argc, char **argv)
       swfHash.av_len = 0;
       swfHash.av_val = NULL;
     }
+#endif
 
   if (flashVer.av_len == 0)
     {
index d5d28f475ef16ec0c972705c5d48990ebc1e8435..5d4acb4ca6f5d799a04954e352fc1c09c2e21907 100644 (file)
@@ -95,6 +95,9 @@ typedef struct
   Flist *f_head, *f_tail;
   Flist *f_cur;
 
+#ifdef CRYPTO
+  unsigned char hash[HASHLEN];
+#endif
 } STREAMING_SERVER;
 
 STREAMING_SERVER *rtmpServer = 0;      // server structure pointer
@@ -212,11 +215,9 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
           else if (AVMATCH(&pname, &av_swfUrl))
             {
 #ifdef CRYPTO
-              unsigned char hash[HASHLEN];
-              if (RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize, hash, 0) == 0)
+              if (RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize, server->hash, 30) == 0)
                 {
-                  server->rc.Link.SWFHash.av_val = malloc(HASHLEN);
-                  memcpy(server->rc.Link.SWFHash.av_val, hash, HASHLEN);
+                  server->rc.Link.SWFHash.av_val = (char *)server->hash;
                   server->rc.Link.SWFHash.av_len = HASHLEN;
                 }
 #endif
@@ -968,7 +969,6 @@ cleanup:
   server->rc.Link.auth.av_val = NULL;
   server->rc.Link.flashVer.av_val = NULL;
 #ifdef CRYPTO
-  free(server->rc.Link.SWFHash.av_val);
   server->rc.Link.SWFHash.av_val = NULL;
 #endif
   LogPrintf("done!\n\n");
index ece3f127cdb01339bedfb546d0ec01d666ec7463..b3f76f54258bcfd0aca63ae0e7a2d72643d08aa4 100644 (file)
--- a/streams.c
+++ b/streams.c
@@ -99,10 +99,14 @@ typedef struct
   AMFObject extras;
   int edepth;
   uint32_t swfSize;
+  int swfAge;
+  int swfVfy;
 
   uint32_t dStartOffset;
   uint32_t dStopOffset;
   uint32_t nTimeStamp;
+
+  unsigned char hash[HASHLEN];
 } RTMP_REQUEST;
 
 #define STR2AVAL(av,str)       av.av_val = str; av.av_len = strlen(av.av_val)
@@ -701,6 +705,15 @@ void processTCPrequest(STREAMING_SERVER * server,  // server socket and state (ou
   if (req.rtmpport == 0)
     req.rtmpport = 1935;
 
+  if (req.swfVfy)
+    {
+        if (RTMP_HashSWF(req.swfUrl.av_val, &req.swfSize, req.hash, req.swfAge) == 0)
+          {
+            req.swfHash.av_val = (char *)req.hash;
+            req.swfHash.av_len = HASHLEN;
+          }
+    }
+
   // after validation of the http request send response header
   sprintf(buf, "HTTP/1.0 200 OK%s", srvhead);
   send(sockfd, buf, (int) strlen(buf), 0);
@@ -1000,17 +1013,23 @@ ParseOption(char opt, char *arg, RTMP_REQUEST * req)
       }
     case 'W':
       {
-        unsigned char hash[HASHLEN];
-
         STR2AVAL(req->swfUrl, arg);
-        if (RTMP_HashSWF(arg, &req->swfSize, hash, 1) == 0)
-          {
-            req->swfHash.av_val = malloc(HASHLEN);
-            req->swfHash.av_len = HASHLEN;
-            memcpy(req->swfHash.av_val, hash, HASHLEN);
-          }
+        req->swfVfy = 1;
       }
       break;
+    case 'X':
+      {
+       int num = atoi(arg);
+       if (num <= 0)
+         {
+           Log(LOGERROR, "SWF Age must be at least 1, ignoring\n");
+         }
+       else
+         {
+           req->swfAge = num;
+         }
+       break;
+      }
 #endif
     case 'b':
       {
@@ -1167,6 +1186,7 @@ main(int argc, char **argv)
   defaultRTMPRequest.timeout = 300;    // timeout connection afte 300 seconds
   defaultRTMPRequest.bufferTime = 20 * 1000;
 
+  defaultRTMPRequest.swfAge = 30;
 
   int opt;
   struct option longopts[] = {
@@ -1184,6 +1204,7 @@ main(int argc, char **argv)
     {"swfhash", 1, NULL, 'w'},
     {"swfsize", 1, NULL, 'x'},
     {"swfVfy", 1, NULL, 'W'},
+    {"swfAge", 1, NULL, 'X'},
 #endif
     {"auth", 1, NULL, 'u'},
     {"conn", 1, NULL, 'C'},
@@ -1215,7 +1236,7 @@ main(int argc, char **argv)
 
   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:W:", longopts,
+                     "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:", longopts,
                      NULL)) != -1)
     {
       switch (opt)
@@ -1246,6 +1267,8 @@ main(int argc, char **argv)
            ("--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");
+         LogPrintf
+           ("--swfAge|-X days        Number of days to use cached SWF hash before refreshing\n");
 #endif
          LogPrintf
            ("--auth|-u string        Authentication string to be appended to the connect string\n");