WebRTC SRTP decryption - encryption

I am trying to build an SRTP to RTP stream converter and I am having issues getting the Master Key from the WebRTC peerconnection I am creating.
From what I understand, with a DES exchange, the key is exchange via the SDP exchange and is shown in the a=crypto field. So, this situation seems pretty straight forward(please correct me if I am wrong), but ultimately useless as WebRTC standardization is now demanding that DES should not be used(only Chrome supports it now and it may be removed in the future).
For DTLS there is the fingerprint field in the SDP, is that a hash of the certificate desired to be utilized in the future exchange?[EDIT: After doing some reading, I am thinking that that is not the case] I would think with knowledge of the fingerprint along side the ability to parse through the DTLS packets in the exchange I should be able to grab the Master Key to decode the SRTP stream, but I am hitting a wall as I do not know where to look or even 100% sure if it is possible.
So, in short, is it even feasible(without getting into the lower C++ API and creating my own implementation of WebRTC) to decode the SRTP feed that is created with a WebRTC PeerConnection in Chrome and FireFox(possibly through packet sniffing with the information gleaned from the SDP exchange)?[EDIT: depressingly, it seems that access to the private part of the key(aka, the master key) is not possible...please correct if I am wrong]

tHere is some code using openssl and libsrtp native api
#define SRTP_MASTER_KEY_KEY_LEN 16
#define SRTP_MASTER_KEY_SALT_LEN 14
static void dtls_srtp_init( struct transport_dtls *dtls )
{
/*
When SRTP mode is in effect, different keys are used for ordinary
DTLS record protection and SRTP packet protection. These keys are
generated using a TLS exporter [RFC5705] to generate
2 * (SRTPSecurityParams.master_key_len +
SRTPSecurityParams.master_salt_len) bytes of data
which are assigned as shown below. The per-association context value
is empty.
client_write_SRTP_master_key[SRTPSecurityParams.master_key_len];
server_write_SRTP_master_key[SRTPSecurityParams.master_key_len];
client_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len];
server_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len];
*/
int code;
err_status_t err;
srtp_policy_t policy;
char dtls_buffer[SRTP_MASTER_KEY_KEY_LEN * 2 + SRTP_MASTER_KEY_SALT_LEN * 2];
char client_write_key[SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN];
char server_write_key[SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN];
size_t offset = 0;
/*
The exporter label for this usage is "EXTRACTOR-dtls_srtp". (The
"EXTRACTOR" prefix is for historical compatibility.)
RFC 5764 4.2. Key Derivation
*/
const char * label = "EXTRACTOR-dtls_srtp";
SRTP_PROTECTION_PROFILE * srtp_profile= SSL_get_selected_srtp_profile( dtls->ssl );
/* SSL_export_keying_material exports a value derived from the master secret,
* as specified in RFC 5705. It writes |olen| bytes to |out| given a label and
* optional context. (Since a zero length context is allowed, the |use_context|
* flag controls whether a context is included.)
*
* It returns 1 on success and zero otherwise.
*/
code = SSL_export_keying_material(dtls->ssl,
dtls_buffer,
sizeof(dtls_buffer),
label,
strlen( label),
NULL,
0,
PJ_FALSE);
memcpy(&client_write_key[0], &dtls_buffer[offset], SRTP_MASTER_KEY_KEY_LEN);
offset += SRTP_MASTER_KEY_KEY_LEN;
memcpy(&server_write_key[0], &dtls_buffer[offset], SRTP_MASTER_KEY_KEY_LEN);
offset += SRTP_MASTER_KEY_KEY_LEN;
memcpy(&client_write_key[SRTP_MASTER_KEY_KEY_LEN], &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN);
offset += SRTP_MASTER_KEY_SALT_LEN;
memcpy(&server_write_key[SRTP_MASTER_KEY_KEY_LEN], &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN);
switch( srtp_profile->id )
{
case SRTP_AES128_CM_SHA1_80:
crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
break;
case SRTP_AES128_CM_SHA1_32:
crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); // rtp is 32,
crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // rtcp still 80
break;
default:
assert(0);
}
policy.ssrc.value = 0;
policy.next = NULL;
/* Init transmit direction */
policy.ssrc.type = ssrc_any_outbound;
policy.key = client_write_key;
err = srtp_create(&dtls->srtp_ctx_rx, &policy);
if (err != err_status_ok) {
printf("not working\n");
}
/* Init receive direction */
policy.ssrc.type = ssrc_any_inbound;
policy.key = server_write_key;
err = srtp_create(&dtls->srtp_ctx_tx, &policy);
if (err != err_status_ok) {
printf("not working\n");
}
}

