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
35 #include <polarssl/sha2.h>
36 #ifndef SHA256_DIGEST_LENGTH
37 #define SHA256_DIGEST_LENGTH 32
39 #define HMAC_CTX sha2_context
40 #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
41 #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
42 #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
43 #define HMAC_close(ctx)
44 #elif defined(USE_GNUTLS)
45 #include <nettle/hmac.h>
46 #ifndef SHA256_DIGEST_LENGTH
47 #define SHA256_DIGEST_LENGTH 32
50 #define HMAC_CTX struct hmac_sha256_ctx
51 #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key)
52 #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf)
53 #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
54 #define HMAC_close(ctx)
55 #else /* USE_OPENSSL */
56 #include <openssl/ssl.h>
57 #include <openssl/sha.h>
58 #include <openssl/hmac.h>
59 #include <openssl/rc4.h>
60 #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
61 #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
62 #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
63 #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
66 extern void RTMP_TLS_Init();
67 extern TLS_CTX RTMP_TLS_ctx
;
75 #define AGENT "Mozilla/5.0"
78 HTTP_get(struct HTTP_ctx
*http
, const char *url
, HTTP_read_callback
*cb
)
91 HTTPResult ret
= HTTPRES_OK
;
92 struct sockaddr_in sa
;
97 memset(&sa
, 0, sizeof(struct sockaddr_in
));
98 sa
.sin_family
= AF_INET
;
100 /* we only handle http here */
101 if (strncasecmp(url
, "http", 4))
102 return HTTPRES_BAD_REQUEST
;
112 return HTTPRES_BAD_REQUEST
;
116 p1
= strchr(url
+ 4, ':');
117 if (!p1
|| strncmp(p1
, "://", 3))
118 return HTTPRES_BAD_REQUEST
;
121 path
= strchr(host
, '/');
123 strncpy(hbuf
, host
, hlen
);
126 p1
= strrchr(host
, ':');
133 sa
.sin_addr
.s_addr
= inet_addr(host
);
134 if (sa
.sin_addr
.s_addr
== INADDR_NONE
)
136 struct hostent
*hp
= gethostbyname(host
);
137 if (!hp
|| !hp
->h_addr
)
138 return HTTPRES_LOST_CONNECTION
;
139 sa
.sin_addr
= *(struct in_addr
*)hp
->h_addr
;
141 sa
.sin_port
= htons(port
);
142 sb
.sb_socket
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
143 if (sb
.sb_socket
== -1)
144 return HTTPRES_LOST_CONNECTION
;
147 "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
148 path
, AGENT
, host
, (int)(path
- url
+ 1), url
);
150 i
+= sprintf(sb
.sb_buf
+ i
, "If-Modified-Since: %s\r\n", http
->date
);
151 i
+= sprintf(sb
.sb_buf
+ i
, "\r\n");
154 (sb
.sb_socket
, (struct sockaddr
*)&sa
, sizeof(struct sockaddr
)) < 0)
156 ret
= HTTPRES_LOST_CONNECTION
;
163 RTMP_Log(RTMP_LOGERROR
, "%s, No SSL/TLS support", __FUNCTION__
);
164 ret
= HTTPRES_BAD_REQUEST
;
167 TLS_client(RTMP_TLS_ctx
, sb
.sb_ssl
);
168 TLS_setfd(sb
.sb_ssl
, sb
.sb_socket
);
169 if (TLS_connect(sb
.sb_ssl
) < 0)
171 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
172 ret
= HTTPRES_LOST_CONNECTION
;
178 RTMPSockBuf_Send(&sb
, sb
.sb_buf
, i
);
181 #define HTTP_TIMEOUT 5
183 SET_RCVTIMEO(tv
, HTTP_TIMEOUT
);
185 (sb
.sb_socket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&tv
, sizeof(tv
)))
187 RTMP_Log(RTMP_LOGERROR
, "%s, Setting socket timeout to %ds failed!",
188 __FUNCTION__
, HTTP_TIMEOUT
);
193 sb
.sb_timedout
= FALSE
;
194 if (RTMPSockBuf_Fill(&sb
) < 1)
196 ret
= HTTPRES_LOST_CONNECTION
;
199 if (strncmp(sb
.sb_buf
, "HTTP/1", 6))
201 ret
= HTTPRES_BAD_REQUEST
;
205 p1
= strchr(sb
.sb_buf
, ' ');
213 ret
= HTTPRES_OK_NOT_MODIFIED
;
217 ret
= HTTPRES_NOT_FOUND
;
219 ret
= HTTPRES_SERVER_ERROR
;
221 ret
= HTTPRES_BAD_REQUEST
;
223 ret
= HTTPRES_REDIRECTED
;
226 p1
= memchr(sb
.sb_buf
, '\n', sb
.sb_size
);
229 ret
= HTTPRES_BAD_REQUEST
;
232 sb
.sb_start
= p1
+ 1;
233 sb
.sb_size
-= sb
.sb_start
- sb
.sb_buf
;
235 while ((p2
= memchr(sb
.sb_start
, '\r', sb
.sb_size
)))
237 if (*sb
.sb_start
== '\r')
245 (sb
.sb_start
, "Content-Length: ", sizeof("Content-Length: ") - 1))
247 flen
= strtol(sb
.sb_start
+ sizeof("Content-Length: ") - 1, NULL
, 10);
248 if (flen
< 1 || flen
> INT_MAX
)
250 ret
= HTTPRES_BAD_REQUEST
;
256 (sb
.sb_start
, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
259 strncpy(http
->date
, sb
.sb_start
+ sizeof("Last-Modified: ") - 1, DATELEN
-1);
260 http
->date
[DATELEN
-1] = '\0';
263 sb
.sb_size
-= p2
- sb
.sb_start
;
267 if (RTMPSockBuf_Fill(&sb
) < 1)
269 ret
= HTTPRES_LOST_CONNECTION
;
275 len_known
= flen
> 0;
276 while ((!len_known
|| flen
> 0) &&
277 (sb
.sb_size
> 0 || RTMPSockBuf_Fill(&sb
) > 0))
279 cb(sb
.sb_start
, 1, sb
.sb_size
, http
->data
);
282 http
->size
+= sb
.sb_size
;
287 ret
= HTTPRES_LOST_CONNECTION
;
290 RTMPSockBuf_Close(&sb
);
308 swfcrunch(void *ptr
, size_t size
, size_t nmemb
, void *stream
)
310 struct info
*i
= stream
;
312 size_t len
= size
* nmemb
;
318 if (!strncmp(p
, "CWS", 3))
323 HMAC_crunch(i
->ctx
, (unsigned char *)p
, 8);
331 unsigned char out
[CHUNK
];
332 i
->zs
->next_in
= (unsigned char *)p
;
333 i
->zs
->avail_in
= len
;
336 i
->zs
->avail_out
= CHUNK
;
337 i
->zs
->next_out
= out
;
338 inflate(i
->zs
, Z_NO_FLUSH
);
339 len
= CHUNK
- i
->zs
->avail_out
;
341 HMAC_crunch(i
->ctx
, out
, len
);
343 while (i
->zs
->avail_out
== 0);
348 HMAC_crunch(i
->ctx
, (unsigned char *)p
, len
);
354 static int tzchecked
;
356 #define JAN02_1980 318340800
358 static const char *monthtab
[12] = { "Jan", "Feb", "Mar",
363 static const char *days
[] =
364 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
366 /* Parse an HTTP datestamp into Unix time */
368 make_unix_time(char *s
)
371 int i
, ysub
= 1900, fmt
= 0;
383 if (*n
== '-' || *n
== ':')
390 /* Day, DD-MMM-YYYY HH:MM:SS GMT */
391 time
.tm_mday
= strtol(n
+ 1, &n
, 0);
393 n
= strchr(month
, ' ');
394 time
.tm_year
= strtol(n
+ 1, &n
, 0);
395 time
.tm_hour
= strtol(n
+ 1, &n
, 0);
396 time
.tm_min
= strtol(n
+ 1, &n
, 0);
397 time
.tm_sec
= strtol(n
+ 1, NULL
, 0);
401 /* Unix ctime() format. Does not conform to HTTP spec. */
402 /* Day MMM DD HH:MM:SS YYYY */
404 n
= strchr(month
, ' ');
407 time
.tm_mday
= strtol(n
, &n
, 0);
408 time
.tm_hour
= strtol(n
+ 1, &n
, 0);
409 time
.tm_min
= strtol(n
+ 1, &n
, 0);
410 time
.tm_sec
= strtol(n
+ 1, &n
, 0);
411 time
.tm_year
= strtol(n
+ 1, NULL
, 0);
413 if (time
.tm_year
> 100)
414 time
.tm_year
-= ysub
;
416 for (i
= 0; i
< 12; i
++)
417 if (!strncasecmp(month
, monthtab
[i
], 3))
422 time
.tm_isdst
= 0; /* daylight saving is never in effect in GMT */
424 /* this is normally the value of extern int timezone, but some
425 * braindead C libraries don't provide it.
430 time_t then
= JAN02_1980
;
431 tc
= localtime(&then
);
432 tzoff
= (12 - tc
->tm_hour
) * 3600 + tc
->tm_min
* 60 + tc
->tm_sec
;
436 /* Unfortunately, mktime() assumes the input is in local time,
437 * not GMT, so we have to correct it here.
444 /* Convert a Unix time to a network time string
445 * Weekday, DD-MMM-YYYY HH:MM:SS GMT
448 strtime(time_t * t
, char *s
)
452 tm
= gmtime((time_t *) t
);
453 sprintf(s
, "%s, %02d %s %d %02d:%02d:%02d GMT",
454 days
[tm
->tm_wday
], tm
->tm_mday
, monthtab
[tm
->tm_mon
],
455 tm
->tm_year
+ 1900, tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
458 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
461 RTMP_HashSWF(const char *url
, unsigned int *size
, unsigned char *hash
,
465 char *path
, date
[DATELEN
], cctim
[DATELEN
];
467 time_t ctim
= -1, cnow
;
468 int i
, got
= 0, ret
= 0;
470 struct info in
= { 0 };
471 struct HTTP_ctx http
= { 0 };
481 home
.av_val
= "\\UserData";
483 hpre
.av_val
= getenv("HOMEDRIVE");
484 hpre
.av_len
= strlen(hpre
.av_val
);
485 home
.av_val
= getenv("HOMEPATH");
492 home
.av_val
= getenv("HOME");
497 home
.av_len
= strlen(home
.av_val
);
499 /* SWF hash info is cached in a fixed-format file.
500 * url: <url of SWF file>
501 * ctim: HTTP datestamp of when we last checked it.
502 * date: HTTP datestamp of the SWF's last modification.
503 * size: SWF size in hex
504 * hash: SWF hash in hex
506 * These fields must be present in this order. All fields
507 * besides URL are fixed size.
509 path
= malloc(hpre
.av_len
+ home
.av_len
+ sizeof(DIRSEP
".swfinfo"));
510 sprintf(path
, "%s%s" DIRSEP
".swfinfo", hpre
.av_val
, home
.av_val
);
512 f
= fopen(path
, "r+");
515 char buf
[4096], *file
, *p
;
517 file
= strchr(url
, '/');
521 file
= strchr(file
, '/');
526 p
= strrchr(file
, '/');
532 while (fgets(buf
, sizeof(buf
), f
))
538 if (strncmp(buf
, "url: ", 5))
540 if (strncmp(buf
+ 5, url
, hlen
))
542 r1
= strrchr(buf
, '/');
545 if (strncmp(r1
, file
, i
))
548 while (got
< 4 && fgets(buf
, sizeof(buf
), f
))
550 if (!strncmp(buf
, "size: ", 6))
552 *size
= strtol(buf
+ 6, NULL
, 16);
555 else if (!strncmp(buf
, "hash: ", 6))
557 unsigned char *ptr
= hash
, *in
= (unsigned char *)buf
+ 6;
558 int l
= strlen((char *)in
) - 1;
559 for (i
= 0; i
< l
; i
+= 2)
560 *ptr
++ = (HEX2BIN(in
[i
]) << 4) | HEX2BIN(in
[i
+ 1]);
563 else if (!strncmp(buf
, "date: ", 6))
565 buf
[strlen(buf
) - 1] = '\0';
566 strncpy(date
, buf
+ 6, sizeof(date
)-1);
567 date
[DATELEN
-1] = '\0';
570 else if (!strncmp(buf
, "ctim: ", 6))
572 buf
[strlen(buf
) - 1] = '\0';
573 ctim
= make_unix_time(buf
+ 6);
576 else if (!strncmp(buf
, "url: ", 5))
585 /* If we got a cache time, see if it's young enough to use directly */
589 ctim
/= 3600 * 24; /* seconds to days */
590 if (ctim
< age
) /* ok, it's new enough */
595 HMAC_setup(in
.ctx
, "Genuine Adobe Flash Player 001", 30);
602 httpres
= HTTP_get(&http
, url
, swfcrunch
);
606 if (httpres
!= HTTPRES_OK
&& httpres
!= HTTPRES_OK_NOT_MODIFIED
)
609 if (httpres
== HTTPRES_LOST_CONNECTION
)
610 RTMP_Log(RTMP_LOGERROR
, "%s: connection lost while downloading swfurl %s",
612 else if (httpres
== HTTPRES_NOT_FOUND
)
613 RTMP_Log(RTMP_LOGERROR
, "%s: swfurl %s not found", __FUNCTION__
, url
);
615 RTMP_Log(RTMP_LOGERROR
, "%s: couldn't contact swfurl %s (HTTP error %d)",
616 __FUNCTION__
, url
, http
.status
);
621 fseek(f
, pos
, SEEK_SET
);
626 f
= fopen(path
, "w");
630 RTMP_Log(RTMP_LOGERROR
,
631 "%s: couldn't open %s for writing, errno %d (%s)",
632 __FUNCTION__
, path
, err
, strerror(err
));
636 fseek(f
, 0, SEEK_END
);
637 q
= strchr(url
, '?');
643 fprintf(f
, "url: %.*s\n", i
, url
);
645 strtime(&cnow
, cctim
);
646 fprintf(f
, "ctim: %s\n", cctim
);
650 HMAC_finish(in
.ctx
, hash
, hlen
);
653 fprintf(f
, "date: %s\n", date
);
654 fprintf(f
, "size: %08x\n", in
.size
);
655 fprintf(f
, "hash: ");
656 for (i
= 0; i
< SHA256_DIGEST_LENGTH
; i
++)
657 fprintf(f
, "%02x", hash
[i
]);
670 RTMP_HashSWF(const char *url
, unsigned int *size
, unsigned char *hash
,