2 * Copyright (C) 2009-2010 Howard Chu
4 * This file is part of librtmp.
6 * librtmp is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1,
9 * or (at your option) any later version.
11 * librtmp is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with librtmp see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 * http://www.gnu.org/copyleft/lgpl.html
36 #include <polarssl/sha2.h>
37 #ifndef SHA256_DIGEST_LENGTH
38 #define SHA256_DIGEST_LENGTH 32
40 #define HMAC_CTX sha2_context
41 #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
42 #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
43 #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
44 #define HMAC_close(ctx)
45 #elif defined(USE_GNUTLS)
46 #include <nettle/hmac.h>
47 #ifndef SHA256_DIGEST_LENGTH
48 #define SHA256_DIGEST_LENGTH 32
51 #define HMAC_CTX struct hmac_sha256_ctx
52 #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key)
53 #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf)
54 #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
55 #define HMAC_close(ctx)
56 #else /* USE_OPENSSL */
57 #include <openssl/ssl.h>
58 #include <openssl/sha.h>
59 #include <openssl/hmac.h>
60 #include <openssl/rc4.h>
61 #if OPENSSL_VERSION_NUMBER >= 0x10100000
62 #define HMAC_CTX HMAC_CTX *
63 #define HMAC_setup(ctx, key, len) ctx = HMAC_CTX_new(); HMAC_Init_ex(ctx, key, len, EVP_sha256(), 0)
64 #define HMAC_crunch(ctx, buf, len) HMAC_Update(ctx, buf, len)
65 #define HMAC_finish(ctx, dig, dlen) HMAC_Final(ctx, dig, &dlen)
66 #define HMAC_close(ctx) HMAC_CTX_free(ctx)
68 #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
69 #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
70 #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
71 #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
75 extern void RTMP_TLS_Init();
76 extern TLS_CTX RTMP_TLS_ctx
;
84 #define AGENT "Mozilla/5.0"
87 HTTP_get(struct HTTP_ctx
*http
, const char *url
, HTTP_read_callback
*cb
)
100 HTTPResult ret
= HTTPRES_OK
;
101 struct sockaddr_in sa
;
102 RTMPSockBuf sb
= {0};
106 memset(&sa
, 0, sizeof(struct sockaddr_in
));
107 sa
.sin_family
= AF_INET
;
109 /* we only handle http here */
110 if (strncasecmp(url
, "http", 4))
111 return HTTPRES_BAD_REQUEST
;
121 return HTTPRES_BAD_REQUEST
;
125 p1
= strchr(url
+ 4, ':');
126 if (!p1
|| strncmp(p1
, "://", 3))
127 return HTTPRES_BAD_REQUEST
;
130 path
= strchr(host
, '/');
132 strncpy(hbuf
, host
, hlen
);
135 p1
= strrchr(host
, ':');
142 sa
.sin_addr
.s_addr
= inet_addr(host
);
143 if (sa
.sin_addr
.s_addr
== INADDR_NONE
)
145 struct hostent
*hp
= gethostbyname(host
);
146 if (!hp
|| !hp
->h_addr
)
147 return HTTPRES_LOST_CONNECTION
;
148 sa
.sin_addr
= *(struct in_addr
*)hp
->h_addr
;
150 sa
.sin_port
= htons(port
);
151 sb
.sb_socket
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
152 if (sb
.sb_socket
== -1)
153 return HTTPRES_LOST_CONNECTION
;
156 "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
157 path
, AGENT
, host
, (int)(path
- url
+ 1), url
);
159 i
+= sprintf(sb
.sb_buf
+ i
, "If-Modified-Since: %s\r\n", http
->date
);
160 i
+= sprintf(sb
.sb_buf
+ i
, "\r\n");
163 (sb
.sb_socket
, (struct sockaddr
*)&sa
, sizeof(struct sockaddr
)) < 0)
165 ret
= HTTPRES_LOST_CONNECTION
;
172 RTMP_Log(RTMP_LOGERROR
, "%s, No SSL/TLS support", __FUNCTION__
);
173 ret
= HTTPRES_BAD_REQUEST
;
176 TLS_client(RTMP_TLS_ctx
, sb
.sb_ssl
);
177 TLS_setfd(sb
.sb_ssl
, sb
.sb_socket
);
178 if (TLS_connect(sb
.sb_ssl
) < 0)
180 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
181 ret
= HTTPRES_LOST_CONNECTION
;
187 RTMPSockBuf_Send(&sb
, sb
.sb_buf
, i
);
190 #define HTTP_TIMEOUT 5
192 SET_RCVTIMEO(tv
, HTTP_TIMEOUT
);
194 (sb
.sb_socket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&tv
, sizeof(tv
)))
196 RTMP_Log(RTMP_LOGERROR
, "%s, Setting socket timeout to %ds failed!",
197 __FUNCTION__
, HTTP_TIMEOUT
);
202 sb
.sb_timedout
= FALSE
;
203 if (RTMPSockBuf_Fill(&sb
) < 1)
205 ret
= HTTPRES_LOST_CONNECTION
;
208 if (strncmp(sb
.sb_buf
, "HTTP/1", 6))
210 ret
= HTTPRES_BAD_REQUEST
;
214 p1
= strchr(sb
.sb_buf
, ' ');
222 ret
= HTTPRES_OK_NOT_MODIFIED
;
226 ret
= HTTPRES_NOT_FOUND
;
228 ret
= HTTPRES_SERVER_ERROR
;
230 ret
= HTTPRES_BAD_REQUEST
;
232 ret
= HTTPRES_REDIRECTED
;
235 p1
= memchr(sb
.sb_buf
, '\n', sb
.sb_size
);
238 ret
= HTTPRES_BAD_REQUEST
;
241 sb
.sb_start
= p1
+ 1;
242 sb
.sb_size
-= sb
.sb_start
- sb
.sb_buf
;
244 while ((p2
= memchr(sb
.sb_start
, '\r', sb
.sb_size
)))
246 if (*sb
.sb_start
== '\r')
254 (sb
.sb_start
, "Content-Length: ", sizeof("Content-Length: ") - 1))
256 flen
= strtol(sb
.sb_start
+ sizeof("Content-Length: ") - 1, NULL
, 10);
257 if (flen
< 1 || flen
> INT_MAX
)
259 ret
= HTTPRES_BAD_REQUEST
;
265 (sb
.sb_start
, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
268 strncpy(http
->date
, sb
.sb_start
+ sizeof("Last-Modified: ") - 1, DATELEN
-1);
269 http
->date
[DATELEN
-1] = '\0';
272 sb
.sb_size
-= p2
- sb
.sb_start
;
276 if (RTMPSockBuf_Fill(&sb
) < 1)
278 ret
= HTTPRES_LOST_CONNECTION
;
284 len_known
= flen
> 0;
285 while ((!len_known
|| flen
> 0) &&
286 (sb
.sb_size
> 0 || RTMPSockBuf_Fill(&sb
) > 0))
288 cb(sb
.sb_start
, 1, sb
.sb_size
, http
->data
);
291 http
->size
+= sb
.sb_size
;
296 ret
= HTTPRES_LOST_CONNECTION
;
299 RTMPSockBuf_Close(&sb
);
317 swfcrunch(void *ptr
, size_t size
, size_t nmemb
, void *stream
)
319 struct info
*i
= stream
;
321 size_t len
= size
* nmemb
;
327 if (!strncmp(p
, "CWS", 3))
332 HMAC_crunch(i
->ctx
, (unsigned char *)p
, 8);
340 unsigned char out
[CHUNK
];
341 i
->zs
->next_in
= (unsigned char *)p
;
342 i
->zs
->avail_in
= len
;
345 i
->zs
->avail_out
= CHUNK
;
346 i
->zs
->next_out
= out
;
347 inflate(i
->zs
, Z_NO_FLUSH
);
348 len
= CHUNK
- i
->zs
->avail_out
;
350 HMAC_crunch(i
->ctx
, out
, len
);
352 while (i
->zs
->avail_out
== 0);
357 HMAC_crunch(i
->ctx
, (unsigned char *)p
, len
);
363 static int tzchecked
;
365 #define JAN02_1980 318340800
367 static const char *monthtab
[12] = { "Jan", "Feb", "Mar",
372 static const char *days
[] =
373 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
375 /* Parse an HTTP datestamp into Unix time */
377 make_unix_time(char *s
)
380 int i
, ysub
= 1900, fmt
= 0;
392 if (*n
== '-' || *n
== ':')
399 /* Day, DD-MMM-YYYY HH:MM:SS GMT */
400 time
.tm_mday
= strtol(n
+ 1, &n
, 0);
402 n
= strchr(month
, ' ');
403 time
.tm_year
= strtol(n
+ 1, &n
, 0);
404 time
.tm_hour
= strtol(n
+ 1, &n
, 0);
405 time
.tm_min
= strtol(n
+ 1, &n
, 0);
406 time
.tm_sec
= strtol(n
+ 1, NULL
, 0);
410 /* Unix ctime() format. Does not conform to HTTP spec. */
411 /* Day MMM DD HH:MM:SS YYYY */
413 n
= strchr(month
, ' ');
416 time
.tm_mday
= strtol(n
, &n
, 0);
417 time
.tm_hour
= strtol(n
+ 1, &n
, 0);
418 time
.tm_min
= strtol(n
+ 1, &n
, 0);
419 time
.tm_sec
= strtol(n
+ 1, &n
, 0);
420 time
.tm_year
= strtol(n
+ 1, NULL
, 0);
422 if (time
.tm_year
> 100)
423 time
.tm_year
-= ysub
;
425 for (i
= 0; i
< 12; i
++)
426 if (!strncasecmp(month
, monthtab
[i
], 3))
431 time
.tm_isdst
= 0; /* daylight saving is never in effect in GMT */
433 /* this is normally the value of extern int timezone, but some
434 * braindead C libraries don't provide it.
439 time_t then
= JAN02_1980
;
440 tc
= localtime(&then
);
441 tzoff
= (12 - tc
->tm_hour
) * 3600 + tc
->tm_min
* 60 + tc
->tm_sec
;
445 /* Unfortunately, mktime() assumes the input is in local time,
446 * not GMT, so we have to correct it here.
453 /* Convert a Unix time to a network time string
454 * Weekday, DD-MMM-YYYY HH:MM:SS GMT
457 strtime(time_t * t
, char *s
)
461 tm
= gmtime((time_t *) t
);
462 sprintf(s
, "%s, %02d %s %d %02d:%02d:%02d GMT",
463 days
[tm
->tm_wday
], tm
->tm_mday
, monthtab
[tm
->tm_mon
],
464 tm
->tm_year
+ 1900, tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
467 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
470 RTMP_HashSWF(const char *url
, unsigned int *size
, unsigned char *hash
,
474 char *path
, date
[DATELEN
], cctim
[DATELEN
];
476 time_t ctim
= -1, cnow
;
477 int i
, got
= 0, ret
= 0;
479 struct info in
= { 0 };
480 struct HTTP_ctx http
= { 0 };
490 home
.av_val
= "\\UserData";
492 hpre
.av_val
= getenv("HOMEDRIVE");
493 hpre
.av_len
= strlen(hpre
.av_val
);
494 home
.av_val
= getenv("HOMEPATH");
501 home
.av_val
= getenv("HOME");
506 home
.av_len
= strlen(home
.av_val
);
508 /* SWF hash info is cached in a fixed-format file.
509 * url: <url of SWF file>
510 * ctim: HTTP datestamp of when we last checked it.
511 * date: HTTP datestamp of the SWF's last modification.
512 * size: SWF size in hex
513 * hash: SWF hash in hex
515 * These fields must be present in this order. All fields
516 * besides URL are fixed size.
518 path
= malloc(hpre
.av_len
+ home
.av_len
+ sizeof(DIRSEP
".swfinfo"));
519 sprintf(path
, "%s%s" DIRSEP
".swfinfo", hpre
.av_val
, home
.av_val
);
521 f
= fopen(path
, "r+");
524 char buf
[4096], *file
, *p
;
526 file
= strchr(url
, '/');
530 file
= strchr(file
, '/');
535 p
= strrchr(file
, '/');
541 while (fgets(buf
, sizeof(buf
), f
))
547 if (strncmp(buf
, "url: ", 5))
549 if (strncmp(buf
+ 5, url
, hlen
))
551 r1
= strrchr(buf
, '/');
554 if (strncmp(r1
, file
, i
))
557 while (got
< 4 && fgets(buf
, sizeof(buf
), f
))
559 if (!strncmp(buf
, "size: ", 6))
561 *size
= strtol(buf
+ 6, NULL
, 16);
564 else if (!strncmp(buf
, "hash: ", 6))
566 unsigned char *ptr
= hash
, *in
= (unsigned char *)buf
+ 6;
567 int l
= strlen((char *)in
) - 1;
568 for (i
= 0; i
< l
; i
+= 2)
569 *ptr
++ = (HEX2BIN(in
[i
]) << 4) | HEX2BIN(in
[i
+ 1]);
572 else if (!strncmp(buf
, "date: ", 6))
574 buf
[strlen(buf
) - 1] = '\0';
575 strncpy(date
, buf
+ 6, sizeof(date
)-1);
576 date
[DATELEN
-1] = '\0';
579 else if (!strncmp(buf
, "ctim: ", 6))
581 buf
[strlen(buf
) - 1] = '\0';
582 ctim
= make_unix_time(buf
+ 6);
585 else if (!strncmp(buf
, "url: ", 5))
594 /* If we got a cache time, see if it's young enough to use directly */
598 ctim
/= 3600 * 24; /* seconds to days */
599 if (ctim
< age
) /* ok, it's new enough */
604 HMAC_setup(in
.ctx
, "Genuine Adobe Flash Player 001", 30);
611 httpres
= HTTP_get(&http
, url
, swfcrunch
);
615 if (httpres
!= HTTPRES_OK
&& httpres
!= HTTPRES_OK_NOT_MODIFIED
)
618 if (httpres
== HTTPRES_LOST_CONNECTION
)
619 RTMP_Log(RTMP_LOGERROR
, "%s: connection lost while downloading swfurl %s",
621 else if (httpres
== HTTPRES_NOT_FOUND
)
622 RTMP_Log(RTMP_LOGERROR
, "%s: swfurl %s not found", __FUNCTION__
, url
);
624 RTMP_Log(RTMP_LOGERROR
, "%s: couldn't contact swfurl %s (HTTP error %d)",
625 __FUNCTION__
, url
, http
.status
);
630 fseek(f
, pos
, SEEK_SET
);
635 f
= fopen(path
, "w");
639 RTMP_Log(RTMP_LOGERROR
,
640 "%s: couldn't open %s for writing, errno %d (%s)",
641 __FUNCTION__
, path
, err
, strerror(err
));
645 fseek(f
, 0, SEEK_END
);
646 q
= strchr(url
, '?');
652 fprintf(f
, "url: %.*s\n", i
, url
);
654 strtime(&cnow
, cctim
);
655 fprintf(f
, "ctim: %s\n", cctim
);
659 HMAC_finish(in
.ctx
, hash
, hlen
);
662 fprintf(f
, "date: %s\n", date
);
663 fprintf(f
, "size: %08x\n", in
.size
);
664 fprintf(f
, "hash: ");
665 for (i
= 0; i
< SHA256_DIGEST_LENGTH
; i
++)
666 fprintf(f
, "%02x", hash
[i
]);
679 RTMP_HashSWF(const char *url
, unsigned int *size
, unsigned char *hash
,