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
;
73 #define AGENT "Mozilla/5.0"
76 HTTP_get(struct HTTP_ctx
*http
, const char *url
, HTTP_read_callback
*cb
)
88 HTTPResult ret
= HTTPRES_OK
;
89 struct sockaddr_in sa
;
94 memset(&sa
, 0, sizeof(struct sockaddr_in
));
95 sa
.sin_family
= AF_INET
;
97 /* we only handle http here */
98 if (strncasecmp(url
, "http", 4))
99 return HTTPRES_BAD_REQUEST
;
109 return HTTPRES_BAD_REQUEST
;
113 p1
= strchr(url
+ 4, ':');
114 if (!p1
|| strncmp(p1
, "://", 3))
115 return HTTPRES_BAD_REQUEST
;
118 path
= strchr(host
, '/');
120 strncpy(hbuf
, host
, hlen
);
123 p1
= strrchr(host
, ':');
130 sa
.sin_addr
.s_addr
= inet_addr(host
);
131 if (sa
.sin_addr
.s_addr
== INADDR_NONE
)
133 struct hostent
*hp
= gethostbyname(host
);
134 if (!hp
|| !hp
->h_addr
)
135 return HTTPRES_LOST_CONNECTION
;
136 sa
.sin_addr
= *(struct in_addr
*)hp
->h_addr
;
138 sa
.sin_port
= htons(port
);
139 sb
.sb_socket
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
140 if (sb
.sb_socket
== -1)
141 return HTTPRES_LOST_CONNECTION
;
144 "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
145 path
, AGENT
, host
, (int)(path
- url
+ 1), url
);
147 i
+= sprintf(sb
.sb_buf
+ i
, "If-Modified-Since: %s\r\n", http
->date
);
148 i
+= sprintf(sb
.sb_buf
+ i
, "\r\n");
151 (sb
.sb_socket
, (struct sockaddr
*)&sa
, sizeof(struct sockaddr
)) < 0)
153 ret
= HTTPRES_LOST_CONNECTION
;
160 RTMP_Log(RTMP_LOGERROR
, "%s, No SSL/TLS support", __FUNCTION__
);
161 ret
= HTTPRES_BAD_REQUEST
;
164 TLS_client(RTMP_TLS_ctx
, sb
.sb_ssl
);
165 TLS_setfd(sb
.sb_ssl
, sb
.sb_socket
);
166 if (TLS_connect(sb
.sb_ssl
) < 0)
168 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
169 ret
= HTTPRES_LOST_CONNECTION
;
175 RTMPSockBuf_Send(&sb
, sb
.sb_buf
, i
);
178 #define HTTP_TIMEOUT 5
180 SET_RCVTIMEO(tv
, HTTP_TIMEOUT
);
182 (sb
.sb_socket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&tv
, sizeof(tv
)))
184 RTMP_Log(RTMP_LOGERROR
, "%s, Setting socket timeout to %ds failed!",
185 __FUNCTION__
, HTTP_TIMEOUT
);
190 sb
.sb_timedout
= FALSE
;
191 if (RTMPSockBuf_Fill(&sb
) < 1)
193 ret
= HTTPRES_LOST_CONNECTION
;
196 if (strncmp(sb
.sb_buf
, "HTTP/1", 6))
198 ret
= HTTPRES_BAD_REQUEST
;
202 p1
= strchr(sb
.sb_buf
, ' ');
210 ret
= HTTPRES_OK_NOT_MODIFIED
;
214 ret
= HTTPRES_NOT_FOUND
;
216 ret
= HTTPRES_SERVER_ERROR
;
218 ret
= HTTPRES_BAD_REQUEST
;
220 ret
= HTTPRES_REDIRECTED
;
223 p1
= memchr(sb
.sb_buf
, '\n', sb
.sb_size
);
226 ret
= HTTPRES_BAD_REQUEST
;
229 sb
.sb_start
= p1
+ 1;
230 sb
.sb_size
-= sb
.sb_start
- sb
.sb_buf
;
232 while ((p2
= memchr(sb
.sb_start
, '\r', sb
.sb_size
)))
234 if (*sb
.sb_start
== '\r')
242 (sb
.sb_start
, "Content-Length: ", sizeof("Content-Length: ") - 1))
244 flen
= atoi(sb
.sb_start
+ sizeof("Content-Length: ") - 1);
248 (sb
.sb_start
, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
251 strcpy(http
->date
, sb
.sb_start
+ sizeof("Last-Modified: ") - 1);
254 sb
.sb_size
-= p2
- sb
.sb_start
;
258 if (RTMPSockBuf_Fill(&sb
) < 1)
260 ret
= HTTPRES_LOST_CONNECTION
;
266 len_known
= flen
> 0;
267 while ((!len_known
|| flen
> 0) &&
268 (sb
.sb_size
> 0 || RTMPSockBuf_Fill(&sb
) > 0))
270 cb(sb
.sb_start
, 1, sb
.sb_size
, http
->data
);
273 http
->size
+= sb
.sb_size
;
278 ret
= HTTPRES_LOST_CONNECTION
;
281 RTMPSockBuf_Close(&sb
);
299 swfcrunch(void *ptr
, size_t size
, size_t nmemb
, void *stream
)
301 struct info
*i
= stream
;
303 size_t len
= size
* nmemb
;
309 if (!strncmp(p
, "CWS", 3))
314 HMAC_crunch(i
->ctx
, (unsigned char *)p
, 8);
322 unsigned char out
[CHUNK
];
323 i
->zs
->next_in
= (unsigned char *)p
;
324 i
->zs
->avail_in
= len
;
327 i
->zs
->avail_out
= CHUNK
;
328 i
->zs
->next_out
= out
;
329 inflate(i
->zs
, Z_NO_FLUSH
);
330 len
= CHUNK
- i
->zs
->avail_out
;
332 HMAC_crunch(i
->ctx
, out
, len
);
334 while (i
->zs
->avail_out
== 0);
339 HMAC_crunch(i
->ctx
, (unsigned char *)p
, len
);
345 static int tzchecked
;
347 #define JAN02_1980 318340800
349 static const char *monthtab
[12] = { "Jan", "Feb", "Mar",
354 static const char *days
[] =
355 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
357 /* Parse an HTTP datestamp into Unix time */
359 make_unix_time(char *s
)
362 int i
, ysub
= 1900, fmt
= 0;
374 if (*n
== '-' || *n
== ':')
381 /* Day, DD-MMM-YYYY HH:MM:SS GMT */
382 time
.tm_mday
= strtol(n
+ 1, &n
, 0);
384 n
= strchr(month
, ' ');
385 time
.tm_year
= strtol(n
+ 1, &n
, 0);
386 time
.tm_hour
= strtol(n
+ 1, &n
, 0);
387 time
.tm_min
= strtol(n
+ 1, &n
, 0);
388 time
.tm_sec
= strtol(n
+ 1, NULL
, 0);
392 /* Unix ctime() format. Does not conform to HTTP spec. */
393 /* Day MMM DD HH:MM:SS YYYY */
395 n
= strchr(month
, ' ');
398 time
.tm_mday
= strtol(n
, &n
, 0);
399 time
.tm_hour
= strtol(n
+ 1, &n
, 0);
400 time
.tm_min
= strtol(n
+ 1, &n
, 0);
401 time
.tm_sec
= strtol(n
+ 1, &n
, 0);
402 time
.tm_year
= strtol(n
+ 1, NULL
, 0);
404 if (time
.tm_year
> 100)
405 time
.tm_year
-= ysub
;
407 for (i
= 0; i
< 12; i
++)
408 if (!strncasecmp(month
, monthtab
[i
], 3))
413 time
.tm_isdst
= 0; /* daylight saving is never in effect in GMT */
415 /* this is normally the value of extern int timezone, but some
416 * braindead C libraries don't provide it.
421 time_t then
= JAN02_1980
;
422 tc
= localtime(&then
);
423 tzoff
= (12 - tc
->tm_hour
) * 3600 + tc
->tm_min
* 60 + tc
->tm_sec
;
427 /* Unfortunately, mktime() assumes the input is in local time,
428 * not GMT, so we have to correct it here.
435 /* Convert a Unix time to a network time string
436 * Weekday, DD-MMM-YYYY HH:MM:SS GMT
439 strtime(time_t * t
, char *s
)
443 tm
= gmtime((time_t *) t
);
444 sprintf(s
, "%s, %02d %s %d %02d:%02d:%02d GMT",
445 days
[tm
->tm_wday
], tm
->tm_mday
, monthtab
[tm
->tm_mon
],
446 tm
->tm_year
+ 1900, tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
449 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
452 RTMP_HashSWF(const char *url
, unsigned int *size
, unsigned char *hash
,
456 char *path
, date
[64], cctim
[64];
458 time_t ctim
= -1, cnow
;
459 int i
, got
= 0, ret
= 0;
461 struct info in
= { 0 };
462 struct HTTP_ctx http
= { 0 };
472 home
.av_val
= "\\UserData";
474 hpre
.av_val
= getenv("HOMEDRIVE");
475 hpre
.av_len
= strlen(hpre
.av_val
);
476 home
.av_val
= getenv("HOMEPATH");
483 home
.av_val
= getenv("HOME");
488 home
.av_len
= strlen(home
.av_val
);
490 /* SWF hash info is cached in a fixed-format file.
491 * url: <url of SWF file>
492 * ctim: HTTP datestamp of when we last checked it.
493 * date: HTTP datestamp of the SWF's last modification.
494 * size: SWF size in hex
495 * hash: SWF hash in hex
497 * These fields must be present in this order. All fields
498 * besides URL are fixed size.
500 path
= malloc(hpre
.av_len
+ home
.av_len
+ sizeof(DIRSEP
".swfinfo"));
501 sprintf(path
, "%s%s" DIRSEP
".swfinfo", hpre
.av_val
, home
.av_val
);
503 f
= fopen(path
, "r+");
506 char buf
[4096], *file
, *p
;
508 file
= strchr(url
, '/');
512 file
= strchr(file
, '/');
517 p
= strrchr(file
, '/');
523 while (fgets(buf
, sizeof(buf
), f
))
529 if (strncmp(buf
, "url: ", 5))
531 if (strncmp(buf
+ 5, url
, hlen
))
533 r1
= strrchr(buf
, '/');
536 if (strncmp(r1
, file
, i
))
539 while (got
< 4 && fgets(buf
, sizeof(buf
), f
))
541 if (!strncmp(buf
, "size: ", 6))
543 *size
= strtol(buf
+ 6, NULL
, 16);
546 else if (!strncmp(buf
, "hash: ", 6))
548 unsigned char *ptr
= hash
, *in
= (unsigned char *)buf
+ 6;
549 int l
= strlen((char *)in
) - 1;
550 for (i
= 0; i
< l
; i
+= 2)
551 *ptr
++ = (HEX2BIN(in
[i
]) << 4) | HEX2BIN(in
[i
+ 1]);
554 else if (!strncmp(buf
, "date: ", 6))
556 buf
[strlen(buf
) - 1] = '\0';
557 strncpy(date
, buf
+ 6, sizeof(date
));
560 else if (!strncmp(buf
, "ctim: ", 6))
562 buf
[strlen(buf
) - 1] = '\0';
563 ctim
= make_unix_time(buf
+ 6);
566 else if (!strncmp(buf
, "url: ", 5))
575 /* If we got a cache time, see if it's young enough to use directly */
579 ctim
/= 3600 * 24; /* seconds to days */
580 if (ctim
< age
) /* ok, it's new enough */
585 HMAC_setup(in
.ctx
, "Genuine Adobe Flash Player 001", 30);
592 httpres
= HTTP_get(&http
, url
, swfcrunch
);
596 if (httpres
!= HTTPRES_OK
&& httpres
!= HTTPRES_OK_NOT_MODIFIED
)
599 if (httpres
== HTTPRES_LOST_CONNECTION
)
600 RTMP_Log(RTMP_LOGERROR
, "%s: connection lost while downloading swfurl %s",
602 else if (httpres
== HTTPRES_NOT_FOUND
)
603 RTMP_Log(RTMP_LOGERROR
, "%s: swfurl %s not found", __FUNCTION__
, url
);
605 RTMP_Log(RTMP_LOGERROR
, "%s: couldn't contact swfurl %s (HTTP error %d)",
606 __FUNCTION__
, url
, http
.status
);
611 fseek(f
, pos
, SEEK_SET
);
616 f
= fopen(path
, "w");
620 RTMP_Log(RTMP_LOGERROR
,
621 "%s: couldn't open %s for writing, errno %d (%s)",
622 __FUNCTION__
, path
, err
, strerror(err
));
626 fseek(f
, 0, SEEK_END
);
627 q
= strchr(url
, '?');
633 fprintf(f
, "url: %.*s\n", i
, url
);
635 strtime(&cnow
, cctim
);
636 fprintf(f
, "ctim: %s\n", cctim
);
640 HMAC_finish(in
.ctx
, hash
, hlen
);
643 fprintf(f
, "date: %s\n", date
);
644 fprintf(f
, "size: %08x\n", in
.size
);
645 fprintf(f
, "hash: ");
646 for (i
= 0; i
< SHA256_DIGEST_LENGTH
; i
++)
647 fprintf(f
, "%02x", hash
[i
]);
660 RTMP_HashSWF(const char *url
, unsigned int *size
, unsigned char *hash
,