| /* |
| * R : A Computer Language for Statistical Data Analysis |
| |
| * Copyright (C) 1998-2015 The R Core Team |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, a copy is available at |
| * https://www.R-project.org/Licenses/ |
| */ |
| |
| /* <UTF8> chars are only handled as a whole */ |
| |
| /* 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. */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <signal.h> |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| |
| #if defined(Win32) |
| # define FD_SETSIZE 1024 |
| # include <winsock2.h> |
| # include <io.h> |
| #else |
| # ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| # endif |
| # include <netdb.h> |
| # include <sys/socket.h> |
| # include <netinet/in.h> |
| #endif |
| |
| #include <R_ext/Error.h> |
| #include "sock.h" |
| |
| #if defined(__hpux) |
| extern int h_errno; /* HP-UX 9.05 forgets to declare this in netdb.h */ |
| #endif |
| |
| #define MAXBACKLOG 5 |
| |
| static int Sock_error(Sock_error_t perr, int e, int he) |
| { |
| if (perr != NULL) { |
| perr->error = e; |
| perr->h_error = he; |
| } |
| return -1; |
| } |
| |
| /* <FIXME> is this classic Mac OS? */ |
| #ifdef MACINTOSH |
| extern void __sinit(void); |
| extern int __initialize (void *ignoredParameter); |
| int __initialize(void *ignoredParameter) { |
| __sinit(); |
| return(0); |
| } |
| #endif |
| /* </FIXME> */ |
| |
| /* Initialize the socket services */ |
| int Sock_init() |
| { |
| #if defined(Win32) |
| WSADATA wsaData; |
| WORD wVers = MAKEWORD(1, 1); |
| if (WSAStartup(wVers, &wsaData) != 0) |
| return 1; |
| #elif defined(MACINTOSH) |
| GUSISetup(GUSIwithInternetSockets); |
| #elif defined(SIGPIPE) |
| struct sigaction act; |
| if (sigaction(SIGPIPE, (struct sigaction *)NULL, &act) < 0) |
| return 1; |
| if (act.sa_handler == SIG_DFL) { |
| act.sa_handler = SIG_IGN; |
| if (sigaction(SIGPIPE, &act, (struct sigaction *)NULL) < 0) |
| return 1; |
| } |
| #endif |
| return 0; |
| } |
| |
| /* 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); |
| |
| #ifndef Win32 |
| /* According to Stephens (1998) "Unix Network Programming, Vol 1", |
| pp. 194 on UNIX we need to set the SO_REUSEADDR socket option |
| if we want to be able to have a server create several |
| connections that are open simultaneously. If this option is |
| not set, the call to `bind' will fail when attempting to open |
| another server socket on a given port if a connection made to |
| that port is still alive. This is not an issue if each socket |
| connection is handled and terminated before the next server |
| socket is created. Stephens recommends setting SO_REUSEADDR on |
| all servers. Even with this option set it remains impossible |
| to establish two simultaneous servers on the same IPADDR/port |
| combination. Tcl 8.3 uses the bit of code used here in |
| tclUnixChan.c, so it should work on most UNIX platforms. |
| |
| Unfortunately things are different on Windows. According to |
| Quinn and Shute (1996) "Windows Sockets Network Programming", |
| pp. 305, Windows sockets are quite happy to allow two servers |
| to use the same IPADDR/port, with unpredictable results, if |
| SO_REUSEADDR is set. So setting this option on Windows is not |
| a good idea. It is unclear whether it is possible on WIndows |
| to establish a new server socket while a connection from a |
| previous server socket is still active. |
| |
| This would be less of an issue, but would not entirely |
| disappear as an issue. if the R interface separated the |
| `socket'/`bind'/`listen' part of setting up a server socket, |
| which is only needed once per server instance, from the |
| `accept' part, which is needed for each connection. LT */ |
| { |
| int val = 1; |
| setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)); |
| } |
| #endif |
| |
| if ((bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0) || |
| (listen(sock, MAXBACKLOG) < 0)) { |
| close(sock); |
| return Sock_error(perr, errno, 0); |
| } |
| return sock; |
| } |
| |
| /* listen on a socket, return name of connecting host in cname */ |
| int Sock_listen(int fd, char *cname, int buflen, Sock_error_t perr) |
| { |
| struct sockaddr_in net_client; |
| R_SOCKLEN_T len = sizeof(struct sockaddr); |
| int retval; |
| struct hostent *hostptr; |
| |
| do |
| retval = accept(fd, (struct sockaddr *)(&net_client), &len); |
| while (retval == -1 && errno == EINTR); |
| if (retval == -1) |
| return Sock_error(perr, errno, 0); |
| |
| if (cname != NULL && buflen > 0) { |
| size_t nlen; |
| const char *name; |
| struct in_addr *iaddr = &(net_client.sin_addr); |
| hostptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), |
| AF_INET); |
| name = (hostptr == NULL) ? "unknown" : hostptr->h_name; |
| nlen = strlen(name); |
| if (buflen < nlen + 1) |
| nlen = buflen - 1; |
| strncpy(cname, name, nlen); |
| cname[nlen] = 0; |
| } |
| return retval; |
| } |
| |
| /* open and connect to a socket */ |
| int Sock_connect(Sock_port_t port, char *sname, Sock_error_t perr) |
| { |
| struct sockaddr_in server; |
| struct hostent *hp; |
| int sock; |
| int retval; |
| |
| if (! (hp = gethostbyname(sname)) |
| || (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
| return Sock_error(perr, errno, h_errno); |
| |
| memcpy((char *)&server.sin_addr, hp->h_addr_list[0], hp->h_length); |
| server.sin_port = htons((short)port); |
| server.sin_family = AF_INET; |
| |
| do |
| retval = connect(sock, (struct sockaddr *) &server, sizeof(server)); |
| while (retval == -1 && errno == EINTR); |
| if (retval == -1) { |
| Sock_error(perr, errno, 0); |
| #ifdef Win32 |
| closesocket(sock); |
| #else |
| close(sock); |
| #endif |
| return -1; |
| } |
| return sock; |
| } |
| |
| /* close a socket */ |
| int Sock_close(int fd, Sock_error_t perr) |
| { |
| #ifdef Win32 |
| if (closesocket(fd) != 0) |
| return Sock_error(perr, WSAENOTSOCK, 0); |
| #else |
| if (close(fd) < 0) |
| return Sock_error(perr, errno, 0); |
| #endif |
| else |
| return 0; |
| } |
| |
| /* read from a socket */ |
| ssize_t Sock_read(int fd, void *buf, size_t size, Sock_error_t perr) |
| { |
| ssize_t retval; |
| do |
| retval = recv(fd, buf, size, 0); |
| while (retval == -1 && errno == EINTR); |
| if (retval == -1) |
| return Sock_error(perr, errno, 0); |
| else |
| return retval; |
| } |
| |
| /* write to a socket */ |
| ssize_t Sock_write(int fd, const void *buf, size_t size, Sock_error_t perr) |
| { |
| ssize_t retval; |
| do |
| retval = send(fd, buf, size, 0); |
| while (retval == -1 && errno == EINTR); |
| if (retval == -1) |
| return Sock_error(perr, errno, 0); |
| else |
| return retval; |
| } |