I found 'SSL_export_keying_material'
Which can take a key from SSL mechanism (after DTLS handshake) and use it for SRTP.
I am not an expert, Just hitting the wall like you...

It's not clear if this is your case, but note it's not possible to access the audio/video from (i.e.:unencrypt) the SRTP merely being a passive observer - that's the whole point of having transport encryption.
The protocol (DTLS-SRTP) works roughly like this:
each browser has a unique keypair, usually generated on installation time
The fingerprint of the public part of the keypair of each side in included in the SDP, in the offer and answer.
Both ends negotiate a DTLS connection, through a ordinary DTLS handshake, thus deriving a kind of session key, which is used to secure the (DTLS) connection
The derived session key is used as the SRTP key
If you don't have access to at least one of the private parts of the keypairs, it's not possible at all to decrypt the connection. If the endpoints choose to use a Diffie-Hellman key exchange on the handshake, a passive attacker will not be able to get the derived key, even with access to both private keys. This property is called forward secrecy.
The only reliable way of accessing the SRTP contents is doing the handshake yourself, implementing a active MITM (changing the fingerprints on the SDP) or getting the private key from the browser and restricting DH key-exchange (which, AFAIK, is not possible at all)

Related

How to Perform Concurrent Request-Reply for Asynchronous Tasks with ZeroMQ?

