netcat命令_使用拍手实现netcat命令行

netcat命令

Socket command lines are a very interesting bit of tech. They essentially allow you to easily run commands against your program while it’s running, rather than just when you first start it with the options you pass to the CLI.

套接字命令行是一项非常有趣的技术。 从本质上讲,它们使您可以在程序运行时轻松地对程序运行命令,而不仅仅是使用传递给CLI的选项首次启动时。

Speaking of which, CLI’s are very easy to use, aren’t they?

说到哪个,CLI很容易使用,不是吗?

You can just type a fairly short identifier, the action you want to take, and a few options or modifiers to that action, and your shell will start a process that does what you want. But, if the process is already running, especially if it’s running on another machine, you’re out of luck with a traditional command line. To keep the same level of convenience, you might want to look into a socket command line.

您只需键入一个相当短的标识符,要执行的操作以及该操作的几个选项或修饰符,您的外壳程序便会启动一个执行所需操作的进程。 但是,如果该进程已经在运行,尤其是如果它在另一台计算机上运行,​​那么使用传统的命令行是不走运的。 为了保持相同级别的便利,您可能需要查看套接字命令行

A socket command line is just a “shell” implemented over a network socket, usually just a raw tcp stream. As a software engineering intern this summer, I worked on Boss, Dwelo’s software that runs on our smart apartment hubs and sends commands to/receives data from smart devices. For a while, it’s had a socket commander that you could netcat to while it was running locally in mock mode; however, it wasn't all that intentionally designed. It was just a bunch of regexes that matched on each line one after another, with a fair few drawbacks:

套接字命令行只是通过网络套接字实现的“外壳”,通常只是原始tcp流。 作为今年夏天的软件工程实习生,我研究了Dwelo的 Boss软件,该软件在我们的智能公寓中心上运行,并向智能设备发送命令/从智能设备接收数据。 有一段时间,它有一个套接字命令程序,当它在模拟模式下本地运行时,您可以将netcat到它。 但是,这并不是故意设计的。 只是一堆正则表达式在每一行中相继匹配,但有一些缺点:

  • Ill-defined syntax: sometimes arguments were delimited by spaces, sometimes by commas, sometimes by = signs

    语法错误:有时参数用空格分隔,有时用逗号分隔,有时用=符号分隔

  • Not very performant: it had to restart parsing from scratch for each regex that failed

    性能不是很好:它必须为每个失败的正则表达式从头开始重新解析
  • Kinda just ugly:

    有点丑陋

I wanted to rewrite it in order to make it more robust and easy to extend, so I decided to use nom to write a parser for a more well defined version of the command language.

我想重写它,以使其更健壮和易于扩展,因此我决定使用nom编写用于更明确定义的命令语言版本的解析器。

I had never looked into nom that extensively (other than reading Amos Wenger’s blog posts about parsing ELF executables), but it was a pretty good experience. I initially avoided the macro parser system at first, but I found myself using it more as time went on; it is a pretty elegant format for defining parsers. However, once I got to the point where I was adding an escaping system (because what if a wifi ssid has a space in it?), I had to stop and ask myself: aren’t I reinventing the wheel a little? I’m just making a worse version of the posix shell syntax, and isn’t that already much more standardized? Then, I remembered the shell-words crate. It parses a command string according to the rules set out in the posix specification, which everyone should be mostly familiar with — 'foo' for raw-ish strings, "bar\"baz\"qux"for escapable strings, escapable spaces so that foo\ bar\ baz is one word — again, very familiar, comfortable syntax.

除了阅读Amos Wenger的有关解析ELF可执行文件的博客文章以外,我从未深入研究nom,但这确实是一个很好的体验。 最初,我最初避免使用宏解析器系统,但是随着时间的推移,我发现自己越来越多地使用它。 这是定义解析器的一种非常优雅的格式。 但是,一旦到达要添加转义系统的地步(因为如果wifi ssid中有空间,该怎么办?),我不得不停下来问自己:我不是在重新发明轮子吗? 我只是在为posix shell语法做一个较差的版本,这是否已经更加标准化了? 然后,我想起了带壳的箱子。 它根据posix规范中列出的规则解析命令字符串,每个人都应该最熟悉该规则- 'foo'表示原始字符串, "bar\"baz\"qux"表示可转义的字符串,可转义的空格,以便foo\ bar\ baz是一个单词-再次非常熟悉,语法舒适。

