package dhcpv4 import ( "testing" ) func TestClientServer(t *testing.T) { svAddr := [4]byte{192, 168, 1, 1} clAddr := svAddr clAddr[3]++ var sv Server var cl Client err := cl.BeginRequest(123, RequestConfig{ RequestedAddr: clAddr, ClientHardwareAddr: [6]byte{1, 2, 3, 4, 5, 6}, Hostname: "lneto", }) if err != nil { t.Fatal(err) } assertClState := func(state ClientState) { t.Helper() if state != cl.State() { t.Errorf("want client state %s, got %s", state.String(), cl.State().String()) } } sv.Reset(svAddr, DefaultServerPort) // CLIENT DISCOVER. assertClState(StateInit) var buf [1024]byte n, err := cl.Encapsulate(buf[:], 0) if err != nil { t.Fatal(err) } else if n == 0 { t.Fatal("no client discover") } assertClState(StateSelecting) err = sv.Demux(buf[:n], 0) if err != nil { t.Fatal(err) } // SERVER REPLY OFFER n, err = sv.Encapsulate(buf[:], 0) if err != nil { t.Fatal(err) } else if n == 0 { t.Fatal("no server offer") } err = cl.Demux(buf[:n], 0) if err != nil { t.Fatal(err) } assertClState(StateSelecting) // CLIENT SEND OUT REQUEST. n, err = cl.Encapsulate(buf[:], 0) if err != nil { t.Fatal(err) } else if n == 0 { t.Fatal("no client request") } assertClState(StateRequesting) err = sv.Demux(buf[:n], 0) if err != nil { t.Fatal(err) } // SERVER REPLIES WITH ACK. n, err = sv.Encapsulate(buf[:], 0) if err != nil { t.Fatal(err) } else if n == 0 { t.Fatal("no server reply") } err = cl.Demux(buf[:n], 0) if err != nil { t.Fatal(err) } assertClState(StateBound) } func TestExample(t *testing.T) { const ( xid = 1 offerLease = 9001 ) var cl Client clientHwaddr := [6]byte{0, 0, 0, 0, 0, 1} clientReqAddr := [4]byte{192, 168, 1, 2} clientHostname := "client" serverIP := [4]byte{192, 168, 1, 1} subnetMask := [4]byte{255, 255, 255, 0} routerAddr := [4]byte{192, 168, 1, 0} dnsAddr := [4]byte{192, 168, 1, 255} cl.BeginRequest(xid, RequestConfig{ RequestedAddr: clientReqAddr, ClientHardwareAddr: clientHwaddr, Hostname: clientHostname, }) buf := make([]byte, 2048) buf2 := make([]byte, len(buf)) n, err := cl.Encapsulate(buf, 0) if err != nil { t.Fatal(err) } else if n <= 0 { t.Fatal("no data sent out by client after starting request") } n, err = cl.Encapsulate(buf2, 0) if err != nil { t.Error("client encaps double tap after discover:", err) } // Fabricate server OFFER response. dfrm, _ := NewFrame(buf) dfrm.ClearHeader() dfrm.SetOp(OpReply) dfrm.SetHardware(1, 6, 0) dfrm.SetFlags(0) dfrm.SetXID(xid) dfrm.SetSecs(1) *dfrm.YIAddr() = clientReqAddr copy(dfrm.CHAddr()[:], clientHwaddr[:]) dfrm.SetMagicCookie(MagicCookie) ntot := 0 nopt, _ := EncodeOption(buf[OptionsOffset+ntot:], OptMessageType, byte(MsgOffer)) ntot += nopt nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptServerIdentification, serverIP[:]...) ntot += nopt nopt, _ = EncodeOption32(buf[OptionsOffset+ntot:], OptServerIdentification, offerLease) ntot += nopt nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptSubnetMask, subnetMask[:]...) ntot += nopt nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptRouter, routerAddr[:]...) ntot += nopt nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptDNSServers, dnsAddr[:]...) ntot += nopt nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptEnd, dnsAddr[:]...) ntot += nopt err = cl.Demux(buf[:OptionsOffset+ntot], 0) if err != nil { t.Fatal(err) } n, err = cl.Encapsulate(buf[:], 0) if err != nil { t.Fatal(err) } else if n <= 0 { t.Fatal("no data written from client in response to offer") } n, err = cl.Encapsulate(buf[:], 0) if err != nil { t.Error("encapsulate double tap after request:", err) } else if n > 0 { t.Error("encapsulate double tap got data!", n) } }