1*62c56f98SSadaf Ebrahimi#!/usr/bin/env perl 2*62c56f98SSadaf Ebrahimi 3*62c56f98SSadaf Ebrahimi# A simple TCP client that sends some data and expects a response. 4*62c56f98SSadaf Ebrahimi# Usage: tcp_client.pl HOSTNAME PORT DATA1 RESPONSE1 5*62c56f98SSadaf Ebrahimi# DATA: hex-encoded data to send to the server 6*62c56f98SSadaf Ebrahimi# RESPONSE: regexp that must match the server's response 7*62c56f98SSadaf Ebrahimi# 8*62c56f98SSadaf Ebrahimi# Copyright The Mbed TLS Contributors 9*62c56f98SSadaf Ebrahimi# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 10*62c56f98SSadaf Ebrahimi 11*62c56f98SSadaf Ebrahimiuse warnings; 12*62c56f98SSadaf Ebrahimiuse strict; 13*62c56f98SSadaf Ebrahimiuse IO::Socket::INET; 14*62c56f98SSadaf Ebrahimi 15*62c56f98SSadaf Ebrahimi# Pack hex digits into a binary string, ignoring whitespace. 16*62c56f98SSadaf Ebrahimisub parse_hex { 17*62c56f98SSadaf Ebrahimi my ($hex) = @_; 18*62c56f98SSadaf Ebrahimi $hex =~ s/\s+//g; 19*62c56f98SSadaf Ebrahimi return pack('H*', $hex); 20*62c56f98SSadaf Ebrahimi} 21*62c56f98SSadaf Ebrahimi 22*62c56f98SSadaf Ebrahimi## Open a TCP connection to the specified host and port. 23*62c56f98SSadaf Ebrahimisub open_connection { 24*62c56f98SSadaf Ebrahimi my ($host, $port) = @_; 25*62c56f98SSadaf Ebrahimi my $socket = IO::Socket::INET->new(PeerAddr => $host, 26*62c56f98SSadaf Ebrahimi PeerPort => $port, 27*62c56f98SSadaf Ebrahimi Proto => 'tcp', 28*62c56f98SSadaf Ebrahimi Timeout => 1); 29*62c56f98SSadaf Ebrahimi die "Cannot connect to $host:$port: $!" unless $socket; 30*62c56f98SSadaf Ebrahimi return $socket; 31*62c56f98SSadaf Ebrahimi} 32*62c56f98SSadaf Ebrahimi 33*62c56f98SSadaf Ebrahimi## Close the TCP connection. 34*62c56f98SSadaf Ebrahimisub close_connection { 35*62c56f98SSadaf Ebrahimi my ($connection) = @_; 36*62c56f98SSadaf Ebrahimi $connection->shutdown(2); 37*62c56f98SSadaf Ebrahimi # Ignore shutdown failures (at least for now) 38*62c56f98SSadaf Ebrahimi return 1; 39*62c56f98SSadaf Ebrahimi} 40*62c56f98SSadaf Ebrahimi 41*62c56f98SSadaf Ebrahimi## Write the given data, expressed as hexadecimal 42*62c56f98SSadaf Ebrahimisub write_data { 43*62c56f98SSadaf Ebrahimi my ($connection, $hexdata) = @_; 44*62c56f98SSadaf Ebrahimi my $data = parse_hex($hexdata); 45*62c56f98SSadaf Ebrahimi my $total_sent = 0; 46*62c56f98SSadaf Ebrahimi while ($total_sent < length($data)) { 47*62c56f98SSadaf Ebrahimi my $sent = $connection->send($data, 0); 48*62c56f98SSadaf Ebrahimi if (!defined $sent) { 49*62c56f98SSadaf Ebrahimi die "Unable to send data: $!"; 50*62c56f98SSadaf Ebrahimi } 51*62c56f98SSadaf Ebrahimi $total_sent += $sent; 52*62c56f98SSadaf Ebrahimi } 53*62c56f98SSadaf Ebrahimi return 1; 54*62c56f98SSadaf Ebrahimi} 55*62c56f98SSadaf Ebrahimi 56*62c56f98SSadaf Ebrahimi## Read a response and check it against an expected prefix 57*62c56f98SSadaf Ebrahimisub read_response { 58*62c56f98SSadaf Ebrahimi my ($connection, $expected_hex) = @_; 59*62c56f98SSadaf Ebrahimi my $expected_data = parse_hex($expected_hex); 60*62c56f98SSadaf Ebrahimi my $start_offset = 0; 61*62c56f98SSadaf Ebrahimi while ($start_offset < length($expected_data)) { 62*62c56f98SSadaf Ebrahimi my $actual_data; 63*62c56f98SSadaf Ebrahimi my $ok = $connection->recv($actual_data, length($expected_data)); 64*62c56f98SSadaf Ebrahimi if (!defined $ok) { 65*62c56f98SSadaf Ebrahimi die "Unable to receive data: $!"; 66*62c56f98SSadaf Ebrahimi } 67*62c56f98SSadaf Ebrahimi if (($actual_data ^ substr($expected_data, $start_offset)) =~ /[^\000]/) { 68*62c56f98SSadaf Ebrahimi printf STDERR ("Received \\x%02x instead of \\x%02x at offset %d\n", 69*62c56f98SSadaf Ebrahimi ord(substr($actual_data, $-[0], 1)), 70*62c56f98SSadaf Ebrahimi ord(substr($expected_data, $start_offset + $-[0], 1)), 71*62c56f98SSadaf Ebrahimi $start_offset + $-[0]); 72*62c56f98SSadaf Ebrahimi return 0; 73*62c56f98SSadaf Ebrahimi } 74*62c56f98SSadaf Ebrahimi $start_offset += length($actual_data); 75*62c56f98SSadaf Ebrahimi } 76*62c56f98SSadaf Ebrahimi return 1; 77*62c56f98SSadaf Ebrahimi} 78*62c56f98SSadaf Ebrahimi 79*62c56f98SSadaf Ebrahimiif (@ARGV != 4) { 80*62c56f98SSadaf Ebrahimi print STDERR "Usage: $0 HOSTNAME PORT DATA1 RESPONSE1\n"; 81*62c56f98SSadaf Ebrahimi exit(3); 82*62c56f98SSadaf Ebrahimi} 83*62c56f98SSadaf Ebrahimimy ($host, $port, $data1, $response1) = @ARGV; 84*62c56f98SSadaf Ebrahimimy $connection = open_connection($host, $port); 85*62c56f98SSadaf Ebrahimiwrite_data($connection, $data1); 86*62c56f98SSadaf Ebrahimiif (!read_response($connection, $response1)) { 87*62c56f98SSadaf Ebrahimi exit(1); 88*62c56f98SSadaf Ebrahimi} 89*62c56f98SSadaf Ebrahimiclose_connection($connection); 90