So I thought, “wow, that will be much nicer”. Then I can just match on the words of the command in order to determine subcommands and arguments. But isn’t that just reinventing the wheel again? Isn’t parsing subcommands and arguments already done by a crate in the ecosystem? One that’s most likely already added as a dependency in our project?

所以我想,“哇,那会好得多”。 然后,我可以匹配命令的单词以确定子命令和参数。 但这不只是重新发明轮子吗? 解析子命令和参数是否已经由生态系统中的箱子完成了? 在我们的项目中最有可能已经添加为依赖项的项目?

(clap)

Turns out, clap is perfect for this use case. Although most usually just use it with its App::get_matches() method, which uses std::env::args() and just exits the process when an error occurs, it also has a very nice App::get_matches_from_safe(iter) method which takes any iterable of string arguments and just returns a result. This is much nicer than the regex:

事实证明, clap 非常适合此用例。 尽管大多数情况下只将其与App::get_matches()方法一起使用,该方法使用std::env::args()并在发生错误时退出进程,但它也有一个非常不错的App::get_matches_from_safe(iter)该方法采用任何可迭代的字符串参数,并仅返回结果。 这比正则表达式好得多:

  • The clap::Error type implements Display with all the coloring and help text that you'd normally expect.

    clap::Error类型使用通常需要的所有颜色和帮助文本来实现Display。

  • Performant: most rust cli tools use clap, and (anecdotally) I’ve always found them to be very fast to start up.

    性能:大多数锈cli工具都使用clap ,而且(轶事)我一直发现它们的启动速度非常快。

  • Auto-generated help strings: with the regex implementation, if we failed to match on any of the commands, we just had a README.txt file that we include_str!'d, which was just a list of the supported commands. clap automatically gives us a printout for --help, with an up-to-date list of the commands/subcommands and any extra info we choose to give them.

    自动生成的帮助字符串:使用正则表达式实现,如果我们在任何命令上都无法匹配,则只有一个include_str!README.txt文件include_str! 'd,这只是受支持命令的列表。 clap自动为我们提供--help的打印输出,其中包含命令/子命令的最新列表以及我们选择为其提供的任何其他信息。

  • We can use structopt, which allows us to simply define the structure of the subcommands and just match on them to map them to the actual commands for the executor.

    我们可以使用structopt ,它使我们能够简单地定义子命令的结构,并在它们上进行match以将它们映射到执行程序的实际命令。

  • structopt also implicitly uses FromStr to parse any arguments, so any arguments that I did want to validate with nom's parsing I could just do with a newtype and FromStr.

    structopt还隐式地使用FromStr来解析任何参数,因此我确实想通过nom的解析来验证的任何参数都可以使用FromStrFromStr

Here’s a simplified (and sanitized) version of what I ended up with:

这是我最终得到的结果的简化(消毒)版本:

It’s perfectly feasible to avoid the command->command translation. It just happens that our program already had a command model + api that we needed to use to execute the commands.

避免使用command-> command转换是完全可行的。 碰巧我们的程序已经具有执行命令所需的命令模型+ API。

In the code that actually handles the tcp stream, we put a BufReader around the TcpStream and iterate through the .lines(), parsing and executing each command. If the ParsedCommand is an Exit, we can just break out of the loop and drop the tcp stream to close the connection. You can also use a runtime like Tokio to handle the network io, which is what we actually do, but if you're not integrating into an existing tokio app, the standard library's network sockets are perfectly fine. Assuming that code above is in a command module, here's what some really basic command line handling code would look like.

在实际处理TCP流的代码,我们把一个BufReader围绕TcpStream和迭代通过.lines()解析和执行每个命令。 如果ParsedCommandExit ,我们可以跳出循环并丢弃tcp流以关闭连接。 您也可以使用像Tokio这样的运行时来处理网络io,这实际上是我们要做的,但是如果您不集成到现有的tokio应用程序中,那么标准库的网络套接字就可以了。 假设上面的代码在command模块中,那么这就是一些真正基本的命令行处理代码。

Then, we can just run the binary!

然后,我们可以运行二进制文件!

The code for this article is available to peruse here. I considered making this into a crate and publishing it but there really isn’t much unique or generic code to it, which is part of why I think it’s so cool. Thanks for reading!

本文的代码可在此处阅读。 我考虑过将其包装成小包装并发布,但是实际上并没有太多独特或通用的代码,这就是为什么我认为它如此酷的原因之一。 谢谢阅读!

翻译自: https://medium.com/dwelo-r-d/implementing-a-netcat-command-line-using-clap-227d7a5ddadd

netcat命令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值