Autodiscovery of LAN devices

Bob Sneidar bobsneidar at iotecdigital.com
Mon Oct 31 10:55:08 EDT 2016


You must not have my latest. Keep in mind that this function requires a CIDR notation or else the IP and subnet mask. Nothing can calculate the subnet with only an IP address. 

Bob S

function IPCalc theIPAddress, theSubnetMask
   /* IPCalc yyy
      Syntax:
      IPCalc theIPAddress, [theSubnetMask]
      Examples:
      
      Description:
      Derive Internet values from either CIDR notation in the IPAddress
      or a standard IP and subnet mask
   
      Input: 
      .    theIPAddress -  the IP address in CIDR notation
       or
      .    theIPAddress - a standard IP address and
     .     theSubNetMask - a standard subNetMask
   
      Returns an array of the following values:
      .   bcastaddr
      .   cidraddr
      .   cidrdepth
      .   firstaddr
      .   ipaddress
      .   lastaddr
      .   subnetaddr
      .   subnetmask
      .   usablecount
      Returns a string beginning with ERROR: if the parameters are out of range
      Check that the returned value is an array to see if there was an error
   
   Source:
       Bob Sneidar,  slylabs13 at icloud.com
   IPCalc */
   
   set the itemdelimiter to "."
   
   -- check parameters
   -- the IP address must be 4 octets of numbers
   if the number of items of theIPAddress <>4 \
         or the last char of theIPAddress is "." \
         or ".." is in theIPAddress then
      return "ERROR: The IP Address must be in the form:" & cr & \
            "'nnn.nnn.nnn.nnn' or 'nnn.nnn.nnn.nnn/nn'. (ipaddress = '" & theIPAddress & "')"
   end if
   
   -- initial setup
   set the numberformat to "00000000"
   
   -- detemine format
   if theIPAddress contains "/" then
      put offset("/", theIPAddress) into theCIDRDelim
      put char theCIDRDelim +1 to -1 of theIPAddress into theCIDRDepth
      
      -- CIDR depth must be a WHOLE number
      put cleanString(theCIDRDepth) into theCIDRDepth
      
      if theCIDRDepth is not a number then
         return "ERROR: The CIDR Depth must be a number between 0 and 32. " & \
               "(CIDRDepth = '" & theCIDRDepth & "')"
      end if
      
      put charx("1", theCIDRDepth) & charx("0", 32-theCIDRDepth) into theBinSubnetMask
      put baseconvert(char 1 to 8 of theBinSubnetMask, 2, 10) into item 1 of theSubnetMask
      put baseconvert(char 9 to 16 of theBinSubnetMask, 2, 10) into item 2 of theSubnetMask
      put baseconvert(char 17 to 24 of theBinSubnetMask, 2, 10) into item 3 of theSubnetMask
      put baseconvert(char 25 to 32 of theBinSubnetMask, 2, 10) into item 4 of theSubnetMask
      put char 1 to theCIDRDelim -1 of theIPAddress into theIPAddress
   else
      -- subnet mask octets must be 4 numbers between 0 and 255
      -- and all octets after the first octet less than 255 must be 0
      
      if the number of items of theSubnetMask <>4 \
            or the last char of theSubnetMask is "." \
            or ".." is in theSubnetMask then
         return "ERROR: The Subnet Mask must be in the form:" & cr & \
               "'nnn.nnn.nnn.nnn' (subnetmask = '" & theSubnetMask & "')"
      end if
      
      put false into mustBeZero
      repeat for each item theOctet in theSubnetMask
         
         if theOctet <0 or theOctet >255 then
            return "Each octet in the subnet mask must be a number between 0 and 255. " & \
                  "(subnetmask = '" & theSubnetMask & "')"
         end if
         
         if mustBeZero and theOctet >0 then
            return "ERROR: All octets after an octet less than 255 must be 0. " & \
                  "(subnetmask = '" & theSubnetMask & "')"
         end if
         
         if theOctet <255 then
            put true into mustBeZero
         end if
      end repeat
      
      -- convert the subnet mask to binary
      put 0 into whichOctet
      repeat for each item theOctet in theSubnetMask
         add 1 to whichOctet
         
         -- subnet mask must contain only 4 octets
         if whichOctet >4 then
            return "ERROR: The Subnet Mask must contain 4 numbers between 0 and 255 " & \
                  "separated by periods. (subnetmask = '" & theSubnetMask & "')"
         end if
         
         put value(baseconvert(theOctet, 10, 2)) after theBinSubnetMask
      end repeat
      put offset("0", theBinSubnetMask) -1 into theCIDRDepth
   end if
   
   -- CIDR depth must be between 0 and 32
   if theCIDRDepth <0 or theCIDRDepth >32 then
      return "ERROR: The CIDR Depth must be between 0 and 32. " & \
            "(CIDRDepth = '" & theCIDRDepth & "')"
   end if
   
   -- All octets of the IP address must be between 0 and 255
   repeat for each item theOctet in theIPAddress
      if theOctet is empty or theOctet < 0 or theOctet > 255 then
         return "ERROR: Each IP Address octet must be a number between 0 and 255. " & \
               "(ipaddress = '" & theIPAddress & "')"
      end if
   end repeat
   
   -- convert the ip address to binary
   put 0 into whichOctet
   repeat for each item theOctet in theIPAddress
      add 1 to whichOctet
      put baseconvert(theOctet, 10, 2) into theBinValue
      add 0 to theBinValue
      put theBinValue after theBinIPAddress
   end repeat
   
   -- calculate the binary subnet address
   put char 1 to theCIDRDepth of theBinIPAddress into theBinNetworkAddr
   put char theCIDRDepth +1 to -1 of theBinIPAddress into theBinNodeAddr
   put theBinNodeAddr into theBinSubnetNodeAddr
   set the numberformat to "0"
   replace "1" with "0" in theBinSubnetNodeAddr
   put theBinNetworkAddr & theBinSubnetNodeAddr into theBinSubnetAddr
   
   -- convert the binary subnet address to decimal
   put baseconvert(char 1 to 8 of theBinSubnetAddr, 2, 10)  into item 1 of theSubnetAddr
   put baseconvert(char 9 to 16 of theBinSubnetAddr, 2, 10)  into item 2 of theSubnetAddr
   put baseconvert(char 17 to 24 of theBinSubnetAddr, 2, 10)  into item 3 of theSubnetAddr
   put baseconvert(char 25 to 32 of theBinSubnetAddr, 2, 10)  into item 4 of theSubnetAddr
   
   -- calculate the first usable IP address
   put theSubnetAddr into theFirstAddr
   add 1 to item 4 of theFirstAddr
   
   -- calculate the binary broadcast address
   put theBinNodeAddr into theBinBcastNodeAddr
   replace "0" with "1" in theBinBcastNodeAddr
   put theBinNetworkAddr & theBinBcastNodeAddr into theBinBcastAddr
   
   -- convert the binary broadcast address to decimal
   put baseconvert(char 1 to 8 of theBinBcastAddr, 2, 10) into item 1 of theBcastAddr
   put baseconvert(char 9 to 16 of theBinBcastAddr, 2, 10) into item 2 of theBcastAddr
   put baseconvert(char 17 to 24 of theBinBcastAddr, 2, 10) into item 3 of theBcastAddr
   put baseconvert(char 25 to 32 of theBinBcastAddr, 2, 10) into item 4 of theBcastAddr
   
   -- calculate the last usable IP address
   put theBcastAddr into theLastAddr
   subtract 1 from item 4 of theLastAddr
   
   -- calculate the number of usable addresses
   -- put item 4 of theLastAddr - item 4 of theFirstAddr +1 into theAddrCount
   put baseconvert(theBinBcastNodeAddr, 2, 10) -1 into theAddrCount
   
   -- calculate the CIDR notation
   put theIPAddress & "/" & theCIDRDepth into theCIDRAddr
   
   -- create array
   put theIPAddress into ipdata ["ipaddress"]
   put theSubnetMask into ipdata ["subnetmask"]
   put theSubnetAddr into ipdata ["subnetaddr"]
   put theFirstAddr into ipdata ["firstaddr"]
   put theBcastAddr into ipdata["bcastaddr"]
   put theLastAddr into ipdata ["lastaddr"]
   put theCIDRDepth into ipdata ["cidrdepth"]
   put theAddrCount into ipdata ["usablecount"]
   put theCIDRAddr into ipdata ["cidraddr"]
   return ipdata
