
Bash: auto-detection of an adversary for a network game on Macs

Recently, I mentally returned to them and thought how cool it would be if the game itself would find a partner for playing online. Those. being launched, the toy must somehow search the network for those who are ready for the fight.
Scanning all addresses is impossible for a long time. There are two ugly “head-on” solutions - scan the current subnet or look at the ARP table , connect to those who are there. But, firstly, this sorting will still be slow, and secondly, it won’t find all potential rivals (rivals can be on other subnets, but in the ARP tablegenerally not all network participants).
In general, a similar problem was solved a long time ago in OSes - for example, when I set up a network printer at home, the operating system found it myself, I did not specify IP or something else. In the “Macs” for this there is the technology “ Bonjour ” (implementation of “ Zerokonf ”).
Is it possible to use this technology in Bash?
I didn’t rewrite the network chess, with the thought of which it all started, instead I set out to make a file transfer utility that would work according to the following algorithm: it runs on two machines, indicating which file should be transferred if computers see each other in network, then the file is transferred. All this without specifying the IP of these machines or their names.
On the Macs there is the dns-sd utility (or its older version of mDNS ), which is engaged in the announcement of network services and their discovery. Those. being started with some keys, she says "this computer can do this," with others - it looks for the specified computer on the network.
Here's how it goes.
Services are indicated by name, the service name is a Unicode string (in UTF-8 format ), up to 63 bytes (not characters) in length, specified in the form " _app-proto._tcp " or " _app-proto._udp ", where and is the name of the service) “ app-proto ” needs to be registered on a special site, but since the utility takes any value, it will do without this step.
In general, if you run dns-sd with the key “-R”, then it will register a new service on the network:

which can then be searched on the network by running the same command with the “-B” key:

As you can see, in addition to the service name, there are other parameters, but in addition to the “Hello!” Line, there is, in fact, nothing interesting, they do not affect anything in this implementation. Instead of “hello” on this line, you can write something more useful. I write down information about the IP machine that announced the service and the checksum of the transferred file.
A bit of code.
This is what the server announcement looks like in my code:
# эта функция сработает при выходе из скрипта и прибъёт работающую в фоне dns-sd
# так же она проверяет действительно ли прибиваемая программа — dns-sd, анонсирующая наш сервис
function _ClearServer {
ps -f | awk"\$2==$1 && /_bolk-fileshare._tcp/ { print \$2 }" | xargs kill
}
dns-sd -R "$myownip $checksum" "_bolk-fileshare._tcp" . 1 >/dev/null &
trap "_ClearServer $!" EXIT
As you can see, in the background I launch the announcement of the service, and at the exit from the utility I beat the dns-sd running in the background . If I would not use the launch in the background, then the execution of my script stopped at this place - dns-sd after the start does not return control and, when exiting, removes the service from the announced ones.
Now service discovery. In this mode, dns-sd is similar to the top utility - the console is also not released, events about announced and de-announced services appear on the screen. Therefore, I had to use the expect utility, which is very useful in such cases :
local info=($(expect <2 {print $7 " " $8}' | sort -u | tr -d '\r' | head -n1
spawn -noecho dns-sd -B _bolk-fileshare._tcp
expect Timestamp
expect -- "_bolk-fileshare._tcp"
exit
CMDS))
The meaning here is: I ask expect to return control to me when dns-sd displays a line with the name of my service. After that, I select the one I need - the very line where the server IP address and the checksum of the transmitted file are transferred to me . In the info variable , I get an array of two elements - the sum and IP .
The rest is not very interesting, the resulting code lies on the GitHub - then I just connect to the specified address and get the file with the netcat utility .
To summarize, the idea is this: a network game, to search for an opponent, announces a service with some given name, scanning at the same time already announced services to ask the player with whom he wants to play. The player chooses someone, then the machines are connected to the specified addresses and the game begins.