sourcestuds::Dhcp.fan

//
// Copyright (c) 2016, Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   4 Oct 2016  Andy Frank  Creation
//

using inet
using concurrent

**
** DhcpClient
**
class DhcpClient
{
  Void discover()
  {
    socket := MulticastSocket()
    socket.loopbackMode = true
    socket.bind(null, 68)
    socket.joinGroup(IpAddr("224.0.0.1"), null, null)

    while (true)
    {
      try
      {
        p := socket.receive
        echo("# got it!")
      }
      catch (Err e)
      {
        echo("# ERR $e.msg")
        Actor.sleep(1sec)
      }
    }

    /*
    // socket := UdpSocket()
    // socket.options.broadcast = true
    socket := MulticastSocket()
    socket.timeToLive = 10
    socket.options.receiveTimeout = 10sec

    // send request
    socket.send(UdpPacket(baddr, port, DhcpPacket {
    //socket.send(UdpPacket(maddr, port, DhcpPacket {
      it.op     = DhcpPacket.opBootRequest
      it.chaddr = IpInterface.findByName("en0").hardwareAddr
    }.encode))

    // wait for response
    res := UdpPacket(null, null, Buf(65536))
    while (true)
    {
      // receive message or on timeout break
      res.data.clear
      try
      {
        socket.receive(res)
        echo("### RECV ###")
        echo(res.data.toHex)
      }
      catch (IOErr e)
      {
        e.trace
        break
      }
    }
    */
  }

  private static const IpAddr baddr := IpAddr("255.255.255.255")
  private static const IpAddr maddr := IpAddr("224.0.0.1")
  private static const Int port     := 67


  // Grid discover()
  // {
  //   // create socket
  //   socket := MulticastSocket()
  //   socket.timeToLive = 4
  //   socket.options.receiveTimeout = 300ms
  //   packet := UdpPacket(null, null, Buf(65536))
  //
  //   // request data
  //   reqGrid := Etc.makeMapGrid(null, ["host":IpAddr.local.hostname])
  //   reqData := ZincWriter.gridToStr(reqGrid).toBuf
  //
  //   // keep track of unique URI endpoints
  //   acc := Uri:Dict[:]
  //
  //   // send out request three times and wait for responses
  //   3.times
  //   {
  //     // send request
  //     socket.send(UdpPacket(multicastGroup, multicastPort, reqData.seek(0)))
  //
  //     // look receiving messages until we have timeout
  //     while (true)
  //     {
  //       // receive message or on timeout break
  //       packet.data.clear
  //       try
  //         socket.receive(packet)
  //       catch (IOErr e)
  //         break
  //
  //       // process the packet
  //       try
  //       {
  //         grid := ZincReader(packet.data.flip.in).readGrid
  //         grid.each |row|
  //         {
  //           uri := row["uri"] as Uri
  //           if (uri == null || uri.scheme == null) return
  //           acc[uri] = row
  //         }
  //       }
  //       catch (Err e) e.trace
  //     }
  //   }
  //
  //   // now build up response of unique URI endpoints
  //   rows := acc.vals.sort |a, b| { a.dis <=> b.dis }
  //   return Etc.makeDictsGrid(null, rows)
  // }
}

**************************************************************************
** DhcpPacket
**************************************************************************

**
** DhcpPacket models a DHCP UDP Packet.  See RFC 2131
** for documentation on fields.
**
@NoDoc const class DhcpPacket
{
  ** It-block ctor.
  new make(|This| f) { f(this) }

  static const Int opBootRequest := 1
  static const Int opBootReply   := 2

  const Int op
  const Int htype    := 1  // eth=1
  const Int hlen     := 6  // always 6 for eth/802
  const Int hops     := 0
  const Int xid      := Int.random(0..0xffff_ffff)
  const Int secs     := 0
  const Int flags    := 0
  const Int ciaddr   := 0
  const Int yiaddr   := 0
  const Int siaddr   := 0
  const Int giaddr   := 0
  const Buf chaddr
  const Str? sname   := null
  const Buf? options := null

  ** Encode this packet into a Buf.
  Buf encode()
  {
    buf := Buf()
    buf.write(op)
    buf.write(htype)
    buf.write(hlen)
    buf.write(hops)
    buf.writeI4(xid)
    buf.writeI2(secs)
    buf.writeI2(flags)
    buf.writeI4(ciaddr)
    buf.writeI4(yiaddr)
    buf.writeI4(siaddr)
    buf.writeI4(giaddr)

    // chaddr
    hlen.times |i| { buf.write(chaddr[i]) }
    (16-hlen).times { buf.write(0) }

    // TODO: sname
    64.times { buf.write(0) }

    // TODO: file
    128.times { buf.write(0) }

    // TODO: options

    // padding
    while (buf.size < 300) buf.write(0)

echo("$buf.toHex [$buf.size]")
    return buf.flip
  }
}