client.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. package dhcp4client
  2. import (
  3. "bytes"
  4. "net"
  5. "time"
  6. "github.com/d2g/dhcp4"
  7. )
  8. const (
  9. MaxDHCPLen = 576
  10. )
  11. type Client struct {
  12. hardwareAddr net.HardwareAddr //The HardwareAddr to send in the request.
  13. ignoreServers []net.IP //List of Servers to Ignore requests from.
  14. timeout time.Duration //Time before we timeout.
  15. broadcast bool //Set the Bcast flag in BOOTP Flags
  16. connection connection //The Connection Method to use
  17. generateXID func([]byte) //Function Used to Generate a XID
  18. }
  19. //Abstracts the type of underlying socket used
  20. type connection interface {
  21. Close() error
  22. Write(packet []byte) error
  23. ReadFrom() ([]byte, net.IP, error)
  24. SetReadTimeout(t time.Duration) error
  25. }
  26. func New(options ...func(*Client) error) (*Client, error) {
  27. c := Client{
  28. timeout: time.Second * 10,
  29. broadcast: true,
  30. generateXID: CryptoGenerateXID,
  31. }
  32. err := c.SetOption(options...)
  33. if err != nil {
  34. return nil, err
  35. }
  36. //if connection hasn't been set as an option create the default.
  37. if c.connection == nil {
  38. conn, err := NewInetSock()
  39. if err != nil {
  40. return nil, err
  41. }
  42. c.connection = conn
  43. }
  44. return &c, nil
  45. }
  46. func (c *Client) SetOption(options ...func(*Client) error) error {
  47. for _, opt := range options {
  48. if err := opt(c); err != nil {
  49. return err
  50. }
  51. }
  52. return nil
  53. }
  54. func Timeout(t time.Duration) func(*Client) error {
  55. return func(c *Client) error {
  56. c.timeout = t
  57. return nil
  58. }
  59. }
  60. func IgnoreServers(s []net.IP) func(*Client) error {
  61. return func(c *Client) error {
  62. c.ignoreServers = s
  63. return nil
  64. }
  65. }
  66. func HardwareAddr(h net.HardwareAddr) func(*Client) error {
  67. return func(c *Client) error {
  68. c.hardwareAddr = h
  69. return nil
  70. }
  71. }
  72. func Broadcast(b bool) func(*Client) error {
  73. return func(c *Client) error {
  74. c.broadcast = b
  75. return nil
  76. }
  77. }
  78. func Connection(conn connection) func(*Client) error {
  79. return func(c *Client) error {
  80. c.connection = conn
  81. return nil
  82. }
  83. }
  84. func GenerateXID(g func([]byte)) func(*Client) error {
  85. return func(c *Client) error {
  86. c.generateXID = g
  87. return nil
  88. }
  89. }
  90. //Close Connections
  91. func (c *Client) Close() error {
  92. if c.connection != nil {
  93. return c.connection.Close()
  94. }
  95. return nil
  96. }
  97. //Send the Discovery Packet to the Broadcast Channel
  98. func (c *Client) SendDiscoverPacket() (dhcp4.Packet, error) {
  99. discoveryPacket := c.DiscoverPacket()
  100. discoveryPacket.PadToMinSize()
  101. return discoveryPacket, c.SendPacket(discoveryPacket)
  102. }
  103. //Retreive Offer...
  104. //Wait for the offer for a specific Discovery Packet.
  105. func (c *Client) GetOffer(discoverPacket *dhcp4.Packet) (dhcp4.Packet, error) {
  106. for {
  107. c.connection.SetReadTimeout(c.timeout)
  108. readBuffer, source, err := c.connection.ReadFrom()
  109. if err != nil {
  110. return dhcp4.Packet{}, err
  111. }
  112. offerPacket := dhcp4.Packet(readBuffer)
  113. offerPacketOptions := offerPacket.ParseOptions()
  114. // Ignore Servers in my Ignore list
  115. for _, ignoreServer := range c.ignoreServers {
  116. if source.Equal(ignoreServer) {
  117. continue
  118. }
  119. if offerPacket.SIAddr().Equal(ignoreServer) {
  120. continue
  121. }
  122. }
  123. if len(offerPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || dhcp4.MessageType(offerPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.Offer || !bytes.Equal(discoverPacket.XId(), offerPacket.XId()) {
  124. continue
  125. }
  126. return offerPacket, nil
  127. }
  128. }
  129. //Send Request Based On the offer Received.
  130. func (c *Client) SendRequest(offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
  131. requestPacket := c.RequestPacket(offerPacket)
  132. requestPacket.PadToMinSize()
  133. return requestPacket, c.SendPacket(requestPacket)
  134. }
  135. //Retreive Acknowledgement
  136. //Wait for the offer for a specific Request Packet.
  137. func (c *Client) GetAcknowledgement(requestPacket *dhcp4.Packet) (dhcp4.Packet, error) {
  138. for {
  139. c.connection.SetReadTimeout(c.timeout)
  140. readBuffer, source, err := c.connection.ReadFrom()
  141. if err != nil {
  142. return dhcp4.Packet{}, err
  143. }
  144. acknowledgementPacket := dhcp4.Packet(readBuffer)
  145. acknowledgementPacketOptions := acknowledgementPacket.ParseOptions()
  146. // Ignore Servers in my Ignore list
  147. for _, ignoreServer := range c.ignoreServers {
  148. if source.Equal(ignoreServer) {
  149. continue
  150. }
  151. if acknowledgementPacket.SIAddr().Equal(ignoreServer) {
  152. continue
  153. }
  154. }
  155. if !bytes.Equal(requestPacket.XId(), acknowledgementPacket.XId()) || len(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || (dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK && dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.NAK) {
  156. continue
  157. }
  158. return acknowledgementPacket, nil
  159. }
  160. }
  161. //Send Decline to the received acknowledgement.
  162. func (c *Client) SendDecline(acknowledgementPacket *dhcp4.Packet) (dhcp4.Packet, error) {
  163. declinePacket := c.DeclinePacket(acknowledgementPacket)
  164. declinePacket.PadToMinSize()
  165. return declinePacket, c.SendPacket(declinePacket)
  166. }
  167. //Send a DHCP Packet.
  168. func (c *Client) SendPacket(packet dhcp4.Packet) error {
  169. return c.connection.Write(packet)
  170. }
  171. //Create Discover Packet
  172. func (c *Client) DiscoverPacket() dhcp4.Packet {
  173. messageid := make([]byte, 4)
  174. c.generateXID(messageid)
  175. packet := dhcp4.NewPacket(dhcp4.BootRequest)
  176. packet.SetCHAddr(c.hardwareAddr)
  177. packet.SetXId(messageid)
  178. packet.SetBroadcast(c.broadcast)
  179. packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Discover)})
  180. //packet.PadToMinSize()
  181. return packet
  182. }
  183. //Create Request Packet
  184. func (c *Client) RequestPacket(offerPacket *dhcp4.Packet) dhcp4.Packet {
  185. offerOptions := offerPacket.ParseOptions()
  186. packet := dhcp4.NewPacket(dhcp4.BootRequest)
  187. packet.SetCHAddr(c.hardwareAddr)
  188. packet.SetXId(offerPacket.XId())
  189. packet.SetCIAddr(offerPacket.CIAddr())
  190. packet.SetSIAddr(offerPacket.SIAddr())
  191. packet.SetBroadcast(c.broadcast)
  192. packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)})
  193. packet.AddOption(dhcp4.OptionRequestedIPAddress, (offerPacket.YIAddr()).To4())
  194. packet.AddOption(dhcp4.OptionServerIdentifier, offerOptions[dhcp4.OptionServerIdentifier])
  195. return packet
  196. }
  197. //Create Request Packet For a Renew
  198. func (c *Client) RenewalRequestPacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
  199. messageid := make([]byte, 4)
  200. c.generateXID(messageid)
  201. acknowledgementOptions := acknowledgement.ParseOptions()
  202. packet := dhcp4.NewPacket(dhcp4.BootRequest)
  203. packet.SetCHAddr(acknowledgement.CHAddr())
  204. packet.SetXId(messageid)
  205. packet.SetCIAddr(acknowledgement.YIAddr())
  206. packet.SetSIAddr(acknowledgement.SIAddr())
  207. packet.SetBroadcast(c.broadcast)
  208. packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)})
  209. packet.AddOption(dhcp4.OptionRequestedIPAddress, (acknowledgement.YIAddr()).To4())
  210. packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
  211. return packet
  212. }
  213. //Create Release Packet For a Release
  214. func (c *Client) ReleasePacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
  215. messageid := make([]byte, 4)
  216. c.generateXID(messageid)
  217. acknowledgementOptions := acknowledgement.ParseOptions()
  218. packet := dhcp4.NewPacket(dhcp4.BootRequest)
  219. packet.SetCHAddr(acknowledgement.CHAddr())
  220. packet.SetXId(messageid)
  221. packet.SetCIAddr(acknowledgement.YIAddr())
  222. packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Release)})
  223. packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
  224. return packet
  225. }
  226. //Create Decline Packet
  227. func (c *Client) DeclinePacket(acknowledgement *dhcp4.Packet) dhcp4.Packet {
  228. messageid := make([]byte, 4)
  229. c.generateXID(messageid)
  230. acknowledgementOptions := acknowledgement.ParseOptions()
  231. packet := dhcp4.NewPacket(dhcp4.BootRequest)
  232. packet.SetCHAddr(acknowledgement.CHAddr())
  233. packet.SetXId(messageid)
  234. packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Decline)})
  235. packet.AddOption(dhcp4.OptionRequestedIPAddress, (acknowledgement.YIAddr()).To4())
  236. packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier])
  237. return packet
  238. }
  239. //Lets do a Full DHCP Request.
  240. func (c *Client) Request() (bool, dhcp4.Packet, error) {
  241. discoveryPacket, err := c.SendDiscoverPacket()
  242. if err != nil {
  243. return false, discoveryPacket, err
  244. }
  245. offerPacket, err := c.GetOffer(&discoveryPacket)
  246. if err != nil {
  247. return false, offerPacket, err
  248. }
  249. requestPacket, err := c.SendRequest(&offerPacket)
  250. if err != nil {
  251. return false, requestPacket, err
  252. }
  253. acknowledgement, err := c.GetAcknowledgement(&requestPacket)
  254. if err != nil {
  255. return false, acknowledgement, err
  256. }
  257. acknowledgementOptions := acknowledgement.ParseOptions()
  258. if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
  259. return false, acknowledgement, nil
  260. }
  261. return true, acknowledgement, nil
  262. }
  263. //Renew a lease backed on the Acknowledgement Packet.
  264. //Returns Sucessfull, The AcknoledgementPacket, Any Errors
  265. func (c *Client) Renew(acknowledgement dhcp4.Packet) (bool, dhcp4.Packet, error) {
  266. renewRequest := c.RenewalRequestPacket(&acknowledgement)
  267. renewRequest.PadToMinSize()
  268. err := c.SendPacket(renewRequest)
  269. if err != nil {
  270. return false, renewRequest, err
  271. }
  272. newAcknowledgement, err := c.GetAcknowledgement(&renewRequest)
  273. if err != nil {
  274. return false, newAcknowledgement, err
  275. }
  276. newAcknowledgementOptions := newAcknowledgement.ParseOptions()
  277. if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
  278. return false, newAcknowledgement, nil
  279. }
  280. return true, newAcknowledgement, nil
  281. }
  282. //Release a lease backed on the Acknowledgement Packet.
  283. //Returns Any Errors
  284. func (c *Client) Release(acknowledgement dhcp4.Packet) error {
  285. release := c.ReleasePacket(&acknowledgement)
  286. release.PadToMinSize()
  287. return c.SendPacket(release)
  288. }