I have to implement an RTSP Client which connects to an existing RTSP session without being able to send commands to the RTSP Server (recvonly).
To simulate such an environment, I have recorded a RTSP/RTP stream between testH264VideoStreamer and testRTSPClient examples from Live555 with Wireshark, and played it back using tcpreplay while trying to receive stream data with a modified version of testRTSPClient.
I've also stored the SDP information provided by the testH264VideoStreamer as an SDP file.
v=0
o=- 1606317166144671 1 IN IP4 192.168.3.92
s=Session streamed by "testH264VideoStreamer"
i=test.264
t=0 0
a=tool:LIVE555 Streaming Media v2020.10.16
a=type:broadcast
a=control:*
a=source-filter: incl IN IP4 * 192.168.3.92
a=rtcp-unicast: reflection
a=range:npt=0-
a=x-qt-text-nam:Session streamed by "testH264VideoStreamer"
a=x-qt-text-inf:test.264
m=video 18888 RTP/AVP 96
c=IN IP4 232.42.39.62/255
b=AS:500
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=640028;sprop-$
a=control:track1
^#
I've modified the testRTSPClient example so that it connects to the RTP stream only by using the data from the SDP File.
Here are two functions which I use to initialise.
void openSDP(UsageEnvironment& env, char const* sdpFile)
{
const char * rtspURL = "rtsp://192.168.3.92:8554/testStream/";
RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL);
if(rtspClient == NULL)
{
env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg();
return;
}
else
{
env << "Connecting to the stream at " << rtspURL;
}
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
std::vector<char> sdpBuffer;
std::ifstream file(sdpFile, std::ios_base::in | std::ios_base::binary);
file.unsetf(std::ios::skipws);
std::streampos fileSize;
file.seekg(0, std::ios::end);
fileSize = file.tellg();
file.seekg(0, std::ios::beg);
sdpBuffer.reserve(fileSize);
sdpBuffer.insert(sdpBuffer.begin(),
std::istream_iterator<unsigned char>(file),
std::istream_iterator<unsigned char>());
char* const sdpDescription = sdpBuffer.data();
// Create a media session object from this SDP description:
scs.session = MediaSession::createNew(env, sdpDescription);
if(scs.session == NULL)
{
env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
}
else
if(!scs.session->hasSubsessions())
{
env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
}
scs.iter = new MediaSubsessionIterator(*scs.session);
setupNextSubsession(rtspClient);
return;
}
void setupNextSubsession(RTSPClient* rtspClient)
{
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
scs.subsession = scs.iter->next();
if(scs.subsession != NULL)
{
if(!scs.subsession->initiate())
{
env << "Failed to initiate the subsession: " << env.getResultMsg();
setupNextSubsession(rtspClient); // give up on this subsession; go to the next one
}
else
{
env << "Initiated the subsession:";
if(scs.subsession->rtcpIsMuxed())
{
env << "client port " << scs.subsession->clientPortNum();
}
else
{
env << "client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1;
}
scs.subsession->sink = DummySink::createNew(env,
*scs.subsession,
rtspClient->url());
// perhaps use your own custom "MediaSink" subclass instead
if(scs.subsession->sink == NULL)
{
env << "Failed to create a data sink for the subsession: " << env.getResultMsg();
}
env << "Created a data sink for the subsession";
scs.subsession->miscPtr = rtspClient; // a hack to let subsession handler functions get the "RTSPClient" from the subsession
scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
subsessionAfterPlaying, scs.subsession);
// Also set a handler to be called if a RTCP "BYE" arrives for this subsession:
if(scs.subsession->rtcpInstance() != NULL)
{
scs.subsession->rtcpInstance()->setByeWithReasonHandler(subsessionByeHandler, scs.subsession);
}
// Set up the next subsession, if any:
setupNextSubsession(rtspClient);
}
}
}
Everything initialises without errors, but DummySink receives no data. Any ideas?
I've found out that although wireshark was showing me the incoming packets with valid checksums, udp port received no packets.
I've tried following commands (as sudo) to avoid kernel discarding the packets but they simply don't help on Debian Buster.
sysctl net.ipv4.conf.eth0.rp_filter=0
sysctl net.ipv4.conf.all.rp_filter=0
echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter
sysctl -a | grep "\.rp_filter" | awk '{print $1 "=0"}' | xargs sysctl
Basically I've ended up streaming the pcap file from another computer, now I'm able to receive NALUs.
How should the client's credentials packet look when communicating with a MySql server?
I am working on using a microcontroller to communicate with a MySql server on AWS.
Once I open a TCP socket to the server I receive the server's greeting packet. However, I am having trouble with the client's credentials packet.
When I send the packet I created in the code below, I do not receive a response from the server.
As a side note, I do receive the error "Got packets out of order" when I set the packet sequence number to 0 instead of 1. Any ideas?
Server's greeting:
4e0000000a352e362e33372d6c6f67006a3300005871507a363d6e3400ffff0802007fc01500000000000000000000542d2364524c65392d752659006d7973716c5f6e61746976655f70617373776f726400
Server's Error message:
21000001ff8404233038533031476f74207061636b657473206f7574206f66206f72646572
char buf[76];
memset(buf, 0, sizeof(buf));
buf[0] = 0x4C;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x01;
buf[4] = 0x08 | 0x04;
buf[5] = 0x80 | 0x02 | 0x01;
buf[6] = 0x00;
buf[7] = 0x00;
buf[8] = 0xB8;
buf[9] = 0x0B;
buf[10] = 0x00;
buf[11] = 0x00;
buf[12] = 0x35;
char username[] = "username";
size_t usernameLength = sizeof(username);
strncpy(&buf[36],username,usernameLength-1);
buf[48] = 0x00;
char password[] = "password";
size_t passwordLength = sizeof(password);
unsigned char hash[SHA_DIGEST_LENGTH];
SHA1(password, passwordLength-1, hash);
buf[49] = 0x14;
memcpy(&buf[50],&hash[0],20);
strncpy(&buf[70],"dbname",sizeof("dbname")-1);
buf[75] = 0x00;
I understand that this was not an appropriate place to ask the question I had. However, after posting, I did find a very helpful repository on Github here: https://github.com/ChuckBell/MySQL_Connector_Arduino
The repository contains all of the source code necessary to write a client which connects to a MySQL database. Most importantly, it shows the proper way to hash the password.
I hope this helps other people since there is very little documentation of MySQL provided by Oracle unless you license the software, regular clients don't provide source code, and observing packet traffic provides limited insight into how the handshake process works.
Aim:- Writing a kernel module,when inserted does the following things.
1.if a user wants to open a website named "abcde.in" in any browser ,he should get "google.co.in" webpage, in place of "abcde.in" web page.
Normally accessing google.in i get:-
Observation..(in http header)
302 Moved
The document has moved
HREF=http://www.google.co.in
:-) next i send an ack And i got the google homepage:-
With Modification:-
Steps i have taken
a. edited /etc/hosts
(google.in IP ) abcde.in
So that it gets ip of "google.in"'s for "abcde.in"
b.Next inserting the following piece of module in nf_hook POST_ROUTING implementation
Observation..
i am able to correctly modify and wireshark shows that in reply i am able to get 302 Moved
But i am sending Tcp retransmissions GET / HTTP/1.1. again and again.
I feel i am missing the ack which i need to send as in original case.
why am i unable to send an ack instead of that why i am retransmitting tcp packets to "GET / HTTP/1.1"
if(skb){
liph=(struct iphdr *)skb_network_header(skb);
if(liph->protocol == 6) {
ltcph = (struct tcphdr *)skb_transport_header(skb);
data=skb->data+(liph->ihl*4) + (ltcph->doff*4);
datalen=skb->len - ((liph->ihl*4)+(ltcph->doff*4));
replace_n=strstr(data,"abcde.in");
if(replace_n) {
temp = kmalloc(512 * sizeof(char), GFP_KERNEL);
memcpy(temp, data, datalen);
replace_n=strstr(temp,"abcde.in");
replace_size=strlen("google.in");
site_diff=replace_size - strlen("abcde.in");
memmove(replace_n+strlen("abcde.in")+site_diff,replace_n+strlen("abcde.in"),strlen(replace_n)-strlen("abcde.in"));
memcpy(replace_n,"google.in",strlen("google.in"));
skb_put(skb,site_diff);
memcpy(data,temp,datalen+site_diff);
liph->tot_len=htons(datalen+site_diff+20+(ltcph->doff*4)); /*modifing necessary fields*/
liph->check=0;
liph->check=ip_fast_csum((unsigned char *)liph,liph->ihl);
int ipl=liph->ihl * 4;
int ihl=ntohs(liph->tot_len);
ltcph->check = 0;
skb->csum = csum_partial(ltcph, ihl - ipl, 0);
ltcph->check = tcp_v4_check(ihl - ipl,liph->saddr, liph->daddr, skb->csum);
skb->ip_summed = CHECKSUM_NONE;
Retransmission tck packet wireshark capture
I am trying to use IP to country database from ip2location.
My question is, does the IPV6 database files already contains the IPV4 address embedded
or do I have to use both IPV4 and IPV6 databases to cover all the IP Ranges for the internet
for all versions.
What I mean if I want to support both IPV4 and IPV6 I should load both database files
into the same mysql table or should I just use IPV6.
I am talking about the file IP2LOCATION-LITE-DB11.CSV.ZIP and IP2LOCATION-LITE-DB11.IPV6.CSV.ZIP
I am answering my own question after two days of search and testing. The IPV4 can be embedded in IPV6 and this file IP2LOCATION-LITE-DB11.IPV6.CSV.ZIP contains the IPV4 embedded as IPV6 and all IPs are stored as decimal(39, 0).
The trick I used here to use the IPV6 database file for both versions of IP's IPV4 and IPV6 is to convert the IPV4 address when searching the database to IPV6 then to Integer and do the search normal. This way your application supports both IPV4 and IPV6.
To convert IPV4 to IPV6, this wiki article is the answer:
http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
IPv4-mapped IPv6 addresses
Hybrid dual-stack IPv6/IPv4 implementations recognize a special class of addresses,
the IPv4-mapped IPv6 addresses. In these addresses, the first 80 bits are zero, the
next 16 bits are one, and the remaining 32 bits are the IPv4 address. One may see these
addresses with the first 96 bits written in the standard IPv6 format, and the
remaining 32 bits written in the customary dot-decimal notation of IPv4. For example,
::ffff:192.0.2.128 represents the IPv4 address 192.0.2.128. A deprecated format for
IPv4-compatible IPv6 addresses was ::192.0.2.128
and this article also good on this issue:
IPv6/IPv4 Address Embedding
All you need to do is prepend the IP with ::ffff: so the IP address 192.0.2.128 will be ::ffff:192.0.2.128 as valid IPV6.
The next step is to convert your IP either IPV4 or IPV6 to Decimail (39,0) and you can now search the database normal.
Since I use Perl, here are some helping code for testing and clearing.
use Net::IP ':PROC';
# IPV4 address
my $ipaddress = '197.36.107.146';
my $ip = new Net::IP ($ipaddress) or die (Net::IP::Error());
print ("IPV4 : ".$ip->ip()."\n");
print ("IPV4 Integer : ".$ip->intip()."\n");
print ("Version : ".$ip->version()."\n");
print ("Size: ".$ip->size()."\n");
print ("Len : ".$ip->prefixlen()."\n");
print ("Type: ".$ip->iptype()."\n");
print "\n";
# Convert IPV4 to IPV6. Just prepend ipv4 with '::ffff:'
my $ip = new Net::IP ("::ffff:".$ipaddress) or die (Net::IP::Error());
print ("IPV6 : ".$ip->ip()."\n");
print ("IPV6 Integer : ".$ip->intip()."\n");
print ("Version : ".$ip->version()."\n");
print ("Size: ".$ip->size()."\n");
print ("Len : ".$ip->prefixlen()."\n");
print ("Type: ".$ip->iptype()."\n");
print "\n";
# detect the user ip address and convert it
my $user_ip = get_user_ip();
$user_ip ||= $ipaddress; # just for testing on command line
print "Detected User IP address: $user_ip\n";
# if user remote address is IPV4 convert it to IPV6
if ($user_ip =~ /\./) {
# Convert IPV4 to IPV6
$user_ip = Net::IP->new("::ffff:$user_ip");
# Now convert it to Integer
$user_ip = $user_ip->intip();
}
else {
# Already IPV6, just convert to Integer
$user_ip = Net::IP->new($user_ip);
$user_ip = $user_ip->intip();
}
print "User IP address in IPV6 format: $user_ip\n";
#----------------------------------
# Now you can search the geo database with IPV4 and IPV6 stored as decimails
# select * from ip_country where $ipaddress<=ip_to limit 1
#----------------------------------
sub get_user_ip {
foreach (qw(REMOTE_ADDR HTTP_CLIENT_IP HTTP_X_FORWARDED_FOR HTTP_X_FORWARDED HTTP_X_CLUSTER_CLIENT_IP
HTTP_FORWARDED_FOR HTTP_FORWARDED)) {
if ($ENV{$_}) {
return $ENV{$_};
}
}
}
and this is the output of this testing code:
IPV4 : 197.36.107.146
IPV4 Integer : 3307498386
Version : 4
Size: 1
Len : 32
Type: PUBLIC
IPV6 : 0000:0000:0000:0000:0000:ffff:c524:6b92
IPV6 Integer : 281473989241746
Version : 6
Size: 1
Len : 128
Type: IPV4MAP
Detected User IP address: 197.36.107.146
User IP address in IPV6 format: 281473989241746
Searching the database until now shows everything working normal.
We use AS3 Event:ProcessEvent.SOCKET_DATA to listen for socket data.
So this is my AS3 code for socket data handle.
private function packetHandler( e:ProgressEvent ):void
{
while( m_socket.bytesAvailable && m_socket.bytesAvailable >= pLen )
{
//pLen means the packet length
//pLen init is zero
if( pLen == 0 )
{
//PACKET_LEN stands for the solid length of one packet
//PACKET_LEN = HEAD_LEN + 4
//the 4 means an unsigned int which means the packet content length
if( m_socket.bytesAvailable > PACKET_LEN )
{
m_socket.readBytes( headByteBuffer, 0, HEAD_LEN );
headByteBuffer.clear();
pLen = m_socket.readUnsignedInt() + 4;
}
else
{
break;
}
}
//recieved a whole packet now handle it
else
{
var newPacket:ByteArray = new ByteArray();
newPacket.endian = Endian.LITTLE_ENDIAN;
m_socket.readBytes( newPacket, 0, pLen );
parasMsg( newPacket, pLen-4 );
pLen = 0;
}
}
}
A whole packet can be described in this picture:
My Problem is: When there has one incomplete packet received in Flash and triggered the handle.
But the left part of the packet will never trigger the handle and it seems like that the left part of the packet has lost!!!
I used a capture tool, find that the tcp packet is ok, but why the left part doesn't trigger the event again?
You can get more debug information below. Thank you!
This is my log:
byteava means bytesAvailable of m_socket
==>sendPacket: {"rangeID":"1","uid":"145962","serviceType":"copyscene","cmd":"CopySceneMoveAsk","pathPoint":[{"col":7,"row":6},{"col":7,"row":5},{"col":7,"row":4},{"col":7,"row":3},{"col":6,"row":3}],"sn":"79","smallPathPoint":[[22,19],[22,18],[22,17],[22,16],[22,15],[22,14],[22,13],[21,13],[21,12],[21,11],[20,11],[20,10]]}, bytesLoaded = 463
ProgressEvent Triggered!0 socket byteava = 373 evt loaded:373 evt total:0 evt:[ProgressEvent type="socketData" bubbles=false cancelable=false eventPhase=2 bytesLoaded=373 bytesTotal=0]
Find a packet from socket, pLen=288 socket byteava = 276
ProgressEvent Triggered!288 socket byteava = 441 evt loaded:165 evt total:0 evt:[ProgressEvent type="socketData" bubbles=false cancelable=false eventPhase=2 bytesLoaded=165 bytesTotal=0]
Start to Read a packet to buffer, pLen=288 socket byteava = 441
whole packet content: Readed a packet to buffer, pLen=288 socket byteava = 153
Server packet content byte buffer ava:288 len:288 pos: 0
Server Paras Data : data len: 284 data content: {"cmd":"CopySceneMoveNotify","gtcmd":"108","layer":"1","pathPoint":[{"col":7,"row":6},{"col":7,"row":5},{"col":7,"row":4},{"col":7,"row":3},{"col":6,"row":3}],"smallPathPoint":[[22,19],[22,18],[22,17],[22,16],[22,15],[22,14],[22,13],[21,13],[21,12],[21,11],[20,11],[20,10]HTTP/1.1 200
_[20,10]HTTP/1.1 200_ This is what went wrong!! The incomplete packet cat with another packet's header.
Here is the capture of the TCP connections:
Hope you can vote it up so that I can put my pictures of this question on!
My English is not very good, hope you can understand what I mean.
Thank you!
The Socket's event flash.events.ProgressEvent.SOCKET_DATA will fire when you receive the data at this point you can get the received bytes ( check .bytesAvailable ). When the msg is split into multiple packages you will receive event for each packet.
In your case maybe the pLen have wrong value when check m_socket.bytesAvailable >= pLen.
I assume you send the msg size in the begging of the message ( in this case you can check if the whole msg is received ). In this case you must have a class member (ByteArray ) as buffer that holds a received bytes. When new data come you must copy the new bytes to this member and than check if you receive whole msg. If buffer contains whole msg than remove the msg from it.
In general your event handler must looks like this:
protected function onSocketData( pEvt: Event ): void
{
try
{
if ( 0 < pEvt.target.bytesAvailable )
{
var byteStream: ByteArray = new ByteArray();
pEvt.target.readBytes( byteStream, 0, Socket( pEvt.target ).bytesAvailable );
// Append readed data to your buffer
do
{
//Check if you have enough bytes to read whole msg and execute it
//do..while because maybe it can be more than one msg in buffer
}
while ( null != msgContent );
}
}
catch ( exc )
{
}
}
Problem should caused by the packet's solid header.
Below is the 93 bytes solid header of a packet.
private static const HTTP_RESPONSE_CONTENT : String = "HTTP/1.1 200 OK \r\n"
+ "Connection: keep-alive \r\n"
+ "Content-Length: 280 \r\n"
+ "Content-Type: text/html \r\n\r\n";
This header will be in every packet's header, which AS3 could treat it to a http and might cut the flow with Content-Length: 280. So the left part of the 280 bytes will never trigger the SOCKET_DATA event.
When I remove this header, it's ok now.