SSDP M-search does not work for unicast - single IP - python-3.x

SSDP M-search request for discovery works great for multicast address 239.255.255.250. However, I have a requirement to send this request to a specific IP. It does not work. As per the UPnP architecture document unicast discover message is supported, with a small difference from the multicast message
The multi-cast message is
M-SEARCH * HTTP/1.1
HOST:239.255.255.250:1900
MAN:ssdp:discover
ST:ssdp:all
MX:1
Unicast message is similar - except that the multicast IP is changed to specific IP and MX:1 is removed (as per the specification - did not find any code sample for unicast discover m-search)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
msg = 'M-SEARCH * HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\nMX:1\r\n\r\n'
s.sendto(msg.encode('utf-8'), ('239.255.255.250', 1900))
resp = s.recv(1024)
print (resp)
This works great. But the following doesn't - the recv() call times out
sock_1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
msg = 'M-SEARCH * HTTP/1.1\r\nHOST:<specific ip>:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\n\r\n'
sock_1.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock_1.settimeout(30)
sock_1.sendto(msg.encode('utf-8'), ('<specific ip>', 1900))
resp = sock_1.recv(1024)
I have tried a few more variations of 'setsockopt' which did not work.
Please share if there are any clues for possible reasons - could it be from the device side that it responds to the multicast on 1900 but not to the unicast message?
Thanks in advance!

msg = 'M-SEARCH * HTTP/1.1\r\nHOST:<specific ip>:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\n'
You're missing a final \r\n in the end (it's for the empty line signaling end of headers: without it the receiver will just keep waiting for more).

The only difference with a unicast m-search is the HOST, like you've got.
Are you binding the socket? It should be bound to port 0. Here's an example of setting up a socket for ssdp: https://github.com/lbryio/aioupnp/blob/master/aioupnp/protocols/multicast.py#L64
In addition to the final \r\n pointed out by Jussi, you may find you need to try different permutations of the M-SEARCH format, some gateways are very finicky. For some the value for MAN must be in quotes, for others it must not. Additional quirks are the order of MAN, MX, ST and the capitalization of HOST/Host and MAN/Man . You may also need to try a variety of search targets.

Related

UDP multicast message is not able to send and receive between Docker conatiners

I am trying to send a UDP multicast message from my container-1 and trying to receive it on container2.I have linked container-2 with container-1 using --link option .I am using these sample codes for testing
multicastsend.py
import socket
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# for all packets sent, after two hops on the network the packet will not
# be re-sent/broadcast (see https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html)
MULTICAST_TTL = 2
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
# For Python 3, change next line to 'sock.sendto(b"robot", ...' to avoid the
# "bytes-like object is required" msg (https://stackoverflow.com/a/42612820)
sock.sendto(b"robot", (MCAST_GRP, MCAST_PORT))
multicastrec.py
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
IS_ALL_GROUPS = True
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
sock.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
sock.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
# For Python 3, change next line to "print(sock.recv(10240))"
print (sock.recv(10240))
The port 5007 on container-2(receiver) is published using -p 5007:5007/udp option with docker run
When i run sender and receiver on the same container messages are getting published and received properly .But when running in two different containers messages are not getting received
UPDATE
when running these containers with --network=host option we are able to establish the connection . But is there any way to make this happen with default Bridge network of docker
These lines don't do what you think they do:
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
INADDR_ANY is the wildcard address, but when used with IP_ADD_MEMBERSHIP that just means that it will pick the 'default' interface--not all interfaces. If you want to add all interfaces, you will need to loop over all of your interfaces and add membership explicitly for each of them. In your case, you probably just want to add membership with docker bridge ip for that container.
Something like this worked for me:
bridge_ip = "172.19.0.1" # change this to match the bridge IP for your container
sock.setsockopt(
socket.SOL_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(MCAST_GRP) + socket.inet_aton(bridge_ip),
)
I the exact same question you had and found the following answers which helped me:
for the insight that INADDR_ANY does not mean all:
https://stackoverflow.com/a/46075375/2069572
for the syntax to IP_ADD_MEMBERSHIP a specific address
https://stackoverflow.com/a/6102513/2069572
If you really do want to add all of your interfaces, you will need to do that in a loop. This answer will get you on the right track for figuring out how to get all the addresses associated with your interfaces:
https://stackoverflow.com/a/24196955/2069572

How to establish a 2 way communication using sockets in Python?

