Grabbing video with RTSP over TCP - 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);
}
}
}
}

Related

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).

WebRTC SRTP decryption

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)

how to determine the starting byte of IP Header and TCP header in a raw packet

I want to use libpcap to capture packets
but since the length of ethernet header or 802.11 header may vary
and the length of IP header may also vary,
how can I determine the starting byte(pointer) of the IP Header and TCP header
besides, how to distinguish whether a packet is a pure IP packet, TCP packet or UDP packet?
are there any APIs or ways to do this?
thanks!
When you're using libpcap, you can determine the size of link layer header, either by directly looking at the pcap file header (for offline captures) pcap_file_header.linktype or (for live and offline captures) from a call to pcap_datalink(). Most of the time this will be LINKTYPE_ETHERNET. To ensure a packet is IPv4, you can cast into an Ethernet header and check to ensure the ethertype is ETHERTYPE_IP (make sure you wrap it inside of ntohs(). I usually apply bpf filters to my pcap instantiations so I never worry about that stuff. To check the higher layer protocols however and assuming you're using pcap_dispatch(), you can write a callback as per the following: (the libnet library is still useful for its wide array of portable packet structures):
#include <libnet.h>
#include <pcap.h>
void
process_packet(u_char *user, const struct pcap_pkthdr *header, const u_char *packet)
{
struct libnet_ipv4_hdr *ip;
struct libnet_udp_hdr *tcp;
uint16_t ip_hl, udp_hl, header_cruft;
ip = (struct libnet_ipv4_hdr *)(packet + LIBNET_ETH_H);
ip_hl = ip->ip_hl << 2;
switch (ip->ip_p)
{
case IPPROTO_UDP:
udp = (struct libnet_udp_hdr *)(packet + LIBNET_ETH_H + ip_hl);
udp_hl = tcp->th_off << 2;
header_cruft = LIBNET_ETH_H + ip_hl + tcp_hl;
break;
case IPPROTO_TCP:
/** you get the idea */
break;
default:
break;
}

Resources