'localhost' connection without firewall popup
Asked Answered
S

4

14

Consider the following R script:

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

Saving these lines as a .R file, and subsequently running it from command line produces (on Windows) a firewall warning. At least, if there is no rule for R under "Windows Firewall with Advanced Security", which appears after the first time. I've been told the same happens on a Mac, but I couldn't verify this myself. How can this be altered to allow for a localhost loopback, but avoid the popup?

Context: I've written some code for people that uses parallel processing (on one single local machine). However, this warning popped up on their screens, and they got suspicious. The silly thing is, that even if people click no or ignore the popup, the parallel processing still seems to works. I take that as a sign that it's possible to modify this code to not give this popup and still function.

I've seen very similar questions in other languages (1, 2, 3), and I was wondering whether it is possible to do the same with R.

Windows 10 Firewall First Time Prompt Example:

Enter image description here

Swim answered 17/11, 2017 at 15:16 Comment(3)
@akashrajkn - Did I address your issue - just checking.Lighterage
@Lighterage I will try your solution today and update you. Thank youSansen
@Sansen - No worries - happy to help.Lighterage
L
12

My sense is that the easiest way to navigate this problem is to add a firewall rule as part of the application install process.

  • You can use netsh to add a rule (administrator privileges are required) to enable firewall access programmatically.

I provide an example script below, and I hope this helps point you in the right direction.

Example firewall configuration script

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

Command Output:

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.

Firewall Rule Added

Enter image description here


Assuming you run the R file using RScript, the above netsh script will enable the RScript application to be able to access the loopback address 127.0.0.1 on port 9999 using TCP on a private network. From this point on you should not get a firewall prompt.

Command line with no prompt for firewall

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

Why do this? Well, as far as I have been able to ascertain there is no way to use R's base::socketConnection on Windows without triggering the Windows Defender firewall prompt, even when using the loopback connector. Interesting to note is that if you use Java you don't get prompted. I looked at both implementations, but I couldn't determine why not.

Test Server Code:

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()

Test Client Code

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()
Lighterage answered 26/11, 2017 at 0:20 Comment(0)
V
6

(For my take on the firewall rule, see the very end)

The functionality simply does not seem to exist.

In C you create a server socket with socket, bind and listen calls, and get the incoming connection with an accept call. src\modules\internet\sock.c is the socket handler code, it has two functions for opening a socket, Sock_connect opens and connects a socket, so this is for client side, and int Sock_open(Sock_port_t port, Sock_error_t perr) is the one which opens a server socket (and the actual accept call is in Sock_listen). The problem is that this Sock_open has a port argument only, and the host/interface is hardcoded:

/* 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;
}

It binds to and listens on INADDR_ANY, which means all interfaces of your PC (not just the loopback), and it triggers the firewall for sure.

The function is called from the neighboring Rsock.c, still with a single port argument, and where everything else is lost seems to be one step earlier, in 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);

This last line is where host part of RSockconn is disregarded, though it contains such field:

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

(This is defined outside, in src\include\Rconnections.h)

Unfortunately this will not solve your problem, just this is why you have it. You may consider raising an error report for the developers of R. Comments suggest they got the net code from ancient times, when firewalls and internet security were not that much of a concern like they are now:

/* 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. */

Which is nice, just it was 21 years ago.


Originally I did not want to steal the netsh thing from others, but I think you may get wrong suggestions. Actually you should not allow anything, but block everything:
netsh advfirewall firewall add rule name="Rtest" dir=in action=block program="<location and name of your executable>"

And that is it. The thing is that the loopback interface is not firewalled at all (so connections to 127.0.0.1 always work - I tested it too, just to be on the safe side), and you do not want anyone else to reach your program. I saw 'allow'-s in other answers, and you do not want that. Depending on other uses, you may have to restrict the rule with 'localport=8' and/or 'protocol=tcp', but the block part is sure.

Vivianaviviane answered 28/11, 2017 at 20:57 Comment(0)
A
5

You are basically forced to use Windows Firewall, as it comes with Windows. So maybe include a .bat file with your .R file that creates an exception and tells the user to run it? Or maybe make a .exe installer with IExpress? That might do the trick.

I would recommend the .exe installer route though, as a .bat file seems a tad suspicious too, as non-tech-savvy users will cry wolf when it asks for administrator privileges. The netsh command can create firewall exceptions for any program if you would rather the .bat file route.

This one accepts outgoing connections by using the dir=out switch, and enabling the exception via the action=allow switch

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

This one accepts incoming connections by using the dir=in switch, and enabling the exception via the action=allow switch

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

How to add a rule to Windows Firewall - DigitalCitizen

Online Tech Tips - Adjust Windows 10 Firewall Rules & Settings

Andradite answered 28/11, 2017 at 21:58 Comment(0)
L
4

An alternative to using PSOCK clusters is to use callr for running multiple R sessions in the background. The future.callr package (*) provides parallel backend for the future framework (disclaimer: I'm the author). It'll allow you to parallelize all OSes including Windows without going through sockets (and therefore no firewall). Example:

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

It also works with doFuture for the foreach framework. Example:

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

FYI, for PSOCK clusters, use plan(multisession).

(*) future.callr is on CRAN as of 2018-02-13 will be submitted to CRAN as soon as the developer's version of callr that it depends on is submitted to CRAN.

Linoleum answered 28/11, 2017 at 23:49 Comment(1)
Thank you. I hope it'll help making parallel programming in R a bit easier.Linoleum

© 2022 - 2024 — McMap. All rights reserved.