Skip to content

Commit

Permalink
Add tcp command (#17)
Browse files Browse the repository at this point in the history
* Add tcp command

* Add support for read /net/tcp/<host>:<port> commands

* Add doc
  • Loading branch information
vinc authored Feb 6, 2020
1 parent d2fe9e6 commit 8ee2ca4
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 5 deletions.
18 changes: 18 additions & 0 deletions doc/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ The `host` command performs DNS lookups:
> host example.com
example.com has address 93.184.216.34


## TCP

The `tcp` command connects to TCP sockets:

> tcp time.nist.gov 13
Connecting to 129.6.15.30:13

58884 20-02-05 19:19:42 00 0 0 49.2 UTC(NIST) *

This could also be done with the `read` command:

> read /net/tcp/time.nist.gov:13
Connecting to 129.6.15.30:13

58884 20-02-05 19:19:55 00 0 0 49.2 UTC(NIST) *


## HTTP

Requesting a resource on a host:
Expand Down
1 change: 0 additions & 1 deletion dsk/ini/boot.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
read /ini/banner.txt
# dhcp
login
shell
clear
8 changes: 4 additions & 4 deletions src/user/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,20 @@ pub fn main(args: &[&str]) -> user::shell::ExitCode {
!arg.starts_with("--")
}).collect();

// Split <server> and <path>
// Split <host> and <path>
if args.len() == 2 {
if let Some(i) = args[1].find('/') {
let arg = args[1].clone();
let (server, path) = arg.split_at(i);
args[1] = server.to_string();
let (host, path) = arg.split_at(i);
args[1] = host.to_string();
args.push(path.to_string());
} else {
args.push("/".to_string());
}
}

if args.len() != 3 {
print!("Usage: http <server> <path>\n");
print!("Usage: http <host> <path>\n");
return user::shell::ExitCode::CommandError;
}

Expand Down
1 change: 1 addition & 0 deletions src/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ pub mod read;
pub mod route;
pub mod shell;
pub mod sleep;
pub mod tcp;
pub mod uptime;
pub mod write;
11 changes: 11 additions & 0 deletions src/user/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,23 @@ pub fn main(args: &[&str]) -> user::shell::ExitCode {
// Examples:
// > read /net/http/example.com/articles
// > read /net/http/example.com:8080/articles/index.html
// > read /net/daytime/time.nist.gov
// > read /net/tcp/time.nist.gov:13
let parts: Vec<_> = pathname.split('/').collect();
if parts.len() < 4 {
print!("Usage: read /net/http/<host>/<path>\n");
user::shell::ExitCode::CommandError
} else {
match parts[2] {
"tcp" => {
let host = parts[3];
user::tcp::main(&["tcp", host])
}
"daytime" => {
let host = parts[3];
let port = "13";
user::tcp::main(&["tcp", host, port])
}
"http" => {
let host = parts[3];
let path = "/".to_owned() + &parts[4..].join("/");
Expand Down
1 change: 1 addition & 0 deletions src/user/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ impl Shell {
"route" => user::route::main(&args),
"dhcp" => user::dhcp::main(&args),
"http" => user::http::main(&args),
"tcp" => user::tcp::main(&args),
"host" => user::host::main(&args),
"ip" => user::ip::main(&args),
"geotime" => user::geotime::main(&args),
Expand Down
133 changes: 133 additions & 0 deletions src/user/tcp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use alloc::vec;
use core::str::{self, FromStr};
use core::time::Duration;
use crate::{print, kernel, user};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;
use smoltcp::wire::IpAddress;

pub fn main(args: &[&str]) -> user::shell::ExitCode {
let mut args: Vec<String> = args.iter().map(ToOwned::to_owned).map(ToOwned::to_owned).collect();

// Split <host> and <port>
if args.len() == 2 {
if let Some(i) = args[1].find(':') {
let arg = args[1].clone();
let (host, path) = arg.split_at(i);
args[1] = host.to_string();
args.push(path[1..].to_string());
}
}

if args.len() != 3 {
print!("Usage: tcp <host> <port>\n");
return user::shell::ExitCode::CommandError;
}

let host = &args[1];
let port: u16 = args[2].parse().expect("Could not parse port");
let timeout = 5.0;
let request = "";

let address = if host.ends_with(char::is_numeric) {
IpAddress::from_str(&host).expect("invalid address format")
} else {
match user::host::resolve(&host) {
Ok(ip_addr) => {
ip_addr
}
Err(e) => {
print!("Could not resolve host: {:?}\n", e);
return user::shell::ExitCode::CommandError;
}
}
};

let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 1024]);
let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 1024]);
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);

let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);

enum State { Connect, Request, Response };
let mut state = State::Connect;

if let Some(ref mut iface) = *kernel::rtl8139::IFACE.lock() {
match iface.ipv4_addr() {
None => {
print!("Interface not ready\n");
return user::shell::ExitCode::CommandError;
}
Some(ip_addr) if ip_addr.is_unspecified() => {
print!("Interface not ready\n");
return user::shell::ExitCode::CommandError;
}
_ => {}
}

let time = kernel::clock::uptime();
loop {
if kernel::clock::uptime() - time > timeout {
print!("Timeout reached\n");
return user::shell::ExitCode::CommandError;
}

let timestamp = Instant::from_millis((kernel::clock::realtime() * 1000.0) as i64);
match iface.poll(&mut sockets, timestamp) {
Err(smoltcp::Error::Unrecognized) => {}
Err(e) => {
print!("Network Error: {}\n", e);
}
Ok(_) => {}
}

{
let mut socket = sockets.get::<TcpSocket>(tcp_handle);

state = match state {
State::Connect if !socket.is_active() => {
let local_port = 49152 + kernel::random::rand16().expect("random port") % 16384;
print!("Connecting to {}:{}\n", address, port);
if socket.connect((address, port), local_port).is_err() {
print!("Could not connect to {}:{}\n", address, port);
return user::shell::ExitCode::CommandError;
}
State::Request
}
State::Request if socket.may_send() => {
if request.len() > 0 {
socket.send_slice(request.as_ref()).expect("cannot send");
}
State::Response
}
State::Response if socket.can_recv() => {
socket.recv(|data| {
let contents = String::from_utf8_lossy(data);
for line in contents.lines() {
print!("{}\n", line);
}
(data.len(), ())
}).unwrap();
State::Response
}
State::Response if !socket.may_recv() => {
break;
}
_ => state
};
}

if let Some(wait_duration) = iface.poll_delay(&sockets, timestamp) {
let wait_duration: Duration = wait_duration.into();
kernel::time::sleep(libm::fmin(wait_duration.as_secs_f64(), timeout));
}
}
user::shell::ExitCode::CommandSuccessful
} else {
user::shell::ExitCode::CommandError
}
}

0 comments on commit 8ee2ca4

Please sign in to comment.