Intention
I want to allow a client to send a task to some server at a fixed address.
The server may take that task and perform it at some arbitrary point in the future, but may still take requests from other clients before then.
After performing the task, the server will reply to the client, which may have been running a blocking wait on the reply.
The work and clients come dynamically, so there can't be a fixed initial number.
The work is done in a non-thread-safe context, so workers can't exist on different threads, so all work should take place in a single thread.
Implementation
The following example1 is not a complete implementation of the server, only a compilable section of the sequence that should be able to take place (but is in reality hanging).
Two clients send an integer each, and the server takes one request, then the next request, echo replies to the first request, then echo replies to the second request.
The intention isn't to get the responses ordered, only to allow for the holding of multiple requests simultaneously by the server.
What actually happens here is that the second worker hangs waiting on the request - this is what confuses me, as DEALER sockets should route outgoing messages in a round-robin strategy.
#include <unistd.h>
#include <stdio.h>
#include <zmq.h>
#include <sys/wait.h>
int client(int num)
{
void *context, *client;
int buf[1];
context = zmq_ctx_new();
client = zmq_socket(context, ZMQ_REQ);
zmq_connect(client, "tcp://localhost:5559");
*buf = num;
zmq_send(client, buf, 1, 0);
*buf = 0;
zmq_recv(client, buf, 1, 0);
printf("client %d receiving: %d\n", num, *buf);
zmq_close(client);
zmq_ctx_destroy(context);
return 0;
}
void multipart_proxy(void *from, void *to)
{
zmq_msg_t message;
while (1) {
zmq_msg_init(&message);
zmq_msg_recv(&message, from, 0);
int more = zmq_msg_more(&message);
zmq_msg_send(&message, to, more ? ZMQ_SNDMORE : 0);
zmq_msg_close(&message);
if (!more) break;
}
}
int main(void)
{
int status;
if (fork() == 0) {
client(1);
return(0);
}
if (fork() == 0) {
client(2);
return 0;
}
/* SERVER */
void *context, *frontend, *backend, *worker1, *worker2;
int wbuf1[1], wbuf2[1];
context = zmq_ctx_new();
frontend = zmq_socket(context, ZMQ_ROUTER);
backend = zmq_socket(context, ZMQ_DEALER);
zmq_bind(frontend, "tcp://*:5559");
zmq_bind(backend, "inproc://workers");
worker1 = zmq_socket(context, ZMQ_REP);
zmq_connect(worker1, "inproc://workers");
multipart_proxy(frontend, backend);
*wbuf1 = 0;
zmq_recv(worker1, wbuf1, 1, 0);
printf("worker1 receiving: %d\n", *wbuf1);
worker2 = zmq_socket(context, ZMQ_REP);
zmq_connect(worker2, "inproc://workers");
multipart_proxy(frontend, backend);
*wbuf2 = 0;
zmq_recv(worker2, wbuf2, 1, 0);
printf("worker2 receiving: %d\n", *wbuf2);
zmq_send(worker1, wbuf1, 1, 0);
multipart_proxy(backend, frontend);
zmq_send(worker2, wbuf2, 1, 0);
multipart_proxy(backend, frontend);
wait(&status);
zmq_close(frontend);
zmq_close(backend);
zmq_close(worker1);
zmq_close(worker2);
zmq_ctx_destroy(context);
return 0;
}
Other Options
I have looked at CLIENT and SERVER sockets and they appear to be capable on paper, however in practice they're sufficiently new that the system version of ZeroMQ that I have doesn't yet support them.
If it is not possible to perform this in ZeroMQ, any alternative suggestions are very welcome.
1 Based on the Shared Queue section of the ZeroMQ guide.
Let me share a view on how ZeroMQ could meet the above defined Intention.
Let's rather use ZeroMQ Scalable Formal Communication Pattern Archetypes ( as they are RTO now, not as we may wish them to be at some, yet unsure, point in (a just potentially happening) future evolution state ).
We need not hesitate to use many more ZeroMQ-based connections among a herd of coming/leaving client-instance(s) and the server
For example :
Client .connect()-s a REQ-socket to Server-address:port-A to ask for a "job"-ticket processing over this connection
Client .connect()-s a SUB-socket to Server-address:port-B to listen ( if present ) about published announcements about already completed "job"-tickets that are Server-ready to deliver results for
Client exposes another REQ-socket to request upon an already broadcast "job"-ticket completion announcement message, it has just heard about over the SUB-socket, to get "job"-ticket results finally delivered, if proving itself, by providing a proper / matching job-ticket-AUTH-key to proof its right to receive the publicly announced results' availability, using this same socket to deliver a POSACK-message to Server upon client has correctly received this "job"-ticket results "in hands"
Server exposes REP-socket to respond each client ad-hoc upon a "job"-ticket request, notifying this way about having "accepted"-job-ticket, delivering also a job-ticket-AUTH-key for later pickup of results
Server exposes PUB-socket to announce any and all not yet picked-up "finished"-job-tickets
Server exposes another REP-socket to receive any possible attempt to request to deliver "job"-ticket-results. Upon verifying there delivered job-ticket-AUTH-key, Server decides whether the respective REQ-message had matching job-ticket-AUTH-key to indeed deliver a proper message with results, or whether a match did not happen, in which case a message will carry some other payload data ( logic is left for further thoughts, so as to prevent potential bruteforcing or eavesdropping and similar, less primitive attacks on stealing the results )
Clients need not stay waiting for results live/online and/or can survive certain amounts of LoS, L2/L3-errors or network-storm stresses
Clients need just to keep some kind of job-ticket-ID and job-ticket-AUTH-key for later retrieving of the Server-processes/maintained/auth-ed results
Server will keep listening for new jobs
Server will accept new job-tickets with providing a privately added job-ticket-AUTH-key
Server will process job-tickets as it will to do so
Server will maintain a circular-buffer of completed job-tickets to be announced
Server will announce, in due time and repeated as decided in public, job-tickets, that are ready for client-initiated retrieval
Server will accept new retrieval requests
Server will verify client-requests for matching any announced job-ticket-ID and testing if job-ticket-AUTH-key match either
Server will respond to either matching / non-matching job-ticket-ID results retrieval request(s)
Server will remove a job-ticket-ID from a circular-buffer only upon both POSACK-ed AUTH-match before a retrieval and a POSACK-message re-confirmed delivery to client

Why did TCP-BSD server stuck in read() even data receives?

