A DHCP parser and encoder for DHCPv4/DHCPv6

Overview

dhcproto

A DHCP parser and encoder for DHCPv4/DHCPv6. dhcproto aims to be a functionally complete DHCP implementation. Many common option types are implemented, PRs are welcome to flesh out missing types.

crates.io

https://crates.io/crates/dhcproto

Minimum Rust Version

This crate uses const generics, Rust 1.53 is required

Examples

(v4) Decoding/Encoding

use dhcproto::v4::{Message, Encoder, Decoder, Decodable, Encodable};
// decode
let bytes = dhcp_offer();
let msg = Message::decode(&mut Decoder::new(&bytes))?;
// now encode
let mut buf = Vec::new();
let mut e = Encoder::new(&mut buf);
msg.encode(&mut e)?;

(v4) Constructing messages

use dhcproto::{v4, Encodable, Encoder};
// hardware addr
let chaddr = vec![
    29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
];
// construct a new Message
let mut msg = v4::Message::default();
msg.set_flags(v4::Flags::default().set_broadcast()) // set broadcast to true
    .set_chaddr(&chaddr) // set chaddr
    .opts_mut()
    .insert(v4::DhcpOption::MessageType(v4::MessageType::Discover)); // set msg type

// set some more options
msg.opts_mut()
    .insert(v4::DhcpOption::ParameterRequestList(vec![
        v4::OptionCode::SubnetMask,
        v4::OptionCode::Router,
        v4::OptionCode::DomainNameServer,
        v4::OptionCode::DomainName,
    ]));
msg.opts_mut()
    .insert(v4::DhcpOption::ClientIdentifier(chaddr));

// now encode to bytes
let mut buf = Vec::new();
let mut e = Encoder::new(&mut buf);
msg.encode(&mut e)?;
// buf now has the contents of the encoded DHCP message

RFCs

DHCPv6:

DHCPv4:

Comments
  • Does DHCP guarantee source host names are UTF-8?

    Does DHCP guarantee source host names are UTF-8?

    This crate assumes the sname field of a v4::Message will be sent as UTF-8, but as far as I can tell in the RFC there is no mention at all of encoding. In addition, the pre-ipv6 RFCs for DHCP predate the UTF-8 RFC.

    Perhaps most importantly, there is no encoding requirement for domain names in DNS, so I'd be a little surprised if that non-requirement doesn't extend to DHCP. This might sound a bit pedantic, but I'm aware of an organization that stores binary data in domain names within DNS, and a quick Google turned up this person who found Windows encoding in DHCP: https://stackoverflow.com/questions/31215085/windows-dhcp-client-hostname-encoding

    Can the UTF-8 assumption/requirement in this crate be lifted?

    opened by saethlin 8
  • v4: add option 121

    v4: add option 121

    This PR adds support for DHCP option 121 (Classless Static Routes) for DHCPv4 as described in RFC3442.

    This option allows a DHCP server to send a whole routing table to a client. It is also sometimes used instead of DHCP option 3 (Router) in some environments.

    Related to this option, this PR adds support for merging some consecutive DHCP options that contains lists of items, option 121 being a prime example. The previous behavior was to replace the previously parsed option when encountering it multiple times in a packet; however it may be useful to specify options multiple times when they are pretty large (e.g. large routing tables).

    opened by Tuetuopay 6
  • make opt not impl Decodable

    make opt not impl Decodable

    Moving the lifetime into Decodable makes it much more restrictive when actually using <'a, T: Decodable<'a>. Opt is an internal type so it doesn't need to implement the trait.

    opened by leshow 3
  • Creating DhcpOptions ?

    Creating DhcpOptions ?

    Hello,

    It seems right now, the dhcproto does not provide any way to create DhcpOption such as IAAddr, or IANA, from scratch. Structures have private fields, and no getter / setter / new methods. I fixed that in a fork by setting all the structure fields to public ( https://github.com/rgrunbla/dhcproto/commit/3ac5f0419d97acfd64360aa9603e09fcb8cbc4ac ) but that might not be what you want for this crate. Could this be solved upstream ?

    Thanks, Rémy

    opened by rgrunbla 3
  • dhcpv6: expose fields & change some types

    dhcpv6: expose fields & change some types

    closes #16

    Exposed some of the pub fields here, other types I just removed the wrapper for so it looks more like the v4 opts. Before merging I want to take a second to make sure we've got the correct type for VendorClass and UserClass though, the rfc looks like we can't count on it being utf-8 encoded, so I think String is maybe not the correct type to have here. For example, the StatusCode option explicitly says the message field is utf-8, so I think that still needs to change...

    @rgrunbla any other thoughts on this?

    opened by leshow 2
  • Add benchmarks based on existing test cases

    Add benchmarks based on existing test cases

    I have a number of optimizations for you too, but I'd like these to be accepted first so we can see a before-and-after.

    The coverage isn't spectacular, but these benchmarks are enough to point out some pretty clear opportunities.

    (I'm /u/Saefroch on Reddit)

    opened by saethlin 2
  • Wish: usage examples

    Wish: usage examples

    Hello,

    This nice looking library has nearly no examples. In documentation comment I found:

    use dhcproto::v4::{Decodable, Decoder, Encodable, Encoder, Message};
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        // decode
        let bytes = dhcp_offer();
        let msg = Message::decode(&mut Decoder::new(&bytes))?;
        println!("{:#?}", msg);
        // now encode
        let mut buf = Vec::new();
        let mut e = Encoder::new(&mut buf);
        msg.encode(&mut e)?;
        Ok(())
    }
    fn dhcp_offer() -> Vec<u8> {
        vec![
            0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x15, 0x5c, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
            0x00, 0xc0, 0xa8, 0x00, 0x03, ..... 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        ]
    }
    

    Thing I looking for is how to create ( how to encode ) byte content for a DHCP packet such a DHCP Discover.

    Seeing output as

    Message {
        opcode: BootReply,
        htype: Eth,
        hlen: 6,
        hops: 0,
        xid: 5468,
        secs: 0,
        flags: Flags {
            broadcast: true,
        },
        ciaddr: 0.0.0.0,
        yiaddr: 192.168.0.3,
        siaddr: 0.0.0.0,
        giaddr: 0.0.0.0,
        chaddr: [
            204,
            0,
            10,
            196,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
        ],
        sname: None,
        file: None,
        magic: [
            99,
            130,
            83,
            99,
        ],
        opts: DhcpOptions(
            {
                Router: Router(
                    [
                        192.168.0.1,
                    ],
                ),
                MessageType: MessageType(
                    Offer,
                ),
                Rebinding: Rebinding(
                    52,
                ),
                DomainNameServer: DomainNameServer(
                    [
                        192.168.0.1,
                        192.168.1.1,
                    ],
                ),
                AddressLeaseTime: AddressLeaseTime(
                    60,
                ),
                SubnetMask: SubnetMask(
                    255.255.255.0,
                ),
                Renewal: Renewal(
                    30,
                ),
                ServerIdentifier: ServerIdentifier(
                    192.168.0.1,
                ),
            },
        ),
    }
    

    does make me think there is a struct involved.

    At https://docs.rs/dhcproto/0.2.1/dhcproto/encoder/struct.Encoder.html is even said

    pub struct Encoder<'a> { /* fields omitted */ }
    

    but the text

    Encoder type, holds a mut ref to a buffer that it will write data to and an offset of the next position to write

    with especial an offset of the next position to write makes me think there is some serialiazation, that I must be carefull in which sequence what is set.

    Hence my wish: Have usage examples

    opened by stappersg 2
  • Draft: move lifetime to Decodable trait

    Draft: move lifetime to Decodable trait

    Had initially kept the bound on decode fn, but it's strictly less expressive because you can't impl Decodable on types that contain references (like the internal Opt<'a>). Realized that HRTBs will help when using the type generically, so dropped an example in there.

    I waffled on this a bit, master has removed then added the lifetime bound in the trait recently. Although none of those changes have actually been published.

    opened by leshow 1
  • set_chaddr: Fix the hardware setting

    set_chaddr: Fix the hardware setting

    Current set_chaddr() function expects a [u8;16] slice due to requirement of copy_from_slice(). This cause the hlen been always set to 16.

    This patch fix it by allowing variable length of input u8 slice and set hlen accordingly.

    Tested in my person project with dnsmasq:

    • Before: DHCPREQUEST(eth1.ep) 192.0.2.32 12:a9:63:e8:f0:b8:00:00:00:00:00:00:00:00:00:00

    • After: DHCPREQUEST(eth1.ep) 192.0.2.32 12:a9:63:e8:f0:b8

    opened by cathay4t 1
  • module examples & v6 methods

    module examples & v6 methods

    closes #2

    We could obviously use more docs, but this gets us started with some examples.

    We were missing some public constructor methods for v6::Message, added those also/clarify which bytes get copied for xid.

    opened by leshow 1
  • Use `Decoder::read` in `read_ipv4`

    Use `Decoder::read` in `read_ipv4`

    Hey there! Amazing work with this crate :D

    Since read_ipv4 will refuse any length different than four, I don't see why we couldn't use the const generics-backed read function

    opened by vrmiguel 1
  • Implement RFC 4361

    Implement RFC 4361

    https://www.rfc-editor.org/rfc/rfc4361

    allows a v6-style DUID to be encoded in the client id option (opt 61). We should probably change the ClientId option so that it is easier to pull out the DUID specified here: https://www.rfc-editor.org/rfc/rfc4361#section-6.1

    maybe:

    enum ClientId {
        Duid(Duid),
        Id(Id)
    }
    struct Duid {
       iaid: [u8; 4],
       duid: Vec<u8>
    }
    struct Id {
       id: Vec<u8>
    }
    

    With some convenience methods on ClientId to pull out either id or duid if a method is called.

    enhancement 
    opened by leshow 0
  • remove Domain and just re-export Name

    remove Domain and just re-export Name

    I've been trying to actually use the Domain type for a few days and it has shortcomings. It's difficult to call some methods. I wonder if it isn't just better to re-export the trust_dns_proto Name type and the error type and let users call the methods directly.

    trust-dns conditionally impls serde also so that is covered.

    opened by leshow 0
  • Add VendorSpecificInfo from RFC 2132

    Add VendorSpecificInfo from RFC 2132

    We need a more robust way to handle vendor specific information from https://www.rfc-editor.org/rfc/rfc2132#section-8.4

    Right now dhcproto just stores the section as a big block of bytes, there is no way to pull out sub-options of vendor info, that's left as an exercise to the user.

    I'm happy to do this, but I thought someone might want to take a crack at it or have thoughts. Here are the kea docs on it, the "notes" section at the bottom of this is helpful https://kea.readthedocs.io/en/kea-1.6.2/arm/dhcp4-srv.html#dhcpv4-private-options

    I was thinking we could represent the vendor specific info as HashMap<VendorCode, UnknownOption>. where VendorCode I think would only have the variants Pad End & Unknown. Having the key literally just be u8 may also make sense.

    good first issue 
    opened by leshow 0
  • API issue with UnknownOption

    API issue with UnknownOption

    We have a bit of an API issue with the UnknownOption

    The fact that this doesn't work, is perhaps a little unexpected:

            let mut opts = DhcpOptions::new();
            opts.insert(DhcpOption::Unknown(UnknownOption {
                code: 1,
                data: vec![192, 168, 1, 1],
            }));
            let opt = opts.get(OptionCode::SubnetMask);
            dbg!(opt); // None
    

    We can modify From<&DhcpOption> for OptionCode' so that the key in the map is a OptionCode::SubnetMask by changing the unknown branch from:

    Unknown(n) => OptionCode::Unknown(n.code),
    

    to

    Unknown(n) => n.code.into(),
    

    This way you don't need to retrieve the option value with OptionCode::Unknown(1) which feels pretty weird. But the value you will get back after making the above change for the original code will be:

    [src/v4/options.rs:1950] opt = Some(
        Unknown(
            UnknownOption {
                code: 1,
                data: [
                    192,
                    168,
                    1,
                    1,
                ],
            },
        ),
    )
    

    Again, this feels a little strange. The Unknown option could have been inserted programmatically and unbeknownst to the programmer, if let Some(DhcpOption::SubnetMask(_)) = opts.get(OptionCode::SubnetMask) will not work.

    We could decode UnknownOption on insert but that feels pretty icky and would likely require re-allocated to prefix the code u8 at the beginning of the data section.

    If anyone has some better ideas, feel free to share

    edit:

    Another option, we could use the repr(u8) for OptionCode and store the keys as the u8 value. That would solve half of this problem. We may want to do this as part of more general option generating macros described in #43

    opened by leshow 0
  • Implementing new options could be simplified

    Implementing new options could be simplified

    Currently, new DHCP Options need to be added in 7 different places:

    1. Added to the OptionCode enum
    2. From<u8> for OptionCode
    3. From<OptionCode> for u8
    4. Added to the DhcpOption enum
    5. decode_inner
    6. and encode
    7. From<&DhcpOption> for OptionCode

    Aside from the extra typing, it increases the risk of human error and adds visual clutter. Would you consider a PR that replaces these 7 steps (or a subsection of them) with a proc-macro?

    I have a small prototype that looks like

    /// implements step 1 to 4 given sets of
    /// {numeric_code, TypeName, "extra doc comment", (DataType)[optional] }
    macros::declare_codes!(
        {0, Pad, "Padding"},
        {1,  SubnetMask, "Subnet Mask", (Ipv4Addr)},
        {2,  TimeOffset, "Time Offset", (i32)},
        {3,  Router, "Router", (Vec<Ipv4Addr>)},
        ///...
        {255, End, "end-of-list marker"}
    );
    

    That I'd be willing to PR after some small tweaks, if desired :)

    opened by HayleyDeckers 3
  • Add support for TFTP related DHCP option

    Add support for TFTP related DHCP option

    Adds support for 3 TFTP related options (TFTServerName, TFTPServerAdress, and BootfileName) which can be used to boot devices from the network.

    The capitalization of BootfileName follows the RFC and IANA spelling, it is different from the existing BootFileSize but that is consistent with the spec 😅

    opened by HayleyDeckers 5
Releases(0.8.0)
  • 0.8.0(Jul 6, 2022)

    Changed

    • dhcpv4 option variants added (breaking)
    • dhcpv4 message type variants added (breaking)
    • ClientNetworkInterface removed inner tuple
    • Change has_msg_type return type to just bool

    Fixed

    • v6 set_xid_num was taking bytes from the wrong end
    • dhcpv6 DomainSearchList (opt 24)

    Added

    • dhcpv4 opt 119 DomainSearch
    • dhcpv4 opt 114 CaptivePortal
    • dhcpv4 message variants 9-18 added, breaking change for MessageType
    • dhcpv4 added DhcpOption for 91/92/93/94/97
    • UnknownOption encode/decode
    • dhcpv4 options 151-157 from bulkleasequery RFC
    • add Display impl for v4::Message
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Jan 20, 2022)

    [0.6.0]

    Added

    • methods for dhcpv6::UnknownOption & RelayMsg

    Changed

    • exposed some dhcpv6 opt fields as pub
    • InterfaceId type changed from String to Vec<u8>
    • VendorClass/UserClass changed to Vec<Vec<u8>>

    Removed

    • ElapsedTime and Preference
    Source code(tar.gz)
    Source code(zip)
  • 0.5.0(Dec 7, 2021)

    [0.5.0]

    Added

    • added clear/is_empty/retain to v4 opts & relay agent sub-opts

    Fixed

    • breaking: options enum for v4::DhcpOption was decoding into the wrong variants for a few types
    Source code(tar.gz)
    Source code(zip)
  • 0.4.1(Nov 12, 2021)

    • expose methods so one can actually create RelayInfo/RelayAgentInformation
    • methods to get the data out of various Unknown variants for opts/relay
    • added option 118 subnet selection
    • return impl Iterator for relay/opt iterator methods
    • more docs for opts/relay info

    Several of these changes are breaking, including enum variant additions & changing return types of methods. The API may continue to break up until 1.0

    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Oct 15, 2021)

  • 0.2.1(Sep 14, 2021)

