AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 771105
Accepted
mesr
mesr
Asked: 2024-02-26 11:50:07 +0800 CST2024-02-26 11:50:07 +0800 CST 2024-02-26 11:50:07 +0800 CST

Estranha inconsistência entre a execução e o fornecimento do script Bash

  • 772

Sei que este não é um título muito descritivo (sugestões são bem-vindas), mas o fato é que estou mexendo nos cabelos há horas e não tenho ideia de onde possa estar a raiz do problema.

Escrevi um script Bash simples para bate-papo CLI entre pares em uma rede local:

#!/usr/bin/env bash

# Usage: ./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>

# set -x
set -o errexit -o nounset -o pipefail

IFS=':' read -a socket <<< "$1"
LOCAL_IP=${socket[0]}
LOCAL_PORT=${socket[1]}

IFS=':' read -a socket <<< "$2"
REMOTE_IP=${socket[0]}
REMOTE_PORT=${socket[1]}

RECV_FIFO=".tmp.lanchat"

trap "rm '$RECV_FIFO'; kill 0" EXIT

mkfifo "$RECV_FIFO"

while true; do nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" > "$RECV_FIFO"; done &

TMUX_TOP="while true; do cat '$RECV_FIFO'; done"
TMUX_BOTTOM="while IFS= read -r line; do nc -n -q 0 '$REMOTE_IP' '$REMOTE_PORT' <<< \$line; done"

tmux new "$TMUX_TOP" \; split -v "$TMUX_BOTTOM"

A máquina no IP 172.16.0.2 é um VPS rodando Debian 11, e em 172.16.0.100 está meu computador local rodando Arch.

Ao executar os comandos manualmente no prompt em ambos os lados, obtenho o resultado desejado, que confirma que não há problema com a comunicação da rede e que a lógica do script está correta.

## VPS (Debian) side as follows; exchange IPs for local (Arch) side.
$ mkfifo .tmp.lanchat
$ while true; do nc -n -l -q 0 -s 172.16.0.2 -p 1234 > .tmp.lanchat; done &
$ tmux new "while true; do cat .tmp.lanchat; done" \; split -v "while IFS= read -r line; do nc -n -q 0 172.16.0.100 1234 <<< \$line; done"
## Test communication in both directions: all right; then CTRL-C twice to exit both tmux panels
$ kill %1; rm .tmp.lanchat

quando executo ambos os lados como um script, porém, apenas o lado local (Arch) imprime mensagens do servidor (Debian). O servidor não imprime nada do meu computador local. Quando rastreio a execução com set -x, tudo em ambos os lados se parece exatamente com os comandos que insiro manualmente, com os valores corretos no lugar das variáveis.

Agora, o estranho é que se eu executar o script no lado do Arch e os comandos no prompt (como acima) no lado do Debian, tudo funcionará bem novamente. Além disso, se eu executar o script no lado do Arch, mas fornecê -lo no lado do Debian, isso também funcionará bem.

