Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Host Configuration #12

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions zvt/data/change_host_config.blob
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
��@4V�Aշiv�
37 changes: 37 additions & 0 deletions zvt/src/feig/packets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ pub struct WriteFile {
pub tlv: Option<tlv::WriteFile>,
}

/// Configuration packages. They all use the "Change Configuration" flow, but
/// with vastly different parameters, hence we have one for each flow. The Change Configuration
/// is described in 2.40, but since this is very hardware manufacturer specific, we put this one
/// here. So mostly see cVEND 6.7-6.16.
#[derive(Debug, PartialEq, Zvt)]
#[zvt_control_field(class = 0x08, instr = 0x13)]
pub struct ChangeConfiguration {
#[zvt_bmp(number = 0x06, length = length::Tlv)]
pub tlv: tlv::ChangeConfiguration,
}

/// Feig, 5.1
#[derive(Debug, PartialEq, Zvt)]
#[zvt_control_field(class = 0x0f, instr = 0xa1)]
Expand All @@ -86,6 +97,7 @@ mod test {
use super::*;
use crate::packets::tests::get_bytes;
use crate::ZvtSerializer;
use std::net::Ipv4Addr;

#[test]
fn test_request_for_data() {
Expand Down Expand Up @@ -230,4 +242,29 @@ mod test {
let actual_bytes = expected.zvt_serialize();
assert_eq!(actual_bytes[..26], bytes[..26]);
}

#[test]
fn test_change_host_config() {
let bytes = get_bytes("change_host_config.blob");
let addr = Ipv4Addr::new(213, 183, 19, 105);
let addr_u32: u32 = addr.into();

let expected = ChangeConfiguration {
tlv: tlv::ChangeConfiguration {
system_information: tlv::SystemInformation {
password: 123456,
host_configuration_data: Some(tlv::HostConfigurationData {
ip: addr_u32,
port: 30401,
config_byte: 1,
}),
},
},
};
assert_eq!(
ChangeConfiguration::zvt_deserialize(&bytes).unwrap().0,
expected
);
assert_eq!(bytes, expected.zvt_serialize());
}
}
27 changes: 27 additions & 0 deletions zvt/src/feig/packets/tlv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,30 @@ pub struct WriteFile {
#[zvt_tlv(tag = 0x2d)]
pub files: Vec<File>,
}

#[derive(Debug, PartialEq, Zvt, Default)]
pub struct HostConfigurationData {
#[zvt_bmp(encoding = encoding::BigEndian)]
pub ip: u32,
SirVer marked this conversation as resolved.
Show resolved Hide resolved

#[zvt_bmp(encoding = encoding::BigEndian)]
pub port: u16,

#[zvt_bmp(encoding = encoding::BigEndian)]
pub config_byte: u8,
}

#[derive(Debug, PartialEq, Zvt, Default)]
pub struct SystemInformation {
#[zvt_tlv(encoding = encoding::Bcd, tag = 0xff40)]
pub password: usize,

#[zvt_tlv(tag = 0xff41)]
pub host_configuration_data: Option<HostConfigurationData>,
}

#[derive(Debug, PartialEq, Zvt, Default)]
pub struct ChangeConfiguration {
#[zvt_tlv(tag = 0xe4)]
pub system_information: SystemInformation,
}
13 changes: 13 additions & 0 deletions zvt/src/feig/sequences.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,16 @@ impl Sequence for FactoryReset {
type Input = super::packets::CVendFunctions;
type Output = FactoryResetResponse;
}

pub struct ChangeHostConfiguration;

#[derive(Debug, ZvtEnum)]
pub enum ChangeHostConfigurationResponse {
CompletionData(packets::CompletionData),
Abort(packets::Abort),
}

impl Sequence for ChangeHostConfiguration {
type Input = super::packets::ChangeConfiguration;
type Output = ChangeHostConfigurationResponse;
}
9 changes: 7 additions & 2 deletions zvt_builder/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ impl Encoding<NaiveDateTime> for Default {
/// The default is when the [Tag] is used as a Bmp-number or as a Tlv-tag.
impl encoding::Encoding<Tag> for Default {
fn encode(input: &Tag) -> Vec<u8> {
if (input.0 >> 8) == 0x1f {
let low = input.0 >> 8;
if low == 0x1f || low == 0xff {
SirVer marked this conversation as resolved.
Show resolved Hide resolved
input.0.to_be_bytes().to_vec()
} else {
vec![input.0 as u8]
Expand All @@ -151,7 +152,11 @@ impl encoding::Encoding<Tag> for Default {

fn decode(bytes: &[u8]) -> ZVTResult<(Tag, &[u8])> {
let (tag, new_bytes): (u8, _) = encoding::BigEndian::decode(bytes)?;
if tag == 0x1f {
// §9.4.1 of the PA00P015 defines a lot of tags, there is a bunch of 2 byte tags that start
// with 0xf1xx, there is also 0xff01-0xff04 defined there.
// TODO(hrapp): The same section also mentions tags 0x9f5a, 0x9f5b, and three byte tags
// 0x1f8000 and 0x1f8001 which we are not handling correctly currently.
if tag == 0x1f || tag == 0xff {
if bytes.len() < 2 {
Err(ZVTError::IncompleteData)
} else {
Expand Down
51 changes: 51 additions & 0 deletions zvt_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::{bail, Result};
use argh::FromArgs;
use env_logger::{Builder, Env};
use std::io::Write;
use std::net::Ipv4Addr;
use tokio::net::TcpStream;
use tokio_stream::StreamExt;
use zvt::sequences::Sequence;
Expand All @@ -23,6 +24,7 @@ enum SubCommands {
ReadCard(ReadCardArgs),
Reservation(ReservationArgs),
PartialReversal(PartialReversalArgs),
ChangeHostConfiguration(ChangeHostConfigurationArgs),
}

#[derive(FromArgs, PartialEq, Debug)]
Expand Down Expand Up @@ -181,6 +183,23 @@ struct PartialReversalArgs {
bmp_data: Option<String>,
}

#[derive(FromArgs, PartialEq, Debug)]
/// Changes the Host the payment terminal connects to.
#[argh(subcommand, name = "change_host_config")]
struct ChangeHostConfigurationArgs {
/// the IP the terminal should connect to.
#[argh(option)]
ip: Ipv4Addr,

/// the port the terminal should connect to.
#[argh(option, default = "30401")]
port: u16,

/// see reservation.
#[argh(option, default = "1")]
configuration_byte: u8,
}

#[derive(FromArgs, Debug)]
/// Example tool to interact with the payment terminal.
struct Args {
Expand Down Expand Up @@ -477,6 +496,35 @@ async fn partial_reversal(socket: &mut PacketTransport, args: PartialReversalArg
Ok(())
}

async fn change_host_config(
socket: &mut PacketTransport,
password: usize,
args: ChangeHostConfigurationArgs,
) -> Result<()> {
let request = feig::packets::ChangeConfiguration {
tlv: feig::packets::tlv::ChangeConfiguration {
system_information: feig::packets::tlv::SystemInformation {
password,
host_configuration_data: Some(feig::packets::tlv::HostConfigurationData {
ip: args.ip.into(),
port: args.port,
config_byte: args.configuration_byte,
}),
},
},
};

let mut stream = feig::sequences::ChangeHostConfiguration::into_stream(&request, socket);
use feig::sequences::ChangeHostConfigurationResponse::*;
while let Some(response) = stream.next().await {
match response? {
CompletionData(_) => (),
Abort(data) => bail!("Received Abort: {:?}", data),
}
}
Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
init_logger();
Expand All @@ -499,6 +547,9 @@ async fn main() -> Result<()> {
SubCommands::ReadCard(a) => read_card(&mut socket, &a).await?,
SubCommands::Reservation(a) => reservation(&mut socket, a).await?,
SubCommands::PartialReversal(a) => partial_reversal(&mut socket, a).await?,
SubCommands::ChangeHostConfiguration(a) => {
change_host_config(&mut socket, args.password, a).await?
}
}

Ok(())
Expand Down