1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::net::{UdpSocket, SocketAddr, Ipv4Addr, IpAddr};
use std;
use std::cell::Cell;

use packet::*;
use options;
use NAK;

///! This is a convenience module that simplifies the writing of a DHCP server service.

pub struct Server {
    out_buf: Cell<[u8; 1500]>,
    socket: UdpSocket,
    src: SocketAddr,
    server_ip: [u8; 4],
}

pub trait Handler {
    fn handle_request(&mut self, &Server, u8, Packet);
}

/// Orders and filters options based on PARAMETER_REQUEST_LIST received from client.
/// DHCP_MESSAGE_TYPE and SERVER_IDENTIFIER are always first and always retained.
/// This function is called by Reply.
pub fn filter_options_by_req(opts: &mut Vec<options::Option>, req_params: &[u8]) {
    let mut pos = 0;
    let h = &[options::DHCP_MESSAGE_TYPE as u8, options::SERVER_IDENTIFIER as u8] as &[u8];
    for z in [h, req_params].iter() {
        for r in z.iter() {
            let mut found = false;
            let mut at = 0;
            for (i, o) in opts[pos..].iter().enumerate() {
                if o.code == *r {
                    found = true;
                    at = i + pos;
                    break;
                }
            }
            if found {
                opts.swap(pos, at);
                pos = pos + 1;
            }
        }
    }
    opts.truncate(pos);
}

impl Server {
    pub fn serve<H: Handler>(udp_soc: UdpSocket,
                             server_ip: [u8; 4],
                             mut handler: H)
                             -> std::io::Error {
        let mut in_buf: [u8; 1500] = [0; 1500];
        let mut s = Server {
            out_buf: Cell::new([0; 1500]),
            socket: udp_soc,
            server_ip: server_ip,
            src: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0),
        };
        loop {
            match s.socket.recv_from(&mut in_buf) {
                Err(e) => return e,
                Ok((l, src)) => {
                    if let Ok(p) = decode(&in_buf[..l]) {
                        if let Some(msg_type) = p.option(options::DHCP_MESSAGE_TYPE) {
                            if msg_type.len() != 1 {
                                continue;
                            }
                            s.src = src;
                            handler.handle_request(&s, msg_type[0], p);
                        }
                    }
                }
            }
        }
    }

    /// Constructs and sends a reply packet back to the client.
    /// additional_options should not include DHCP_MESSAGE_TYPE nor SERVER_IDENTIFIER as these
    /// are added automatically.
    pub fn reply(&self,
                 msg_type: u8,
                 additional_options: Vec<options::Option>,
                 offer_ip: [u8; 4],
                 req_packet: Packet)
                 -> std::io::Result<usize> {
        let mt = &[msg_type];

        let mut opts: Vec<options::Option> = Vec::with_capacity(additional_options.len() + 2);
        opts.push(options::Option {
            code: options::DHCP_MESSAGE_TYPE,
            data: mt,
        });
        opts.push(options::Option {
            code: options::SERVER_IDENTIFIER,
            data: &self.server_ip,
        });
        opts.extend(additional_options);

        if let Some(prl) = req_packet.option(options::PARAMETER_REQUEST_LIST) {
            filter_options_by_req(&mut opts, &prl);
        }

        self.send(Packet {
            reply: true,
            hops: 0,
            xid: req_packet.xid,
            secs: 0,
            broadcast: req_packet.broadcast,
            ciaddr: if msg_type == NAK {
                [0, 0, 0, 0]
            } else {
                req_packet.ciaddr
            },
            yiaddr: offer_ip,
            siaddr: [0, 0, 0, 0],
            giaddr: req_packet.giaddr,
            chaddr: req_packet.chaddr,
            options: opts,
        })
    }

    /// Checks the packet see if it was intended for this DHCP server (as opposed to some other also on the network).
    pub fn for_this_server(&self, packet: &Packet) -> bool {
        match packet.option(options::SERVER_IDENTIFIER) {
            None => false,
            Some(x) => (x == &self.server_ip),
        }
    }

    /// Encodes and sends a DHCP packet back to the client.
    pub fn send(&self, p: Packet) -> std::io::Result<usize> {
        let mut addr = self.src;
        if p.broadcast || addr.ip() == IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)) {
            addr.set_ip(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)));
        }
        self.socket.send_to(p.encode(&mut self.out_buf.get()), addr)
    }
}