How to send multicast messages and reuse a port in

2019-03-27 11:49发布

问题:

I have gotten a good start on my program, my first REAL Erlang program. I have it listening for messages, reading them and parsing them. I also have it sending them. The one little thing that is bothering me is I can't SEND on Port 5353, I have tried everything. All the other applications on my machine can listen AND send on port 5353, SubEthaEdit, iTunes, iChat.

The solution MUST broadcast send on port 5353 and here is why.

" If the source UDP port in a received Multicast DNS Query is not port 5353, this indicates that the client originating the query is a simple client that does not fully implement all of Multicast DNS. In this case, the Multicast DNS Responder MUST send a UDP response directly back to the client, via unicast, to the query packet's source IP address and port. This unicast response MUST be a conventional unicast response as would be generated by a conventional unicast DNS server; for example, it MUST repeat the query ID and the question given in the query packet. "

They all report Port: 5353 when sending multicast messages. I really want my application to play nice and do the same thing, send on Port 5353. Here is my module as it stands now.

-module(zeroconf).

-include("zeroconf.hrl").

-export([open/0,start/0]).
-export([stop/1,receiver/0]).
-export([send/1]).

-define(ADDR, {224,0,0,251}).
-define(PORT, 5353).

send(Domain) ->
    {ok,S} = gen_udp:open(0,[{broadcast,true}]), % I really want this Port to be 5353 :-(
    % this doesn't complain or throw errors but it also doesn't work :-(        
    %{ok,S} = gen_udp:open(?PORT,[{reuseaddr,true}, {ip,?ADDR}, {broadcast,true},multicast_ttl,4}, {multicast_loop,false}, binary]),
    P = #dns_rec{header=#dns_header{},qdlist=[#dns_query{domain=Domain,type=ptr,class=in}]},
    gen_udp:send(S,?ADDR,?PORT,inet_dns:encode(P)),
    gen_udp:close(S).

Here is what some output looks like.

This is a QUERY from SubEthaEdit looking for other instances on the local network, notice that it says Port: 5353

From: {192,168,0,105}
Port: 5353
Data: {ok,{dns_rec,{dns_header,0,true,'query',true,false,false,false,false,0},
                   [],
                   [{dns_rr,"_see._tcp.local",ptr,in,0,0,
                            "jhr@Blackintosh._see._tcp.local",undefined,[],
                            false}],
                   [],[]}}

Now here is a QUERY from my module looking for instances of iTunes on the local network, notice it says Port: 59795 With the code the way it is now, that port is random. I really want it to be 5353.

From: {192,168,0,105}
Port: 59795
Data: {ok,{dns_rec,{dns_header,0,false,'query',false,false,false,false,false,
                               0},
                   [{dns_query,"_daap._tcp.local",ptr,in}],
                   [],[],[]}}

Does anyone have any arcane insight in to UDP multicast at all? Updating so I can try and accept an answer. I think I just can't do this.

回答1:

UPDATED: ok, I have found what I believe to be a working solution. The crucial point it seems relate to joining a multicast group.

{ok, Socket} = gen_udp:open(Port=5353, [binary, {active, false}, {reuseaddr, true},
                                        {ip, Addr}, {add_membership, {Addr, IAddr}}]).
  1. Addr: multicast group (e.g. {224, 0, 0, 251}
  2. IAddr is a local IP interface (e.g. can use default {0,0,0,0})

( Of course, make sure that you are not running DNS daemon that might enter in conflict)



回答2:

don't have enough rep to reply to the {broadcast,true} discussion under emil's post, sorry.

The SO_BROADCAST socket flag (which I assume that maps to) must be set or sendto(a broadcast address) will fail. This is a safety catch to prevent abuse or errors with programs that didn't intend to broadcast. Otherwise secure programs would have to try to check for broadcast addresses themselves.

enabling SO_BROADCAST doesn't stop you from sending non-broadcast packets. (again, assuming erlang's stuff just maps directly to setsockopts; I don't know erlang, just networking!)

You might want to try strace to see what system calls actually happen. look for socket(), and then what happens to that file descriptor.



回答3:

You try to open a socket which is already open? Can't you use the same socket for sending and receiving?