Owner
BlueCat Engineering
BlueCat Engineering
Simple DHCP client for the Wiznet W5500 internet offload chip.

w5500-dhcp Simple DHCP client for the Wiznet W5500 SPI internet offload chip. Warning Please proceed with caution, and review the code before use in a

Alex 0 Mar 20, 2022
BitTorrent peer ID registry/parser/(soon) encoder for Rust

BitTorrent peer ID registry/parser/(soon) encoder By convention, BitTorrent clients identify themselves and their versions in peer IDs they send to tr

TORRENTDYNE 3 Oct 16, 2023
Rust port of ffmpeg's native AAC encoder

raash ?? An attempt at RIIR-ing the native AAC encoder from ffmpeg. First, I used c2rust to translate all relevant C code into Rust, and I'm in the pr

Yotam Ofek 6 Dec 1, 2023
MASQ Network 121 Dec 20, 2022
An end-to-end encrypted, anonymous IP-hiding, decentralized, audio/video/file sharing/offline messaging multi-device platform built for both communications and application security and performance.

An end-to-end encrypted, anonymous IP-hiding, decentralized, audio/video/file sharing/offline messaging multi-device platform built for both communications and application security and performance.

null 2 Apr 27, 2022
A fast, stable, efficient, and lightweight intranet penetration, port forwarding tool supports multiple connections, cascading proxy, and transmission encryption

A fast, stable, efficient, and lightweight intranet penetration, port forwarding tool supports multiple connections, cascading proxy, and transmission encryption

editso 1.3k Dec 30, 2022
A proxy implement with http / socks5 in-bound and vmess out-bound, written in Rust and tokio.rs

tokio-vmess an Asynchronous proxy implement with http / socks5 in-bound and vmess out-bound, written in Rust and tokio Run example first, Fill out the

irumeria 7 Oct 3, 2022
A simple web server(and library) to display server stats over HTTP and Websockets/SSE or stream it to other systems.

x-server-stats A simple web server(and library) to display server stats over HTTP and Websockets/SSE or stream it to other systems. x-server(in x-serv

Pratyaksh 11 Oct 17, 2022
RDE1 (Rusty Data Exfiltrator) is client and server tool allowing auditor to extract files from DNS and HTTPS protocols written in Rust. 🦀

Information: RDE1 is an old personal project (end 2022) that I didn't continue development on. It's part of a list of projects that helped me to learn

Quentin Texier (g0h4n) 32 Oct 6, 2023
The gRPC library for Rust built on C Core library and futures

gRPC-rs gRPC-rs is a Rust wrapper of gRPC Core. gRPC is a high performance, open source universal RPC framework that puts mobile and HTTP/2 first. Sta

TiKV Project 1.6k Jan 7, 2023
🥧 Savoury implementation of the QUIC transport protocol and HTTP/3

quiche is an implementation of the QUIC transport protocol and HTTP/3 as specified by the IETF. It provides a low level API for processing QUIC packet

Cloudflare 7.1k Jan 8, 2023
Imagine your SSH server only listens on an IPv6 address, and where the last 6 digits are changing every 30 seconds as a TOTP code...

tosh Imagine your SSH server only listens on an IPv6 address, and where the last 6 digits are changing every 30 seconds as a TOTP code... Inspired fro

Mark Vainomaa 409 Oct 23, 2022
Library + CLI-Tool to measure the TTFB (time to first byte) of HTTP requests. Additionally, this crate measures the times of DNS lookup, TCP connect and TLS handshake.

TTFB: CLI + Lib to Measure the TTFB of HTTP/1.1 Requests Similar to the network tab in Google Chrome or Mozilla Firefox, this crate helps you find the

Philipp Schuster 24 Dec 1, 2022
A versatile and efficient proxy framework with nice features suitable for various use cases.

A versatile and efficient proxy framework with nice features suitable for various use cases.

null 1.7k Jan 9, 2023
A multiplayer web based roguelike built on Rust and WebRTC

Gorgon A multiplayer web-based roguelike build on Rust and WebRTC. License This project is licensed under either of Apache License, Version 2.0, (LICE

RICHΛRD ΛNΛYΛ 2 Sep 19, 2022
Simple and fast layer 4 proxy in Rust

Fourth 这一波在第四层。 English Fourth是一个Rust实现的Layer 4代理,用于监听指定端口TCP流量,并根据规则转发到指定目标。 功能 监听指定端口代理到本地或远端指定端口 监听指定端口,通过TLS ClientHello消息中的SNI进行分流 安装方法 为了确保获得您架构

Rui Li 17 Nov 8, 2022
Drop-in proxy for Discord gateway connections and sessions allowing for zero downtime deploys

gateway-proxy This is a very hacky project, so it might stop working if Discord changes their API core. This is unlikely, but keep that in mind while

Jens Reidel 39 Nov 26, 2022
A remote shell, TCP tunnel and HTTP proxy for Replit.

Autobahn A remote shell, TCP tunnel and HTTP proxy for Replit. Hybrid SSH/HTTP server for Replit. Based on leon332157/replish. Autobahn runs a WebSock

Patrick Winters 12 Sep 24, 2022
The Graph is a protocol for building decentralized applications (dApps) quickly on Ethereum and IPFS using GraphQL.

Graph Node The Graph is a protocol for building decentralized applications (dApps) quickly on Ethereum and IPFS using GraphQL. Graph Node is an open s

Mindy.wang 2 Jun 18, 2022