I've created a TCP server application using BSD sockets and NUCLEO-H743ZI2 development board with STM32CubeMX 5.6.0 & LwIP 2.0.3 in Keil-MDKARM.
I noticed that:
If a client connects and sends 11 bytes or more at first, server
receives the data correctly and read() responds displaying the data.
However, if client sends the first data lower than 11
bytes, read() function blocks even next received data is higher than 11 bytes, until client disconnects. After the disconnection, all the data queued is displayed.
Namely, if first data sent from a client to my server is lower than 11 bytes, event_callback for a rcvevent is not triggered until disconnection.
My aim is to make the server available to one byte reception.
I've pasted my Server task/thread below. Let me have your kind response at your earliest convenience and feel free to request other related files/libraries(lwip.h, lwipopts.h..).
Kind Regards
void StartTask01(void const * argument)
{
/* USER CODE BEGIN StartTask01 */
MX_LWIP_Init();
/*start a listening tcp server*/
int iServerSocket;
struct sockaddr_in address;
if ((iServerSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket could not be created\n");
}
else
{
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(iServerSocket, (struct sockaddr *)&address, sizeof (address)) < 0)
{
printf("socket could not be bound\n");
}
else
{
listen(iServerSocket, MEMP_NUM_NETCONN);
}
}
/*server started listening*/
struct sockaddr_in remoteHost;
int newconn;
char caReadBuffer[1500];
memset(caReadBuffer, 0, 1500);
for(;;)
{
/*block until accepting an incoming connection*/
newconn = accept(iServerSocket, (struct sockaddr *)&remoteHost, (socklen_t *)(sizeof(remoteHost)));
if (newconn != -1)/*if accepted well*/
{
/*block until data arrives*/
read(newconn, caReadBuffer, sizeof(caReadBuffer));
printf("data read: %s\n", caReadBuffer);
memset(caReadBuffer, 0, 1500);
}
}
/* USER CODE END StartTask01 */
}
The problem that's causing this issue is that you only call read once on each connection. If you don't happen to receive all the data from that single call to read (which is entirely unpredictable), you will never call read on that connection again.
When you call read on a blocking TCP connection, it will only block if there is no data available. Otherwise, it will give you whatever data is available up to the maximum number of bytes you ask for. It will not wait for more data if only some is available. It's up to you to call read again if you didn't receive all the data you expected.
One your second iteration of the for loop, you overwrite newconn with a new connection. You don't close the old connection. So you have a socket leak.
SOLVED:
The problem is, my server was listening port 80. I changed it to port 7 and thankfully bug is resolved, now read() works as expected.
This bug let me think that LwIP had problems on listening that web(80) port instead of others. There should be a some kind of discrimination between listening some spectacular ports even for unimplemented protocols.

dev_queue_xmit randomly returns NET_XMIT_CN with tun/tap device

I have a userspace program which construct its own packet (App, UDP, IP) and write()s it to the TUN device. The packet is intercepted by my own Netfilter module, which it checks if the packet it received is one which we want to process. Then my Netfilter module will skb_clone() the original skb and create a response packet which I fill in with some data to be returned to the user-space program. To send the response, I use dev_queue_xmit(). It randomly returns NET_XMIT_CN even though I just created a fresh TUN device and there is no other traffic passing through. If I keep executing the user-space program (sending new packets to the TUN device), eventually the TUN device will respond, but not consistently. I can't seem to track down why it is behaving so erratically.
Essentially I am using the TUN device as a mechanism to communicate from user-space to kernel-space, and vice versa.
Here's my user-space app:
tun_fd = tun_alloc(dev_name);
packet = ... /* Construct request...*/
nwrite = write(tun_fd, packet, packet_len);
...
unsigned recv_buf[1500];
int received = 0;
while (!received) {
nread = read(tun_fd, recv_buf, 1500);
...
}
...
close(tun_fd);
Here's my Netfilter module:
static struct nf_hook_ops nfho;
static int __init my_hook(void)
{
nfho.hook = hook_func;
nfho.hooknum = 0;
nfho.hook = PF_INET;
nfho.hook = NF_IP_PRI_FIRST;
nf_register_hook(&nfho);
}
unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct sk_buff *clone_skb = skb_clone(skb, GFP_KERNEL):
...
/*
* Check if packet is for us.
* Check IP & UDP header, etc
* If so, parse request, put together response clone_skb
*/
...
if ((err = dev_queue_xmit(clone_skb)) != 0) {
printk(....)
/* Either it return 0 (success) or 2, meaning NET_XMIT_CN */
}
return NF_STOLEN;
}
I can't seem to figure out this behavior. Am I misusing the TUN device? Is there an easier way than this?
Please let me know if I should provide any extra details or clarify something.

