Add functions for doing server side TLS initialization
[rtmpdump.git] / rtmpsrv.c
blobfc727bc53e129576c425797870056ed90b910ba3
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
96 STREAMING_SERVER *startStreaming(const char *address, int port);
97 void stopStreaming(STREAMING_SERVER * server);
98 void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
100 static const AVal av_dquote = AVC("\"");
101 static const AVal av_escdquote = AVC("\\\"");
103 typedef struct
105 char *hostname;
106 int rtmpport;
107 int protocol;
108 int bLiveStream; // is it a live stream? then we can't seek/resume
110 long int timeout; // timeout connection afte 300 seconds
111 uint32_t bufferTime;
113 char *rtmpurl;
114 AVal playpath;
115 AVal swfUrl;
116 AVal tcUrl;
117 AVal pageUrl;
118 AVal app;
119 AVal auth;
120 AVal swfHash;
121 AVal flashVer;
122 AVal subscribepath;
123 uint32_t swfSize;
125 uint32_t dStartOffset;
126 uint32_t dStopOffset;
127 uint32_t nTimeStamp;
128 } RTMP_REQUEST;
130 #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
132 /* this request is formed from the parameters and used to initialize a new request,
133 * thus it is a default settings list. All settings can be overriden by specifying the
134 * parameters in the GET request. */
135 RTMP_REQUEST defaultRTMPRequest;
137 #ifdef _DEBUG
138 uint32_t debugTS = 0;
140 int pnum = 0;
142 FILE *netstackdump = NULL;
143 FILE *netstackdump_read = NULL;
144 #endif
146 #define SAVC(x) static const AVal av_##x = AVC(#x)
148 SAVC(app);
149 SAVC(connect);
150 SAVC(flashVer);
151 SAVC(swfUrl);
152 SAVC(pageUrl);
153 SAVC(tcUrl);
154 SAVC(fpad);
155 SAVC(capabilities);
156 SAVC(audioCodecs);
157 SAVC(videoCodecs);
158 SAVC(videoFunction);
159 SAVC(objectEncoding);
160 SAVC(_result);
161 SAVC(createStream);
162 SAVC(getStreamLength);
163 SAVC(play);
164 SAVC(fmsVer);
165 SAVC(mode);
166 SAVC(level);
167 SAVC(code);
168 SAVC(description);
169 SAVC(secureToken);
171 static int
172 SendConnectResult(RTMP *r, double txn)
174 RTMPPacket packet;
175 char pbuf[384], *pend = pbuf+sizeof(pbuf);
176 AMFObject obj;
177 AMFObjectProperty p, op;
178 AVal av;
180 packet.m_nChannel = 0x03; // control channel (invoke)
181 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
182 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
183 packet.m_nTimeStamp = 0;
184 packet.m_nInfoField2 = 0;
185 packet.m_hasAbsTimestamp = 0;
186 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
188 char *enc = packet.m_body;
189 enc = AMF_EncodeString(enc, pend, &av__result);
190 enc = AMF_EncodeNumber(enc, pend, txn);
191 *enc++ = AMF_OBJECT;
193 STR2AVAL(av, "FMS/3,5,1,525");
194 enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
195 enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
196 enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
197 *enc++ = 0;
198 *enc++ = 0;
199 *enc++ = AMF_OBJECT_END;
201 *enc++ = AMF_OBJECT;
203 STR2AVAL(av, "status");
204 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av);
205 STR2AVAL(av, "NetConnection.Connect.Success");
206 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av);
207 STR2AVAL(av, "Connection succeeded.");
208 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av);
209 enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
210 #if 0
211 STR2AVAL(av, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
212 enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
213 #endif
214 STR2AVAL(p.p_name, "version");
215 STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
216 p.p_type = AMF_STRING;
217 obj.o_num = 1;
218 obj.o_props = &p;
219 op.p_type = AMF_OBJECT;
220 STR2AVAL(op.p_name, "data");
221 op.p_vu.p_object = obj;
222 enc = AMFProp_Encode(&op, enc, pend);
223 *enc++ = 0;
224 *enc++ = 0;
225 *enc++ = AMF_OBJECT_END;
227 packet.m_nBodySize = enc - packet.m_body;
229 return RTMP_SendPacket(r, &packet, FALSE);
232 static int
233 SendResultNumber(RTMP *r, double txn, double ID)
235 RTMPPacket packet;
236 char pbuf[256], *pend = pbuf+sizeof(pbuf);
238 packet.m_nChannel = 0x03; // control channel (invoke)
239 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
240 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
241 packet.m_nTimeStamp = 0;
242 packet.m_nInfoField2 = 0;
243 packet.m_hasAbsTimestamp = 0;
244 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
246 char *enc = packet.m_body;
247 enc = AMF_EncodeString(enc, pend, &av__result);
248 enc = AMF_EncodeNumber(enc, pend, txn);
249 *enc++ = AMF_NULL;
250 enc = AMF_EncodeNumber(enc, pend, ID);
252 packet.m_nBodySize = enc - packet.m_body;
254 return RTMP_SendPacket(r, &packet, FALSE);
257 SAVC(onStatus);
258 SAVC(status);
259 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
260 static const AVal av_Started_playing = AVC("Started playing");
261 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
262 static const AVal av_Stopped_playing = AVC("Stopped playing");
263 SAVC(details);
264 SAVC(clientid);
265 static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
267 static int
268 SendPlayStart(RTMP *r)
270 RTMPPacket packet;
271 char pbuf[512], *pend = pbuf+sizeof(pbuf);
273 packet.m_nChannel = 0x03; // control channel (invoke)
274 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
275 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
276 packet.m_nTimeStamp = 0;
277 packet.m_nInfoField2 = 0;
278 packet.m_hasAbsTimestamp = 0;
279 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
281 char *enc = packet.m_body;
282 enc = AMF_EncodeString(enc, pend, &av_onStatus);
283 enc = AMF_EncodeNumber(enc, pend, 0);
284 *enc++ = AMF_OBJECT;
286 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
287 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
288 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Started_playing);
289 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
290 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
291 *enc++ = 0;
292 *enc++ = 0;
293 *enc++ = AMF_OBJECT_END;
295 packet.m_nBodySize = enc - packet.m_body;
296 return RTMP_SendPacket(r, &packet, FALSE);
299 static int
300 SendPlayStop(RTMP *r)
302 RTMPPacket packet;
303 char pbuf[512], *pend = pbuf+sizeof(pbuf);
305 packet.m_nChannel = 0x03; // control channel (invoke)
306 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
307 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
308 packet.m_nTimeStamp = 0;
309 packet.m_nInfoField2 = 0;
310 packet.m_hasAbsTimestamp = 0;
311 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
313 char *enc = packet.m_body;
314 enc = AMF_EncodeString(enc, pend, &av_onStatus);
315 enc = AMF_EncodeNumber(enc, pend, 0);
316 *enc++ = AMF_OBJECT;
318 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
319 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Stop);
320 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Stopped_playing);
321 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
322 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
323 *enc++ = 0;
324 *enc++ = 0;
325 *enc++ = AMF_OBJECT_END;
327 packet.m_nBodySize = enc - packet.m_body;
328 return RTMP_SendPacket(r, &packet, FALSE);
331 static void
332 spawn_dumper(int argc, AVal *av, char *cmd)
334 #ifdef WIN32
335 STARTUPINFO si = {0};
336 PROCESS_INFORMATION pi = {0};
338 si.cb = sizeof(si);
339 if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL,
340 &si, &pi))
342 CloseHandle(pi.hThread);
343 CloseHandle(pi.hProcess);
345 #else
346 /* reap any dead children */
347 while (waitpid(-1, NULL, WNOHANG) > 0);
349 if (fork() == 0) {
350 char **argv = malloc((argc+1) * sizeof(char *));
351 int i;
353 for (i=0; i<argc; i++) {
354 argv[i] = av[i].av_val;
355 argv[i][av[i].av_len] = '\0';
357 argv[i] = NULL;
358 if ((i = execvp(argv[0], argv)))
359 _exit(i);
361 #endif
364 static int
365 countAMF(AMFObject *obj, int *argc)
367 int i, len;
369 for (i=0, len=0; i < obj->o_num; i++)
371 AMFObjectProperty *p = &obj->o_props[i];
372 len += 4;
373 (*argc)+= 2;
374 if (p->p_name.av_val)
375 len += 1;
376 len += 2;
377 if (p->p_name.av_val)
378 len += p->p_name.av_len + 1;
379 switch(p->p_type)
381 case AMF_BOOLEAN:
382 len += 1;
383 break;
384 case AMF_STRING:
385 len += p->p_vu.p_aval.av_len;
386 break;
387 case AMF_NUMBER:
388 len += 40;
389 break;
390 case AMF_OBJECT:
391 len += 9;
392 len += countAMF(&p->p_vu.p_object, argc);
393 (*argc) += 2;
394 break;
395 case AMF_NULL:
396 default:
397 break;
400 return len;
403 static char *
404 dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
406 int i, len, ac = *argc;
407 const char opt[] = "NBSO Z";
409 for (i=0, len=0; i < obj->o_num; i++)
411 AMFObjectProperty *p = &obj->o_props[i];
412 argv[ac].av_val = ptr+1;
413 argv[ac++].av_len = 2;
414 ptr += sprintf(ptr, " -C ");
415 argv[ac].av_val = ptr;
416 if (p->p_name.av_val)
417 *ptr++ = 'N';
418 *ptr++ = opt[p->p_type];
419 *ptr++ = ':';
420 if (p->p_name.av_val)
421 ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
422 switch(p->p_type)
424 case AMF_BOOLEAN:
425 *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
426 argv[ac].av_len = ptr - argv[ac].av_val;
427 break;
428 case AMF_STRING:
429 memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
430 ptr += p->p_vu.p_aval.av_len;
431 argv[ac].av_len = ptr - argv[ac].av_val;
432 break;
433 case AMF_NUMBER:
434 ptr += sprintf(ptr, "%f", p->p_vu.p_number);
435 argv[ac].av_len = ptr - argv[ac].av_val;
436 break;
437 case AMF_OBJECT:
438 *ptr++ = '1';
439 argv[ac].av_len = ptr - argv[ac].av_val;
440 ac++;
441 *argc = ac;
442 ptr = dumpAMF(&p->p_vu.p_object, ptr, argv, argc);
443 ac = *argc;
444 argv[ac].av_val = ptr+1;
445 argv[ac++].av_len = 2;
446 argv[ac].av_val = ptr+4;
447 argv[ac].av_len = 3;
448 ptr += sprintf(ptr, " -C O:0");
449 break;
450 case AMF_NULL:
451 default:
452 argv[ac].av_len = ptr - argv[ac].av_val;
453 break;
455 ac++;
457 *argc = ac;
458 return ptr;
461 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
463 ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset)
465 const char *body;
466 unsigned int nBodySize;
467 int ret = 0, nRes;
469 body = packet->m_body + offset;
470 nBodySize = packet->m_nBodySize - offset;
472 if (body[0] != 0x02) // make sure it is a string method name we start with
474 RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
475 __FUNCTION__);
476 return 0;
479 AMFObject obj;
480 nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
481 if (nRes < 0)
483 RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
484 return 0;
487 AMF_Dump(&obj);
488 AVal method;
489 AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
490 double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
491 RTMP_Log(RTMP_LOGDEBUG, "%s, client invoking <%s>", __FUNCTION__, method.av_val);
493 if (AVMATCH(&method, &av_connect))
495 AMFObject cobj;
496 AVal pname, pval;
497 int i;
499 server->connect = packet->m_body;
500 packet->m_body = NULL;
502 AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj);
503 for (i=0; i<cobj.o_num; i++)
505 pname = cobj.o_props[i].p_name;
506 pval.av_val = NULL;
507 pval.av_len = 0;
508 if (cobj.o_props[i].p_type == AMF_STRING)
509 pval = cobj.o_props[i].p_vu.p_aval;
510 if (AVMATCH(&pname, &av_app))
512 r->Link.app = pval;
513 pval.av_val = NULL;
514 if (!r->Link.app.av_val)
515 r->Link.app.av_val = "";
516 server->arglen += 6 + pval.av_len;
517 server->argc += 2;
519 else if (AVMATCH(&pname, &av_flashVer))
521 r->Link.flashVer = pval;
522 pval.av_val = NULL;
523 server->arglen += 6 + pval.av_len;
524 server->argc += 2;
526 else if (AVMATCH(&pname, &av_swfUrl))
528 r->Link.swfUrl = pval;
529 pval.av_val = NULL;
530 server->arglen += 6 + pval.av_len;
531 server->argc += 2;
533 else if (AVMATCH(&pname, &av_tcUrl))
535 r->Link.tcUrl = pval;
536 pval.av_val = NULL;
537 server->arglen += 6 + pval.av_len;
538 server->argc += 2;
540 else if (AVMATCH(&pname, &av_pageUrl))
542 r->Link.pageUrl = pval;
543 pval.av_val = NULL;
544 server->arglen += 6 + pval.av_len;
545 server->argc += 2;
547 else if (AVMATCH(&pname, &av_audioCodecs))
549 r->m_fAudioCodecs = cobj.o_props[i].p_vu.p_number;
551 else if (AVMATCH(&pname, &av_videoCodecs))
553 r->m_fVideoCodecs = cobj.o_props[i].p_vu.p_number;
555 else if (AVMATCH(&pname, &av_objectEncoding))
557 r->m_fEncoding = cobj.o_props[i].p_vu.p_number;
560 /* Still have more parameters? Copy them */
561 if (obj.o_num > 3)
563 int i = obj.o_num - 3;
564 r->Link.extras.o_num = i;
565 r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty));
566 memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty));
567 obj.o_num = 3;
568 server->arglen += countAMF(&r->Link.extras, &server->argc);
570 SendConnectResult(r, txn);
572 else if (AVMATCH(&method, &av_createStream))
574 SendResultNumber(r, txn, ++server->streamID);
576 else if (AVMATCH(&method, &av_getStreamLength))
578 SendResultNumber(r, txn, 10.0);
580 else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
582 AVal usherToken;
583 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
584 AVreplace(&usherToken, &av_dquote, &av_escdquote);
585 server->arglen += 6 + usherToken.av_len;
586 server->argc += 2;
587 r->Link.usherToken = usherToken;
589 else if (AVMATCH(&method, &av_play))
591 char *file, *p, *q, *cmd, *ptr;
592 AVal *argv, av;
593 int len, argc;
594 uint32_t now;
595 RTMPPacket pc = {0};
596 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
598 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
599 if (obj.o_num > 5)
600 r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
602 if (r->Link.tcUrl.av_len)
604 len = server->arglen + r->Link.playpath.av_len + 4 +
605 sizeof("rtmpdump") + r->Link.playpath.av_len + 12;
606 server->argc += 5;
608 cmd = malloc(len + server->argc * sizeof(AVal));
609 ptr = cmd;
610 argv = (AVal *)(cmd + len);
611 argv[0].av_val = cmd;
612 argv[0].av_len = sizeof("rtmpdump")-1;
613 ptr += sprintf(ptr, "rtmpdump");
614 argc = 1;
616 argv[argc].av_val = ptr + 1;
617 argv[argc++].av_len = 2;
618 argv[argc].av_val = ptr + 5;
619 ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
620 argv[argc++].av_len = r->Link.tcUrl.av_len;
622 if (r->Link.app.av_val)
624 argv[argc].av_val = ptr + 1;
625 argv[argc++].av_len = 2;
626 argv[argc].av_val = ptr + 5;
627 ptr += sprintf(ptr, " -a \"%s\"", r->Link.app.av_val);
628 argv[argc++].av_len = r->Link.app.av_len;
630 if (r->Link.flashVer.av_val)
632 argv[argc].av_val = ptr + 1;
633 argv[argc++].av_len = 2;
634 argv[argc].av_val = ptr + 5;
635 ptr += sprintf(ptr, " -f \"%s\"", r->Link.flashVer.av_val);
636 argv[argc++].av_len = r->Link.flashVer.av_len;
638 if (r->Link.swfUrl.av_val)
640 argv[argc].av_val = ptr + 1;
641 argv[argc++].av_len = 2;
642 argv[argc].av_val = ptr + 5;
643 ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
644 argv[argc++].av_len = r->Link.swfUrl.av_len;
646 if (r->Link.pageUrl.av_val)
648 argv[argc].av_val = ptr + 1;
649 argv[argc++].av_len = 2;
650 argv[argc].av_val = ptr + 5;
651 ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val);
652 argv[argc++].av_len = r->Link.pageUrl.av_len;
654 if (r->Link.usherToken.av_val)
656 argv[argc].av_val = ptr + 1;
657 argv[argc++].av_len = 2;
658 argv[argc].av_val = ptr + 5;
659 ptr += sprintf(ptr, " -j \"%s\"", r->Link.usherToken.av_val);
660 argv[argc++].av_len = r->Link.usherToken.av_len;
661 free(r->Link.usherToken.av_val);
662 r->Link.usherToken.av_val = NULL;
663 r->Link.usherToken.av_len = 0;
665 if (r->Link.extras.o_num) {
666 ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
667 AMF_Reset(&r->Link.extras);
669 argv[argc].av_val = ptr + 1;
670 argv[argc++].av_len = 2;
671 argv[argc].av_val = ptr + 5;
672 ptr += sprintf(ptr, " -y \"%.*s\"",
673 r->Link.playpath.av_len, r->Link.playpath.av_val);
674 argv[argc++].av_len = r->Link.playpath.av_len;
676 av = r->Link.playpath;
677 /* strip trailing URL parameters */
678 q = memchr(av.av_val, '?', av.av_len);
679 if (q)
681 if (q == av.av_val)
683 av.av_val++;
684 av.av_len--;
686 else
688 av.av_len = q - av.av_val;
691 /* strip leading slash components */
692 for (p=av.av_val+av.av_len-1; p>=av.av_val; p--)
693 if (*p == '/')
695 p++;
696 av.av_len -= p - av.av_val;
697 av.av_val = p;
698 break;
700 /* skip leading dot */
701 if (av.av_val[0] == '.')
703 av.av_val++;
704 av.av_len--;
706 file = malloc(av.av_len+5);
708 memcpy(file, av.av_val, av.av_len);
709 file[av.av_len] = '\0';
710 for (p=file; *p; p++)
711 if (*p == ':')
712 *p = '_';
714 /* Add extension if none present */
715 if (file[av.av_len - 4] != '.')
717 av.av_len += 4;
719 /* Always use flv extension, regardless of original */
720 if (strcmp(file+av.av_len-4, ".flv"))
722 strcpy(file+av.av_len-4, ".flv");
724 argv[argc].av_val = ptr + 1;
725 argv[argc++].av_len = 2;
726 argv[argc].av_val = file;
727 argv[argc].av_len = av.av_len;
728 ptr += sprintf(ptr, " -o %s", file);
729 now = RTMP_GetTime();
730 if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
732 printf("Duplicate request, skipping.\n");
733 free(file);
735 else
737 printf("\n%s\n\n", cmd);
738 fflush(stdout);
739 server->filetime = now;
740 free(server->filename.av_val);
741 server->filename = argv[argc++];
742 spawn_dumper(argc, argv, cmd);
745 free(cmd);
747 pc.m_body = server->connect;
748 server->connect = NULL;
749 RTMPPacket_Free(&pc);
750 ret = 1;
751 RTMP_SendCtrl(r, 0, 1, 0);
752 SendPlayStart(r);
753 RTMP_SendCtrl(r, 1, 1, 0);
754 SendPlayStop(r);
756 AMF_Reset(&obj);
757 return ret;
761 ServePacket(STREAMING_SERVER *server, RTMP *r, RTMPPacket *packet)
763 int ret = 0;
765 RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %u bytes", __FUNCTION__,
766 packet->m_packetType, packet->m_nBodySize);
768 switch (packet->m_packetType)
770 case RTMP_PACKET_TYPE_CHUNK_SIZE:
771 // HandleChangeChunkSize(r, packet);
772 break;
774 case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
775 break;
777 case RTMP_PACKET_TYPE_CONTROL:
778 // HandleCtrl(r, packet);
779 break;
781 case RTMP_PACKET_TYPE_SERVER_BW:
782 // HandleServerBW(r, packet);
783 break;
785 case RTMP_PACKET_TYPE_CLIENT_BW:
786 // HandleClientBW(r, packet);
787 break;
789 case RTMP_PACKET_TYPE_AUDIO:
790 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
791 break;
793 case RTMP_PACKET_TYPE_VIDEO:
794 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
795 break;
797 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
798 break;
800 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
801 break;
803 case RTMP_PACKET_TYPE_FLEX_MESSAGE:
805 RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %u bytes, not fully supported",
806 __FUNCTION__, packet->m_nBodySize);
807 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
809 // some DEBUG code
810 /*RTMP_LIB_AMFObject obj;
811 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
812 if(nRes < 0) {
813 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
814 //return;
817 obj.Dump(); */
819 if (ServeInvoke(server, r, packet, 1))
820 RTMP_Close(r);
821 break;
823 case RTMP_PACKET_TYPE_INFO:
824 break;
826 case RTMP_PACKET_TYPE_SHARED_OBJECT:
827 break;
829 case RTMP_PACKET_TYPE_INVOKE:
830 RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,
831 packet->m_nBodySize);
832 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
834 if (ServeInvoke(server, r, packet, 0))
835 RTMP_Close(r);
836 break;
838 case RTMP_PACKET_TYPE_FLASH_VIDEO:
839 break;
840 default:
841 RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
842 packet->m_packetType);
843 #ifdef _DEBUG
844 RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize);
845 #endif
847 return ret;
850 TFTYPE
851 controlServerThread(void *unused)
853 char ich;
854 while (1)
856 ich = getchar();
857 switch (ich)
859 case 'q':
860 RTMP_LogPrintf("Exiting\n");
861 stopStreaming(rtmpServer);
862 exit(0);
863 break;
864 default:
865 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
868 TFRET();
872 void doServe(STREAMING_SERVER * server, // server socket and state (our listening socket)
873 int sockfd // client connection socket
876 server->state = STREAMING_IN_PROGRESS;
878 RTMP *rtmp = RTMP_Alloc(); /* our session with the real client */
879 RTMPPacket packet = { 0 };
881 // timeout for http requests
882 fd_set fds;
883 struct timeval tv;
885 memset(&tv, 0, sizeof(struct timeval));
886 tv.tv_sec = 5;
888 FD_ZERO(&fds);
889 FD_SET(sockfd, &fds);
891 if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0)
893 RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request");
894 goto quit;
896 else
898 RTMP_Init(rtmp);
899 rtmp->m_sb.sb_socket = sockfd;
900 if (!RTMP_Serve(rtmp))
902 RTMP_Log(RTMP_LOGERROR, "Handshake failed");
903 goto cleanup;
906 server->arglen = 0;
907 while (RTMP_IsConnected(rtmp) && RTMP_ReadPacket(rtmp, &packet))
909 if (!RTMPPacket_IsReady(&packet))
910 continue;
911 ServePacket(server, rtmp, &packet);
912 RTMPPacket_Free(&packet);
915 cleanup:
916 RTMP_LogPrintf("Closing connection... ");
917 RTMP_Close(rtmp);
918 /* Should probably be done by RTMP_Close() ... */
919 rtmp->Link.playpath.av_val = NULL;
920 rtmp->Link.tcUrl.av_val = NULL;
921 rtmp->Link.swfUrl.av_val = NULL;
922 rtmp->Link.pageUrl.av_val = NULL;
923 rtmp->Link.app.av_val = NULL;
924 rtmp->Link.flashVer.av_val = NULL;
925 if (rtmp->Link.usherToken.av_val)
927 free(rtmp->Link.usherToken.av_val);
928 rtmp->Link.usherToken.av_val = NULL;
930 RTMP_Free(rtmp);
931 RTMP_LogPrintf("done!\n\n");
933 quit:
934 if (server->state == STREAMING_IN_PROGRESS)
935 server->state = STREAMING_ACCEPTING;
937 return;
940 TFTYPE
941 serverThread(void *arg)
943 STREAMING_SERVER *server = arg;
944 server->state = STREAMING_ACCEPTING;
946 while (server->state == STREAMING_ACCEPTING)
948 struct sockaddr_in addr;
949 socklen_t addrlen = sizeof(struct sockaddr_in);
950 int sockfd =
951 accept(server->socket, (struct sockaddr *) &addr, &addrlen);
953 if (sockfd > 0)
955 #ifdef linux
956 struct sockaddr_in dest;
957 char destch[16];
958 socklen_t destlen = sizeof(struct sockaddr_in);
959 getsockopt(sockfd, SOL_IP, SO_ORIGINAL_DST, &dest, &destlen);
960 strcpy(destch, inet_ntoa(dest.sin_addr));
961 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s to %s\n", __FUNCTION__,
962 inet_ntoa(addr.sin_addr), destch);
963 #else
964 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s\n", __FUNCTION__,
965 inet_ntoa(addr.sin_addr));
966 #endif
967 /* Create a new thread and transfer the control to that */
968 doServe(server, sockfd);
969 RTMP_Log(RTMP_LOGDEBUG, "%s: processed request\n", __FUNCTION__);
971 else
973 RTMP_Log(RTMP_LOGERROR, "%s: accept failed", __FUNCTION__);
976 server->state = STREAMING_STOPPED;
977 TFRET();
980 STREAMING_SERVER *
981 startStreaming(const char *address, int port)
983 struct sockaddr_in addr;
984 int sockfd, tmp;
985 STREAMING_SERVER *server;
987 sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
988 if (sockfd == -1)
990 RTMP_Log(RTMP_LOGERROR, "%s, couldn't create socket", __FUNCTION__);
991 return 0;
994 tmp = 1;
995 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
996 (char *) &tmp, sizeof(tmp) );
998 addr.sin_family = AF_INET;
999 addr.sin_addr.s_addr = inet_addr(address); //htonl(INADDR_ANY);
1000 addr.sin_port = htons(port);
1002 if (bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) ==
1005 RTMP_Log(RTMP_LOGERROR, "%s, TCP bind failed for port number: %d", __FUNCTION__,
1006 port);
1007 return 0;
1010 if (listen(sockfd, 10) == -1)
1012 RTMP_Log(RTMP_LOGERROR, "%s, listen failed", __FUNCTION__);
1013 closesocket(sockfd);
1014 return 0;
1017 server = (STREAMING_SERVER *) calloc(1, sizeof(STREAMING_SERVER));
1018 server->socket = sockfd;
1020 ThreadCreate(serverThread, server);
1022 return server;
1025 void
1026 stopStreaming(STREAMING_SERVER * server)
1028 assert(server);
1030 if (server->state != STREAMING_STOPPED)
1032 if (server->state == STREAMING_IN_PROGRESS)
1034 server->state = STREAMING_STOPPING;
1036 // wait for streaming threads to exit
1037 while (server->state != STREAMING_STOPPED)
1038 msleep(1);
1041 if (closesocket(server->socket))
1042 RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
1043 __FUNCTION__, GetSockError());
1045 server->state = STREAMING_STOPPED;
1050 void
1051 sigIntHandler(int sig)
1053 RTMP_ctrlC = TRUE;
1054 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
1055 if (rtmpServer)
1056 stopStreaming(rtmpServer);
1057 signal(SIGINT, SIG_DFL);
1061 main(int argc, char **argv)
1063 int nStatus = RD_SUCCESS;
1065 // http streaming server
1066 char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device
1068 char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0
1069 int nRtmpStreamingPort = 1935; // port
1071 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION);
1072 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1074 RTMP_debuglevel = RTMP_LOGINFO;
1076 if (argc > 1 && !strcmp(argv[1], "-z"))
1077 RTMP_debuglevel = RTMP_LOGALL;
1079 // init request
1080 memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST));
1082 defaultRTMPRequest.rtmpport = -1;
1083 defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED;
1084 defaultRTMPRequest.bLiveStream = FALSE; // is it a live stream? then we can't seek/resume
1086 defaultRTMPRequest.timeout = 300; // timeout connection afte 300 seconds
1087 defaultRTMPRequest.bufferTime = 20 * 1000;
1090 signal(SIGINT, sigIntHandler);
1091 #ifndef WIN32
1092 signal(SIGPIPE, SIG_IGN);
1093 #endif
1095 #ifdef _DEBUG
1096 netstackdump = fopen("netstackdump", "wb");
1097 netstackdump_read = fopen("netstackdump_read", "wb");
1098 #endif
1100 InitSockets();
1102 // start text UI
1103 ThreadCreate(controlServerThread, 0);
1105 // start http streaming
1106 if ((rtmpServer =
1107 startStreaming(rtmpStreamingDevice, nRtmpStreamingPort)) == 0)
1109 RTMP_Log(RTMP_LOGERROR, "Failed to start RTMP server, exiting!");
1110 return RD_FAILED;
1112 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice,
1113 nRtmpStreamingPort);
1115 while (rtmpServer->state != STREAMING_STOPPED)
1117 sleep(1);
1119 RTMP_Log(RTMP_LOGDEBUG, "Done, exiting...");
1121 CleanupSockets();
1123 #ifdef _DEBUG
1124 if (netstackdump != 0)
1125 fclose(netstackdump);
1126 if (netstackdump_read != 0)
1127 fclose(netstackdump_read);
1128 #endif
1129 return nStatus;
1132 void
1133 AVreplace(AVal *src, const AVal *orig, const AVal *repl)
1135 char *srcbeg = src->av_val;
1136 char *srcend = src->av_val + src->av_len;
1137 char *dest, *sptr, *dptr;
1138 int n = 0;
1140 /* count occurrences of orig in src */
1141 sptr = src->av_val;
1142 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1144 n++;
1145 sptr += orig->av_len;
1147 if (!n)
1148 return;
1150 dest = malloc(src->av_len + 1 + (repl->av_len - orig->av_len) * n);
1152 sptr = src->av_val;
1153 dptr = dest;
1154 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1156 n = sptr - srcbeg;
1157 memcpy(dptr, srcbeg, n);
1158 dptr += n;
1159 memcpy(dptr, repl->av_val, repl->av_len);
1160 dptr += repl->av_len;
1161 sptr += orig->av_len;
1162 srcbeg = sptr;
1164 n = srcend - srcbeg;
1165 memcpy(dptr, srcbeg, n);
1166 dptr += n;
1167 *dptr = '\0';
1168 src->av_val = dest;
1169 src->av_len = dptr - dest;