How to force client to switch RTP transport from UDP to TCP? - rtp

If the client wants to watch a stream that is on my RTSP server, it first tries to setup a stream through the UDP protocol. How can I tell it that my server only supports RTP/AVP/TCP and that it should switch transports?
I want to terminate the UDP support on my server, but all the clients first try to SETUP the session over UDP, and later they do so over TCP... and I want to switch them to TCP as soon as possible in RTSP protocol.
How can I do that?

As far as I know, there is no control at server side for transport type preference. Server should be made generic it should support RTP over UDP, RTP over TCP, RTP over RTSP and RTP over RTSP over HTTP(S). And its clients choice which transport to choose. Transport field is first sent in SETUP request
1) UDP
C->A: SETUP rtsp://audio.example.com/twister/audio.en RTSP/1.0
CSeq: 1
Transport: RTP/AVP/UDP;unicast;client_port=3056-3057
2) TCP
C->A: SETUP rtsp://audio.example.com/twister/audio.en RTSP/1.0
CSeq: 1
Transport: RTP/AVP/TCP;unicast;client_port=3056-3057
3) RTP over RTSP and RTP over RTSP over HTTP(S)
S->C: RTSP/1.0 200 OK
CSeq: 2
Date: 05 Jun 1997 18:57:18 GMT
Transport: RTP/AVP/TCP;interleaved=0-1
As we can see "Transport type" request is sent by client side.
If you want to support TCP only server you can send "400 Bad Request" or "461 Unsupported transport" in response to SETUP request as suggested by you or another way is to send 200 OK but do not transmit any RTP packets. Client will timeout and get to know that it is behind proxy and it will send SETUP request again with RTP/AVP/TCP parameter (Not an ideal case).

To expand on the answer for android,
For Android clients, they will always attempt to establish a UDP connection first.
For both OpenCore and StageFright I can confirm that if I return "461 Unsupported Transport" from my server in response to the first SETUP request for UDP transfer, both of these clients will then IMMEDIATELY attempt to establish a TCP based connection over the RTSP port.
All other responses are detailed here: http://www.ietf.org/rfc/rfc2326.txt

OK one way is to send "400 Bad Request" as the response to the client's SETUP request... and it automatically switches to TCP protocol. This is for RealOne and QuickTime.
But I am not sure that it will work on all other players since this is a hack.
Any other ideas? =|

If you used ffmpeg, you can force switch rtsp transport layer protocol.
av_dict_set(&format_opts, "rtsp_transport", "tcp", 0);
err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);

What client connects to your server? Some clients can be triggered through the URI method in the URL. For example, you could specify rtspt://myhost/path.sdp.
If you have control over client/servers you could use the Require header on clients and Unsupported on servers to indicate that UDP isn't supported; but most clients I've seen don't use this.

You can try to pass "transport" header in a response to Describe request, and state there that your server only supports RTP/AVP/TCP transport, and the client should know that UDP is unsupported.

Related

Questions about UDP requests in SOCKS5

I have couple of questions about the UDP requests in SOCKS5. I've already read this paper https://www.rfc-editor.org/rfc/rfc1928#section-7 and i'm trying to implement it in C++.
Questions:
Do i need to use the "UDP request header" in both sending and receiving?
Can i use something like "WSASendTo" from winsock2 to redirect packets trough the SOCKS5 server?
Do i need to use the "UDP request header" in both sending and receiving?
Yes. This is covered in the document section you linked to:
A UDP-based client MUST send its datagrams to the UDP relay server at the UDP port indicated by BND.PORT in the reply to the UDP ASSOCIATE request. If the selected authentication method provides encapsulation for the purposes of authenticity, integrity, and/or confidentiality, the datagram MUST be encapsulated using the appropriate encapsulation. Each UDP datagram carries a UDP request header with it:
...
When a UDP relay server decides to relay a UDP datagram, it does so silently, without any notification to the requesting client. Similarly, it will drop datagrams it cannot or will not relay. When a UDP relay server receives a reply datagram from a remote host, it MUST encapsulate that datagram using the above UDP request header, and any authentication-method-dependent encapsulation.
That means that both UDP datagrams sent by you to the SOCKS relay, and UDP datagrams you receive from the SOCKS relay, carry the same header.
Can i use something like "WSASendTo" from winsock2 to redirect packets trough the SOCKS5 server?
You can use WSASendTo() to send your own datagrams to the SOCKS relay, yes.
But, if by redirect you mean to make other applications' UDP sockets send/receive datagarms through the SOCKS relay without their knowledge, then no.

How to use SOCKS 5 proxy with TIdUDPClient properly