So, I have a server script that receives an image from a client script and is supposed to send an acknowledgement saying "OK". But the acknowledgement never goes through.
Server script -
import socket,sys
s = socket.socket()
print("Socket successfully created")
port =80
s.bind(('', port))
print("socket binded to %s" %(port))
s.listen(5)
print("socket is listening")
while True:
c, addr = s.accept()
print('Got connection from', addr)
file_name=s.recv(1024)
file_name=fil_ename.decode("utf-8")
with open(file_name,"wb")as f:
while True:
data=c.recv(1024)
if not data:
break
f.write(data)
c.send(bytes('Thank you ! File received.',"utf-8"))
c.close()
Client script -
import socket
s = socket.socket()
# Define the port on which you want to connect
port = 80
s.connect(('IP address of my server', port))
s.send(bytes("hand.jpeg","utf-8"))
f=open("back.jpeg","rb")
data=f.read(512)
while data:
s.send(data)
data=f.read(512)
f.close()
print(s.recv(10))
The server does not send any acknowledgement and seems to get stuck in the for loop. But if i remove the line c.send(bytes('Thank you ! File received.',"utf-8")) from the server script, the code works well. Also, if I remove the receive part from server side and just send the acknowledgent part , i.e c.send(bytes('Thank you ! File received.',"utf-8")) , the client receives the message. But If a combination of receive(the image file) and the acknowledgement is made in the server side as shown in the code, the server side fails to respond.
Important thing to note is that on KeyBoardInterrupt-ing the above program, it shows that the server side script is hanged/stuck in the data=c.recv(1024) line. But the same problem vanishes if the acknowledgement line is removed.
Note:- The client side script is running on my local machine and the server side script is running on a Google Cloud VM Instance.
Please help.
Thank you.
Hmm... I don't think I completely believe your description of the behavior. But I do know what's wrong. It's entirely reasonable that your server is sitting in the receive loop, because the client hasn't signaled EOF to the connection. Under what circumstances do you believe that this will actually break?
if not data:
break
The answer is that the client needs to either close the socket, or use shutdown(SHUT_WR) to indicate that it will not be sending any more data. So to do what you want, on the client side:
...
f.close()
s.shutdown(socket.SHUT_WR)
...
Now the next time the server calls recv, it will get an empty string returned and the break above will be taken.
This leaves the connection open in one direction but not the other. So the client will not be able to send any more data. However, the server will still be able to send to the client until it closes the socket (or uses shutdown itself).
There is another more subtle problem. You are assuming that your first server-side recv will receive only and exactly the bytes containing your file name. 99.9% of the time that will work. But it's possible that the data from your next client-side send will also be available when the server calls recv for the first time. That will likely give you a bogus file name (though not necessarily an illegal one) and will certainly mean that your file is not transferred faithfully.
You should never assume that the data provided by a single send by one peer will be received by a corresponding single recv on the other side. The data received could be more or less and it's up to the application to frame the data to ensure that it receives exactly the intended amount.
Here
while True:
data=c.recv(1024)
if not data:
break
f.write(data)
it loops back to waiting for a message after it has received one because you don't break the while loop after receiving data. the if not data: doesn't do anything since recv() stops and waits until it gets a message, and thus data is never nothing. You should either break the loop after receiving a message by adding a break after f.write(data), or send the OK in the loop.

how to make outgoing call from freeswitch and play file after destination answer call?

I want to write a web app that connects to freeswitch and makes outgoing call to some destination number (gateway for landline or internal sip devices) and plays some sounds (may be do some logic in lua script).
After reading freeswitch wiki, I found originate command but it doesn't work for me (I just test for internal sip number - sofia/internal/username#ip ). If originate command can do this, how to use it properly? If there is another way please tell me.
Originate command is used to make the call and bridge command is used to bridge the call. You can call originate command externally by using esl socket.
Examples:
originate {ignore_early_media=true,originate_timeout=60}sofia/gateway/name/number &playback(message)
Refer to this for esl written in node.js
https://github.com/englercj/node-esl
one way that I test and it work is run a lua script from freeswitch console or ESL:(ex "luarun test.lua")
https://freeswitch.org/confluence/display/FREESWITCH/Lua+API+Reference#LuaAPIReference-session:hangupCause
obSession = freeswitch.Session("sofia/192.168.0.4/1002")
-- Check to see if the call was answered
if obSession:ready() then
-- Play file here
else
-- This means the call was not answered ... Check for the reason
local obCause = obSession:hangupCause()
freeswitch.consoleLog("info", "obSession:hangupCause() = " .. obCause )
if ( obCause == "USER_BUSY" ) then -- SIP 486
-- For BUSY you may reschedule the call for later
elseif ( obCause == "NO_ANSWER" ) then
-- Call them back in an hour
elseif ( obCause == "ORIGINATOR_CANCEL" ) then -- SIP 487
-- May need to check for network congestion or problems
else
-- Log these issues
end
end
You can do it very easily from dial plan:
<action function="play-file" data="myfile.wav"/>
You can make the wav play when someone start a call, follow these steps.
Place your wave into your freeswitch/conf folder.
Add the code bellow to your freeswitch/conf/autoload_configs
Run a HTTP server that receives a POST request and returns your dialplan(which tells freeswitch to play your wav).
Make sure your freeswitch/conf/autoload_configs/xml_curl.conf.xml looks like this
<param name="gateway-url" value="http://yourIP:yourServerPort/dialplan.xml" bindings="dialplan"/>
Hope this helps.
you can achieve By using a socket[ESL] application.
https://wiki.freeswitch.org/wiki/Event_Socket_Outbound

