123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- /*
- * Copyright (C) 2009-2010 Howard Chu
- *
- * This file is part of librtmp.
- *
- * librtmp is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1,
- * or (at your option) any later version.
- *
- * librtmp 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 Lesser General Public License
- * along with librtmp see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/lgpl.html
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <time.h>
- #include "rtmp_sys.h"
- #include "log.h"
- #include "http.h"
- #ifdef CRYPTO
- #ifdef USE_POLARSSL
- #include <polarssl/sha2.h>
- #ifndef SHA256_DIGEST_LENGTH
- #define SHA256_DIGEST_LENGTH 32
- #endif
- #define HMAC_CTX sha2_context
- #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
- #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
- #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
- #define HMAC_close(ctx)
- #elif defined(USE_GNUTLS)
- #include <nettle/hmac.h>
- #ifndef SHA256_DIGEST_LENGTH
- #define SHA256_DIGEST_LENGTH 32
- #endif
- #undef HMAC_CTX
- #define HMAC_CTX struct hmac_sha256_ctx
- #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key)
- #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf)
- #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
- #define HMAC_close(ctx)
- #else /* USE_OPENSSL */
- #include <openssl/ssl.h>
- #include <openssl/sha.h>
- #include <openssl/hmac.h>
- #include <openssl/rc4.h>
- #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
- #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
- #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
- #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
- #endif
- extern void RTMP_TLS_Init();
- extern TLS_CTX RTMP_TLS_ctx;
- #include <zlib.h>
- #endif /* CRYPTO */
- #define AGENT "Mozilla/5.0"
- HTTPResult
- HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
- {
- char *host, *path;
- char *p1, *p2;
- char hbuf[256];
- int port = 80;
- #ifdef CRYPTO
- int ssl = 0;
- #endif
- int hlen, flen = 0;
- int rc, i;
- int len_known;
- HTTPResult ret = HTTPRES_OK;
- struct sockaddr_in sa;
- RTMPSockBuf sb = {0};
- http->status = -1;
- memset(&sa, 0, sizeof(struct sockaddr_in));
- sa.sin_family = AF_INET;
- /* we only handle http here */
- if (strncasecmp(url, "http", 4))
- return HTTPRES_BAD_REQUEST;
- if (url[4] == 's')
- {
- #ifdef CRYPTO
- ssl = 1;
- port = 443;
- if (!RTMP_TLS_ctx)
- RTMP_TLS_Init();
- #else
- return HTTPRES_BAD_REQUEST;
- #endif
- }
- p1 = strchr(url + 4, ':');
- if (!p1 || strncmp(p1, "://", 3))
- return HTTPRES_BAD_REQUEST;
- host = p1 + 3;
- path = strchr(host, '/');
- hlen = path - host;
- strncpy(hbuf, host, hlen);
- hbuf[hlen] = '\0';
- host = hbuf;
- p1 = strrchr(host, ':');
- if (p1)
- {
- *p1++ = '\0';
- port = atoi(p1);
- }
- sa.sin_addr.s_addr = inet_addr(host);
- if (sa.sin_addr.s_addr == INADDR_NONE)
- {
- struct hostent *hp = gethostbyname(host);
- if (!hp || !hp->h_addr)
- return HTTPRES_LOST_CONNECTION;
- sa.sin_addr = *(struct in_addr *)hp->h_addr;
- }
- sa.sin_port = htons(port);
- sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (sb.sb_socket == -1)
- return HTTPRES_LOST_CONNECTION;
- i =
- sprintf(sb.sb_buf,
- "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
- path, AGENT, host, (int)(path - url + 1), url);
- if (http->date[0])
- i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
- i += sprintf(sb.sb_buf + i, "\r\n");
- if (connect
- (sb.sb_socket, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0)
- {
- ret = HTTPRES_LOST_CONNECTION;
- goto leave;
- }
- #ifdef CRYPTO
- if (ssl)
- {
- #ifdef NO_SSL
- RTMP_Log(RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__);
- ret = HTTPRES_BAD_REQUEST;
- goto leave;
- #else
- TLS_client(RTMP_TLS_ctx, sb.sb_ssl);
- TLS_setfd(sb.sb_ssl, sb.sb_socket);
- if (TLS_connect(sb.sb_ssl) < 0)
- {
- RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
- ret = HTTPRES_LOST_CONNECTION;
- goto leave;
- }
- #endif
- }
- #endif
- RTMPSockBuf_Send(&sb, sb.sb_buf, i);
- /* set timeout */
- #define HTTP_TIMEOUT 5
- {
- SET_RCVTIMEO(tv, HTTP_TIMEOUT);
- if (setsockopt
- (sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
- {
- RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
- __FUNCTION__, HTTP_TIMEOUT);
- }
- }
- sb.sb_size = 0;
- sb.sb_timedout = FALSE;
- if (RTMPSockBuf_Fill(&sb) < 1)
- {
- ret = HTTPRES_LOST_CONNECTION;
- goto leave;
- }
- if (strncmp(sb.sb_buf, "HTTP/1", 6))
- {
- ret = HTTPRES_BAD_REQUEST;
- goto leave;
- }
- p1 = strchr(sb.sb_buf, ' ');
- rc = atoi(p1 + 1);
- http->status = rc;
- if (rc >= 300)
- {
- if (rc == 304)
- {
- ret = HTTPRES_OK_NOT_MODIFIED;
- goto leave;
- }
- else if (rc == 404)
- ret = HTTPRES_NOT_FOUND;
- else if (rc >= 500)
- ret = HTTPRES_SERVER_ERROR;
- else if (rc >= 400)
- ret = HTTPRES_BAD_REQUEST;
- else
- ret = HTTPRES_REDIRECTED;
- }
- p1 = memchr(sb.sb_buf, '\n', sb.sb_size);
- if (!p1)
- {
- ret = HTTPRES_BAD_REQUEST;
- goto leave;
- }
- sb.sb_start = p1 + 1;
- sb.sb_size -= sb.sb_start - sb.sb_buf;
- while ((p2 = memchr(sb.sb_start, '\r', sb.sb_size)))
- {
- if (*sb.sb_start == '\r')
- {
- sb.sb_start += 2;
- sb.sb_size -= 2;
- break;
- }
- else
- if (!strncasecmp
- (sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1))
- {
- flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1);
- }
- else
- if (!strncasecmp
- (sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
- {
- *p2 = '\0';
- strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1);
- }
- p2 += 2;
- sb.sb_size -= p2 - sb.sb_start;
- sb.sb_start = p2;
- if (sb.sb_size < 1)
- {
- if (RTMPSockBuf_Fill(&sb) < 1)
- {
- ret = HTTPRES_LOST_CONNECTION;
- goto leave;
- }
- }
- }
- len_known = flen > 0;
- while ((!len_known || flen > 0) &&
- (sb.sb_size > 0 || RTMPSockBuf_Fill(&sb) > 0))
- {
- cb(sb.sb_start, 1, sb.sb_size, http->data);
- if (len_known)
- flen -= sb.sb_size;
- http->size += sb.sb_size;
- sb.sb_size = 0;
- }
- if (flen > 0)
- ret = HTTPRES_LOST_CONNECTION;
- leave:
- RTMPSockBuf_Close(&sb);
- return ret;
- }
- #ifdef CRYPTO
- #define CHUNK 16384
- struct info
- {
- z_stream *zs;
- HMAC_CTX ctx;
- int first;
- int zlib;
- int size;
- };
- 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_crunch(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_crunch(i->ctx, out, len);
- }
- while (i->zs->avail_out == 0);
- }
- else
- {
- i->size += len;
- HMAC_crunch(i->ctx, (unsigned char *)p, len);
- }
- return size * nmemb;
- }
- static int tzoff;
- static int tzchecked;
- #define JAN02_1980 318340800
- 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 */
- /* this is normally the value of extern int timezone, but some
- * braindead C libraries don't provide it.
- */
- if (!tzchecked)
- {
- struct tm *tc;
- time_t then = JAN02_1980;
- tc = localtime(&then);
- tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec;
- tzchecked = 1;
- }
- 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 += tzoff;
- return res;
- }
- /* Convert a Unix time to a network time string
- * Weekday, DD-MMM-YYYY HH:MM:SS GMT
- */
- static 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 age)
- {
- FILE *f = NULL;
- char *path, date[64], cctim[64];
- long pos = 0;
- time_t ctim = -1, cnow;
- int i, got = 0, ret = 0;
- unsigned int hlen;
- struct info in = { 0 };
- struct HTTP_ctx http = { 0 };
- HTTPResult httpres;
- z_stream zs = { 0 };
- AVal home, hpre;
- date[0] = '\0';
- #ifdef _WIN32
- #ifdef XBMC4XBOX
- hpre.av_val = "Q:";
- hpre.av_len = 2;
- home.av_val = "\\UserData";
- #else
- hpre.av_val = getenv("HOMEDRIVE");
- hpre.av_len = strlen(hpre.av_val);
- home.av_val = getenv("HOMEPATH");
- #endif
- #define DIRSEP "\\"
- #else /* !_WIN32 */
- hpre.av_val = "";
- hpre.av_len = 0;
- home.av_val = getenv("HOME");
- #define DIRSEP "/"
- #endif
- if (!home.av_val)
- home.av_val = ".";
- home.av_len = strlen(home.av_val);
- /* 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(hpre.av_len + home.av_len + sizeof(DIRSEP ".swfinfo"));
- sprintf(path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val);
- f = fopen(path, "r+");
- while (f)
- {
- char buf[4096], *file, *p;
- file = strchr(url, '/');
- if (!file)
- break;
- file += 2;
- file = strchr(file, '/');
- if (!file)
- break;
- file++;
- hlen = file - url;
- p = strrchr(file, '/');
- if (p)
- file = p;
- else
- file--;
- while (fgets(buf, sizeof(buf), f))
- {
- char *r1;
- got = 0;
- if (strncmp(buf, "url: ", 5))
- continue;
- if (strncmp(buf + 5, url, hlen))
- continue;
- r1 = strrchr(buf, '/');
- i = strlen(r1);
- r1[--i] = '\0';
- if (strncmp(r1, file, i))
- continue;
- pos = ftell(f);
- while (got < 4 && 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 + 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;
- }
- break;
- }
- break;
- }
- cnow = time(NULL);
- /* If we got a cache time, see if it's young enough to use directly */
- if (age && ctim > 0)
- {
- ctim = cnow - ctim;
- ctim /= 3600 * 24; /* seconds to days */
- if (ctim < age) /* ok, it's new enough */
- goto out;
- }
- in.first = 1;
- HMAC_setup(in.ctx, "Genuine Adobe Flash Player 001", 30);
- inflateInit(&zs);
- in.zs = &zs;
- http.date = date;
- http.data = ∈
- httpres = HTTP_get(&http, url, swfcrunch);
- inflateEnd(&zs);
- if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED)
- {
- ret = -1;
- if (httpres == HTTPRES_LOST_CONNECTION)
- RTMP_Log(RTMP_LOGERROR, "%s: connection lost while downloading swfurl %s",
- __FUNCTION__, url);
- else if (httpres == HTTPRES_NOT_FOUND)
- RTMP_Log(RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url);
- else
- RTMP_Log(RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)",
- __FUNCTION__, url, http.status);
- }
- else
- {
- if (got && pos)
- fseek(f, pos, SEEK_SET);
- else
- {
- char *q;
- if (!f)
- f = fopen(path, "w");
- if (!f)
- {
- int err = errno;
- RTMP_Log(RTMP_LOGERROR,
- "%s: couldn't open %s for writing, errno %d (%s)",
- __FUNCTION__, path, err, strerror(err));
- ret = -1;
- goto out;
- }
- fseek(f, 0, SEEK_END);
- q = strchr(url, '?');
- if (q)
- i = q - url;
- else
- i = strlen(url);
- fprintf(f, "url: %.*s\n", i, url);
- }
- strtime(&cnow, cctim);
- fprintf(f, "ctim: %s\n", cctim);
- if (!in.first)
- {
- HMAC_finish(in.ctx, 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_close(in.ctx);
- out:
- free(path);
- if (f)
- fclose(f);
- return ret;
- }
- #else
- int
- RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
- int age)
- {
- return -1;
- }
- #endif
|