dhcp4r/
server.rs

1use std::cell::Cell;
2use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
3
4use crate::options;
5use crate::options::{DhcpOption, MessageType};
6use crate::packet::*;
7
8///! This is a convenience module that simplifies the writing of a DHCP server service.
9
10pub struct Server {
11    out_buf: Cell<[u8; 1500]>,
12    socket: UdpSocket,
13    src: SocketAddr,
14    server_ip: Ipv4Addr,
15}
16
17pub trait Handler {
18    fn handle_request(&mut self, server: &Server, in_packet: Packet);
19}
20
21/// Orders and filters options based on PARAMETER_REQUEST_LIST received from client.
22/// DHCP_MESSAGE_TYPE and SERVER_IDENTIFIER are always first and always retained.
23/// This function is called by Reply.
24pub fn filter_options_by_req(opts: &mut Vec<DhcpOption>, req_params: &[u8]) {
25    let mut pos = 0;
26    let h = &[
27        options::DHCP_MESSAGE_TYPE as u8,
28        options::SERVER_IDENTIFIER as u8,
29        options::IP_ADDRESS_LEASE_TIME as u8,
30    ] as &[u8];
31    for z in [h, req_params].iter() {
32        for r in z.iter() {
33            let mut found = false;
34            let mut at = 0;
35            for (i, o) in opts[pos..].iter().enumerate() {
36                if o.code() == *r {
37                    found = true;
38                    at = i + pos;
39                    break;
40                }
41            }
42            if found {
43                opts.swap(pos, at);
44                pos += 1;
45            }
46        }
47    }
48    opts.truncate(pos);
49}
50
51impl Server {
52    pub fn serve<H: Handler>(
53        udp_soc: UdpSocket,
54        server_ip: Ipv4Addr,
55        mut handler: H,
56    ) -> std::io::Error {
57        let mut in_buf: [u8; 1500] = [0; 1500];
58        let mut s = Server {
59            out_buf: Cell::new([0; 1500]),
60            socket: udp_soc,
61            server_ip,
62            src: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0),
63        };
64        loop {
65            match s.socket.recv_from(&mut in_buf) {
66                Err(e) => return e,
67                Ok((l, src)) => {
68                    if let Ok(p) = Packet::from(&in_buf[..l]) {
69                        s.src = src;
70                        handler.handle_request(&s, p);
71                    }
72                }
73            }
74        }
75    }
76
77    /// Constructs and sends a reply packet back to the client.
78    /// additional_options should not include DHCP_MESSAGE_TYPE nor SERVER_IDENTIFIER as these
79    /// are added automatically.
80    pub fn reply(
81        &self,
82        msg_type: MessageType,
83        additional_options: Vec<DhcpOption>,
84        offer_ip: Ipv4Addr,
85        req_packet: Packet,
86    ) -> std::io::Result<usize> {
87        let ciaddr = match msg_type {
88            MessageType::Nak => Ipv4Addr::new(0, 0, 0, 0),
89            _ => req_packet.ciaddr,
90        };
91
92        //let mt = &[msg_type as u8];
93
94        let mut opts: Vec<DhcpOption> = Vec::with_capacity(additional_options.len() + 2);
95        opts.push(DhcpOption::DhcpMessageType(msg_type));
96        opts.push(DhcpOption::ServerIdentifier(self.server_ip));
97        /*opts.push(DhcpOption {
98            code: options::DHCP_MESSAGE_TYPE,
99            data: mt,
100        });
101        opts.push(DhcpOption {
102            code: options::SERVER_IDENTIFIER,
103            data: &self.server_ip,
104        });*/
105        opts.extend(additional_options);
106
107        if let Some(DhcpOption::ParameterRequestList(prl)) =
108            req_packet.option(options::PARAMETER_REQUEST_LIST)
109        {
110            filter_options_by_req(&mut opts, &prl);
111        }
112
113        self.send(Packet {
114            reply: true,
115            hops: 0,
116            xid: req_packet.xid,
117            secs: 0,
118            broadcast: req_packet.broadcast,
119            ciaddr,
120            yiaddr: offer_ip,
121            siaddr: Ipv4Addr::new(0, 0, 0, 0),
122            giaddr: req_packet.giaddr,
123            chaddr: req_packet.chaddr,
124            options: opts,
125        })
126    }
127
128    /// Checks the packet see if it was intended for this DHCP server (as opposed to some other also on the network).
129    pub fn for_this_server(&self, packet: &Packet) -> bool {
130        match packet.option(options::SERVER_IDENTIFIER) {
131            Some(DhcpOption::ServerIdentifier(x)) => (x == &self.server_ip),
132            _ => false,
133        }
134    }
135
136    /// Encodes and sends a DHCP packet back to the client.
137    pub fn send(&self, p: Packet) -> std::io::Result<usize> {
138        let mut addr = self.src;
139        if p.broadcast || addr.ip() == IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)) {
140            addr.set_ip(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)));
141        }
142        self.socket.send_to(p.encode(&mut self.out_buf.get()), addr)
143    }
144}