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.
Conclusion. Localhost refused to connect is a type of network connection error you might encounter when working on a project on your local web server. It is triggered if the firewall wrongly blocks your server or you're using the wrong port.
To do this, open the "Windows Firewall" page in Control Panel and click "Restore defaults". Save this answer. Show activity on this post. I ran some program and it popped up Windows Firewall notification.
The firewall doesn't block/inspect the localhost/loopback address (127.0. 0.1) because it's your computer. So since the target and source are the same, there's really nothing to firewall. Unless one has a transparent HTTP proxy operating on 127.0.
My sense is that the easiest way to navigate this problem is to add a firewall rule as part of the application install process.
I provide an example script below, and I hope this helps point you in the right direction.
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.
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.
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.
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()
(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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With