C Socket - captured packets have same ip_dst and ip_src

I'm using C SOCK_RAW socket to capture incoming packets on my machine (192.168.0.16), when trying to display some fields of the IP packet captured, everything is correct (TTLs, length, etc...), except that my ip_dst and ip_src are... the same ! (in fact the ip_dst seems to have been overwritten at some point...
The structure of the code used:
sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_TCP))
recvfrom(sockfd, buffer, IP_MAXPACKET, 0, &from, &fromlen)
...
struct ip iphdr; //and then copying the right section of "buffer" into it...
printf("Source: %s - Dest: %s \n",inet_ntoa(iphdr.ip_src),inet_ntoa(iphdr.ip_dst));
Displays:
Source: 192.168.0.5 - Dest: 192.168.0.5
while i can see (from Wireshark) that the real destination is 192.168.0.16 (which is indeed a VM running on 192.168.0.5 - in case that could explain this weird phenomenenon).
Thanks for helping me to understand this !
EDIT: this (listening) program is running on the VM (192.168.0.16)
according to the inet_ntoa manual:
The inet_ntoa() function converts the Internet host address in, given in network byte order, to a string in IPv4 dotted-decimal notation.
The string is returned in a statically allocated buffer, which subsequent calls will overwrite.
Which means every time you call this function, the results will be overwritten to the same address. so to keep the result somewhere else, you need to copy it into some buffer using something like strcpy.
char src[15], dst[15];
strcpy(src, inet_ntoa(iphdr.ip_src));
strcpy(dst, inet_ntoa(iphdr.ip_dst));
printf("Source: %s - Dest: %s \n", src, dst);
Note: this answer is solely for the those who comes here through a search result since this post is really old.

linux raw ethernet socket bind to specific protocol

I'm writing code to send raw Ethernet frames between two Linux boxes. To test this I just want to get a simple client-send and server-receive.
I have the client correctly making packets (I can see them using a packet sniffer).
On the server side I initialize the socket like so:
fd = socket(PF_PACKET, SOCK_RAW, htons(MY_ETH_PROTOCOL));
where MY_ETH_PROTOCOL is a 2 byte constant I use as an ethertype so I don't hear extraneous network traffic.
when I bind this socket to my interface I must pass it a protocol again in the socket_addr struct:
socket_address.sll_protocol = htons(MY_ETH_PROTOCOL);
If I compile and run the code like this then it fails. My server does not see the packet. However if I change the code like so:
socket_address.sll_protocol = htons(ETH_P_ALL);
The server then can see the packet sent from the client (as well as many other packets) so I have to do some checking of the packet to see that it matches MY_ETH_PROTOCOL.
But I don't want my server to hear traffic that isn't being sent on the specified protocol so this isn't a solution. How do I do this?
I have resolved the issue.
According to http://linuxreviews.org/dictionary/Ethernet/ referring to the 2 byte field following the MAC addresses:
"values of that field between 64 and 1522 indicated the use of the new 802.3 Ethernet format with a length field, while values of 1536 decimal (0600 hexadecimal) and greater indicated the use of the original DIX or Ethernet II frame format with an EtherType sub-protocol identifier."
so I have to make sure my ethertype is >= 0x0600.
According to http://standards.ieee.org/regauth/ethertype/eth.txt use of 0x88b5 and 0x88b6 is "available for public use for prototype and vendor-specific protocol development." So this is what I am going to use as an ethertype. I shouldn't need any further filtering as the kernel should make sure to only pick up ethernet frames with the right destination MAC address and using that protocol.
I've worked around this problem in the past by using a packet filter.
Hand Waving (untested pseudocode)
struct bpf_insn my_filter[] = {
...
}
s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
struct sock_fprog pf;
pf.filter = my_filter;
pf.len = my_filter_len;
setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf));
sll.sll_family = PF_PACKET;
sll.sll_protocol = htons(protocol);
sll.sll_ifindex = if_nametoindex("eth0");
bind(s, &sll, sizeof(sll));
Error checking and getting the packet filter right is left as an exercise for the reader...
Depending on your application, an alternative that may be easier to get working is libpcap.

Resources