end IPCalc


> On Oct 27, 2016, at 17:58 , Richard Gaskin <ambassador at fourthworld.com> wrote:
> 
> Thanks, Alex.  I may need something more refined later on, but for now my modest needs appear to be well met through simple brute force:
> 
> At the moment this is for some network tools for students to use in a classroom.  Ideally there would be no Internet connection, just a local network of a small number of devices all using a single wifi router.
> 
> I had experimented briefly with a UDP broadcast, but ran into an issue in which it seemed the server wasn't receiving the message.
> 
> Before spending more time on that I tried a different tack with TCP.
> 
> With Bob's IPCalc function I'm able to get the first and last addresses of the local subnet.  His function needs the local IP and subnet mask, which can be obtained on Mac and Linux with a shell call to ifconfig, and on Win with ipconfig.
> 
> Armed with that I just attempt a TCP connection to each device in turn, looking for a specific reply.  When I get what I expect, I know I've reached my app on the other machine.  Takes less than a second to scan the network.
> 
> Done in a few minutes' work.
> 
> Of course this won't do much for mobile devices (how do I get the local address on iOS and Android without shell?), but since UDP is off the table there anyway I'm no worse off, and can at least get started with laptops and Raspberry Pis happily talking to one another...
> 
> --
> Richard Gaskin
> Fourth World Systems
> 
> 
> Alex Tweedly wrote:
> 
>> it might help if you were to more precisely describe the problem you are
>> seeking to solve.
>> 
>> If you need to discover all/any/arbitrary listeners - then "what Bob said".
>> 
>> If you need to discover listeners for a specific port/service provided
>> by some other apps/servers in a standard way - then "what Monte said".
>> 
>> If you need to discover instances of "your own" server on the LAN, then
>> there may be an other solution..... depending on how well defined "my
>> LAN" is.
>> 
>> Basically,
>> 
>> - every server listens for UDP packet on some port
>> 
>>  - client sends a broadcast UDP request to that port
>> 
>>  - servers respond.
>> 
>> This assumes that you can *reliably* depend on your LAN being a single
>> subnet, that your LAN is not too huge :-), that all servers are within
>> your control and you can add such a listener to them, etc.
>> 
>> NB - this is only a solution for desktops (only they can send broadcasts).
>> 
>> For ios you can use mergSomething :-)
>> 
>> For Android, afaik there isn't anything to allow broadcast transmission,
>> so I think your only solution for now is to move to a
>> broadcast/advertisement approach - servers (currently desktop only, you
>> said) must advertise their service, and Android clients can listen for
>> such adverts and thus learn where the service is available. This is only
>> feasible if you know of limits on the number of servers & services - or
>> (for-android as-client) if you are willing to add significant complexity
>> and have proxy-servers handle it for you; you can do that in an
>> automatic way (i.e. no configuration needed) but it is complex.
>> 
>> Don't even start down that road unless you need this for Android before
>> the timeframe for either bonjour or sockets on android.
>> 
>> (But if you do want to go down that road, get in touch I'd be happy to
>> collaborate)
>> 
>> -- Alex.
>> 
>> 
>> On 27/10/2016 22:12, Richard Gaskin wrote:
>>> I'd like to have an app automatically discover and attempt connection
>>> to other devices on my LAN.
>>> 
>>> Looking through the list archives I can find a few half-solutions, but
>>> not one which works well across the platforms LC supports (Mac, Win,
>>> Linux, iOS, Android).
>>> 
>>> At this time the only things listening on a port will be desktop
>>> computers, but I still need to be able to connect with them from any
>>> other device on the local network, and down the road I may want to
>>> allow even handheld devices to take on an accept role.
>>> 
>>> Any robust, tested solutions available?
>>> 
> 
> 
> _______________________________________________
> use-livecode mailing list
> use-livecode at lists.runrev.com
> Please visit this url to subscribe, unsubscribe and manage your subscription preferences:
> http://lists.runrev.com/mailman/listinfo/use-livecode





More information about the use-livecode mailing list