I'm trying to use SOCKS v5 proxy with TIdUDPClient. I'm using C++ Builder 10.3 in Windows x64.
Here is my code so far:
TIdSocksInfo* socksInfo = new TIdSocksInfo();
socksInfo->Host = "ip address of the proxy server";
socksInfo->Port = proxyPort;
socksInfo->Version = svSocks5;
socksInfo->Authentication = saNoAuthentication;
IdUDPClient1->Host = "ip address of my udp server";
IdUDPClient1->Port = port;
IdUDPClient1->TransparentProxy = socksInfo;
IdUDPClient1->Connect();
I can connect to my server perfectly without the proxy.
But, it fails to connect with the proxy, so I sniffed the network with wireshark:
The UDP client sends "0.0.0.0" and 0 as the remote info, instead of sending the host and port I set in the code.
Is there anything I can do to fix this?
TIdUDPClient::Connect() will call TIdSocksInfo::OpenUDP() to send a UDP ASSOCIATION request to a SOCKS v5 proxy. TIdUDPClient passes its Host and Port property values as parameters to OpenUDP().
However, TIdSocksInfo::OpenUDP() ignores these Host and Port values that are passed to it. It is hard-coded to send IP 0.0.0.0 (or ::0 for IPv6) and port 0 to the SOCKS proxy. And this is OK, per the SOCKS v5 spec, RFC 1928:
UDP ASSOCIATE
The UDP ASSOCIATE request is used to establish an association within the UDP relay process to handle UDP datagrams. The DST.ADDR and DST.PORT fields contain the address and port that the client expects to use to send UDP datagrams on for the association. The server MAY use this information to limit access to the association. If the client is not in possesion of the information at the time of the UDP ASSOCIATE, the client MUST use a port number and address of all zeros.
A UDP association terminates when the TCP connection that the UDP ASSOCIATE request arrived on terminates.
In the reply to a UDP ASSOCIATE request, the BND.PORT and BND.ADDR fields indicate the port number/address where the client MUST send UDP request messages to be relayed.
So, in a UDP ASSOCIATION request, the specified Host/Port is the local Host/Port that the client will send outgoing datagrams from, or zeros if it does not know that info yet. This lets the proxy's UDP relay know where datagrams will come from when datagrams are being sent from the client to a target peer, and where to send datagrams to when datagrams are being sent from a target peer to the client. If the requested Host/Port is all zeros, the relay should simply use the Host/Port that sent the request.
The Host/Port specified in a UDP ASSOCIATION request is NOT the target peer Host/Port that datagrams are to be forwarded to, like you are thinking. That target info is specified in the individual datagrams that the client will pass to the proxy for forwarding AFTER the association is created. Unlike TCP, UDP is connection-less, so a client can create a single association and then send datagrams to multiple targets, if it wants to.
There is no connection in UDP, so there is nothing for the proxy to send to the target server while processing a UDP ASSOCIATION request. It is simply setting up the proxy's listening port that will then relay subsequent datagrams between the client and the target server.
When sending datagrams for forwarding afterwards, TIdUDPClient::SendBuffer() will call TIdSocksInfo::SendToUDP(), which is also passed the TIdUDPClient's Host and Port property values. SendUDP() will then send a datagram with that target info to the proxy's relay port:
A UDP-based client MUST send its datagrams to the UDP relay server at the UDP port indicated by BND.PORT in the reply to the UDP ASSOCIATE request. If the selected authentication method provides encapsulation for the purposes of authenticity, integrity, and/or confidentiality, the datagram MUST be encapsulated using the appropriate encapsulation. Each UDP datagram carries a UDP request header with it:
... [request header, containing DST.ADDR and DST.PORT in it] ...
When a UDP relay server decides to relay a UDP datagram, it does so silently, without any notification to the requesting client. Similarly, it will drop datagrams it cannot or will not relay. When a UDP relay server receives a reply datagram from a remote host, it MUST encapsulate that datagram using the above UDP request header, and any authentication-method-dependent encapsulation.
The UDP relay server MUST acquire from the SOCKS server the expected IP address of the client that will send datagrams to the BND.PORT given in the reply to UDP ASSOCIATE. It MUST drop any datagrams arriving from any source IP address other than the one recorded for the particular association.
...
So, whatever problem you are having with communicating with your target server, it is likely not related to TIdUDPClient1::Connect() at all, it is more likely related to TIdUDPClient::SendBuffer() and TIdUDPClient::ReceiveBuffer() instead. Either:
the proxy's UDP relay is ignoring the datagrams that TIdUDPClient is requesting be forwarded to the target server, or it is ignoring the server's replies.
datagrams are getting lost in flight on their way to the target server, or on their way back to the proxy.
If you sniff TIdUDPClient's traffic again and see your actual communication datagrams targeting your server properly, then you are going to have to sniff the traffic on the other side of the proxy to make sure those datagrams are actually being forwarded onward. If they are, then the problem has to be on the network or the server itself. If they are not, then the problem has to be on the proxy itself.

