NetCat: ответить клиенту с его IP-адресом?

Посколькуread -n "$n"(не является функцией POSIX ), и если stdin является терминальным устройством, readвыводит терминал из режима icanon, иначе readбудет видеть только полные строки, возвращаемые встроенный строковый редактор терминальной строки, а затем считывает по одному байту за раз до тех пор, пока не будет прочитано $nсимволов или новой строки (вы можете увидеть неожиданные результаты, если введены недопустимые символы ).

Читает до $nсимволов из одной строки. Вам также нужно будет очистить $IFS, чтобы он не удалял символы IFS из ввода.

Поскольку мы покидаем режим icanon, ^Dбольше не является особым. Поэтому, если вы нажмете Ctrl+D , будет прочитан символ ^D.

Вы не увидите eof с терминального устройства, если терминал каким-либо образом не отключен. Если stdin — это другой тип файла, вы можете увидеть eof (, как в : | IFS= read -rn 1; echo "$?", где stdin — пустой канал, или с перенаправлением stdin из/dev/null)

readвозвращает 0, если $nсимволов (байтов, не являющихся частью допустимых символов, считаются за 1 символ )или была прочитана целая строка.

Таким образом, в особом случае запрашивается только один символ:

if IFS= read -rn 1 var; then
  if [ "${#var}" -eq 0 ]; then
    echo an empty line was read
  else
    printf %s "${#var} character "
    (export LC_ALL=C; printf '%s\n' "made of ${#var} byte(s) was read")
  fi
else
  echo "EOF found"
fi

Сделать это с помощью POSIX довольно сложно.

Это было бы что-то вроде (, предполагая, что система ASCII -основана (, а не EBCDIC, например, )система):

readk() {
  REPLY= ret=1
  if [ -t 0 ]; then
    saved_settings=$(stty -g)
    stty -icanon min 1 time 0 icrnl
  fi
  while true; do
    code=$(dd bs=1 count=1 2> /dev/null | od -An -vto1 | tr -cd 0-7)
    [ -n "$code" ] || break
    case $code in
      000 | 012) ret=0; break;; # can't store NUL in variable anyway
      (*) REPLY=$REPLY$(printf "\\$code");;
    esac
    if expr " $REPLY" : '.' > /dev/null; then
      ret=0
      break
    fi
  done
  if [ -t 0 ]; then
    stty "$saved_settings"
  fi
  return "$ret"
}

Обратите внимание, что мы возвращаемся только тогда, когда прочитан полный символ. Если ввод в неправильной кодировке (, отличной от кодировки локали ), например, если ваш терминал отправляет éв кодировке iso8859 -1 (0xe9 ), когда мы ожидаем UTF -8 (0xc3 0xa9 ), тогда вы можете ввести сколько угодно é, функция не вернется. bash's read -n1вернется после второго 0xe9 (и сохранит оба значения в переменной ), что немного лучше.

Если вы также хотите прочитать символ ^Cпри Ctrl+C(вместо того, чтобы позволить ему убить ваш скрипт; также для ^Z, ^\... )или ^S/ ^Qпосле Ctrl+S/Q(вместо управления потоком )вы можете добавить -isig -ixonк строке stty. Обратите внимание, что bashread -n1тоже этого не делает (, он даже восстанавливает isig, если он был выключен ).

Это не восстановит настройки tty, если скрипт будет убит (, например, если вы нажмете Ctrl+C . Вы можете добавить trap, но это может переопределить другие trapв скрипте.

Вы также можете использовать zshвместо bash, где read -k(, который предшествует ksh93или bashдля read -n/-N), считывает один символ с терминала и обрабатывает ^Dсам по себе (возвращает не -ноль, если этот символ введен )и не обрабатывает новую строку специально.

if read -k k; then
  printf '1 character entered: %q\n' $k
fi
0
16.02.2017, 09:48
2 ответа

Конечно, это возможно. Просто настройте nc как сопроцесс:

#!/bin/bash -x

# Greets the client with their hostname.

coproc ( nc -l -v localhost 3000 2>&1 )

{
    declare "$( sed -e 's/^Connection from \([^ ]*\).*$/client="\1"/' -e 'q' )"
    printf 'Hello there, user on %s\n' "$client"
} <&${COPROC[0]} >&${COPROC[1]}

kill "$COPROC_PID"

Для чего-то более сложного, например, обработки HTTP-запросов:

#!/bin/bash

# Handles one request, then kills nc and restarts

while true; do
    coproc ( nc -l localhost 3000 2>&1 )

    {
      get_http_request_header
      process_request
      send_http_reply
    } <&${COPROC[0]} >&${COPROC[1]}

    kill "$COPROC_PID"
done  

Команды get_http_request_header , process_request , и send_http_reply могут быть функциями оболочки или отдельными сценариями, которые вы пишете.

Процедура get_http_request_header будет получать свой стандартный ввод от клиента, подключенного к nc , и анализировать запрос, возможно, сохраняя его во временном файле или устанавливая некоторые глобальные переменные или другой способ передать необходимую информацию в process_request .

process_request может обрабатывать компиляцию результата любым необходимым способом.

send_http_reply будет печатать клиенту, просто записывая на стандартный вывод.

Другая возможная установка, при которой информация на стороне сервера просто передается по внутренним шагам через каналы:

#!/bin/bash

# Handles one request, then kills nc and restarts

while true; do
    coproc ( nc -l localhost 3000 2>&1 )

    {
      get_http_request_header |
      process_request |
      send_http_reply
    } <&${COPROC[0]} >&${COPROC[1]}

    kill "$COPROC_PID"
done  
4
28.01.2020, 02:19

На этот вопрос уже был дан ответ или его местонахождение в display-welcome -message-при-успешном-netcat-connection

Но, по сути, вы будете делать со своим слушателем следующее:

(echo "$VAR"; cat) |nc -nvlp 4444

Когда ваш клиент подключается, слушатель выводит эту переменную на стандартный вывод клиента.

0
28.01.2020, 02:19

Теги

Похожие вопросы