More for input buffer checks
[rtmpdump.git] / rtmpsrv.c
blob5df4d3a746de43d4d65f44ab8970aa1acd764435
1 /* Simple RTMP Server
2 * Copyright (C) 2009 Andrej Stepanchuk
3 * Copyright (C) 2009-2011 Howard Chu
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with RTMPDump; see the file COPYING. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
23 /* This is just a stub for an RTMP server. It doesn't do anything
24 * beyond obtaining the connection parameters from the client.
27 #include <stdlib.h>
28 #include <string.h>
29 #include <math.h>
30 #include <limits.h>
32 #include <signal.h>
33 #include <getopt.h>
35 #include <assert.h>
37 #include "librtmp/rtmp_sys.h"
38 #include "librtmp/log.h"
40 #include "thread.h"
42 #ifdef linux
43 #include <linux/netfilter_ipv4.h>
44 #endif
46 #ifndef WIN32
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #endif
51 #define RD_SUCCESS 0
52 #define RD_FAILED 1
53 #define RD_INCOMPLETE 2
55 #define PACKET_SIZE 1024*1024
57 #ifdef WIN32
58 #define InitSockets() {\
59 WORD version; \
60 WSADATA wsaData; \
62 version = MAKEWORD(1,1); \
63 WSAStartup(version, &wsaData); }
65 #define CleanupSockets() WSACleanup()
66 #else
67 #define InitSockets()
68 #define CleanupSockets()
69 #endif
71 #define DUPTIME 5000 /* interval we disallow duplicate requests, in msec */
73 enum
75 STREAMING_ACCEPTING,
76 STREAMING_IN_PROGRESS,
77 STREAMING_STOPPING,
78 STREAMING_STOPPED
81 typedef struct
83 int socket;
84 int state;
85 int streamID;
86 int arglen;
87 int argc;
88 uint32_t filetime; /* time of last download we started */
89 AVal filename; /* name of last download */
90 char *connect;
92 } STREAMING_SERVER;
94 STREAMING_SERVER *rtmpServer = 0; // server structure pointer
95 void *sslCtx = NULL;
97 STREAMING_SERVER *startStreaming(const char *address, int port);
98 void stopStreaming(STREAMING_SERVER * server);
99 void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
101 static const AVal av_dquote = AVC("\"");
102 static const AVal av_escdquote = AVC("\\\"");
104 typedef struct
106 char *hostname;
107 int rtmpport;
108 int protocol;
109 int bLiveStream; // is it a live stream? then we can't seek/resume
111 long int timeout; // timeout connection afte 300 seconds
112 uint32_t bufferTime;
114 char *rtmpurl;
115 AVal playpath;
116 AVal swfUrl;
117 AVal tcUrl;
118 AVal pageUrl;
119 AVal app;
120 AVal auth;
121 AVal swfHash;
122 AVal flashVer;
123 AVal subscribepath;
124 uint32_t swfSize;
126 uint32_t dStartOffset;
127 uint32_t dStopOffset;
128 uint32_t nTimeStamp;
129 } RTMP_REQUEST;
131 #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
133 /* this request is formed from the parameters and used to initialize a new request,
134 * thus it is a default settings list. All settings can be overriden by specifying the
135 * parameters in the GET request. */
136 RTMP_REQUEST defaultRTMPRequest;
138 #ifdef _DEBUG
139 uint32_t debugTS = 0;
141 int pnum = 0;
143 FILE *netstackdump = NULL;
144 FILE *netstackdump_read = NULL;
145 #endif
147 #define SAVC(x) static const AVal av_##x = AVC(#x)
149 SAVC(app);
150 SAVC(connect);
151 SAVC(flashVer);
152 SAVC(swfUrl);
153 SAVC(pageUrl);
154 SAVC(tcUrl);
155 SAVC(fpad);
156 SAVC(capabilities);
157 SAVC(audioCodecs);
158 SAVC(videoCodecs);
159 SAVC(videoFunction);
160 SAVC(objectEncoding);
161 SAVC(_result);
162 SAVC(createStream);
163 SAVC(getStreamLength);
164 SAVC(play);
165 SAVC(fmsVer);
166 SAVC(mode);
167 SAVC(level);
168 SAVC(code);
169 SAVC(description);
170 SAVC(secureToken);
172 static int
173 SendConnectResult(RTMP *r, double txn)
175 RTMPPacket packet;
176 char pbuf[384], *pend = pbuf+sizeof(pbuf);
177 AMFObject obj;
178 AMFObjectProperty p, op;
179 AVal av;
181 packet.m_nChannel = 0x03; // control channel (invoke)
182 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
183 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
184 packet.m_nTimeStamp = 0;
185 packet.m_nInfoField2 = 0;
186 packet.m_hasAbsTimestamp = 0;
187 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
189 char *enc = packet.m_body;
190 enc = AMF_EncodeString(enc, pend, &av__result);
191 enc = AMF_EncodeNumber(enc, pend, txn);
192 *enc++ = AMF_OBJECT;
194 STR2AVAL(av, "FMS/3,5,1,525");
195 enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
196 enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
197 enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
198 *enc++ = 0;
199 *enc++ = 0;
200 *enc++ = AMF_OBJECT_END;
202 *enc++ = AMF_OBJECT;
204 STR2AVAL(av, "status");
205 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av);
206 STR2AVAL(av, "NetConnection.Connect.Success");
207 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av);
208 STR2AVAL(av, "Connection succeeded.");
209 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av);
210 enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
211 #if 0
212 STR2AVAL(av, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
213 enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
214 #endif
215 STR2AVAL(p.p_name, "version");
216 STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
217 p.p_type = AMF_STRING;
218 obj.o_num = 1;
219 obj.o_props = &p;
220 op.p_type = AMF_OBJECT;
221 STR2AVAL(op.p_name, "data");
222 op.p_vu.p_object = obj;
223 enc = AMFProp_Encode(&op, enc, pend);
224 *enc++ = 0;
225 *enc++ = 0;
226 *enc++ = AMF_OBJECT_END;
228 packet.m_nBodySize = enc - packet.m_body;
230 return RTMP_SendPacket(r, &packet, FALSE);
233 static int
234 SendResultNumber(RTMP *r, double txn, double ID)
236 RTMPPacket packet;
237 char pbuf[256], *pend = pbuf+sizeof(pbuf);
239 packet.m_nChannel = 0x03; // control channel (invoke)
240 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
241 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
242 packet.m_nTimeStamp = 0;
243 packet.m_nInfoField2 = 0;
244 packet.m_hasAbsTimestamp = 0;
245 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
247 char *enc = packet.m_body;
248 enc = AMF_EncodeString(enc, pend, &av__result);
249 enc = AMF_EncodeNumber(enc, pend, txn);
250 *enc++ = AMF_NULL;
251 enc = AMF_EncodeNumber(enc, pend, ID);
253 packet.m_nBodySize = enc - packet.m_body;
255 return RTMP_SendPacket(r, &packet, FALSE);
258 SAVC(onStatus);
259 SAVC(status);
260 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
261 static const AVal av_Started_playing = AVC("Started playing");
262 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
263 static const AVal av_Stopped_playing = AVC("Stopped playing");
264 SAVC(details);
265 SAVC(clientid);
266 static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
268 static int
269 SendPlayStart(RTMP *r)
271 RTMPPacket packet;
272 char pbuf[512], *pend = pbuf+sizeof(pbuf);
274 packet.m_nChannel = 0x03; // control channel (invoke)
275 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
276 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
277 packet.m_nTimeStamp = 0;
278 packet.m_nInfoField2 = 0;
279 packet.m_hasAbsTimestamp = 0;
280 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
282 char *enc = packet.m_body;
283 enc = AMF_EncodeString(enc, pend, &av_onStatus);
284 enc = AMF_EncodeNumber(enc, pend, 0);
285 *enc++ = AMF_OBJECT;
287 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
288 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
289 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Started_playing);
290 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
291 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
292 *enc++ = 0;
293 *enc++ = 0;
294 *enc++ = AMF_OBJECT_END;
296 packet.m_nBodySize = enc - packet.m_body;
297 return RTMP_SendPacket(r, &packet, FALSE);
300 static int
301 SendPlayStop(RTMP *r)
303 RTMPPacket packet;
304 char pbuf[512], *pend = pbuf+sizeof(pbuf);
306 packet.m_nChannel = 0x03; // control channel (invoke)
307 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
308 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
309 packet.m_nTimeStamp = 0;
310 packet.m_nInfoField2 = 0;
311 packet.m_hasAbsTimestamp = 0;
312 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
314 char *enc = packet.m_body;
315 enc = AMF_EncodeString(enc, pend, &av_onStatus);
316 enc = AMF_EncodeNumber(enc, pend, 0);
317 *enc++ = AMF_OBJECT;
319 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
320 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Stop);
321 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Stopped_playing);
322 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
323 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
324 *enc++ = 0;
325 *enc++ = 0;
326 *enc++ = AMF_OBJECT_END;
328 packet.m_nBodySize = enc - packet.m_body;
329 return RTMP_SendPacket(r, &packet, FALSE);
332 static void
333 spawn_dumper(int argc, AVal *av, char *cmd)
335 #ifdef WIN32
336 STARTUPINFO si = {0};
337 PROCESS_INFORMATION pi = {0};
339 si.cb = sizeof(si);
340 if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL,
341 &si, &pi))
343 CloseHandle(pi.hThread);
344 CloseHandle(pi.hProcess);
346 #else
347 /* reap any dead children */
348 while (waitpid(-1, NULL, WNOHANG) > 0);
350 if (fork() == 0) {
351 char **argv = malloc((argc+1) * sizeof(char *));
352 int i;
354 for (i=0; i<argc; i++) {
355 argv[i] = av[i].av_val;
356 argv[i][av[i].av_len] = '\0';
358 argv[i] = NULL;
359 if ((i = execvp(argv[0], argv)))
360 _exit(i);
362 #endif
365 static int
366 countAMF(AMFObject *obj, int *argc)
368 int i, len;
370 for (i=0, len=0; i < obj->o_num; i++)
372 AMFObjectProperty *p = &obj->o_props[i];
373 len += 4;
374 (*argc)+= 2;
375 if (p->p_name.av_val)
376 len += 1;
377 len += 2;
378 if (p->p_name.av_val)
379 len += p->p_name.av_len + 1;
380 switch(p->p_type)
382 case AMF_BOOLEAN:
383 len += 1;
384 break;
385 case AMF_STRING:
386 len += p->p_vu.p_aval.av_len;
387 break;
388 case AMF_NUMBER:
389 len += 40;
390 break;
391 case AMF_OBJECT:
392 len += 9;
393 len += countAMF(&p->p_vu.p_object, argc);
394 (*argc) += 2;
395 break;
396 case AMF_NULL:
397 default:
398 break;
401 return len;
404 static char *
405 dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
407 int i, ac = *argc;
408 const char opt[] = "NBSO Z";
410 for (i=0; i < obj->o_num; i++)
412 AMFObjectProperty *p = &obj->o_props[i];
413 argv[ac].av_val = ptr+1;
414 argv[ac++].av_len = 2;
415 ptr += sprintf(ptr, " -C ");
416 argv[ac].av_val = ptr;
417 if (p->p_name.av_val)
418 *ptr++ = 'N';
419 *ptr++ = opt[p->p_type];
420 *ptr++ = ':';
421 if (p->p_name.av_val)
422 ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
423 switch(p->p_type)
425 case AMF_BOOLEAN:
426 *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
427 argv[ac].av_len = ptr - argv[ac].av_val;
428 break;
429 case AMF_STRING:
430 memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
431 ptr += p->p_vu.p_aval.av_len;
432 argv[ac].av_len = ptr - argv[ac].av_val;
433 break;
434 case AMF_NUMBER:
435 ptr += sprintf(ptr, "%f", p->p_vu.p_number);
436 argv[ac].av_len = ptr - argv[ac].av_val;
437 break;
438 case AMF_OBJECT:
439 *ptr++ = '1';
440 argv[ac].av_len = ptr - argv[ac].av_val;
441 ac++;
442 *argc = ac;
443 ptr = dumpAMF(&p->p_vu.p_object, ptr, argv, argc);
444 ac = *argc;
445 argv[ac].av_val = ptr+1;
446 argv[ac++].av_len = 2;
447 argv[ac].av_val = ptr+4;
448 argv[ac].av_len = 3;
449 ptr += sprintf(ptr, " -C O:0");
450 break;
451 case AMF_NULL:
452 default:
453 argv[ac].av_len = ptr - argv[ac].av_val;
454 break;
456 ac++;
458 *argc = ac;
459 return ptr;
462 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
464 ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset)
466 const char *body;
467 unsigned int nBodySize;
468 int ret = 0, nRes;
470 body = packet->m_body + offset;
471 nBodySize = packet->m_nBodySize - offset;
473 if (body[0] != 0x02) // make sure it is a string method name we start with
475 RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
476 __FUNCTION__);
477 return 0;
480 AMFObject obj;
481 nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
482 if (nRes < 0)
484 RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
485 return 0;
488 AMF_Dump(&obj);
489 AVal method;
490 AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
491 double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
492 RTMP_Log(RTMP_LOGDEBUG, "%s, client invoking <%s>", __FUNCTION__, method.av_val);
494 if (AVMATCH(&method, &av_connect))
496 AMFObject cobj;
497 AVal pname, pval;
498 int i;
500 server->connect = packet->m_body;
501 packet->m_body = NULL;
503 AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj);
504 for (i=0; i<cobj.o_num; i++)
506 pname = cobj.o_props[i].p_name;
507 pval.av_val = NULL;
508 pval.av_len = 0;
509 if (cobj.o_props[i].p_type == AMF_STRING)
510 pval = cobj.o_props[i].p_vu.p_aval;
511 if (AVMATCH(&pname, &av_app))
513 r->Link.app = pval;
514 pval.av_val = NULL;
515 if (!r->Link.app.av_val)
516 r->Link.app.av_val = "";
517 server->arglen += 6 + pval.av_len;
518 server->argc += 2;
520 else if (AVMATCH(&pname, &av_flashVer))
522 r->Link.flashVer = pval;
523 pval.av_val = NULL;
524 server->arglen += 6 + pval.av_len;
525 server->argc += 2;
527 else if (AVMATCH(&pname, &av_swfUrl))
529 r->Link.swfUrl = pval;
530 pval.av_val = NULL;
531 server->arglen += 6 + pval.av_len;
532 server->argc += 2;
534 else if (AVMATCH(&pname, &av_tcUrl))
536 r->Link.tcUrl = pval;
537 pval.av_val = NULL;
538 server->arglen += 6 + pval.av_len;
539 server->argc += 2;
541 else if (AVMATCH(&pname, &av_pageUrl))
543 r->Link.pageUrl = pval;
544 pval.av_val = NULL;
545 server->arglen += 6 + pval.av_len;
546 server->argc += 2;
548 else if (AVMATCH(&pname, &av_audioCodecs))
550 r->m_fAudioCodecs = cobj.o_props[i].p_vu.p_number;
552 else if (AVMATCH(&pname, &av_videoCodecs))
554 r->m_fVideoCodecs = cobj.o_props[i].p_vu.p_number;
556 else if (AVMATCH(&pname, &av_objectEncoding))
558 r->m_fEncoding = cobj.o_props[i].p_vu.p_number;
561 /* Still have more parameters? Copy them */
562 if (obj.o_num > 3)
564 int i = obj.o_num - 3;
565 r->Link.extras.o_num = i;
566 r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty));
567 memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty));
568 obj.o_num = 3;
569 server->arglen += countAMF(&r->Link.extras, &server->argc);
571 SendConnectResult(r, txn);
573 else if (AVMATCH(&method, &av_createStream))
575 SendResultNumber(r, txn, ++server->streamID);
577 else if (AVMATCH(&method, &av_getStreamLength))
579 SendResultNumber(r, txn, 10.0);
581 else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
583 AVal usherToken;
584 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
585 AVreplace(&usherToken, &av_dquote, &av_escdquote);
586 server->arglen += 6 + usherToken.av_len;
587 server->argc += 2;
588 r->Link.usherToken = usherToken;
590 else if (AVMATCH(&method, &av_play))
592 char *file, *p, *q, *cmd, *ptr;
593 AVal *argv, av;
594 int len, argc;
595 uint32_t now;
596 RTMPPacket pc = {0};
597 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
598 if (!r->Link.playpath.av_len)
599 return 0;
601 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
602 if (obj.o_num > 5)
603 r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
605 if (r->Link.tcUrl.av_len)
607 len = server->arglen + r->Link.playpath.av_len + 4 +
608 sizeof("rtmpdump") + r->Link.playpath.av_len + 12;
609 server->argc += 5;
611 cmd = malloc(len + server->argc * sizeof(AVal));
612 ptr = cmd;
613 argv = (AVal *)(cmd + len);
614 argv[0].av_val = cmd;
615 argv[0].av_len = sizeof("rtmpdump")-1;
616 ptr += sprintf(ptr, "rtmpdump");
617 argc = 1;
619 argv[argc].av_val = ptr + 1;
620 argv[argc++].av_len = 2;
621 argv[argc].av_val = ptr + 5;
622 ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
623 argv[argc++].av_len = r->Link.tcUrl.av_len;
625 if (r->Link.app.av_val)
627 argv[argc].av_val = ptr + 1;
628 argv[argc++].av_len = 2;
629 argv[argc].av_val = ptr + 5;
630 ptr += sprintf(ptr, " -a \"%s\"", r->Link.app.av_val);
631 argv[argc++].av_len = r->Link.app.av_len;
633 if (r->Link.flashVer.av_val)
635 argv[argc].av_val = ptr + 1;
636 argv[argc++].av_len = 2;
637 argv[argc].av_val = ptr + 5;
638 ptr += sprintf(ptr, " -f \"%s\"", r->Link.flashVer.av_val);
639 argv[argc++].av_len = r->Link.flashVer.av_len;
641 if (r->Link.swfUrl.av_val)
643 argv[argc].av_val = ptr + 1;
644 argv[argc++].av_len = 2;
645 argv[argc].av_val = ptr + 5;
646 ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
647 argv[argc++].av_len = r->Link.swfUrl.av_len;
649 if (r->Link.pageUrl.av_val)
651 argv[argc].av_val = ptr + 1;
652 argv[argc++].av_len = 2;
653 argv[argc].av_val = ptr + 5;
654 ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val);
655 argv[argc++].av_len = r->Link.pageUrl.av_len;
657 if (r->Link.usherToken.av_val)
659 argv[argc].av_val = ptr + 1;
660 argv[argc++].av_len = 2;
661 argv[argc].av_val = ptr + 5;
662 ptr += sprintf(ptr, " -j \"%s\"", r->Link.usherToken.av_val);
663 argv[argc++].av_len = r->Link.usherToken.av_len;
664 free(r->Link.usherToken.av_val);
665 r->Link.usherToken.av_val = NULL;
666 r->Link.usherToken.av_len = 0;
668 if (r->Link.extras.o_num) {
669 ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
670 AMF_Reset(&r->Link.extras);
672 argv[argc].av_val = ptr + 1;
673 argv[argc++].av_len = 2;
674 argv[argc].av_val = ptr + 5;
675 ptr += sprintf(ptr, " -y \"%.*s\"",
676 r->Link.playpath.av_len, r->Link.playpath.av_val);
677 argv[argc++].av_len = r->Link.playpath.av_len;
679 av = r->Link.playpath;
680 /* strip trailing URL parameters */
681 q = memchr(av.av_val, '?', av.av_len);
682 if (q)
684 if (q == av.av_val)
686 av.av_val++;
687 av.av_len--;
689 else
691 av.av_len = q - av.av_val;
694 /* strip leading slash components */
695 for (p=av.av_val+av.av_len-1; p>=av.av_val; p--)
696 if (*p == '/')
698 p++;
699 av.av_len -= p - av.av_val;
700 av.av_val = p;
701 break;
703 /* skip leading dot */
704 if (av.av_val[0] == '.')
706 av.av_val++;
707 av.av_len--;
709 file = malloc(av.av_len+5);
711 memcpy(file, av.av_val, av.av_len);
712 file[av.av_len] = '\0';
713 for (p=file; *p; p++)
714 if (*p == ':')
715 *p = '_';
717 /* Add extension if none present */
718 if (file[av.av_len - 4] != '.')
720 av.av_len += 4;
722 /* Always use flv extension, regardless of original */
723 if (strcmp(file+av.av_len-4, ".flv"))
725 strcpy(file+av.av_len-4, ".flv");
727 argv[argc].av_val = ptr + 1;
728 argv[argc++].av_len = 2;
729 argv[argc].av_val = file;
730 argv[argc].av_len = av.av_len;
731 ptr += sprintf(ptr, " -o %s", file);
732 now = RTMP_GetTime();
733 if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
735 printf("Duplicate request, skipping.\n");
736 free(file);
738 else
740 printf("\n%s\n\n", cmd);
741 fflush(stdout);
742 server->filetime = now;
743 free(server->filename.av_val);
744 server->filename = argv[argc++];
745 spawn_dumper(argc, argv, cmd);
748 free(cmd);
750 pc.m_body = server->connect;
751 server->connect = NULL;
752 RTMPPacket_Free(&pc);
753 ret = 1;
754 RTMP_SendCtrl(r, 0, 1, 0);
755 SendPlayStart(r);
756 RTMP_SendCtrl(r, 1, 1, 0);
757 SendPlayStop(r);
759 AMF_Reset(&obj);
760 return ret;
764 ServePacket(STREAMING_SERVER *server, RTMP *r, RTMPPacket *packet)
766 int ret = 0;
768 RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %u bytes", __FUNCTION__,
769 packet->m_packetType, packet->m_nBodySize);
771 switch (packet->m_packetType)
773 case RTMP_PACKET_TYPE_CHUNK_SIZE:
774 // HandleChangeChunkSize(r, packet);
775 break;
777 case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
778 break;
780 case RTMP_PACKET_TYPE_CONTROL:
781 // HandleCtrl(r, packet);
782 break;
784 case RTMP_PACKET_TYPE_SERVER_BW:
785 // HandleServerBW(r, packet);
786 break;
788 case RTMP_PACKET_TYPE_CLIENT_BW:
789 // HandleClientBW(r, packet);
790 break;
792 case RTMP_PACKET_TYPE_AUDIO:
793 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
794 break;
796 case RTMP_PACKET_TYPE_VIDEO:
797 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
798 break;
800 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
801 break;
803 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
804 break;
806 case RTMP_PACKET_TYPE_FLEX_MESSAGE:
808 RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %u bytes, not fully supported",
809 __FUNCTION__, packet->m_nBodySize);
810 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
812 // some DEBUG code
813 /*RTMP_LIB_AMFObject obj;
814 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
815 if(nRes < 0) {
816 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
817 //return;
820 obj.Dump(); */
822 if (ServeInvoke(server, r, packet, 1))
823 RTMP_Close(r);
824 break;
826 case RTMP_PACKET_TYPE_INFO:
827 break;
829 case RTMP_PACKET_TYPE_SHARED_OBJECT:
830 break;
832 case RTMP_PACKET_TYPE_INVOKE:
833 RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,
834 packet->m_nBodySize);
835 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
837 if (ServeInvoke(server, r, packet, 0))
838 RTMP_Close(r);
839 break;
841 case RTMP_PACKET_TYPE_FLASH_VIDEO:
842 break;
843 default:
844 RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
845 packet->m_packetType);
846 #ifdef _DEBUG
847 RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize);
848 #endif
850 return ret;
853 TFTYPE
854 controlServerThread(void *unused)
856 char ich;
857 while (1)
859 ich = getchar();
860 switch (ich)
862 case 'q':
863 RTMP_LogPrintf("Exiting\n");
864 stopStreaming(rtmpServer);
865 exit(0);
866 break;
867 default:
868 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
871 TFRET();
875 void doServe(STREAMING_SERVER * server, // server socket and state (our listening socket)
876 int sockfd // client connection socket
879 server->state = STREAMING_IN_PROGRESS;
881 RTMP *rtmp = RTMP_Alloc(); /* our session with the real client */
882 RTMPPacket packet = { 0 };
884 // timeout for http requests
885 fd_set fds;
886 struct timeval tv;
888 memset(&tv, 0, sizeof(struct timeval));
889 tv.tv_sec = 5;
891 FD_ZERO(&fds);
892 FD_SET(sockfd, &fds);
894 if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0)
896 RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request");
897 goto quit;
899 else
901 RTMP_Init(rtmp);
902 rtmp->m_sb.sb_socket = sockfd;
903 if (sslCtx && !RTMP_TLS_Accept(rtmp, sslCtx))
905 RTMP_Log(RTMP_LOGERROR, "TLS handshake failed");
906 goto cleanup;
908 if (!RTMP_Serve(rtmp))
910 RTMP_Log(RTMP_LOGERROR, "Handshake failed");
911 goto cleanup;
914 server->arglen = 0;
915 while (RTMP_IsConnected(rtmp) && RTMP_ReadPacket(rtmp, &packet))
917 if (!RTMPPacket_IsReady(&packet))
918 continue;
919 ServePacket(server, rtmp, &packet);
920 RTMPPacket_Free(&packet);
923 cleanup:
924 RTMP_LogPrintf("Closing connection... ");
925 RTMP_Close(rtmp);
926 /* Should probably be done by RTMP_Close() ... */
927 rtmp->Link.playpath.av_val = NULL;
928 rtmp->Link.tcUrl.av_val = NULL;
929 rtmp->Link.swfUrl.av_val = NULL;
930 rtmp->Link.pageUrl.av_val = NULL;
931 rtmp->Link.app.av_val = NULL;
932 rtmp->Link.flashVer.av_val = NULL;
933 if (rtmp->Link.usherToken.av_val)
935 free(rtmp->Link.usherToken.av_val);
936 rtmp->Link.usherToken.av_val = NULL;
938 RTMP_Free(rtmp);
939 RTMP_LogPrintf("done!\n\n");
941 quit:
942 if (server->state == STREAMING_IN_PROGRESS)
943 server->state = STREAMING_ACCEPTING;
945 return;
948 TFTYPE
949 serverThread(void *arg)
951 STREAMING_SERVER *server = arg;
952 server->state = STREAMING_ACCEPTING;
954 while (server->state == STREAMING_ACCEPTING)
956 struct sockaddr_in addr;
957 socklen_t addrlen = sizeof(struct sockaddr_in);
958 int sockfd =
959 accept(server->socket, (struct sockaddr *) &addr, &addrlen);
961 if (sockfd > 0)
963 #ifdef linux
964 struct sockaddr_in dest;
965 char destch[16];
966 socklen_t destlen = sizeof(struct sockaddr_in);
967 getsockopt(sockfd, SOL_IP, SO_ORIGINAL_DST, &dest, &destlen);
968 strcpy(destch, inet_ntoa(dest.sin_addr));
969 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s to %s\n", __FUNCTION__,
970 inet_ntoa(addr.sin_addr), destch);
971 #else
972 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s\n", __FUNCTION__,
973 inet_ntoa(addr.sin_addr));
974 #endif
975 /* Create a new thread and transfer the control to that */
976 doServe(server, sockfd);
977 RTMP_Log(RTMP_LOGDEBUG, "%s: processed request\n", __FUNCTION__);
979 else
981 RTMP_Log(RTMP_LOGERROR, "%s: accept failed", __FUNCTION__);
984 server->state = STREAMING_STOPPED;
985 TFRET();
988 STREAMING_SERVER *
989 startStreaming(const char *address, int port)
991 struct sockaddr_in addr;
992 int sockfd, tmp;
993 STREAMING_SERVER *server;
995 sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
996 if (sockfd == -1)
998 RTMP_Log(RTMP_LOGERROR, "%s, couldn't create socket", __FUNCTION__);
999 return 0;
1002 tmp = 1;
1003 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
1004 (char *) &tmp, sizeof(tmp) );
1006 addr.sin_family = AF_INET;
1007 addr.sin_addr.s_addr = inet_addr(address); //htonl(INADDR_ANY);
1008 addr.sin_port = htons(port);
1010 if (bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) ==
1013 RTMP_Log(RTMP_LOGERROR, "%s, TCP bind failed for port number: %d", __FUNCTION__,
1014 port);
1015 return 0;
1018 if (listen(sockfd, 10) == -1)
1020 RTMP_Log(RTMP_LOGERROR, "%s, listen failed", __FUNCTION__);
1021 closesocket(sockfd);
1022 return 0;
1025 server = (STREAMING_SERVER *) calloc(1, sizeof(STREAMING_SERVER));
1026 server->socket = sockfd;
1028 ThreadCreate(serverThread, server);
1030 return server;
1033 void
1034 stopStreaming(STREAMING_SERVER * server)
1036 assert(server);
1038 if (server->state != STREAMING_STOPPED)
1040 if (server->state == STREAMING_IN_PROGRESS)
1042 server->state = STREAMING_STOPPING;
1044 // wait for streaming threads to exit
1045 while (server->state != STREAMING_STOPPED)
1046 msleep(1);
1049 if (closesocket(server->socket))
1050 RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
1051 __FUNCTION__, GetSockError());
1053 server->state = STREAMING_STOPPED;
1058 void
1059 sigIntHandler(int sig)
1061 RTMP_ctrlC = TRUE;
1062 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
1063 if (rtmpServer)
1064 stopStreaming(rtmpServer);
1065 signal(SIGINT, SIG_DFL);
1069 main(int argc, char **argv)
1071 int nStatus = RD_SUCCESS;
1072 int i;
1074 // http streaming server
1075 char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device
1077 char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0
1078 int nRtmpStreamingPort = 1935; // port
1079 char *cert = NULL, *key = NULL;
1081 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION);
1082 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1084 RTMP_debuglevel = RTMP_LOGINFO;
1086 for (i = 1; i < argc; i++)
1088 if (!strcmp(argv[i], "-z"))
1089 RTMP_debuglevel = RTMP_LOGALL;
1090 else if (!strcmp(argv[i], "-c") && i + 1 < argc)
1091 cert = argv[++i];
1092 else if (!strcmp(argv[i], "-k") && i + 1 < argc)
1093 key = argv[++i];
1096 if (cert && key)
1097 sslCtx = RTMP_TLS_AllocServerContext(cert, key);
1099 // init request
1100 memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST));
1102 defaultRTMPRequest.rtmpport = -1;
1103 defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED;
1104 defaultRTMPRequest.bLiveStream = FALSE; // is it a live stream? then we can't seek/resume
1106 defaultRTMPRequest.timeout = 300; // timeout connection afte 300 seconds
1107 defaultRTMPRequest.bufferTime = 20 * 1000;
1110 signal(SIGINT, sigIntHandler);
1111 #ifndef WIN32
1112 signal(SIGPIPE, SIG_IGN);
1113 #endif
1115 #ifdef _DEBUG
1116 netstackdump = fopen("netstackdump", "wb");
1117 netstackdump_read = fopen("netstackdump_read", "wb");
1118 #endif
1120 InitSockets();
1122 // start text UI
1123 ThreadCreate(controlServerThread, 0);
1125 // start http streaming
1126 if ((rtmpServer =
1127 startStreaming(rtmpStreamingDevice, nRtmpStreamingPort)) == 0)
1129 RTMP_Log(RTMP_LOGERROR, "Failed to start RTMP server, exiting!");
1130 return RD_FAILED;
1132 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice,
1133 nRtmpStreamingPort);
1135 while (rtmpServer->state != STREAMING_STOPPED)
1137 sleep(1);
1139 RTMP_Log(RTMP_LOGDEBUG, "Done, exiting...");
1141 if (sslCtx)
1142 RTMP_TLS_FreeServerContext(sslCtx);
1144 CleanupSockets();
1146 #ifdef _DEBUG
1147 if (netstackdump != 0)
1148 fclose(netstackdump);
1149 if (netstackdump_read != 0)
1150 fclose(netstackdump_read);
1151 #endif
1152 return nStatus;
1155 void
1156 AVreplace(AVal *src, const AVal *orig, const AVal *repl)
1158 char *srcbeg = src->av_val;
1159 char *srcend = src->av_val + src->av_len;
1160 char *dest, *sptr, *dptr;
1161 int n = 0;
1163 /* count occurrences of orig in src */
1164 sptr = src->av_val;
1165 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1167 n++;
1168 sptr += orig->av_len;
1170 if (!n)
1171 return;
1173 dest = malloc(src->av_len + 1 + (repl->av_len - orig->av_len) * n);
1175 sptr = src->av_val;
1176 dptr = dest;
1177 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1179 n = sptr - srcbeg;
1180 memcpy(dptr, srcbeg, n);
1181 dptr += n;
1182 memcpy(dptr, repl->av_val, repl->av_len);
1183 dptr += repl->av_len;
1184 sptr += orig->av_len;
1185 srcbeg = sptr;
1187 n = srcend - srcbeg;
1188 memcpy(dptr, srcbeg, n);
1189 dptr += n;
1190 *dptr = '\0';
1191 src->av_val = dest;
1192 src->av_len = dptr - dest;