подключение localhost без всплывающего окна

Рассмотрим следующий R-скрипт:

con <- socketConnection(host = "localhost", port = 8, server = TRUE, blocking = TRUE, open = "a+b")
close(con = con)

Сохранение этих строк в виде файла .R и последующий запуск из командной строки выдает (в Windows) предупреждение брандмауэра. По крайней мере, если для R нет правила "Брандмауэр Windows в режиме повышенной безопасности", которое появляется после первого раза. Мне сказали, что то же самое происходит на Mac, но я не мог проверить это сам. Как это можно изменить, чтобы разрешить обратную связь локального узла, но избежать всплывающего окна?

Контекст: я написал код для людей, использующих параллельную обработку (на одной локальной машине). Однако, это предупреждение появилось на их экранах, и они стали подозрительными. Глупость в том, что даже если люди нажимают "нет" или игнорируют всплывающее окно, параллельная обработка все равно работает. Я воспринимаю это как знак того, что можно изменить этот код, чтобы он не выдавал всплывающее окно и все еще работал.

Я видел очень похожие вопросы на других языках (1, 2, 3), и мне было интересно, можно ли сделать то же самое с R.

Пример первого запроса брандмауэра Windows 10:

Enter image description here

Ответ 1

Мне кажется, что самый простой способ решить эту проблему - добавить правило брандмауэра в процессе установки приложения.

  • Вы можете использовать netsh, чтобы добавить правило (требуются права администратора), чтобы программно разрешить доступ через брандмауэр.

Ниже приведен пример сценария, и я надеюсь, что это поможет вам в правильном направлении.

Пример скрипта настройки брандмауэра

netsh advfirewall firewall add rule name="RScript" action=allow program="C:\Program Files\Microsoft\R Client\R_SERVER\bin\x64\Rscript.exe" enable=yes Localip="127.0.0.1" localport="9999" protocol=tcp interfacetype=any profile=private dir=in

Вывод команды:

PS <hidden>\dev\stackoverflow\47353848> netsh advfirewall firewall add rule name="RScript" action=allow program="C
:\Program Files\Microsoft\R Client\R_SERVER\bin\x64\Rscript.exe" enable=yes Localip="127.0.0.1" localport="9999" protoco
l=tcp interfacetype=any profile=private dir=in
Ok.

Добавлено правило брандмауэра

Enter image description here


Предполагая, что вы запускаете файл R с использованием RScript, приведенный выше скрипт netsh позволит приложению RScript иметь доступ к адресу обратной связи 127.0.0.1 на порту 9999 с использованием TCP в частной сети. С этого момента вы не должны получать приглашение брандмауэра.

Командная строка без запроса брандмауэра

c:\<hidden>\dev\stackoverflow\47353848> Rscript.exe .\server.R
Listening...

Зачем это делать? Ну, насколько я смог убедиться, что нет способа использовать R base::socketConnection в Windows без запуска запроса брандмауэра Защитника Windows, даже при использовании соединителя обратной связи. Интересно отметить, что если вы используете Java, вы не получите приглашение. Я посмотрел на обе реализации, но не мог определить, почему нет.

Тестовый серверный код:

server <- function() {
  while (TRUE) {
    writeLines("Listening...")
    con <- socketConnection(host = "loopback",
                            port = 9999,
                            server = TRUE,
                            blocking = TRUE,
                            timeout = 0,
                            open = "r+")
    data <- readLines(con, 1)
    print(data)
    response <- toupper(data)
    writeLines(response, con)
    close(con)
  }
}
server()

Тестовый код клиента

client <- function() {
  while (TRUE) {
    con <- socketConnection(host = "loopback",
                            port = 9999,
                            server = FALSE,
                            blocking = TRUE,
                            open = "r+")
    f <- file("stdin")
    open(f)
    print("Enter text to be upper-cased, q to quit")
    sendme <- readLines(f, n = 1)
    if (tolower(sendme) == "q") {
      break
    }

    write_resp <- writeLines(sendme, con)
    server_resp <- readLines(con, 1)
    print(paste("Your upper cased text: ", server_resp))
    close(con)
  }
}
client()

Ответ 2

Вы в основном вынуждены использовать брандмауэр Windows, как это происходит с Windows. Поэтому, возможно, включите файл .bat в файл .R, который создаст исключение и попросит пользователя запустить его? Или, может быть, сделать установщик .exe с IExpress? Это могло бы помочь.

Я бы порекомендовал маршрут установщика .exe, так как .bat файл тоже выглядит немного подозрительно, так как неопытные пользователи будут плакать, как волк, когда он запрашивает права администратора. Команда netsh может создать исключения брандмауэра для любой программы, если вы предпочитаете маршрут файла .bat.

Он принимает исходящие соединения с помощью переключателя dir = out и разрешает исключение с помощью переключателя action = allow

netsh advfirewall firewall add rule name="PROGRAM_NAME" dir=out action=allow program="C:\PROGRAMPATH" enable=yes