Is it possible to send TCP packet to UDP-only server?

I have a UDP server, but I have no UDP capability on client side. Is there a way to send packet via TCP so that UDP server can receive it normally? UDP server cannot be transformed into TCP server.
Reason for wanting to do this is that I'm using a SOCKS5 proxy that does not support UDP associate capability, but I have to use that proxy.
No. The UDP server cannot complete the 3-way handshake required for TCP. If you have IP-raw sockets, it is actually easier to implement UDP on top. However it seems extremely unlikely that you don't have a UDP-stack.
When you talk about "no UDP capability" do you mean the client has no UDP-stack, or is he just behind some firewall that blocks UDP? In the latter case use a proxy.
No, UDP server will not going to accept TCP packets at all (it's a different protocol, so network stack will never propagate those to the application).
If you have to use SOCKS5 proxy, the only way to connect to the said server is by using another proxy-like application, which will stand beyond SOCKS5, accept TCP connections (initiated by app behind SOCKS5 proxy) and retransmit data as UDP.

Additional data along with TCP connect request

I have a client implementation and a server implementation. I want the client to send data about itself along with its connection request to the server. So for example, I could send an additional packet with the username. I was wondering if this is at all possible using connect() before a stream has actually been established. I would then accept only based on that information.
Thanks
Don't mix layers. The TCP connect is on the network layer, establishing a communication channel between the machines.
Instead, define your protocol (the rules for how data is transmitted in your TCP stream) to contain a handshake first. E.g. SMTP uses a header and handshake to first set up the connection before any actual emails are sent.

Send data over Internet

I have a requirement to send some 100 bytes data over internet .My machine is connected to internet.
I can do this with HTTP by sending requests and receiving responses.
But my requirement is just to send data not receive response.
I am thinking of doing this using UDP Client server program. But to do that I need to host UDP client on internet?
Is there any other way to do that?
any suggestions?
Cheap answer to send 100 bytes of data on the internet.
C:\Windows\system32>ping -n 1 -l 100 -4 google.com
Pinging google.com [209.85.171.99] with 100 bytes of data:
Reply from 209.85.171.99: bytes=56 (sent 100) time=174ms TTL=233
Ping statistics for 209.85.171.99:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 174ms, Maximum = 174ms, Average = 174ms
Anything that happens on the internet requires a client and a server.
One box is in the role of client, the other is in the role of server for your specific transaction.
Usually (but not always) your local box is a client and some other box is the server.
Software MUST be running on both to implement some protocol for exchanging data.
A server can listen on TCP or UDP sockets, with some restrictions. Some port numbers are privileged. Some port numbers are blocked by firewalls.
Port 80, while rarely blocked by firewalls is a privileged port. Generally, you need a web server (e.g., Apache) or privileges to listen on port 80.
"Sending 100 bytes" can be done using a lot of available protocols: Echo, Telnet, FTP, HTTP to name a few.
The big advantage of HTTP is that port 80 is very often open. With other protocols you have to rely on the operators to open the port.
In order to send data but not receive a response, you can simply write your program in such a way that it does not listen for a response. This doesn't mean one won't be sent to you, just that you won't get it.
For example, you can make sure you don't call "recv" on the socket. Also, you can use "shutdown" to disable reads on the socket. Depending on the underlying implementation, going the "shutdown" route might cause all incoming packets to simply be dropped.
As far as how to send the packets, really any sort of protocol will work. Of course, you need to know of a destination server on the Internet, but you've got plenty of options. Perhaps the simplest route to take is what you have suggested: HTTP (perhaps use www.google.com as your destination server).
You need a client (you) and a server (other end). For UDP, you send datagrams over the Internet (using IP). UDP doesn't provide the safety that TCP does, but doesn't require a response (but such responses are part of their protocols, not yours).
I would suggest using TCP to save you some headache.
Also, make sure you're not behind a firewall, else your packets won't make it to their destination as you'd expect.
Hmmm...
You want to send short messages over the internet, but without any response.
Your application wouldn't by any chance be some kind of spyware, would it?
Use UDP. Open a socket, send the data, close the socket. That's it. Here is a Python version of the client:
import socket
data = 100*'x'
address = ('192.168.0.123', 8080) # Host, port
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.connect(address)
sock.send(data)
sock.close()
On the Wikipedia page about UDP there is some corresponding WinSock code.
Of course the other side must be reachable, and there must be someone listening there, otherwise the target machine will reply with an ICMP "port unreachable" packet (at least if it complies with standards).
If you want a UDP listener on the internet, it will have to be hosted somewhere.
You can get HTTP hosting much easier, it's everywhere, UDP you may need your own machine or at least a VM.
curl is a command line tool for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. curl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, kerberos...), file transfer resume, proxy tunneling and a busload of other useful tricks.
See examples here