How can I send a simple HTTP request with a lwIP stack?

Please move/close this if the question isn't relevant.
Core: Cortex-M4
Microprocessor: TI TM4C1294NCPDT.
IP Stack: lwIP 1.4.1
I am using this microprocessor to do some data logging, and I want to send some information to a separate web server via a HTTP request in the form of:
http://123.456.789.012:8800/process.php?data1=foo&data2=bar&time=1234568789
and I want the processor to be able to see the response header (i.e if it was 200 OK or something went wrong) - it does not have to do display/recieve the actual content.
lwIP has a http server for the microprocessor, but I'm after the opposite (microprocessor is the client).
I am not sure how packets correlate to request/response headers, so I'm not sure how I'm meant to actually send/recieve information.
This ended up being pretty simple to implement, forgot to update this question.
I pretty much followed the instructions given on this site, which is the Raw/TCP 'documentation'.
Basically, The HTTP request is encoded in TCP packets, so to send data to my PHP server, I sent an HTTP request using TCP packets (lwIP does all the work).
The HTTP packet I want to send looks like this:
HEAD /process.php?data1=12&data2=5 HTTP/1.0
Host: mywebsite.com
To "translate" this to text which is understood by an HTTP server, you have to add "\r\n" carriage return/newline in your code. So it looks like this:
char *string = "HEAD /process.php?data1=12&data2=5 HTTP/1.0\r\nHost: mywebsite.com\r\n\r\n ";
Note that the end has two lots of "\r\n"
You can use GET or HEAD, but because I didn't care about HTML site my PHP server returned, I used HEAD (it returns a 200 OK on success, or a different code on failure).
The lwIP raw/tcp works on callbacks. You basically set up all the callback functions, then push the data you want to a TCP buffer (in this case, the TCP string specified above), and then you tell lwIP to send the packet.
Function to set up a TCP connection (this function is directly called by my application every time I want to send a TCP packet):
void tcp_setup(void)
{
uint32_t data = 0xdeadbeef;
/* create an ip */
struct ip_addr ip;
IP4_ADDR(&ip, 110,777,888,999); //IP of my PHP server
/* create the control block */
testpcb = tcp_new(); //testpcb is a global struct tcp_pcb
// as defined by lwIP
/* dummy data to pass to callbacks*/
tcp_arg(testpcb, &data);
/* register callbacks with the pcb */
tcp_err(testpcb, tcpErrorHandler);
tcp_recv(testpcb, tcpRecvCallback);
tcp_sent(testpcb, tcpSendCallback);
/* now connect */
tcp_connect(testpcb, &ip, 80, connectCallback);
}
Once a connection to my PHP server is established, the 'connectCallback' function is called by lwIP:
/* connection established callback, err is unused and only return 0 */
err_t connectCallback(void *arg, struct tcp_pcb *tpcb, err_t err)
{
UARTprintf("Connection Established.\n");
UARTprintf("Now sending a packet\n");
tcp_send_packet();
return 0;
}
This function calls the actual function tcp_send_packet() which sends the HTTP request, as follows:
uint32_t tcp_send_packet(void)
{
char *string = "HEAD /process.php?data1=12&data2=5 HTTP/1.0\r\nHost: mywebsite.com\r\n\r\n ";
uint32_t len = strlen(string);
/* push to buffer */
error = tcp_write(testpcb, string, strlen(string), TCP_WRITE_FLAG_COPY);
if (error) {
UARTprintf("ERROR: Code: %d (tcp_send_packet :: tcp_write)\n", error);
return 1;
}
/* now send */
error = tcp_output(testpcb);
if (error) {
UARTprintf("ERROR: Code: %d (tcp_send_packet :: tcp_output)\n", error);
return 1;
}
return 0;
}
Once the TCP packet has been sent (this is all need if you want to "hope for the best" and don't care if the data actually sent), the PHP server return a TCP packet (with a 200 OK, etc. and the HTML code if you used GET instead of HEAD). This code can be read and verified in the following code:
err_t tcpRecvCallback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
UARTprintf("Data recieved.\n");
if (p == NULL) {
UARTprintf("The remote host closed the connection.\n");
UARTprintf("Now I'm closing the connection.\n");
tcp_close_con();
return ERR_ABRT;
} else {
UARTprintf("Number of pbufs %d\n", pbuf_clen(p));
UARTprintf("Contents of pbuf %s\n", (char *)p->payload);
}
return 0;
}
p->payload contains the actual "200 OK", etc. information. Hopefully this helps someone.
I have left out some error checking in my code above to simplify the answer.
Take a look at the HTTP example in Wikipedia. The client will send the GET and HOST lines. The server will respond with many lines for a response. The first line will have the response code.
I managed to create an HTTP client for raspberry pi Pico W using the example here.
It uses the httpc_get_file or httpc_get_file_dns functions from the sdk.
However, that example is incomplete since it has a memory leak.
You will need to free the memory taken by the struct pbuf *hdr in the headers function and struct pbuf *p in the body function with respectively pbuf_free(hdr); and pbuf_free(p);
Without those modifications, it will stop working after about 20 calls (probably depends on the size of the response).

