From: hyc Date: Tue, 19 Jan 2010 00:58:21 +0000 (+0000) Subject: Add ctim to ~/.swfinfo, recording the last time we checked on the X-Git-Tag: v2.4~281 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=edbaec164a0d3e6dfecfddb3b252d5b76a9f1854;p=rtmpdump Add ctim to ~/.swfinfo, recording the last time we checked on the 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 --- diff --git a/hashswf.c b/hashswf.c index 9cf7a0b..3a5b86a 100644 --- a/hashswf.c +++ b/hashswf.c @@ -20,6 +20,8 @@ #include #include +#include +#include #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: + * 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 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) { diff --git a/rtmpsuck.c b/rtmpsuck.c index d5d28f4..5d4acb4 100644 --- a/rtmpsuck.c +++ b/rtmpsuck.c @@ -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"); diff --git a/streams.c b/streams.c index ece3f12..b3f76f5 100644 --- 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");