'localhost' connection without firewall popup
(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.
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
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
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()