Grabbing video with RTSP over TCP

I want to retrieve some video data from a device with RTSP.
RTSP over UDP works well, but I need it over TCP.
After issuing the RTSP commands, I receive RTPs but I do not how to handle them here.
The payload is as follows: $[channel - 1 byte][length - 2bytes][data]
The thing is that I receive such packets and sometimes further packets where channel is 10 or 99 etc.
So could anyone please provide some input on how to handle the payload ?
You have it all in RFC 2326 "Real Time Streaming Protocol (RTSP)". You need "10.12 Embedded (Interleaved) Binary Data":
Stream data such as RTP packets is encapsulated by an ASCII dollar sign (24 hexadecimal), followed by a one-byte channel identifier, followed by the length of the encapsulated binary data as a binary, two-byte integer in network byte order. The stream data follows immediately afterwards, without a CRLF, but including the upper-layer protocol headers. Each $ block contains exactly one upper-layer protocol data unit, e.g., one RTP packet.
There is a small example there as well:
S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}
S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}
S->C: $\001{2 byte length}{"length" bytes RTCP packet}
Getting PPS is IMO straightforward and does not really require parseing the packet.
Your request for SPS , im guessing , will require getting into the packet ( i dont think you need to worry about WS msg 'invalid packet'.
What about using Type at PT at 0x09 ?
see here for packet description
sample implementations of unpacking RTP in the answer here
try looking here for more info related to RTSP control and SDP over TCP. If you are getting into inspecting the details of the RTSP session/conversation and the messaging details about control protocol selection for the respective tracks in your video. If u can get your answer without a diversion into SDP / RTCP then , obviously, thats faster/better.
this is the packet format for TCP/RTP :
[$ - 1byte][Transport Channel - 1byte][RTP data length - 2bytes][RTP data]
the rest is like upd
for more info read process raw rtp packets
Just explain something, I'm working for this as well, If you want use RTSP over TCP, please be careful about your socket reading code.
The suitable socket process like below:
while (socket.connected) {
char magic = socket.read a char;
if (magic == '$') { // is a RTP over TCP packet
byte channel = socket.read 1 byte;
unsigned short len = socket.read 2 byte; // len = ((byte1 & 0xFF) << 8) + (byte2 &0xFF);
int readTotal = 0;
byte rtpPacket[len];
while (readTotal < len) {
// read remaing bytes to rtpPacket from index readTotal
int r = socket.read(rtpPacket, readTotal, len - readTotal);
if (r > 0)
readTotal += r;
else // -1 means socket read error
break;
}
// now we get full RTP packet, process it!
call back channel, rtpPacket;
} else { // is RTSP protocol response
string array header;
while (line = socket.readline() != null) {
if (line == "") {
int body_len = Parse header "Content-Length";
byte body[body_len];
int readBody = 0;
while (readBody < body_len) {
int r = socket.read(body, readBody, body_len - readBody);
if (r>0)
readBody += r;
else
break;
}
// Now we get full header, body of RTSP response, process it
Callback header, body;
} else {
header.add(line);
}
}
}
}

Resources