/src/gpac/src/ietf/rtsp_session.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2023 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | #include <gpac/internal/ietf_dev.h> |
27 | | #include <gpac/base_coding.h> |
28 | | |
29 | | |
30 | | #ifndef GPAC_DISABLE_STREAMING |
31 | | |
32 | | static GF_Err gf_rtsp_write_sock(GF_RTSPSession *sess, u8 *data, u32 len); |
33 | | static GF_Err gf_rstp_flush_buffer(GF_RTSPSession *sess); |
34 | | static GF_Err gf_rtsp_http_tunnel_setup(GF_RTSPSession *sess); |
35 | | |
36 | | #ifdef GPAC_HAS_SSL |
37 | | #include <openssl/ssl.h> |
38 | | #include <openssl/err.h> |
39 | | #include <openssl/x509.h> |
40 | | #include <openssl/x509v3.h> |
41 | | #include <openssl/rand.h> |
42 | | |
43 | | void *gf_ssl_new(void *ssl_server_ctx, GF_Socket *client_sock, GF_Err *e); |
44 | | void gf_ssl_del(void *ssl); |
45 | | Bool gf_ssl_check_cert(SSL *ssl, const char *server_name); |
46 | | |
47 | | #endif |
48 | | |
49 | | |
50 | | /*default packet size to use when storing incomplete packets*/ |
51 | 0 | #define RTSP_PCK_SIZE 1000 |
52 | | |
53 | | GF_Err RTSP_UnpackURL(char *sURL, char Server[1024], u16 *Port, char Service[1024], Bool *useTCP, char User[1024], char Pass[1024]) |
54 | 0 | { |
55 | 0 | char schema[10], *test, text[1024], *retest, *sep, *service_start; |
56 | 0 | u32 i, len; |
57 | 0 | Bool is_ipv6; |
58 | 0 | if (!sURL) return GF_BAD_PARAM; |
59 | | |
60 | 0 | Server[0] = 0; |
61 | 0 | Service[0] = 0; |
62 | 0 | User[0] = 0; |
63 | 0 | Pass[0] = 0; |
64 | |
|
65 | 0 | *Port = 0; |
66 | 0 | *useTCP = GF_FALSE; |
67 | |
|
68 | 0 | if (!strchr(sURL, ':')) return GF_BAD_PARAM; |
69 | | |
70 | 0 | sep = strchr(sURL, '?'); |
71 | 0 | if (sep) sep[0] = 0; |
72 | | //extract the schema |
73 | 0 | i = 0; |
74 | 0 | while (i<=strlen(sURL)) { |
75 | 0 | if (i==10) return GF_BAD_PARAM; |
76 | 0 | if (sURL[i] == ':') goto found; |
77 | 0 | schema[i] = sURL[i]; |
78 | 0 | i += 1; |
79 | 0 | } |
80 | 0 | if (sep) sep[0] = '?'; |
81 | 0 | return GF_BAD_PARAM; |
82 | | |
83 | 0 | found: |
84 | 0 | schema[i] = 0; |
85 | 0 | if (stricmp(schema, "rtsp") && stricmp(schema, "rtspu") && stricmp(schema, "rtsph") && stricmp(schema, "rtsps") && stricmp(schema, "satip")) return GF_URL_ERROR; |
86 | | //check for user/pass - not allowed |
87 | | /* |
88 | | test = strstr(sURL, "@"); |
89 | | if (test) return GF_NOT_SUPPORTED; |
90 | | */ |
91 | 0 | test = strstr(sURL, "://"); |
92 | 0 | if (!test) { |
93 | 0 | if (sep) sep[0] = '?'; |
94 | 0 | return GF_URL_ERROR; |
95 | 0 | } |
96 | 0 | test += 3; |
97 | | //check for service |
98 | 0 | retest = strstr(test, "/"); |
99 | 0 | if (!retest) { |
100 | 0 | if (sep) sep[0] = '?'; |
101 | 0 | return GF_URL_ERROR; |
102 | 0 | } |
103 | 0 | if (!stricmp(schema, "rtsp") || !stricmp(schema, "satip") || !stricmp(schema, "rtsph") || !stricmp(schema, "rtsps")) |
104 | 0 | *useTCP = GF_TRUE; |
105 | |
|
106 | 0 | service_start = retest; |
107 | | //check for port |
108 | 0 | char *port = strrchr(test, ':'); |
109 | 0 | retest = (port<retest) ? port : NULL; |
110 | | /*IPV6 address*/ |
111 | 0 | if (retest && strchr(retest, ']')) retest = NULL; |
112 | |
|
113 | 0 | if (retest && strstr(retest, "/")) { |
114 | 0 | retest += 1; |
115 | 0 | i=0; |
116 | 0 | while (i<strlen(retest) && i<1023) { |
117 | 0 | if (retest[i] == '/') break; |
118 | 0 | text[i] = retest[i]; |
119 | 0 | i += 1; |
120 | 0 | } |
121 | 0 | text[i] = 0; |
122 | 0 | *Port = atoi(text); |
123 | 0 | } |
124 | |
|
125 | 0 | char *sep_auth = strchr(test, '@'); |
126 | 0 | if (sep_auth>service_start) sep_auth=NULL; |
127 | 0 | if (sep_auth) { |
128 | 0 | sep_auth[0] = 0; |
129 | 0 | char *psep = strchr(test, ':'); |
130 | 0 | if (psep) psep[0] = 0; |
131 | 0 | strncpy(User, test, 1023); |
132 | 0 | User[1023]=0; |
133 | 0 | if (psep) { |
134 | 0 | strncpy(Pass, psep+1, 1023); |
135 | 0 | Pass[1023]=0; |
136 | 0 | if (psep) psep[0] = ':'; |
137 | 0 | } |
138 | |
|
139 | 0 | sep_auth[0] = '@'; |
140 | 0 | test = sep_auth+1; |
141 | 0 | } |
142 | | |
143 | | //get the server name |
144 | 0 | is_ipv6 = GF_FALSE; |
145 | 0 | len = (u32) strlen(test); |
146 | 0 | i=0; |
147 | 0 | while (i<len) { |
148 | 0 | if (test[i]=='[') is_ipv6 = GF_TRUE; |
149 | 0 | else if (test[i]==']') is_ipv6 = GF_FALSE; |
150 | 0 | if ( (test[i] == '/') || (!is_ipv6 && (test[i] == ':')) ) break; |
151 | 0 | if (i>=GF_ARRAY_LENGTH(text)) break; |
152 | 0 | text[i] = test[i]; |
153 | 0 | i += 1; |
154 | 0 | } |
155 | 0 | text[MIN(i, GF_ARRAY_LENGTH(text)-1)] = 0; |
156 | 0 | strncpy(Server, text, 1024); |
157 | 0 | Server[1023]=0; |
158 | 0 | if (sep) sep[0] = '?'; |
159 | |
|
160 | 0 | if (service_start) { |
161 | 0 | strncpy(Service, service_start+1, 1023); |
162 | 0 | Service[1023]=0; |
163 | 0 | } else { |
164 | 0 | Service[0]=0; |
165 | 0 | } |
166 | 0 | return GF_OK; |
167 | 0 | } |
168 | | |
169 | | |
170 | | //create a new GF_RTSPSession from URL - DO NOT USE WITH SDP |
171 | | GF_EXPORT |
172 | | GF_RTSPSession *gf_rtsp_session_new(char *sURL, u16 DefaultPort) |
173 | 0 | { |
174 | 0 | GF_RTSPSession *sess; |
175 | 0 | char server[1024], service[1024], user[1024], pass[1024]; |
176 | 0 | GF_Err e; |
177 | 0 | u16 Port; |
178 | 0 | Bool UseTCP; |
179 | |
|
180 | 0 | if (!sURL) return NULL; |
181 | | |
182 | 0 | e = RTSP_UnpackURL(sURL, server, &Port, service, &UseTCP, user, pass); |
183 | 0 | if (e) return NULL; |
184 | | |
185 | 0 | GF_SAFEALLOC(sess, GF_RTSPSession); |
186 | 0 | if (!sess) return NULL; |
187 | 0 | sess->ConnectionType = UseTCP ? GF_SOCK_TYPE_TCP : GF_SOCK_TYPE_UDP; |
188 | |
|
189 | 0 | #ifdef GPAC_HAS_SSL |
190 | 0 | if (!strncmp(sURL, "rtsps://", 8) |
191 | 0 | || (!strncmp(sURL, "rtsph://", 8) && ((Port==443) || (Port == 8443))) |
192 | 0 | ) { |
193 | 0 | sess->use_ssl = GF_TRUE; |
194 | 0 | sess->ConnectionType = GF_SOCK_TYPE_TCP; |
195 | 0 | } |
196 | 0 | #endif |
197 | |
|
198 | 0 | if (Port) sess->Port = Port; |
199 | 0 | #ifdef GPAC_HAS_SSL |
200 | 0 | else if (sess->use_ssl) sess->Port = 322; |
201 | 0 | #endif |
202 | 0 | else if (DefaultPort) sess->Port = DefaultPort; |
203 | 0 | else sess->Port = 554; |
204 | | |
205 | | //HTTP tunnel |
206 | 0 | if ((sess->Port == 80) || (sess->Port == 8080) || !strnicmp(sURL, "rtsph://", 8)){ |
207 | 0 | sess->ConnectionType = GF_SOCK_TYPE_TCP; |
208 | 0 | sess->tunnel_mode = RTSP_HTTP_CLIENT; |
209 | 0 | } |
210 | |
|
211 | 0 | gf_rtsp_set_buffer_size(sess, RTSP_PCK_SIZE); |
212 | |
|
213 | 0 | sess->Server = gf_strdup(server); |
214 | 0 | sess->Service = gf_strdup(service); |
215 | 0 | if (user[0]) |
216 | 0 | sess->User = gf_strdup(user); |
217 | 0 | if (pass[0]) |
218 | 0 | sess->Pass = gf_strdup(pass); |
219 | 0 | sess->TCPChannels = gf_list_new(); |
220 | 0 | gf_rtsp_session_reset(sess, GF_FALSE); |
221 | 0 | return sess; |
222 | 0 | } |
223 | | |
224 | | GF_EXPORT |
225 | | void gf_rtsp_session_set_netcap_id(GF_RTSPSession *sess, const char *netcap_id) |
226 | 0 | { |
227 | 0 | if (sess) sess->netcap_id = netcap_id; |
228 | 0 | } |
229 | | |
230 | | GF_EXPORT |
231 | | void gf_rtsp_reset_aggregation(GF_RTSPSession *sess) |
232 | 0 | { |
233 | 0 | if (!sess) return; |
234 | | |
235 | 0 | if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL) { |
236 | 0 | strcpy(sess->RTSPLastRequest, "RESET"); |
237 | | //skip all we haven't received |
238 | 0 | sess->CSeq += sess->NbPending; |
239 | 0 | sess->NbPending = 0; |
240 | 0 | } |
241 | 0 | sess->RTSP_State = GF_RTSP_STATE_INIT; |
242 | 0 | } |
243 | | |
244 | | void RemoveTCPChannels(GF_RTSPSession *sess) |
245 | 0 | { |
246 | 0 | while (gf_list_count(sess->TCPChannels)) { |
247 | 0 | GF_TCPChan *ch = (GF_TCPChan*)gf_list_get(sess->TCPChannels, 0); |
248 | 0 | gf_free(ch); |
249 | 0 | gf_list_rem(sess->TCPChannels, 0); |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | |
254 | | GF_EXPORT |
255 | | u32 gf_rtsp_session_reset(GF_RTSPSession *sess, Bool ResetConnection) |
256 | 0 | { |
257 | 0 | sess->last_session_id = NULL; |
258 | 0 | sess->NeedConnection = 1; |
259 | |
|
260 | 0 | if (ResetConnection) { |
261 | 0 | if (sess->connection) gf_sk_del(sess->connection); |
262 | 0 | sess->connection = NULL; |
263 | 0 | if (sess->http) { |
264 | 0 | gf_sk_del(sess->http); |
265 | 0 | sess->http = NULL; |
266 | 0 | if (sess->tunnel_mode<RTSP_HTTP_DISABLE) |
267 | 0 | sess->tunnel_mode = 0; |
268 | 0 | } |
269 | 0 | sess->tunnel_state = 0; |
270 | 0 | #ifdef GPAC_HAS_SSL |
271 | 0 | if (sess->ssl) { |
272 | 0 | gf_ssl_del(sess->ssl); |
273 | 0 | sess->ssl = NULL; |
274 | 0 | } |
275 | 0 | if (sess->ssl_http) { |
276 | 0 | gf_ssl_del(sess->ssl_http); |
277 | 0 | sess->ssl_http = NULL; |
278 | 0 | } |
279 | 0 | #endif |
280 | |
|
281 | 0 | } |
282 | |
|
283 | 0 | sess->RTSP_State = GF_RTSP_STATE_INIT; |
284 | 0 | sess->InterID = (u8) -1; |
285 | 0 | sess->pck_start = sess->payloadSize = 0; |
286 | 0 | sess->CurrentPos = sess->CurrentSize = 0; |
287 | 0 | strcpy(sess->RTSPLastRequest, ""); |
288 | 0 | RemoveTCPChannels(sess); |
289 | | //CSeq 1 is for regular rtsp -> rtsps switch, 0 is for rtsph->rtsph+tls |
290 | 0 | if (sess->CSeq<=1) { |
291 | 0 | sess->nb_retry++; |
292 | |
|
293 | 0 | #ifdef GPAC_HAS_SSL |
294 | 0 | if ((sess->nb_retry>2) |
295 | 0 | && !sess->use_ssl |
296 | 0 | && !gf_opts_get_bool("core", "no-tls-rcfg") |
297 | 0 | ) { |
298 | 0 | sess->use_ssl = GF_TRUE; |
299 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Connection closed by server %s, retrying with TLS\n", sess->Server)); |
300 | 0 | } |
301 | 0 | #endif |
302 | 0 | return sess->nb_retry; |
303 | 0 | } |
304 | 0 | return 0; |
305 | 0 | } |
306 | | |
307 | | |
308 | | #ifdef GPAC_HAS_SSL |
309 | | Bool gf_rtsp_session_needs_ssl(GF_RTSPSession *sess) |
310 | 0 | { |
311 | 0 | if (!sess) return GF_FALSE; |
312 | 0 | if (sess->use_ssl && !sess->ssl_ctx) return GF_TRUE; |
313 | 0 | return GF_FALSE; |
314 | 0 | } |
315 | | #endif |
316 | | |
317 | | GF_EXPORT |
318 | | void gf_rtsp_session_del(GF_RTSPSession *sess) |
319 | 0 | { |
320 | 0 | if (!sess) return; |
321 | | |
322 | 0 | gf_rtsp_session_reset(sess, GF_FALSE); |
323 | |
|
324 | 0 | if (sess->connection) gf_sk_del(sess->connection); |
325 | 0 | if (sess->http) gf_sk_del(sess->http); |
326 | 0 | if (sess->Server) gf_free(sess->Server); |
327 | 0 | if (sess->Service) gf_free(sess->Service); |
328 | 0 | gf_list_del(sess->TCPChannels); |
329 | 0 | if (sess->rtsp_pck_buf) gf_free(sess->rtsp_pck_buf); |
330 | 0 | gf_free(sess->tcp_buffer); |
331 | 0 | if (sess->HTTP_Cookie) gf_free(sess->HTTP_Cookie); |
332 | 0 | if (sess->User) gf_free(sess->User); |
333 | 0 | if (sess->Pass) gf_free(sess->Pass); |
334 | 0 | if (sess->async_buf) gf_free(sess->async_buf); |
335 | |
|
336 | 0 | #ifdef GPAC_HAS_SSL |
337 | 0 | if (sess->ssl) gf_ssl_del(sess->ssl); |
338 | 0 | if (sess->ssl_http) gf_ssl_del(sess->ssl_http); |
339 | 0 | #endif |
340 | |
|
341 | 0 | gf_free(sess); |
342 | 0 | } |
343 | | |
344 | | #ifdef GPAC_HAS_SSL |
345 | | GF_Err gf_rtsp_set_ssl_ctx(GF_RTSPSession *sess, void *ssl_CTX) |
346 | 0 | { |
347 | 0 | if (!sess) return GF_BAD_PARAM; |
348 | 0 | sess->ssl_ctx = ssl_CTX; |
349 | 0 | return GF_OK; |
350 | 0 | } |
351 | | #endif |
352 | | |
353 | | |
354 | | GF_EXPORT |
355 | | u32 gf_rtsp_get_session_state(GF_RTSPSession *sess) |
356 | 0 | { |
357 | 0 | u32 state; |
358 | 0 | if (!sess) return GF_RTSP_STATE_INVALIDATED; |
359 | | |
360 | 0 | state = sess->RTSP_State; |
361 | 0 | return state; |
362 | 0 | } |
363 | | |
364 | | #if 0 //unused |
365 | | char *gf_rtsp_get_last_request(GF_RTSPSession *sess) |
366 | | { |
367 | | if (!sess) return NULL; |
368 | | return sess->RTSPLastRequest; |
369 | | } |
370 | | #endif |
371 | | |
372 | | |
373 | | //check whether the url contains server and service name |
374 | | //no thread protection as this is const throughout the session |
375 | | GF_EXPORT |
376 | | Bool gf_rtsp_is_my_session(GF_RTSPSession *sess, char *url) |
377 | 0 | { |
378 | 0 | if (!sess) return GF_FALSE; |
379 | 0 | if (!strstr(url, sess->Server)) return GF_FALSE; |
380 | | //same url or sub-url |
381 | 0 | if (strstr(url, sess->Service)) return GF_TRUE; |
382 | 0 | return GF_FALSE; |
383 | 0 | } |
384 | | |
385 | | #if 0 //unused |
386 | | const char *gf_rtsp_get_last_session_id(GF_RTSPSession *sess) |
387 | | { |
388 | | if (!sess) return NULL; |
389 | | return sess->last_session_id; |
390 | | } |
391 | | #endif |
392 | | |
393 | | GF_EXPORT |
394 | | const char *gf_rtsp_get_server_name(GF_RTSPSession *sess) |
395 | 0 | { |
396 | 0 | if (!sess) return NULL; |
397 | 0 | return sess->Server; |
398 | 0 | } |
399 | | |
400 | | GF_EXPORT |
401 | | u16 gf_rtsp_get_session_port(GF_RTSPSession *sess) |
402 | 0 | { |
403 | 0 | return (sess ? sess->Port : 0); |
404 | 0 | } |
405 | | |
406 | | GF_EXPORT |
407 | | Bool gf_rtsp_use_tls(GF_RTSPSession *sess) |
408 | 0 | { |
409 | | #ifdef GPAC_HAS_TLS |
410 | | return (sess ? sess->use_tls : GF_FALSE); |
411 | | #endif |
412 | 0 | return GF_FALSE; |
413 | 0 | } |
414 | | |
415 | | GF_Err gf_rtsp_check_connection(GF_RTSPSession *sess) |
416 | 0 | { |
417 | 0 | GF_Err e; |
418 | 0 | if (!sess) return GF_BAD_PARAM; |
419 | | //active, return |
420 | 0 | if (!sess->NeedConnection) { |
421 | 0 | if (sess->tunnel_state) return gf_rtsp_http_tunnel_setup(sess); |
422 | 0 | return gf_rstp_flush_buffer(sess); |
423 | 0 | } |
424 | | |
425 | | //socket is destroyed, recreate |
426 | 0 | if (!sess->connection) { |
427 | 0 | sess->connection = gf_sk_new_ex(sess->ConnectionType, sess->netcap_id); |
428 | 0 | if (!sess->connection) return GF_OUT_OF_MEM; |
429 | | |
430 | | //work in non-blocking mode |
431 | 0 | gf_sk_set_block_mode(sess->connection, GF_TRUE); |
432 | 0 | sess->timeout_in = gf_opts_get_int("core", "tcp-timeout"); |
433 | 0 | if (!sess->timeout_in) sess->timeout_in = 5000; |
434 | 0 | sess->timeout_in += gf_sys_clock(); |
435 | 0 | if (sess->SockBufferSize) gf_sk_set_buffer_size(sess->connection, GF_FALSE, sess->SockBufferSize); |
436 | 0 | } |
437 | | |
438 | | //the session is down, reconnect |
439 | 0 | #ifdef GPAC_HAS_SSL |
440 | 0 | if (!sess->ssl_connect_pending) |
441 | 0 | #endif |
442 | 0 | { |
443 | 0 | e = gf_sk_connect(sess->connection, sess->Server, sess->Port, NULL); |
444 | 0 | if (e) { |
445 | 0 | if (e==GF_IP_NETWORK_EMPTY) { |
446 | 0 | if (sess->timeout_in < gf_sys_clock()) |
447 | 0 | e = GF_IP_CONNECTION_FAILURE; |
448 | 0 | } |
449 | 0 | return e; |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | 0 | #ifdef GPAC_HAS_SSL |
454 | 0 | if (sess->use_ssl) { |
455 | 0 | if (!sess->ssl_ctx) |
456 | 0 | return GF_IP_CONNECTION_FAILURE; |
457 | | |
458 | 0 | if (!sess->ssl) { |
459 | 0 | sess->ssl = SSL_new(sess->ssl_ctx); |
460 | 0 | SSL_set_fd(sess->ssl, gf_sk_get_handle(sess->connection)); |
461 | 0 | SSL_ctrl(sess->ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void*) sess->Server); |
462 | 0 | SSL_set_connect_state(sess->ssl); |
463 | 0 | SSL_set_mode(sess->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER|SSL_MODE_ENABLE_PARTIAL_WRITE); |
464 | 0 | SSL_set_alpn_protos(sess->ssl, NULL, 0); |
465 | 0 | } |
466 | |
|
467 | 0 | sess->ssl_connect_pending = 0; |
468 | 0 | int ret = SSL_connect(sess->ssl); |
469 | 0 | if (ret<=0) { |
470 | 0 | ret = SSL_get_error(sess->ssl, ret); |
471 | 0 | if (ret==SSL_ERROR_SSL) { |
472 | 0 | char msg[1024]; |
473 | 0 | SSL_load_error_strings(); |
474 | 0 | ERR_error_string_n(ERR_get_error(), msg, 1023); |
475 | 0 | msg[1023]=0; |
476 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %s\n", msg)); |
477 | 0 | return GF_IP_CONNECTION_FAILURE; |
478 | 0 | } else if ((ret==SSL_ERROR_WANT_READ) || (ret==SSL_ERROR_WANT_WRITE)) { |
479 | 0 | sess->ssl_connect_pending = 1; |
480 | 0 | return GF_IP_NETWORK_EMPTY; |
481 | 0 | } else { |
482 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %d\n", ret)); |
483 | 0 | return GF_REMOTE_SERVICE_ERROR; |
484 | 0 | } |
485 | 0 | } else { |
486 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[SSL] connected\n")); |
487 | 0 | } |
488 | | |
489 | 0 | Bool success = gf_ssl_check_cert(sess->ssl, sess->Server); |
490 | 0 | if (!success) { |
491 | 0 | return GF_AUTHENTICATION_FAILURE; |
492 | 0 | } |
493 | 0 | } |
494 | 0 | #endif |
495 | | |
496 | 0 | sess->NeedConnection = 0; |
497 | 0 | if (!sess->http && (sess->tunnel_mode==RTSP_HTTP_CLIENT)) { |
498 | 0 | e = gf_rtsp_http_tunnel_setup(sess); |
499 | 0 | if (e) return e; |
500 | 0 | } |
501 | 0 | return gf_rstp_flush_buffer(sess); |
502 | 0 | } |
503 | | |
504 | | |
505 | | GF_Err gf_rtsp_send_data(GF_RTSPSession *sess, u8 *buffer, u32 Size) |
506 | 0 | { |
507 | 0 | GF_Err e; |
508 | 0 | u32 Size64; |
509 | |
|
510 | 0 | e = gf_rtsp_check_connection(sess); |
511 | 0 | if (e) return e; |
512 | | |
513 | | //RTSP requests on HTTP are base 64 encoded |
514 | 0 | if (sess->tunnel_mode==RTSP_HTTP_CLIENT) { |
515 | 0 | char *buf64 = gf_malloc(sizeof(char)*Size*2); |
516 | 0 | if (!buf64) return GF_OUT_OF_MEM; |
517 | 0 | Size64 = gf_base64_encode(buffer, Size, buf64, Size*2); |
518 | | //send on http connection |
519 | 0 | e = gf_rtsp_write_sock(sess, buf64, Size64); |
520 | 0 | gf_free(buf64); |
521 | 0 | return e; |
522 | 0 | } else { |
523 | 0 | return gf_rtsp_write_sock(sess, buffer, Size); |
524 | 0 | } |
525 | 0 | } |
526 | | |
527 | | |
528 | | |
529 | | static GF_TCPChan *GetTCPChannel(GF_RTSPSession *sess, u8 rtpID, u8 rtcpID, Bool RemoveIt) |
530 | 0 | { |
531 | 0 | GF_TCPChan *ptr; |
532 | 0 | u32 i, count = gf_list_count(sess->TCPChannels); |
533 | 0 | for (i=0; i<count; i++) { |
534 | 0 | ptr = (GF_TCPChan *)gf_list_get(sess->TCPChannels, i); |
535 | 0 | if (ptr->rtpID == rtpID) goto exit; |
536 | 0 | if (ptr->rtcpID == rtcpID) goto exit; |
537 | 0 | } |
538 | 0 | return NULL; |
539 | 0 | exit: |
540 | 0 | if (RemoveIt) gf_list_rem(sess->TCPChannels, i); |
541 | 0 | return ptr; |
542 | 0 | } |
543 | | |
544 | | |
545 | | GF_Err gf_rtsp_do_deinterleave(GF_RTSPSession *sess) |
546 | 0 | { |
547 | 0 | GF_TCPChan *ch; |
548 | 0 | Bool IsRTCP; |
549 | 0 | u8 InterID; |
550 | 0 | u16 paySize; |
551 | 0 | u32 res, Size; |
552 | 0 | char *buffer; |
553 | |
|
554 | 0 | if (!sess) return GF_SERVICE_ERROR; |
555 | | |
556 | 0 | Size = sess->CurrentSize - sess->CurrentPos; |
557 | 0 | buffer = sess->tcp_buffer + sess->CurrentPos; |
558 | | |
559 | | //we do not work with just a header -> force a refill |
560 | 0 | if (!Size) |
561 | 0 | return gf_rtsp_fill_buffer(sess); |
562 | 0 | if (Size <= 4) |
563 | 0 | return gf_rtsp_refill_buffer(sess); |
564 | | |
565 | | //break if we get RTSP response on the wire |
566 | 0 | if (!strncmp(buffer, "RTSP", 4) || !strncmp(buffer, "HTTP", 4)) |
567 | 0 | return GF_EOS; |
568 | | |
569 | | //new packet |
570 | 0 | if (!sess->pck_start && (buffer[0] == '$')) { |
571 | 0 | InterID = buffer[1]; |
572 | 0 | paySize = ((buffer[2] << 8) & 0xFF00) | (buffer[3] & 0xFF); |
573 | | /*this may be NULL (data fetched after a teardown) - resync and return*/ |
574 | 0 | ch = GetTCPChannel(sess, InterID, InterID, GF_FALSE); |
575 | | |
576 | | /*then check whether this is a full packet or a split*/ |
577 | 0 | if (paySize <= Size-4) { |
578 | 0 | if (ch) { |
579 | 0 | IsRTCP = (ch->rtcpID == InterID) ? GF_TRUE : GF_FALSE; |
580 | 0 | sess->RTSP_SignalData(sess, ch->ch_ptr, buffer+4, paySize, IsRTCP); |
581 | 0 | } |
582 | 0 | sess->CurrentPos += paySize+4; |
583 | 0 | gf_fatal_assert(sess->CurrentPos <= sess->CurrentSize); |
584 | 0 | } else { |
585 | | /*missed end of pck ?*/ |
586 | 0 | if (sess->payloadSize) { |
587 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP over RTSP] Missed end of packet (%d bytes) in stream %d\n", sess->payloadSize - sess->pck_start, sess->InterID)); |
588 | 0 | ch = GetTCPChannel(sess, sess->InterID, sess->InterID, GF_FALSE); |
589 | 0 | if (ch) { |
590 | 0 | IsRTCP = (ch->rtcpID == sess->InterID) ? GF_TRUE : GF_FALSE; |
591 | 0 | sess->RTSP_SignalData(sess, ch->ch_ptr, sess->rtsp_pck_buf, sess->payloadSize, IsRTCP); |
592 | 0 | } |
593 | 0 | } |
594 | 0 | sess->InterID = InterID; |
595 | 0 | sess->payloadSize = paySize; |
596 | 0 | sess->pck_start = Size-4; |
597 | 0 | if (sess->rtsp_pck_size < paySize) { |
598 | 0 | sess->rtsp_pck_buf = (char *)gf_realloc(sess->rtsp_pck_buf, sizeof(char)*paySize); |
599 | 0 | sess->rtsp_pck_size = paySize; |
600 | 0 | } |
601 | 0 | memcpy(sess->rtsp_pck_buf, buffer+4, Size-4); |
602 | 0 | sess->CurrentPos += Size; |
603 | 0 | gf_fatal_assert(sess->CurrentPos <= sess->CurrentSize); |
604 | 0 | } |
605 | 0 | } |
606 | | /*end of packet*/ |
607 | 0 | else if (sess->payloadSize - sess->pck_start <= Size) { |
608 | | // GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP over RTSP] Missed beginning of packet (%d bytes) in stream %d\n", Size, sess->InterID)); |
609 | |
|
610 | 0 | res = sess->payloadSize - sess->pck_start; |
611 | 0 | if (res) |
612 | 0 | memcpy(sess->rtsp_pck_buf + sess->pck_start, buffer, res); |
613 | | //flush - same as above, don't complain if channel not found |
614 | 0 | ch = GetTCPChannel(sess, sess->InterID, sess->InterID, GF_FALSE); |
615 | 0 | if (ch) { |
616 | 0 | IsRTCP = (ch->rtcpID == sess->InterID) ? GF_TRUE : GF_FALSE; |
617 | 0 | sess->RTSP_SignalData(sess, ch->ch_ptr, sess->rtsp_pck_buf, sess->payloadSize, IsRTCP); |
618 | 0 | } |
619 | 0 | sess->payloadSize = 0; |
620 | 0 | sess->pck_start = 0; |
621 | 0 | sess->InterID = (u8) -1; |
622 | 0 | sess->CurrentPos += res; |
623 | 0 | gf_fatal_assert(sess->CurrentPos <= sess->CurrentSize); |
624 | 0 | } |
625 | | /*middle of packet*/ |
626 | 0 | else { |
627 | | // GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP over RTSP] Missed beginning of RTP packet in stream %d\n", sess->InterID)); |
628 | 0 | memcpy(sess->rtsp_pck_buf + sess->pck_start, buffer, Size); |
629 | 0 | sess->pck_start += Size; |
630 | 0 | sess->CurrentPos += Size; |
631 | 0 | gf_fatal_assert(sess->CurrentPos <= sess->CurrentSize); |
632 | 0 | } |
633 | 0 | return GF_OK; |
634 | 0 | } |
635 | | |
636 | | |
637 | | GF_EXPORT |
638 | | GF_Err gf_rtsp_session_read(GF_RTSPSession *sess) |
639 | 0 | { |
640 | 0 | GF_Err e; |
641 | 0 | if (!sess) return GF_BAD_PARAM; |
642 | | |
643 | 0 | e = gf_rtsp_fill_buffer(sess); |
644 | 0 | if (!e) { |
645 | | //read as much as possible (will break if no data or if "RTSP" is found) |
646 | 0 | while (1) { |
647 | 0 | e = gf_rtsp_do_deinterleave(sess); |
648 | 0 | if (e) return e; |
649 | 0 | } |
650 | 0 | } |
651 | 0 | return e; |
652 | 0 | } |
653 | | |
654 | | |
655 | | GF_EXPORT |
656 | | u32 gf_rtsp_unregister_interleave(GF_RTSPSession *sess, u8 LowInterID) |
657 | 0 | { |
658 | 0 | u32 res; |
659 | 0 | GF_TCPChan *ptr; |
660 | 0 | if (!sess) return 0; |
661 | | |
662 | 0 | ptr = GetTCPChannel(sess, LowInterID, LowInterID, GF_TRUE); |
663 | 0 | if (ptr) gf_free(ptr); |
664 | 0 | res = gf_list_count(sess->TCPChannels); |
665 | 0 | if (!res) sess->interleaved = GF_FALSE; |
666 | 0 | return res; |
667 | 0 | } |
668 | | |
669 | | GF_EXPORT |
670 | | GF_Err gf_rtsp_register_interleave(GF_RTSPSession *sess, void *the_ch, u8 LowInterID, u8 HighInterID) |
671 | 0 | { |
672 | 0 | GF_TCPChan *ptr; |
673 | |
|
674 | 0 | if (!sess) return GF_BAD_PARAM; |
675 | | |
676 | | //do NOT register twice |
677 | 0 | ptr = GetTCPChannel(sess, LowInterID, HighInterID, GF_FALSE); |
678 | 0 | if (!ptr) { |
679 | 0 | ptr = (GF_TCPChan *)gf_malloc(sizeof(GF_TCPChan)); |
680 | 0 | ptr->ch_ptr = the_ch; |
681 | 0 | ptr->rtpID = LowInterID; |
682 | 0 | ptr->rtcpID = HighInterID; |
683 | 0 | gf_list_add(sess->TCPChannels, ptr); |
684 | 0 | } |
685 | 0 | sess->interleaved=GF_TRUE; |
686 | 0 | return GF_OK; |
687 | 0 | } |
688 | | |
689 | | |
690 | | GF_EXPORT |
691 | | GF_Err gf_rtsp_set_interleave_callback(GF_RTSPSession *sess, gf_rtsp_interleave_callback SignalData) |
692 | 0 | { |
693 | 0 | if (!sess) return GF_BAD_PARAM; |
694 | | |
695 | | //only if existing |
696 | 0 | if (SignalData) sess->RTSP_SignalData = SignalData; |
697 | |
|
698 | 0 | if (!sess->rtsp_pck_buf || (sess->rtsp_pck_size != RTSP_PCK_SIZE) ) { |
699 | 0 | if (!sess->rtsp_pck_buf) |
700 | 0 | sess->pck_start = 0; |
701 | 0 | sess->rtsp_pck_size = RTSP_PCK_SIZE; |
702 | 0 | sess->rtsp_pck_buf = (char *)gf_realloc(sess->rtsp_pck_buf, sizeof(char)*sess->rtsp_pck_size); |
703 | 0 | } |
704 | 0 | return GF_OK; |
705 | 0 | } |
706 | | |
707 | | GF_EXPORT |
708 | | GF_Err gf_rtsp_set_buffer_size(GF_RTSPSession *sess, u32 BufferSize) |
709 | 0 | { |
710 | 0 | if (!sess) return GF_BAD_PARAM; |
711 | 0 | if (sess->SockBufferSize >= BufferSize) return GF_OK; |
712 | 0 | sess->SockBufferSize = BufferSize; |
713 | 0 | sess->tcp_buffer = gf_realloc(sess->tcp_buffer, (BufferSize+1)); |
714 | 0 | return GF_OK; |
715 | 0 | } |
716 | | |
717 | | static GF_Err rstp_do_write_sock(GF_RTSPSession *sess, GF_Socket *sock, const u8 *buffer, u32 size, u32 *written) |
718 | 0 | { |
719 | 0 | #ifdef GPAC_HAS_SSL |
720 | 0 | SSL *ssl_sock = (sock==sess->http) ? sess->ssl_http : sess->ssl; |
721 | 0 | if (ssl_sock) { |
722 | 0 | u32 idx=0; |
723 | 0 | s32 nb_tls_blocks = size/16000; |
724 | 0 | if (written) |
725 | 0 | *written = 0; |
726 | 0 | while (nb_tls_blocks>=0) { |
727 | 0 | u32 len, to_write = 16000; |
728 | 0 | if (nb_tls_blocks==0) |
729 | 0 | to_write = size - idx*16000; |
730 | |
|
731 | 0 | len = SSL_write(ssl_sock, buffer + idx*16000, to_write); |
732 | 0 | nb_tls_blocks--; |
733 | 0 | idx++; |
734 | |
|
735 | 0 | if (len != to_write) { |
736 | 0 | int err = SSL_get_error(ssl_sock, len); |
737 | 0 | if ((err==SSL_ERROR_WANT_READ) || (err==SSL_ERROR_WANT_WRITE)) { |
738 | 0 | return GF_IP_NETWORK_EMPTY; |
739 | 0 | } |
740 | 0 | if (err==SSL_ERROR_SSL) { |
741 | 0 | char msg[1024]; |
742 | 0 | SSL_load_error_strings(); |
743 | 0 | ERR_error_string_n(ERR_get_error(), msg, 1023); |
744 | 0 | msg[1023]=0; |
745 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot send, error %s\n", msg)); |
746 | 0 | } |
747 | 0 | return GF_IP_NETWORK_FAILURE; |
748 | 0 | } |
749 | 0 | if (written) |
750 | 0 | *written += to_write; |
751 | 0 | } |
752 | 0 | return GF_OK; |
753 | 0 | } |
754 | 0 | #endif |
755 | 0 | return gf_sk_send_ex(sock, buffer, size, written); |
756 | 0 | } |
757 | | |
758 | | |
759 | | static Bool HTTP_RandInit = GF_TRUE; |
760 | | |
761 | 0 | #define HTTP10_RSP_OK "HTTP/1.0 200 OK" |
762 | 0 | #define HTTP11_RSP_OK "HTTP/1.1 200 OK" |
763 | | |
764 | | static GF_Err gf_rtsp_http_tunnel_setup(GF_RTSPSession *sess) |
765 | 0 | { |
766 | 0 | GF_Err e; |
767 | 0 | u32 size; |
768 | 0 | s32 pos; |
769 | 0 | const char *ua; |
770 | 0 | char buffer[GF_RTSP_DEFAULT_BUFFER]; |
771 | |
|
772 | 0 | if (!sess) return GF_BAD_PARAM; |
773 | | |
774 | 0 | if (!sess->tunnel_state) { |
775 | | //generate http cookie |
776 | 0 | if (HTTP_RandInit) { |
777 | 0 | gf_rand_init(GF_FALSE); |
778 | 0 | HTTP_RandInit = GF_FALSE; |
779 | 0 | } |
780 | 0 | if (!sess->HTTP_Cookie) { |
781 | 0 | char szBuf[30]; |
782 | 0 | u32 r = gf_rand(); |
783 | 0 | snprintf(szBuf, 29, "GPAC_%x_"LLX, r, gf_sys_clock_high_res() ); |
784 | 0 | szBuf[29]=0; |
785 | 0 | sess->HTTP_Cookie = gf_strdup(szBuf); |
786 | 0 | if (!sess->HTTP_Cookie) return GF_OUT_OF_MEM; |
787 | 0 | } |
788 | | |
789 | 0 | ua = gf_opts_get_key("core", "user-agent"); |
790 | 0 | if (!ua) ua = "GPAC " GPAC_VERSION; |
791 | | |
792 | | // 1. send "GET /sample.mov HTTP/1.0\r\n ..." |
793 | 0 | memset(buffer, 0, GF_RTSP_DEFAULT_BUFFER); |
794 | 0 | pos = 0; |
795 | 0 | pos += sprintf(buffer + pos, "GET /%s HTTP/1.0\r\n", sess->Service); |
796 | 0 | pos += sprintf(buffer + pos, "User-Agent: %s\r\n", ua); |
797 | 0 | pos += sprintf(buffer + pos, "x-sessioncookie: %s\r\n", sess->HTTP_Cookie); |
798 | 0 | pos += sprintf(buffer + pos, "Accept: application/x-rtsp-tunnelled\r\n" ); |
799 | 0 | pos += sprintf(buffer + pos, "Pragma: no-cache\r\n" ); |
800 | 0 | /*pos += */sprintf(buffer + pos, "Cache-Control: no-cache\r\n\r\n" ); |
801 | |
|
802 | 0 | sess->tunnel_state = 1; |
803 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPTunnel] Sending %s", buffer)); |
804 | | |
805 | | // send it - we assume this will fit the socket buffer |
806 | 0 | e = rstp_do_write_sock(sess, sess->connection, buffer, (u32) strlen(buffer), NULL); |
807 | 0 | if (e) return e; |
808 | 0 | } |
809 | | |
810 | 0 | if (sess->tunnel_state == 1) { |
811 | | // 2. wait for "HTTP/1.0 200 OK" |
812 | 0 | e = gf_rstp_do_read_sock(sess, sess->connection, buffer, GF_RTSP_DEFAULT_BUFFER, &size); |
813 | 0 | if (e) { |
814 | 0 | if ((e==GF_IP_NETWORK_EMPTY) && (sess->timeout_in < gf_sys_clock())) |
815 | 0 | e = GF_IP_CONNECTION_FAILURE; |
816 | 0 | return e; |
817 | 0 | } |
818 | 0 | gf_assert(size); |
819 | 0 | sess->tunnel_state = 2; |
820 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPTunnel] Got reply %s", buffer)); |
821 | | |
822 | | //get HTTP/1.0 200 OK |
823 | 0 | if (strncmp(buffer, HTTP10_RSP_OK, strlen(HTTP10_RSP_OK)) && strncmp(buffer, HTTP11_RSP_OK, strlen(HTTP11_RSP_OK))) { |
824 | 0 | sess->tunnel_state = 0; |
825 | 0 | return GF_REMOTE_SERVICE_ERROR; |
826 | 0 | } |
827 | | |
828 | | // 3. send "POST /sample.mov HTTP/1.0\r\n ..." |
829 | 0 | sess->http = gf_sk_new_ex(GF_SOCK_TYPE_TCP, sess->netcap_id); |
830 | 0 | if (!sess->http) { |
831 | 0 | sess->tunnel_state = 0; |
832 | 0 | return GF_IP_NETWORK_FAILURE; |
833 | 0 | } |
834 | 0 | gf_sk_set_block_mode(sess->http, GF_TRUE); |
835 | 0 | } |
836 | | |
837 | 0 | #ifdef GPAC_HAS_SSL |
838 | 0 | if (!sess->ssl_connect_pending) |
839 | 0 | #endif |
840 | 0 | { |
841 | 0 | e = gf_sk_connect(sess->http, sess->Server, sess->Port, NULL); |
842 | 0 | if (e) { |
843 | 0 | if ((e==GF_IP_NETWORK_EMPTY) && (sess->timeout_in < gf_sys_clock())) |
844 | 0 | e = GF_IP_CONNECTION_FAILURE; |
845 | 0 | return e; |
846 | 0 | } |
847 | 0 | } |
848 | | |
849 | | |
850 | 0 | #ifdef GPAC_HAS_SSL |
851 | 0 | if (sess->use_ssl) { |
852 | 0 | if (!sess->ssl_http) { |
853 | 0 | sess->ssl_http = SSL_new(sess->ssl_ctx); |
854 | 0 | SSL_set_fd(sess->ssl_http, gf_sk_get_handle(sess->http)); |
855 | 0 | SSL_ctrl(sess->ssl_http, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void*) sess->Server); |
856 | 0 | SSL_set_connect_state(sess->ssl_http); |
857 | 0 | SSL_set_mode(sess->ssl_http, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER|SSL_MODE_ENABLE_PARTIAL_WRITE); |
858 | 0 | SSL_set_alpn_protos(sess->ssl_http, NULL, 0); |
859 | 0 | } |
860 | |
|
861 | 0 | sess->ssl_connect_pending = 0; |
862 | 0 | int ret = SSL_connect(sess->ssl_http); |
863 | 0 | if (ret<=0) { |
864 | 0 | ret = SSL_get_error(sess->ssl_http, ret); |
865 | 0 | if (ret==SSL_ERROR_SSL) { |
866 | 0 | char msg[1024]; |
867 | 0 | SSL_load_error_strings(); |
868 | 0 | ERR_error_string_n(ERR_get_error(), msg, 1023); |
869 | 0 | msg[1023]=0; |
870 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %s\n", msg)); |
871 | 0 | return GF_IP_CONNECTION_FAILURE; |
872 | 0 | } else if ((ret==SSL_ERROR_WANT_READ) || (ret==SSL_ERROR_WANT_WRITE)) { |
873 | 0 | sess->ssl_connect_pending = 1; |
874 | 0 | return GF_IP_NETWORK_EMPTY; |
875 | 0 | } else { |
876 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %d\n", ret)); |
877 | 0 | return GF_REMOTE_SERVICE_ERROR; |
878 | 0 | } |
879 | 0 | } else { |
880 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[SSL] connected\n")); |
881 | 0 | } |
882 | 0 | } |
883 | 0 | #endif |
884 | | |
885 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPTunnel] POST channel connected")); |
886 | | |
887 | |
|
888 | 0 | ua = gf_opts_get_key("core", "user-agent"); |
889 | 0 | if (!ua) ua = "GPAC " GPAC_VERSION; |
890 | 0 | memset(buffer, 0, GF_RTSP_DEFAULT_BUFFER); |
891 | 0 | pos = 0; |
892 | 0 | pos += sprintf(buffer + pos, "POST /%s HTTP/1.0\r\n", sess->Service); |
893 | 0 | pos += sprintf(buffer + pos, "User-Agent: %s\r\n", ua); |
894 | 0 | pos += sprintf(buffer + pos, "x-sessioncookie: %s\r\n", sess->HTTP_Cookie); |
895 | 0 | pos += sprintf(buffer + pos, "Accept: application/x-rtsp-tunnelled\r\n"); |
896 | 0 | pos += sprintf(buffer + pos, "Pragma: no-cache\r\n"); |
897 | 0 | pos += sprintf(buffer + pos, "Cache-Control: no-cache\r\n"); |
898 | 0 | pos += sprintf(buffer + pos, "Content-Length: 32767\r\n"); |
899 | 0 | /*pos += */sprintf(buffer + pos, "Expires: Sun. 9 Jan 1972 00:00:00 GMT\r\n\r\n"); |
900 | |
|
901 | 0 | sess->tunnel_state = 0; |
902 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPTunnel] Sending request %s", buffer)); |
903 | | // send it, no need to wait for answer |
904 | 0 | return rstp_do_write_sock(sess, sess->http, buffer, (u32) strlen(buffer), NULL); |
905 | 0 | } |
906 | | |
907 | | /*server-side RTSP sockets*/ |
908 | | |
909 | | static u32 SessionID_RandInit = 0; |
910 | | |
911 | | |
912 | | GF_EXPORT |
913 | | GF_RTSPSession *gf_rtsp_session_new_server(GF_Socket *rtsp_listener, Bool allow_http_tunnel, void *ssl_ctx) |
914 | 0 | { |
915 | 0 | GF_RTSPSession *sess; |
916 | 0 | GF_Socket *new_conn; |
917 | 0 | GF_Err e; |
918 | 0 | u32 fam; |
919 | 0 | u16 port; |
920 | 0 | #ifdef GPAC_HAS_SSL |
921 | 0 | SSL *ssl = NULL; |
922 | 0 | #endif |
923 | |
|
924 | 0 | if (!rtsp_listener) return NULL; |
925 | | |
926 | | |
927 | 0 | e = gf_sk_accept(rtsp_listener, &new_conn); |
928 | 0 | if (!new_conn || e) return NULL; |
929 | | |
930 | 0 | #ifdef GPAC_HAS_SSL |
931 | 0 | if (ssl_ctx) { |
932 | 0 | ssl = gf_ssl_new(ssl_ctx, new_conn, &e); |
933 | 0 | if (e) { |
934 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[SSL] Failed to create TLS session: %s\n", gf_error_to_string(e) )); |
935 | 0 | gf_sk_del(new_conn); |
936 | 0 | return NULL; |
937 | 0 | } |
938 | 0 | SSL_set_mode(ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER|SSL_MODE_ENABLE_PARTIAL_WRITE); |
939 | 0 | } |
940 | 0 | #endif |
941 | | |
942 | 0 | e = gf_sk_get_local_info(new_conn, &port, &fam); |
943 | 0 | if (e) { |
944 | 0 | gf_sk_del(new_conn); |
945 | 0 | return NULL; |
946 | 0 | } |
947 | 0 | e = gf_sk_set_block_mode(new_conn, GF_TRUE); |
948 | 0 | if (e) { |
949 | 0 | gf_sk_del(new_conn); |
950 | 0 | return NULL; |
951 | 0 | } |
952 | 0 | e = gf_sk_server_mode(new_conn, GF_TRUE); |
953 | 0 | if (e) { |
954 | 0 | gf_sk_del(new_conn); |
955 | 0 | return NULL; |
956 | 0 | } |
957 | | |
958 | | //OK create a new session |
959 | 0 | GF_SAFEALLOC(sess, GF_RTSPSession); |
960 | 0 | if (!sess) return NULL; |
961 | | |
962 | 0 | sess->connection = new_conn; |
963 | 0 | sess->Port = port; |
964 | 0 | sess->ConnectionType = fam; |
965 | 0 | #ifdef GPAC_HAS_SSL |
966 | 0 | sess->ssl_ctx = ssl_ctx; |
967 | 0 | sess->ssl = ssl; |
968 | 0 | #endif |
969 | 0 | const char *name = gf_opts_get_key("core", "user-agent"); |
970 | 0 | if (name) { |
971 | 0 | sess->Server = gf_strdup(name); |
972 | 0 | } else { |
973 | 0 | sess->Server = gf_strdup("GPAC-"); |
974 | 0 | gf_dynstrcat(&sess->Server, gf_gpac_version(), NULL); |
975 | 0 | } |
976 | 0 | gf_rtsp_set_buffer_size(sess, 4096); |
977 | 0 | sess->TCPChannels = gf_list_new(); |
978 | 0 | if (!allow_http_tunnel) |
979 | 0 | sess->tunnel_mode = RTSP_HTTP_DISABLE; |
980 | |
|
981 | 0 | return sess; |
982 | 0 | } |
983 | | |
984 | | |
985 | | #if 0 //unused |
986 | | GF_Err gf_rtsp_load_service_name(GF_RTSPSession *sess, char *URL) |
987 | | { |
988 | | char server[1024], service[1024], user[1024], pass[1024]; |
989 | | GF_Err e; |
990 | | u16 Port; |
991 | | Bool UseTCP; |
992 | | u32 type; |
993 | | |
994 | | if (!sess || !URL) return GF_BAD_PARAM; |
995 | | e = RTSP_UnpackURL(URL, server, &Port, service, &UseTCP, user, pass); |
996 | | if (e) return e; |
997 | | |
998 | | type = UseTCP ? GF_SOCK_TYPE_TCP : GF_SOCK_TYPE_UDP; |
999 | | //check the network type matches, otherwise deny client |
1000 | | if (sess->ConnectionType != type) return GF_URL_ERROR; |
1001 | | if (sess->Port != Port) return GF_URL_ERROR; |
1002 | | |
1003 | | //ok |
1004 | | sess->Server = gf_strdup(server); |
1005 | | sess->Service = gf_strdup(service); |
1006 | | return GF_OK; |
1007 | | } |
1008 | | #endif |
1009 | | |
1010 | | GF_EXPORT |
1011 | | char *gf_rtsp_generate_session_id(GF_RTSPSession *sess) |
1012 | 0 | { |
1013 | 0 | u32 one; |
1014 | 0 | u64 res; |
1015 | 0 | char buffer[30]; |
1016 | |
|
1017 | 0 | if (!sess) return NULL; |
1018 | | |
1019 | 0 | if (!SessionID_RandInit) { |
1020 | 0 | SessionID_RandInit = 1; |
1021 | 0 | gf_rand_init(GF_FALSE); |
1022 | 0 | } |
1023 | 0 | one = gf_rand(); |
1024 | 0 | res = one; |
1025 | 0 | res <<= 32; |
1026 | 0 | res+= (PTR_TO_U_CAST sess) + sess->CurrentPos + sess->CurrentSize; |
1027 | 0 | sprintf(buffer, LLU, res); |
1028 | 0 | return gf_strdup(buffer); |
1029 | 0 | } |
1030 | | |
1031 | | |
1032 | | GF_EXPORT |
1033 | | GF_Err gf_rtsp_get_session_ip(GF_RTSPSession *sess, char buffer[GF_MAX_IP_NAME_LEN]) |
1034 | 0 | { |
1035 | 0 | if (!sess || !sess->connection) return GF_BAD_PARAM; |
1036 | 0 | if (gf_sk_get_local_ip(sess->connection, buffer) != GF_OK) |
1037 | 0 | strcpy(buffer, "127.0.0.1"); |
1038 | 0 | return GF_OK; |
1039 | 0 | } |
1040 | | |
1041 | | |
1042 | | #if 0 //unused |
1043 | | u8 gf_rtsp_get_next_interleave_id(GF_RTSPSession *sess) |
1044 | | { |
1045 | | u32 i; |
1046 | | u8 id; |
1047 | | GF_TCPChan *ch; |
1048 | | id = 0; |
1049 | | i=0; |
1050 | | while ((ch = (GF_TCPChan *)gf_list_enum(sess->TCPChannels, &i))) { |
1051 | | if (ch->rtpID >= id) id = ch->rtpID + 1; |
1052 | | if (ch->rtcpID >= id) id = ch->rtcpID + 1; |
1053 | | } |
1054 | | return id; |
1055 | | } |
1056 | | #endif |
1057 | | |
1058 | | GF_EXPORT |
1059 | | GF_Err gf_rtsp_get_remote_address(GF_RTSPSession *sess, char *buf) |
1060 | 0 | { |
1061 | 0 | if (!sess || !sess->connection) return GF_BAD_PARAM; |
1062 | 0 | return gf_sk_get_remote_address(sess->connection, buf); |
1063 | 0 | } |
1064 | | |
1065 | | static GF_Err gf_rtsp_write_sock(GF_RTSPSession *sess, u8 *data, u32 len) |
1066 | 0 | { |
1067 | 0 | u32 remain, written=0; |
1068 | 0 | if (!sess->async_buf_size) { |
1069 | 0 | GF_Err e = rstp_do_write_sock(sess, (sess->http && (sess->tunnel_mode==RTSP_HTTP_CLIENT)) ? sess->http : sess->connection, data, len, &written); |
1070 | 0 | if (e && (e!= GF_IP_NETWORK_EMPTY)) |
1071 | 0 | return e; |
1072 | 0 | if (written==len) return GF_OK; |
1073 | 0 | } |
1074 | | |
1075 | 0 | remain = len - written; |
1076 | 0 | if (sess->async_buf_size + remain > sess->async_buf_alloc) { |
1077 | 0 | sess->async_buf_alloc = sess->async_buf_size+remain; |
1078 | 0 | sess->async_buf = gf_realloc(sess->async_buf, sess->async_buf_alloc); |
1079 | 0 | if (!sess->async_buf) return GF_OUT_OF_MEM; |
1080 | 0 | } |
1081 | 0 | memcpy(sess->async_buf + sess->async_buf_size, data+written, remain); |
1082 | 0 | sess->async_buf_size += remain; |
1083 | 0 | return GF_OK; |
1084 | 0 | } |
1085 | | |
1086 | | GF_EXPORT |
1087 | | GF_Err gf_rtsp_session_write_interleaved(GF_RTSPSession *sess, u32 idx, u8 *pck, u32 pck_size) |
1088 | 0 | { |
1089 | 0 | GF_Err e; |
1090 | 0 | char streamID[4]; |
1091 | 0 | if (!sess || !sess->connection) return GF_BAD_PARAM; |
1092 | | |
1093 | 0 | streamID[0] = '$'; |
1094 | 0 | streamID[1] = (u8) idx; |
1095 | 0 | streamID[2] = (pck_size>>8) & 0xFF; |
1096 | 0 | streamID[3] = pck_size & 0xFF; |
1097 | |
|
1098 | 0 | e = gf_rtsp_write_sock(sess, streamID, 4); |
1099 | 0 | e |= gf_rtsp_write_sock(sess, pck, pck_size); |
1100 | 0 | return e; |
1101 | 0 | } |
1102 | | |
1103 | | |
1104 | | static GF_Err gf_rstp_flush_buffer(GF_RTSPSession *sess) |
1105 | 0 | { |
1106 | 0 | while (sess->async_buf_size) { |
1107 | 0 | u32 written = 0; |
1108 | 0 | GF_Err e = rstp_do_write_sock(sess, (sess->tunnel_mode==RTSP_HTTP_CLIENT) ? sess->http : sess->connection, sess->async_buf, sess->async_buf_size, &written); |
1109 | 0 | if (e) { |
1110 | 0 | if (e!= GF_IP_NETWORK_EMPTY) |
1111 | 0 | sess->async_buf_size = 0; |
1112 | 0 | return e; |
1113 | 0 | } |
1114 | 0 | if (written==sess->async_buf_size) { |
1115 | 0 | sess->async_buf_size = 0; |
1116 | 0 | return GF_OK; |
1117 | 0 | } |
1118 | 0 | memmove(sess->async_buf, sess->async_buf + written, sess->async_buf_size - written); |
1119 | 0 | sess->async_buf_size -= written; |
1120 | 0 | } |
1121 | 0 | return GF_OK; |
1122 | 0 | } |
1123 | | |
1124 | | const char *gf_rtsp_get_session_cookie(GF_RTSPSession *sess) |
1125 | 0 | { |
1126 | 0 | return sess ? sess->HTTP_Cookie : NULL; |
1127 | 0 | } |
1128 | | |
1129 | | GF_Err gf_rtsp_merge_tunnel(GF_RTSPSession *sess, GF_RTSPSession *post_sess) |
1130 | 0 | { |
1131 | 0 | if (!sess || !post_sess) return GF_BAD_PARAM; |
1132 | 0 | if (!sess->HTTP_Cookie || !post_sess->HTTP_Cookie) return GF_BAD_PARAM; |
1133 | 0 | if (strcmp(sess->HTTP_Cookie, post_sess->HTTP_Cookie)) return GF_BAD_PARAM; |
1134 | 0 | if (!post_sess->connection) return GF_BAD_PARAM; |
1135 | | |
1136 | | //move any pending data |
1137 | 0 | if (post_sess->CurrentPos<post_sess->CurrentSize) { |
1138 | 0 | u8 *buf = post_sess->tcp_buffer + post_sess->CurrentPos; |
1139 | 0 | u32 remain = post_sess->CurrentSize - post_sess->CurrentPos; |
1140 | 0 | if (sess->CurrentSize + remain > sess->SockBufferSize) { |
1141 | 0 | sess->SockBufferSize = sess->CurrentSize + remain; |
1142 | 0 | sess->tcp_buffer = gf_realloc(sess->tcp_buffer, sess->SockBufferSize); |
1143 | 0 | if (!sess->tcp_buffer) return GF_OUT_OF_MEM; |
1144 | 0 | } |
1145 | 0 | memcpy(sess->tcp_buffer+sess->CurrentPos, buf, remain); |
1146 | 0 | sess->CurrentSize += remain; |
1147 | 0 | } |
1148 | 0 | sess->tunnel_mode = RTSP_HTTP_SERVER; |
1149 | 0 | sess->http = post_sess->connection; |
1150 | 0 | post_sess->connection = NULL; |
1151 | |
|
1152 | 0 | #ifdef GPAC_HAS_SSL |
1153 | 0 | sess->ssl_http = post_sess->ssl; |
1154 | 0 | post_sess->ssl = NULL; |
1155 | 0 | #endif |
1156 | |
|
1157 | 0 | return GF_OK; |
1158 | 0 | } |
1159 | | |
1160 | | const char *gf_rtsp_get_user(GF_RTSPSession *sess) |
1161 | 0 | { |
1162 | 0 | return sess ? sess->User : NULL; |
1163 | 0 | } |
1164 | | const char *gf_rtsp_get_password(GF_RTSPSession *sess) |
1165 | 0 | { |
1166 | 0 | return sess ? sess->Pass : NULL; |
1167 | 0 | } |
1168 | | |
1169 | | |
1170 | | #endif /*GPAC_DISABLE_STREAMING*/ |