Adicionar saída detalhada a ambas as chamadas nc no lado do Arch até imprime Connection to 172.16.0.2 1234 port [tcp/*] succeeded!. No entanto, adicionar a tee log.txtà chamada para nc no modo de escuta no lado Debian não captura nada:

#...
while true; do
    nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" | tee log.txt > "$RECV_FIFO";
done &
#...

Tentei estabelecer a conexão em todas as ordens possíveis entre os dois pares. Até reiniciei o servidor e minha máquina local para ter certeza de que não havia instâncias órfãs ou zumbis de nc abraçando o soquete que de alguma forma escapou da detecção.

Agora, o Debian e o Arch executam versões diferentes do nc . Então, aparentemente, parece que essa poderia ser uma explicação possível. Mas o fato de obter o script do lado do Debian funcionar bem não exclui essa possibilidade?

O que diabos está acontecendo aqui?

bash
  • 1 1 respostas
  • 93 Views

1 respostas

  • Voted
  1. Best Answer
    Kamil Maciorowski
    2024-02-26T19:51:10+08:002024-02-26T19:51:10+08:00

    Testei seu script no Debian 12 (localhost para localhost, diretórios de trabalho separados) e confirmo o problema. Meu ncé de netcat-traditional 1.10-47( ou seja, não denetcat-openbsd ).

    O problema está -q 0na escuta nc. De man 1 nc:

    -q seconds
    após EOF em stdin, aguarde o número especificado de segundos e saia. Se segundos for negativo, espere para sempre.

    Parece que a escuta ncespera por uma conexão de entrada antes de encerrar por causa de -q 0, mas não espera pela entrada de dados. Estabelecer uma conexão e transmitir dados são eventos separados e, por causa -q 0da ferramenta, geralmente é encerrada no meio. É uma corrida; em meus testes, a escuta nc às vezes retransmitiu os dados recebidos para o canal.

    O EOF que desencadeia o comportamento inesperado acontece imediatamente porque quando um shell sem controle de job executa um comando assíncrono (terminado por &, é assim que você executa o loop com listen nc), ele é obrigado a redirecionar seu stdin para /dev/nullou para um arquivo equivalente.

    Quando você origina o script, seu shell interativo o interpreta. Provavelmente é o bash com o controle de trabalho ativado (o comportamento padrão para um bash interativo). Nesse caso, ele executa o loop em segundo plano em um grupo de processos separado, mas seu stdin ainda está conectado ao terminal (em geral, isso nos permite fazer fgum trabalho em segundo plano e digitar nele). Para um trabalho em segundo plano, a incapacidade de roubar informações do terminal vem do SIGTTIN, o EOF nunca acontece. Dessa forma, quando o script está sendo originado, a escuta ncnão sofre com -q 0esse problema quando você executa o script sem fonte.

    Especificar -q 1para ouvir ncajudará na prática (embora ainda seja atrevido na teoria, eu acho), mas acho melhor usar -q -1(esperar para sempre) ou simplesmente omitir -q(em meus testes o comportamento padrão parece ser "esperar para sempre").

    -q 0para a conexão nc(aquela dentro do tmux) faz sentido, você deseja que isso ncseja encerrado imediatamente após o envio da carga útil.

    ncno seu Arch se comportou de maneira diferente, talvez porque seja diferente, ou talvez porque o estresse geral no sistema operacional naquele momento afetou a corrida.

    A lição é: no caso de um par nc+ nc -lque envia dados em apenas uma direção (você usa um par para cada linha), -q 0é uma opção útil para o remetente; mas para o receptor é desnecessário e, em algumas circunstâncias, até prejudicial.


    Observe que esta resposta aborda a questão explícita, nada mais. Por exemplo:

    • existe uma vulnerabilidade de injeção de código ( ./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>"'; rogue command'");
    • há janelas de tempo curtas quando não há escuta ncde um lado ou de outro;
    • um par de ncs pode ser suficiente para lidar com toda a "sessão".

    A resposta não aborda isso, mas posso fornecer um esboço de um script alternativo:

    #!/usr/bin/env bash
    
    target="$(tmux new -dP 'tail -f /dev/null')"
    uptty="$(tmux display-message -p -F '#{pane_tty}' -t "$target")"
    tmux split -t "$target" -v "
       rlwrap tee    >(sed -u 's/^/      < /' | ts %H:%M >${uptty@Q}) \
       | nc ${*@Q} > >(sed -u 's/^/> /'       | ts %H:%M >${uptty@Q})
    "
    tmux a -t "$target"
    

    O script requer bash (para si e dentro do tmux). Você o executa com argumentos que deseja fornecer nc, por exemplo

    • primeiro um lado ouvinte: ./lanchat -n -l -s 192.168.11.22 -p 2345,
    • então um lado de conexão: ./lanchat 192.168.11.22 2345.

    Uma conexão única nclida nccom toda a comunicação em ambas as direções. O script usa tscarimbos de data e hora (você pode remover ambas as instâncias, | ts %H:%M se quiser) e rlwrappara edição de linha com readline (você pode remover, rlwrap se quiser). sed -unão é portátil; sedsem -ucausará problemas de buffer, a menos que você também se livre do ts.

    Testado no bash 5.2.15, tmux 3.3a.

    • 5

relate perguntas

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve