diff --git a/src/proto/http.rs b/src/proto/http.rs
index a31f924..8879c54 100644
--- a/src/proto/http.rs
+++ b/src/proto/http.rs
@@ -232,6 +232,11 @@ pub fn repl<'a>(
debug!("pstate: {}", pstate.state);
return None;
}
+ /* if not in CONTENT state, not responding yet (it means the client
+ * has not finished sending headers yet) */
+ if pstate.state != HTTP_STATE_CONTENT {
+ return None;
+ }
let content = "\
401 Authorization Required
diff --git a/src/proto/mod.rs b/src/proto/mod.rs
index 9000eca..291002b 100644
--- a/src/proto/mod.rs
+++ b/src/proto/mod.rs
@@ -285,4 +285,34 @@ mod tests {
};
}
}
+
+ #[test]
+ fn test_proto_repl_http() {
+ /* ensure that HTTP FSM does not answer until completion of request
+ * (at least headers) */
+ let mut client_info = ClientInfo::new();
+ let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
+ client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
+ client_info.port.src = Some(65000);
+ let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
+ let mut ips = HashSet::new();
+ ips.insert(IpAddr::V4(masscanned_ip_addr));
+ /* Construct masscanned context object */
+ let masscanned = Masscanned {
+ synack_key: [0, 0],
+ mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
+ iface: None,
+ ip_addresses: Some(&ips),
+ };
+ /***** TEST COMPLETE REQUEST *****/
+ let payload = b"GET / HTTP/1.1\r\n\r\n";
+ if let None = repl(&payload.to_vec(), &masscanned, &mut client_info) {
+ panic!("expected an answer, got nothing");
+ }
+ /***** TEST INCOMPLETE REQUEST *****/
+ let payload = b"GET / HTTP/1.1\r\n";
+ if let Some(_) = repl(&payload.to_vec(), &masscanned, &mut client_info) {
+ panic!("expected no answer, got one");
+ }
+ }
}
diff --git a/test/src/all.py b/test/src/all.py
index c73d65b..214a249 100644
--- a/test/src/all.py
+++ b/test/src/all.py
@@ -537,6 +537,56 @@ def test_ipv4_tcp_http():
assert tcp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n")
+@test
+def test_ipv4_tcp_http_incomplete():
+ sport = 24595
+ dports = [80, 443, 5000, 53228]
+ for dport in dports:
+ seq_init = int(RandInt())
+ syn = (
+ Ether(dst=MAC_ADDR)
+ / IP(dst=IPV4_ADDR)
+ / TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
+ )
+ syn_ack = srp1(syn, timeout=1)
+ assert syn_ack is not None, "expecting answer, got nothing"
+ check_ip_checksum(syn_ack)
+ assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
+ syn_ack = syn_ack[TCP]
+ assert syn_ack.flags == "SA", "expecting TCP SA, got %r" % syn_ack.flags
+ ack = (
+ Ether(dst=MAC_ADDR)
+ / IP(dst=IPV4_ADDR)
+ / TCP(
+ flags="A",
+ sport=sport,
+ dport=dport,
+ seq=seq_init + 1,
+ ack=syn_ack.seq + 1,
+ )
+ )
+ _ = srp1(ack, timeout=1)
+ req = (
+ Ether(dst=MAC_ADDR)
+ / IP(dst=IPV4_ADDR)
+ / TCP(
+ flags="PA",
+ sport=sport,
+ dport=dport,
+ seq=seq_init + 1,
+ ack=syn_ack.seq + 1,
+ )
+ # purposedly incomplete request (missing additionnal ending \r\n)
+ / Raw("GET / HTTP/1.1\r\n")
+ )
+ resp = srp1(req, timeout=1)
+ assert resp is not None, "expecting an answer, got none"
+ check_ip_checksum(resp)
+ assert TCP in resp, "expecting TCP, got %r" % resp.summary()
+ tcp = resp[TCP]
+ assert tcp.flags == "A", "expecting TCP flag A, got {}".format(tcp.flags)
+
+
@test
def test_ipv6_tcp_http():
sport = 24592