Он принимает входящие соединения с помощью переключателя dir = in и разрешает исключение с помощью переключателя action = allow

netsh advfirewall firewall add rule name="PROGRAM_NAME" dir=out action=allow program="C:\PROGRAMPATH" enable=yes

Как добавить правило в брандмауэр Windows - DigitalCitizen

Технические советы по Интернету - настройка правил и настроек брандмауэра Windows 10

Ответ 3

Альтернативой использованию кластеров PSOCK является использование callr для запуска нескольких сеансов R в фоновом режиме. Пакет future.callr (*) предоставляет параллельный бэкэнд для future (отказ от ответственности: я автор). Это позволит вам распараллелить все ОС, включая Windows, без прохождения через сокеты (и, следовательно, без брандмауэра). Пример:

library("future")
plan(future.callr::callr)
y <- future_lapply(x, FUN = my_fcn_in_parallel)

Он также работает с doFuture для foreach. Пример:

library("doFuture")
plan(future.callr::callr)
registerDoFuture()
y <- foreach(i in seq_along(x)) %dopar% my_fcn_in_parallel(x[[i]])

FYI, для кластеров PSOCK используйте plan(multisession).

(*) future.callr находится на CRAN по состоянию на 2018-02-13 будет отправлен в CRAN, как только версия разработчика callr, от которой это зависит, отправляется в CRAN.

Ответ 4

(Для моего правила брандмауэра см. самый конец)

Функциональность просто не существует.

В C вы создаете серверный сокет с вызовами socket, bind и listen и получаете входящее соединение с вызовом accept. src\modules\internet\sock.c - это код обработчика сокета, он имеет две функции для открытия сокета, Sock_connect открывается и подключает сокет, так что это для клиентской стороны, а int Sock_open(Sock_port_t port, Sock_error_t perr) - тот, который открывает (и фактический вызов accept находится в Sock_listen). Проблема в том, что этот Sock_open имеет только аргумент port, а хост/интерфейс жестко закодирован:

/* open a socket for listening */
int Sock_open(Sock_port_t port, Sock_error_t perr)
{
    int sock;
    struct sockaddr_in server;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    return Sock_error(perr, errno, 0);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons((short)port);

    if ((bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0) ||
        (listen(sock, MAXBACKLOG) < 0)) {
        close(sock);
        return Sock_error(perr, errno, 0);
    }
    return sock;
}

Он связывается с INADDR_ANY и слушает его, что означает все интерфейсы вашего ПК (а не только loopback), и он наверняка запускает брандмауэр.

Функция вызывается из соседнего Rsock.c, все еще с одним аргументом порта, и где все остальное потеряно, кажется, на один шаг раньше, в sockconn.c:

static Rboolean sock_open(Rconnection con)
{
    Rsockconn this = (Rsockconn)con->private;
    int sock, sock1, mlen;
    int timeout = this->timeout;
    char buf[256];

    if(timeout == NA_INTEGER || timeout <= 0) timeout = 60;
    this->pend = this->pstart = this->inbuf;

    if(this->server) {
        sock1 = R_SockOpen(this->port);

В этой последней строке игнорируется хост-часть RSockconn, хотя она содержит такое поле:

/* used in internet module */
typedef struct sockconn {
    int port;
    int server;
    int fd;
    int timeout;
    char *host;
    char inbuf[4096], *pstart, *pend;
} *Rsockconn;

(Это определено вне, в src\include\Rconnections.h)

К сожалению, это не решит вашу проблему, именно поэтому у вас это есть. Вы можете подумать о подготовке отчета об ошибках для разработчиков из R. Комментарии предполагают, что они получили чистый код с древних времен, когда брандмауэры и интернет-безопасность не были настолько важны, как сейчас:

/* Simple sockets interface derived from the sockets UICI
   implementation in Appendix B of Practical UNIX Programming,
   K. A. Robbins and S. Robbins, Prentice Hall, 1996. */

Это хорошо, только 21 год назад.


Изначально я не хотел красть вещи netsh от других, но я думаю, что вы можете ошибаться. На самом деле вы ничего не должны допускать, но блокируете все:
netsh advfirewall firewall add rule name="Rtest" dir=in action=block program="<location and name of your executable>"

И это все. Дело в том, что интерфейс loopback вообще не защищен брандмауэром (поэтому всегда работают подключения к 127.0.0.1 - я тоже тестировал его, просто чтобы быть в безопасности), и вы не хотите, чтобы кто-либо еще мог связаться с вашей программой. Я видел "разрешить" другие ответы, и вы этого не хотите. В зависимости от других применений вам может потребоваться ограничить правило "localport = 8" и/или "protocol = tcp", но часть блока уверен.

Ответ 5

Это отличный пост. Это был замечательный опыт при чтении вашего блога. Если у вас возникли проблемы, связанные с принтером, посетите нас Brother Printer Offline Windows 10>