rustydht_lib/packets/
builder.rs

1use crate::common::{Id, Node};
2use crate::errors::RustyDHTError;
3use crate::packets;
4use rand::prelude::*;
5use std::convert::TryInto;
6use std::net::SocketAddr;
7use std::time::Duration;
8
9/// Allows building [packets::Message](crate::packets::Message) structs in a more human-friendly way.
10///
11/// # Example
12/// ```
13/// use rustydht_lib::common::Id;
14/// use rustydht_lib::packets::MessageBuilder;
15///
16/// let client_id = Id::from_hex("0000000000000000000000000000000000000001").unwrap();
17/// let server_id = Id::from_hex("0000000000000000000000000000000000000002").unwrap();
18///
19/// // To build a ping request
20/// let ping_req = MessageBuilder::new_ping_request()
21///     .sender_id(client_id)
22///     .build()
23///     .unwrap();
24///
25/// // To build a ping response
26/// let ping_res = MessageBuilder::new_ping_response()
27///     .sender_id(server_id)
28///     .transaction_id(ping_req.transaction_id.clone())
29///     .build()
30///     .unwrap();
31/// ```
32#[derive(Clone)]
33pub struct MessageBuilder {
34    message_type: BuilderMessageType,
35
36    transaction_id: Option<Vec<u8>>,
37    version: Option<Vec<u8>>,
38    requester_ip: Option<SocketAddr>,
39    read_only: Option<bool>,
40
41    sender_id: Option<Id>,
42    target: Option<Id>,
43    port: Option<u16>,
44    implied_port: Option<bool>,
45    token: Option<Vec<u8>>,
46    nodes: Option<Vec<Node>>,
47    peers: Option<Vec<SocketAddr>>,
48    interval: Option<Duration>,
49    samples: Option<Vec<Id>>,
50    num_infohashes: Option<usize>,
51    code: Option<i32>,
52    description: Option<String>,
53}
54
55/// All the different types of Message that a MesssageBuilder can build
56#[derive(Clone)]
57enum BuilderMessageType {
58    PingRequest,
59    PingResponse,
60    FindNodeRequest,
61    FindNodeResponse,
62    GetPeersRequest,
63    GetPeersResponse,
64    AnnouncePeerRequest,
65    AnnouncePeerResponse,
66    SampleInfoHashesRequest,
67    SampleInfoHashesResponse,
68    Error,
69}
70
71impl MessageBuilder {
72    /// Create a new MessageBuilder for a ping request
73    pub fn new_ping_request() -> MessageBuilder {
74        MessageBuilder::new(BuilderMessageType::PingRequest)
75    }
76
77    /// Create a new MessageBuilder for a ping response
78    pub fn new_ping_response() -> MessageBuilder {
79        MessageBuilder::new(BuilderMessageType::PingResponse)
80    }
81
82    /// Create a new MessageBuilder for a find_node request
83    pub fn new_find_node_request() -> MessageBuilder {
84        MessageBuilder::new(BuilderMessageType::FindNodeRequest)
85    }
86
87    /// Create a new MessageBuilder for a find_node response
88    pub fn new_find_node_response() -> MessageBuilder {
89        MessageBuilder::new(BuilderMessageType::FindNodeResponse)
90    }
91
92    /// Create a new MessageBuilder for a get_peers request
93    pub fn new_get_peers_request() -> MessageBuilder {
94        MessageBuilder::new(BuilderMessageType::GetPeersRequest)
95    }
96
97    /// Create a new MessageBuilder for a get_peers response
98    pub fn new_get_peers_response() -> MessageBuilder {
99        MessageBuilder::new(BuilderMessageType::GetPeersResponse)
100    }
101
102    /// Create a new MessageBuilder for an announce_peer request
103    pub fn new_announce_peer_request() -> MessageBuilder {
104        MessageBuilder::new(BuilderMessageType::AnnouncePeerRequest)
105    }
106
107    /// Create a new MessageBuilder for an announce_peer response
108    pub fn new_announce_peer_response() -> MessageBuilder {
109        MessageBuilder::new(BuilderMessageType::AnnouncePeerResponse)
110    }
111
112    /// Create a new MessageBuilder for a sample_infohashes request
113    pub fn new_sample_infohashes_request() -> MessageBuilder {
114        MessageBuilder::new(BuilderMessageType::SampleInfoHashesRequest)
115    }
116
117    /// Create a new MessageBuilder for a sample_infohashes response
118    pub fn new_sample_infohashes_response() -> MessageBuilder {
119        MessageBuilder::new(BuilderMessageType::SampleInfoHashesResponse)
120    }
121
122    /// Create a new MessageBuilder for an error
123    pub fn new_error() -> MessageBuilder {
124        MessageBuilder::new(BuilderMessageType::Error)
125    }
126
127    fn new(message_type: BuilderMessageType) -> MessageBuilder {
128        MessageBuilder {
129            message_type,
130            transaction_id: None,
131            version: None,
132            requester_ip: None,
133            read_only: None,
134            sender_id: None,
135            target: None,
136            port: None,
137            implied_port: None,
138            token: None,
139            nodes: None,
140            peers: None,
141            interval: None,
142            samples: None,
143            num_infohashes: None,
144            code: None,
145            description: None,
146        }
147    }
148
149    /// Set the transaction id of the packet. If one is not specified,
150    /// generated requests will get a random transaction id and responses
151    /// will receive an error.
152    pub fn transaction_id(mut self, transaction_id: Vec<u8>) -> Self {
153        self.transaction_id = Some(transaction_id);
154        self
155    }
156
157    /// Set the string of bytes that should be included in the packet to
158    /// identify the version of the software participating on the DHT.
159    ///
160    /// If one is not specified, the builder will omit the version field
161    /// from the generated packet (it is optional).
162    pub fn version(mut self, version: Vec<u8>) -> Self {
163        self.version = Some(version);
164        self
165    }
166
167    /// For response packets, set the IP address and port that we saw the
168    /// request come from. This is used to help other nodes on the DHT
169    /// know what their external IPv4 address is.
170    ///
171    /// Has no effect for request packets. If not specified on
172    /// response packets, the builder will omit it from the generated
173    /// response packet.
174    pub fn requester_ip(mut self, remote: SocketAddr) -> Self {
175        self.requester_ip = Some(remote);
176        self
177    }
178
179    /// For request packets, specifies whether the read only flag should be set.
180    ///
181    /// Has no effect on response packets.
182    pub fn read_only(mut self, read_only: bool) -> Self {
183        self.read_only = Some(read_only);
184        self
185    }
186
187    /// Set the Id of the DHT node sending the packet (whether it's a request or response).
188    pub fn sender_id(mut self, sender_id: Id) -> Self {
189        self.sender_id = Some(sender_id);
190        self
191    }
192
193    /// Set the Id of the target node or info_hash (for get_peers, find_node,
194    /// sample_infohashes, announce_peer)
195    pub fn target(mut self, target: Id) -> Self {
196        self.target = Some(target);
197        self
198    }
199
200    /// Set the port field for announce_peer requests.
201    ///
202    /// If not specified, 0 will be used and implied_port will automatically
203    /// be implicitly set to true (unless explicitly set to false, in which
204    /// case an error will occur).
205    pub fn port(mut self, port: u16) -> Self {
206        self.port = Some(port);
207        self
208    }
209
210    /// Set the true/false value of implied port for announce_peer requests.
211    pub fn implied_port(mut self, implied_port: bool) -> Self {
212        self.implied_port = Some(implied_port);
213        self
214    }
215
216    /// Set the token byte string. Used for announce_peer requests and
217    /// get_peers responses.
218    pub fn token(mut self, token: Vec<u8>) -> Self {
219        self.token = Some(token);
220        self
221    }
222
223    /// Set the list of Nodes used in get_peers, find_node, and
224    /// sample_infohashes responses.
225    ///
226    /// nodes will be ignored for get_peers packets if peers are specified.
227    pub fn nodes(mut self, nodes: Vec<Node>) -> Self {
228        self.nodes = Some(nodes);
229        self
230    }
231
232    /// Set the list of peers used in get_peers responses.
233    ///
234    /// nodes will be ignored for get_peers packets if peers are specified.
235    pub fn peers(mut self, peers: Vec<SocketAddr>) -> Self {
236        self.peers = Some(peers);
237        self
238    }
239
240    /// Set the interval used in sample_infohashes responses.
241    pub fn interval(mut self, interval: Duration) -> Self {
242        self.interval = Some(interval);
243        self
244    }
245
246    /// Set the list of info_hashes used in sample_infohashes responses.
247    pub fn samples(mut self, samples: Vec<Id>) -> Self {
248        self.samples = Some(samples);
249        self
250    }
251
252    /// Set the number of info_hashes as reported in sample_infohashes
253    /// responses.
254    pub fn num_infohashes(mut self, num: usize) -> Self {
255        self.num_infohashes = Some(num);
256        self
257    }
258
259    /// Set the error code .for error messages.
260    pub fn code(mut self, code: i32) -> Self {
261        self.code = Some(code);
262        self
263    }
264
265    /// Set the description for error messages.
266    pub fn description(mut self, description: String) -> Self {
267        self.description = Some(description);
268        self
269    }
270
271    /// Build the Message, consuming this MessageBuilder in the process
272    pub fn build(self) -> Result<packets::Message, RustyDHTError> {
273        match self.message_type {
274            BuilderMessageType::PingRequest => self.build_ping_request(),
275            BuilderMessageType::PingResponse => self.build_ping_response(),
276            BuilderMessageType::FindNodeRequest => self.build_find_node_request(),
277            BuilderMessageType::FindNodeResponse => self.build_find_node_response(),
278            BuilderMessageType::GetPeersRequest => self.build_get_peers_request(),
279            BuilderMessageType::GetPeersResponse => self.build_get_peers_response(),
280            BuilderMessageType::AnnouncePeerRequest => self.build_announce_peer_request(),
281            BuilderMessageType::AnnouncePeerResponse => self.build_announce_peer_response(),
282            BuilderMessageType::SampleInfoHashesRequest => self.build_sample_infohashes_request(),
283            BuilderMessageType::SampleInfoHashesResponse => self.build_sample_infohashes_response(),
284            BuilderMessageType::Error => self.build_error(),
285        }
286    }
287}
288
289macro_rules! required_or_error {
290    ($self:ident, $builder_field:ident) => {
291        match $self.$builder_field {
292            None => {
293                return Err(RustyDHTError::BuilderMissingFieldError(stringify!(
294                    $builder_field
295                )));
296            }
297            Some($builder_field) => $builder_field,
298        }
299    };
300}
301
302macro_rules! build_request_common {
303    ($self:ident, $x:expr) => {
304        packets::Message {
305            transaction_id: match $self.transaction_id {
306                Some(transaction_id) => transaction_id,
307                None => {
308                    let mut rng = thread_rng();
309                    vec![rng.gen(), rng.gen()]
310                }
311            },
312            version: $self.version,
313            requester_ip: None,
314            message_type: packets::MessageType::Request($x),
315            read_only: $self.read_only,
316        }
317    };
318}
319
320macro_rules! build_response_common {
321    ($self:ident, $x:expr) => {
322        packets::Message {
323            transaction_id: required_or_error!($self, transaction_id),
324            version: $self.version,
325            requester_ip: $self.requester_ip,
326            message_type: packets::MessageType::Response($x),
327            read_only: None,
328        }
329    };
330}
331
332impl MessageBuilder {
333    fn build_ping_request(self) -> Result<packets::Message, RustyDHTError> {
334        Ok(build_request_common!(
335            self,
336            packets::RequestSpecific::PingRequest(packets::PingRequestArguments {
337                requester_id: required_or_error!(self, sender_id),
338            },)
339        ))
340    }
341
342    fn build_ping_response(self) -> Result<packets::Message, RustyDHTError> {
343        Ok(build_response_common!(
344            self,
345            packets::ResponseSpecific::PingResponse(packets::PingResponseArguments {
346                responder_id: required_or_error!(self, sender_id),
347            },)
348        ))
349    }
350
351    fn build_find_node_request(self) -> Result<packets::Message, RustyDHTError> {
352        Ok(build_request_common!(
353            self,
354            packets::RequestSpecific::FindNodeRequest(packets::FindNodeRequestArguments {
355                requester_id: required_or_error!(self, sender_id),
356                target: required_or_error!(self, target),
357            },)
358        ))
359    }
360
361    fn build_find_node_response(self) -> Result<packets::Message, RustyDHTError> {
362        Ok(build_response_common!(
363            self,
364            packets::ResponseSpecific::FindNodeResponse(packets::FindNodeResponseArguments {
365                responder_id: required_or_error!(self, sender_id),
366                nodes: required_or_error!(self, nodes),
367            },)
368        ))
369    }
370
371    fn build_get_peers_request(self) -> Result<packets::Message, RustyDHTError> {
372        Ok(build_request_common!(
373            self,
374            packets::RequestSpecific::GetPeersRequest(packets::GetPeersRequestArguments {
375                requester_id: required_or_error!(self, sender_id),
376                info_hash: required_or_error!(self, target),
377            })
378        ))
379    }
380
381    fn build_get_peers_response(self) -> Result<packets::Message, RustyDHTError> {
382        Ok(build_response_common!(
383            self,
384            packets::ResponseSpecific::GetPeersResponse(packets::GetPeersResponseArguments {
385                responder_id: required_or_error!(self, sender_id),
386                token: required_or_error!(self, token),
387
388                values: match self.peers {
389                    Some(peers) => packets::GetPeersResponseValues::Peers(peers),
390                    None => match self.nodes {
391                        Some(nodes) => packets::GetPeersResponseValues::Nodes(nodes),
392                        None => {
393                            return Err(RustyDHTError::BuilderMissingFieldError("peers or nodes"));
394                        }
395                    },
396                }
397            })
398        ))
399    }
400
401    fn build_announce_peer_request(self) -> Result<packets::Message, RustyDHTError> {
402        Ok(build_request_common!(
403            self,
404            packets::RequestSpecific::AnnouncePeerRequest(packets::AnnouncePeerRequestArguments {
405                requester_id: required_or_error!(self, sender_id),
406                info_hash: required_or_error!(self, target),
407                token: required_or_error!(self, token),
408                port: self.port.unwrap_or(0),
409
410                implied_port: match self.implied_port {
411                    Some(implied_port) => {
412                        if !implied_port && self.port.is_none() {
413                            return Err(RustyDHTError::BuilderInvalidComboError(
414                                "implied_port must be true or port must be specified",
415                            ));
416                        }
417                        Some(implied_port)
418                    }
419                    None => {
420                        match self.port {
421                            Some(_) => None,
422                            None => Some(true),
423                        }
424                    }
425                }
426            })
427        ))
428    }
429
430    fn build_announce_peer_response(self) -> Result<packets::Message, RustyDHTError> {
431        self.build_ping_response()
432    }
433
434    fn build_sample_infohashes_request(self) -> Result<packets::Message, RustyDHTError> {
435        Ok(build_request_common!(
436            self,
437            packets::RequestSpecific::SampleInfoHashesRequest(
438                packets::SampleInfoHashesRequestArguments {
439                    requester_id: required_or_error!(self, sender_id),
440                    target: required_or_error!(self, target),
441                }
442            )
443        ))
444    }
445
446    fn build_sample_infohashes_response(self) -> Result<packets::Message, RustyDHTError> {
447        Ok(build_response_common!(
448            self,
449            packets::ResponseSpecific::SampleInfoHashesResponse(
450                packets::SampleInfoHashesResponseArguments {
451                    responder_id: required_or_error!(self, sender_id),
452                    interval: required_or_error!(self, interval),
453                    nodes: required_or_error!(self, nodes),
454                    num: match self.num_infohashes {
455                        Some(num) => num.try_into().unwrap(),
456                        None => {
457                            return Err(RustyDHTError::BuilderMissingFieldError("num_infohashes"));
458                        }
459                    },
460                    samples: required_or_error!(self, samples),
461                }
462            )
463        ))
464    }
465
466    fn build_error(self) -> Result<packets::Message, RustyDHTError> {
467        Ok(packets::Message {
468            transaction_id: required_or_error!(self, transaction_id),
469            version: None,
470            requester_ip: None,
471            message_type: packets::MessageType::Error(packets::ErrorSpecific {
472                code: required_or_error!(self, code),
473                description: required_or_error!(self, description),
474            }),
475            read_only: None,
476        })
477    }
478}
479
480#[cfg(test)]
481mod test {
482    use super::*;
483
484    #[test]
485    fn test_sender_id_is_required_in_requests() {
486        let transaction_id = vec![0, 1, 2, 3];
487        let err = MessageBuilder::new_ping_request()
488            .transaction_id(transaction_id)
489            .build();
490        assert!(err.is_err());
491        assert!(matches!(
492            err.unwrap_err(),
493            RustyDHTError::BuilderMissingFieldError("sender_id")
494        ));
495    }
496
497    #[test]
498    fn test_sender_id_is_required_in_responses() {
499        let transaction_id = vec![0, 1, 2, 3];
500        let err = MessageBuilder::new_ping_response()
501            .transaction_id(transaction_id)
502            .build();
503        assert!(err.is_err());
504        assert!(matches!(
505            err.unwrap_err(),
506            RustyDHTError::BuilderMissingFieldError("sender_id")
507        ));
508    }
509
510    #[test]
511    fn test_transaction_id_optional_in_requests() {
512        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
513        let b = MessageBuilder::new_ping_request().sender_id(our_id).build();
514        assert!(b.is_ok());
515        assert!(!b.unwrap().transaction_id.is_empty());
516    }
517
518    #[test]
519    fn test_transaction_id_required_in_responses() {
520        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
521        let err = MessageBuilder::new_ping_response()
522            .sender_id(our_id)
523            .build();
524        assert!(err.is_err());
525        assert!(matches!(
526            err.unwrap_err(),
527            RustyDHTError::BuilderMissingFieldError("transaction_id")
528        ));
529    }
530
531    #[test]
532    fn test_version_field_populated() {
533        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
534        let b = MessageBuilder::new_ping_request()
535            .sender_id(our_id)
536            .version(vec![6, 6, 6])
537            .build();
538        assert!(b.is_ok());
539        assert_eq!(b.unwrap().version.unwrap_or_default(), vec!(6, 6, 6));
540    }
541
542    #[test]
543    fn test_requester_ip_pointless_on_requests() {
544        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
545        let b = MessageBuilder::new_ping_request()
546            .sender_id(our_id)
547            .requester_ip("1.0.1.0:53".parse().unwrap())
548            .build();
549        assert!(b.is_ok());
550        assert_eq!(b.unwrap().requester_ip, None);
551    }
552
553    #[test]
554    fn test_requester_ip_useful_on_responses() {
555        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
556        let b = MessageBuilder::new_ping_response()
557            .sender_id(our_id)
558            .requester_ip("1.0.1.0:53".parse().unwrap())
559            .transaction_id(vec![1])
560            .build();
561        assert!(b.is_ok());
562        assert_eq!(b.unwrap().requester_ip, Some("1.0.1.0:53".parse().unwrap()));
563    }
564
565    #[test]
566    fn test_read_only_useful_on_requests() {
567        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
568        let b = MessageBuilder::new_ping_request()
569            .sender_id(our_id)
570            .read_only(true)
571            .build();
572        assert!(b.is_ok());
573        assert_eq!(b.unwrap().read_only, Some(true));
574    }
575
576    #[test]
577    fn test_read_only_pointless_on_responses() {
578        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
579        let b = MessageBuilder::new_ping_response()
580            .sender_id(our_id)
581            .read_only(true)
582            .transaction_id(vec![1])
583            .build();
584        assert!(b.is_ok());
585        assert_eq!(b.unwrap().read_only, None);
586    }
587
588    #[test]
589    fn test_build_ping_request() {
590        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
591        let transaction_id = vec![0, 1, 2, 3];
592        assert_eq!(
593            MessageBuilder::new_ping_request()
594                .sender_id(our_id)
595                .transaction_id(transaction_id.clone())
596                .build()
597                .expect("Failed to build message"),
598            packets::Message {
599                transaction_id,
600                version: None,
601                requester_ip: None,
602                message_type: packets::MessageType::Request(packets::RequestSpecific::PingRequest(
603                    packets::PingRequestArguments {
604                        requester_id: our_id
605                    }
606                )),
607                read_only: None,
608            }
609        );
610    }
611
612    #[test]
613    fn test_build_ping_response() {
614        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
615        let transaction_id = vec![0, 1, 2, 3];
616        assert_eq!(
617            MessageBuilder::new_ping_response()
618                .sender_id(our_id)
619                .transaction_id(transaction_id.clone())
620                .build()
621                .expect("Failed to build message"),
622            packets::Message {
623                transaction_id,
624                version: None,
625                requester_ip: None,
626                message_type: packets::MessageType::Response(
627                    packets::ResponseSpecific::PingResponse(packets::PingResponseArguments {
628                        responder_id: our_id
629                    })
630                ),
631                read_only: None,
632            }
633        );
634    }
635
636    #[test]
637    fn test_find_node_request() {
638        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
639        let target = Id::from_hex("1111111111111111111100000000000000000000").unwrap();
640        let transaction_id = vec![0, 1, 2, 3];
641        assert_eq!(
642            MessageBuilder::new_find_node_request()
643                .sender_id(our_id)
644                .transaction_id(transaction_id.clone())
645                .target(target)
646                .build()
647                .expect("Failed to build message"),
648            packets::Message {
649                transaction_id,
650                version: None,
651                requester_ip: None,
652                message_type: packets::MessageType::Request(
653                    packets::RequestSpecific::FindNodeRequest(packets::FindNodeRequestArguments {
654                        requester_id: our_id,
655                        target,
656                    })
657                ),
658                read_only: None,
659            }
660        );
661    }
662
663    #[test]
664    fn test_find_node_response() {
665        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
666        let transaction_id = vec![0, 1, 2, 3];
667        let nodes = vec![Node::new(our_id, "1.2.3.4:53".parse().unwrap())];
668        assert_eq!(
669            MessageBuilder::new_find_node_response()
670                .sender_id(our_id)
671                .transaction_id(transaction_id.clone())
672                .nodes(nodes.clone())
673                .build()
674                .expect("Failed to build message"),
675            packets::Message {
676                transaction_id,
677                version: None,
678                requester_ip: None,
679                message_type: packets::MessageType::Response(
680                    packets::ResponseSpecific::FindNodeResponse(
681                        packets::FindNodeResponseArguments {
682                            responder_id: our_id,
683                            nodes,
684                        }
685                    )
686                ),
687                read_only: None,
688            }
689        );
690    }
691
692    #[test]
693    fn test_get_peers_request() {
694        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
695        let target = Id::from_hex("1111111111111111111100000000000000000000").unwrap();
696        let transaction_id = vec![0, 1, 2, 3];
697        assert_eq!(
698            MessageBuilder::new_get_peers_request()
699                .sender_id(our_id)
700                .transaction_id(transaction_id.clone())
701                .target(target)
702                .build()
703                .expect("Failed to build message"),
704            packets::Message {
705                transaction_id,
706                version: None,
707                requester_ip: None,
708                message_type: packets::MessageType::Request(
709                    packets::RequestSpecific::GetPeersRequest(packets::GetPeersRequestArguments {
710                        requester_id: our_id,
711                        info_hash: target
712                    })
713                ),
714                read_only: None,
715            }
716        );
717    }
718
719    #[test]
720    fn test_get_peers_response_nodes() {
721        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
722        let transaction_id = vec![0, 1, 2, 3];
723        let nodes = vec![Node::new(our_id, "1.2.3.4:53".parse().unwrap())];
724        let token = vec![45, 56];
725        assert_eq!(
726            MessageBuilder::new_get_peers_response()
727                .sender_id(our_id)
728                .transaction_id(transaction_id.clone())
729                .nodes(nodes.clone())
730                .token(token.clone())
731                .build()
732                .expect("Failed to build message"),
733            packets::Message {
734                transaction_id,
735                version: None,
736                requester_ip: None,
737                message_type: packets::MessageType::Response(
738                    packets::ResponseSpecific::GetPeersResponse(
739                        packets::GetPeersResponseArguments {
740                            responder_id: our_id,
741                            values: packets::GetPeersResponseValues::Nodes(nodes),
742                            token
743                        }
744                    )
745                ),
746                read_only: None,
747            }
748        );
749    }
750
751    #[test]
752    fn test_get_peers_response_peers() {
753        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
754        let transaction_id = vec![0, 1, 2, 3];
755        let peers = vec!["1.2.3.4:53".parse().unwrap()];
756        let token = vec![45, 56];
757        assert_eq!(
758            MessageBuilder::new_get_peers_response()
759                .sender_id(our_id)
760                .transaction_id(transaction_id.clone())
761                .peers(peers.clone())
762                .token(token.clone())
763                .build()
764                .expect("Failed to build message"),
765            packets::Message {
766                transaction_id,
767                version: None,
768                requester_ip: None,
769                message_type: packets::MessageType::Response(
770                    packets::ResponseSpecific::GetPeersResponse(
771                        packets::GetPeersResponseArguments {
772                            responder_id: our_id,
773                            values: packets::GetPeersResponseValues::Peers(peers),
774                            token
775                        }
776                    )
777                ),
778                read_only: None,
779            }
780        );
781    }
782
783    #[test]
784    fn test_get_peers_response_peers_precedent() {
785        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
786        let transaction_id = vec![0, 1, 2, 3];
787        let nodes = vec![Node::new(our_id, "1.2.3.4:53".parse().unwrap())];
788        let peers = vec!["1.2.3.4:53".parse().unwrap()];
789        let token = vec![45, 56];
790        assert_eq!(
791            MessageBuilder::new_get_peers_response()
792                .sender_id(our_id)
793                .transaction_id(transaction_id.clone())
794                .peers(peers.clone())
795                .nodes(nodes)
796                .token(token.clone())
797                .build()
798                .expect("Failed to build message"),
799            packets::Message {
800                transaction_id,
801                version: None,
802                requester_ip: None,
803                message_type: packets::MessageType::Response(
804                    packets::ResponseSpecific::GetPeersResponse(
805                        packets::GetPeersResponseArguments {
806                            responder_id: our_id,
807                            values: packets::GetPeersResponseValues::Peers(peers),
808                            token
809                        }
810                    )
811                ),
812                read_only: None,
813            }
814        );
815    }
816
817    #[test]
818    fn test_announce_peer_request() {
819        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
820        let target = Id::from_hex("1111111111111111111100000000000000000000").unwrap();
821        let transaction_id = vec![0, 1, 2, 3];
822        let token = vec![6, 6, 6];
823        assert_eq!(
824            MessageBuilder::new_announce_peer_request()
825                .sender_id(our_id)
826                .transaction_id(transaction_id.clone())
827                .target(target)
828                .port(5050)
829                .token(token.clone())
830                .build()
831                .expect("Failed to build message"),
832            packets::Message {
833                transaction_id,
834                version: None,
835                requester_ip: None,
836                message_type: packets::MessageType::Request(
837                    packets::RequestSpecific::AnnouncePeerRequest(
838                        packets::AnnouncePeerRequestArguments {
839                            requester_id: our_id,
840                            info_hash: target,
841                            port: 5050,
842                            implied_port: None,
843                            token
844                        }
845                    )
846                ),
847                read_only: None,
848            }
849        );
850    }
851
852    #[test]
853    fn test_announce_peer_request_default_implied_port() {
854        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
855        let target = Id::from_hex("1111111111111111111100000000000000000000").unwrap();
856        let transaction_id = vec![0, 1, 2, 3];
857        let token = vec![6, 6, 6];
858        assert_eq!(
859            MessageBuilder::new_announce_peer_request()
860                .sender_id(our_id)
861                .transaction_id(transaction_id.clone())
862                .target(target)
863                .token(token.clone())
864                .build()
865                .expect("Failed to build message"),
866            packets::Message {
867                transaction_id,
868                version: None,
869                requester_ip: None,
870                message_type: packets::MessageType::Request(
871                    packets::RequestSpecific::AnnouncePeerRequest(
872                        packets::AnnouncePeerRequestArguments {
873                            requester_id: our_id,
874                            info_hash: target,
875                            port: 0,
876                            implied_port: Some(true),
877                            token
878                        }
879                    )
880                ),
881                read_only: None,
882            }
883        );
884    }
885
886    #[test]
887    fn test_announce_peer_request_default_implied_port_conflict() {
888        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
889        let target = Id::from_hex("1111111111111111111100000000000000000000").unwrap();
890        let transaction_id = vec![0, 1, 2, 3];
891        let token = vec![6, 6, 6];
892        assert!(matches!(
893            MessageBuilder::new_announce_peer_request()
894                .sender_id(our_id)
895                .transaction_id(transaction_id)
896                .target(target)
897                .token(token)
898                .implied_port(false)
899                .build()
900                .unwrap_err(),
901            RustyDHTError::BuilderInvalidComboError(_)
902        ));
903    }
904
905    #[test]
906    fn test_announce_peer_response() {
907        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
908        let transaction_id = vec![0, 1, 2, 3];
909        assert_eq!(
910            MessageBuilder::new_announce_peer_response()
911                .sender_id(our_id)
912                .transaction_id(transaction_id.clone())
913                .build()
914                .expect("Failed to build message"),
915            packets::Message {
916                transaction_id,
917                version: None,
918                requester_ip: None,
919                message_type: packets::MessageType::Response(
920                    packets::ResponseSpecific::PingResponse(packets::PingResponseArguments {
921                        responder_id: our_id
922                    })
923                ),
924                read_only: None,
925            }
926        );
927    }
928
929    #[test]
930    fn test_sample_infohashes_request() {
931        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
932        let target = Id::from_hex("1111111111111111111100000000000000000000").unwrap();
933        let transaction_id = vec![0, 1, 2, 3];
934        assert_eq!(
935            MessageBuilder::new_sample_infohashes_request()
936                .sender_id(our_id)
937                .transaction_id(transaction_id.clone())
938                .target(target)
939                .build()
940                .expect("Failed to build message"),
941            packets::Message {
942                transaction_id,
943                version: None,
944                requester_ip: None,
945                message_type: packets::MessageType::Request(
946                    packets::RequestSpecific::SampleInfoHashesRequest(
947                        packets::SampleInfoHashesRequestArguments {
948                            requester_id: our_id,
949                            target
950                        }
951                    )
952                ),
953                read_only: None,
954            }
955        );
956    }
957
958    #[test]
959    fn test_sample_infohashes_response() {
960        let our_id = Id::from_hex("0000000000000000000011111111111111111111").unwrap();
961        let transaction_id = vec![0, 1, 2, 3];
962        let nodes = vec![Node::new(our_id, "1.2.3.4:53".parse().unwrap())];
963        let samples = vec![Id::from_hex("2222222222222222222233333333333333333333").unwrap()];
964        assert_eq!(
965            MessageBuilder::new_sample_infohashes_response()
966                .sender_id(our_id)
967                .transaction_id(transaction_id.clone())
968                .interval(Duration::from_secs(30))
969                .num_infohashes(50)
970                .nodes(nodes.clone())
971                .samples(samples.clone())
972                .build()
973                .expect("Failed to build message"),
974            packets::Message {
975                transaction_id,
976                version: None,
977                requester_ip: None,
978                message_type: packets::MessageType::Response(
979                    packets::ResponseSpecific::SampleInfoHashesResponse(
980                        packets::SampleInfoHashesResponseArguments {
981                            responder_id: our_id,
982                            interval: Duration::from_secs(30),
983                            num: 50,
984                            nodes,
985                            samples
986                        }
987                    )
988                ),
989                read_only: None,
990            }
991        );
992    }
993
994    #[test]
995    fn test_error() {
996        let transaction_id = vec![0, 1, 2, 3];
997        let code = 42;
998        let description = "Oh no";
999
1000        assert_eq!(
1001            MessageBuilder::new_error()
1002                .transaction_id(transaction_id.clone())
1003                .code(code)
1004                .description(description.to_string())
1005                .build()
1006                .unwrap(),
1007            packets::Message {
1008                transaction_id,
1009                version: None,
1010                requester_ip: None,
1011                message_type: packets::MessageType::Error(packets::ErrorSpecific {
1012                    code,
1013                    description: description.to_string()
1014                }),
1015                read_only: None,
1016            }
1017        )
1018    }
1019}