rizin/libr/socket/socket.c
yossizap ebfbf1a039 Fix r_socket_connect not using the given timeout in unix ##network
Connect isn't effected by r_socket_block_time since it can only change the
timeout of read(and not implemented, write) actions with setsockopt.
Also, connect may wait for an unspecified amount of time so we have to use
select.
2019-11-18 01:38:08 +01:00

876 lines
18 KiB
C

/* radare - LGPL - Copyright 2006-2019 - pancake */
/* must be included first because of winsock2.h and windows.h */
#include <r_socket.h>
#include <r_types.h>
#include <r_util.h>
#include <errno.h>
#if EMSCRIPTEN
#define NETWORK_DISABLED 1
#else
#define NETWORK_DISABLED 0
#endif
R_LIB_VERSION(r_socket);
#if NETWORK_DISABLED
/* no network */
R_API RSocket *r_socket_new (bool is_ssl) {
return NULL;
}
R_API bool r_socket_is_connected (RSocket *s) {
return false;
}
R_API bool r_socket_connect (RSocket *s, const char *host, const char *port, int proto, unsigned int timeout) {
return false;
}
R_API bool r_socket_spawn (RSocket *s, const char *cmd, unsigned int timeout) {
return -1;
}
R_API int r_socket_close_fd (RSocket *s) {
return -1;
}
R_API int r_socket_close (RSocket *s) {
return -1;
}
R_API int r_socket_free (RSocket *s) {
return -1;
}
R_API int r_socket_port_by_name(const char *name) {
return -1;
}
R_API bool r_socket_listen (RSocket *s, const char *port, const char *certfile) {
return false;
}
R_API RSocket *r_socket_accept(RSocket *s) {
return NULL;
}
R_API RSocket *r_socket_accept_timeout(RSocket *s, unsigned int timeout) {
return NULL;
}
R_API int r_socket_block_time (RSocket *s, int block, int sec, int usec) {
return -1;
}
R_API int r_socket_flush(RSocket *s) {
return -1;
}
R_API int r_socket_ready(RSocket *s, int secs, int usecs) {
return -1;
}
R_API char *r_socket_to_string(RSocket *s) {
return NULL;
}
R_API int r_socket_write(RSocket *s, void *buf, int len) {
return -1;
}
R_API int r_socket_puts(RSocket *s, char *buf) {
return -1;
}
R_API void r_socket_printf(RSocket *s, const char *fmt, ...) {
/* nothing here */
}
R_API int r_socket_read(RSocket *s, unsigned char *buf, int len) {
return -1;
}
R_API int r_socket_read_block(RSocket *s, unsigned char *buf, int len) {
return -1;
}
R_API int r_socket_gets(RSocket *s, char *buf, int size) {
return -1;
}
R_API RSocket *r_socket_new_from_fd (int fd) {
return NULL;
}
R_API ut8* r_socket_slurp(RSocket *s, int *len) {
return NULL;
}
#else
#if 0
winsock api notes
=================
close: closes the socket without flushing the data
WSACleanup: closes all network connections
#endif
#define BUFFER_SIZE 4096
R_API bool r_socket_is_connected(RSocket *s) {
#if __WINDOWS__
char buf[2];
r_socket_block_time (s, 0, 0, 0);
#ifdef _MSC_VER
int ret = recv (s->fd, (char*)&buf, 1, MSG_PEEK);
#else
ssize_t ret = recv (s->fd, (char*)&buf, 1, MSG_PEEK);
#endif
r_socket_block_time (s, 1, 0, 0);
return ret? true: false;
#else
char buf[2];
int ret = recv (s->fd, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
return ret? true: false;
#endif
}
#if __UNIX__
static bool __connect_unix(RSocket *s, const char *file) {
struct sockaddr_un addr;
int sock = socket (PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
free (s);
return false;
}
// TODO: set socket options
addr.sun_family = AF_UNIX;
strncpy (addr.sun_path, file, sizeof (addr.sun_path)-1);
if (connect (sock, (struct sockaddr *)&addr, sizeof(addr))==-1) {
close (sock);
free (s);
return false;
}
s->fd = sock;
s->is_ssl = false;
return true;
}
static bool __listen_unix (RSocket *s, const char *file) {
struct sockaddr_un unix_name;
int sock = socket (PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
return false;
}
// TODO: set socket options
unix_name.sun_family = AF_UNIX;
strncpy (unix_name.sun_path, file, sizeof (unix_name.sun_path)-1);
/* just to make sure there is no other socket file */
unlink (unix_name.sun_path);
if (bind (sock, (struct sockaddr *) &unix_name, sizeof (unix_name)) < 0) {
close (sock);
return false;
}
r_sys_signal (SIGPIPE, SIG_IGN);
/* change permissions */
if (chmod (unix_name.sun_path, 0777) != 0) {
close (sock);
return false;
}
if (listen (sock, 1)) {
close (sock);
return false;
}
s->fd = sock;
return true;
}
#endif
R_API RSocket *r_socket_new(bool is_ssl) {
RSocket *s = R_NEW0 (RSocket);
if (!s) {
return NULL;
}
s->is_ssl = is_ssl;
s->port = 0;
#if __UNIX_
r_sys_signal (SIGPIPE, SIG_IGN);
#endif
s->local = 0;
s->fd = R_INVALID_SOCKET;
#if HAVE_LIB_SSL
if (is_ssl) {
s->sfd = NULL;
s->ctx = NULL;
s->bio = NULL;
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
if (!SSL_library_init ()) {
r_socket_free (s);
return NULL;
}
SSL_load_error_strings ();
#endif
}
#endif
return s;
}
R_API bool r_socket_spawn(RSocket *s, const char *cmd, unsigned int timeout) {
// XXX TODO: dont use sockets, we can achieve the same with pipes
const int port = 2000 + r_num_rand (2000);
int childPid = r_sys_fork ();
if (childPid == 0) {
char *a = r_str_replace (strdup (cmd), "\\", "\\\\", true);
int res = r_sys_cmdf ("rarun2 system=\"%s\" listen=%d", a, port);
free (a);
#if 0
// TODO: use the api
char *profile = r_str_newf (
"system=%s\n"
"listen=%d\n", cmd, port);
RRunProfile *rp = r_run_new (profile);
r_run_start (rp);
r_run_free (rp);
free (profile);
#endif
if (res != 0) {
eprintf ("r_socket_spawn: rarun2 failed\n");
exit (1);
}
eprintf ("r_socket_spawn: %s is dead\n", cmd);
exit (0);
}
r_sys_sleep (1);
r_sys_usleep (timeout);
char aport[32];
sprintf (aport, "%d", port);
// redirect stdin/stdout/stderr
bool sock = r_socket_connect (s, "127.0.0.1", aport, R_SOCKET_PROTO_TCP, 2000);
if (!sock) {
return false;
}
#if __UNIX__
r_sys_sleep (4);
r_sys_usleep (timeout);
int status = 0;
int ret = waitpid (childPid, &status, WNOHANG);
if (ret != 0) {
r_socket_close (s);
return false;
}
#endif
return true;
}
R_API bool r_socket_connect(RSocket *s, const char *host, const char *port, int proto, unsigned int timeout) {
r_return_val_if_fail (s, false);
#if __WINDOWS__
struct sockaddr_in sa;
struct hostent *he;
WSADATA wsadata;
TIMEVAL Timeout;
Timeout.tv_sec = timeout;
Timeout.tv_usec = 0;
if (WSAStartup (MAKEWORD (1, 1), &wsadata) == SOCKET_ERROR) {
eprintf ("Error creating socket.");
return false;
}
s->fd = socket (AF_INET, SOCK_STREAM, 0);
if (s->fd == R_INVALID_SOCKET) {
return false;
}
unsigned long iMode = 1;
int iResult = ioctlsocket (s->fd, FIONBIO, &iMode);
if (iResult != NO_ERROR) {
eprintf ("ioctlsocket error: %d\n", iResult);
}
memset (&sa, 0, sizeof (sa));
sa.sin_family = AF_INET;
he = (struct hostent *)gethostbyname (host);
if (he == (struct hostent*)0) {
#ifdef _MSC_VER
closesocket (s->fd);
#else
close (s->fd);
#endif
return false;
}
sa.sin_addr = *((struct in_addr *)he->h_addr);
s->port = r_socket_port_by_name (port);
s->proto = proto;
sa.sin_port = htons (s->port);
if (!connect (s->fd, (const struct sockaddr*)&sa, sizeof (struct sockaddr))) {
#ifdef _MSC_VER
closesocket (s->fd);
#else
close (s->fd);
#endif
return false;
}
iMode = 0;
iResult = ioctlsocket (s->fd, FIONBIO, &iMode);
if (iResult != NO_ERROR) {
eprintf ("ioctlsocket error: %d\n", iResult);
}
if (timeout > 0) {
r_socket_block_time (s, 1, timeout, 0);
}
fd_set Write, Err;
FD_ZERO (&Write);
FD_ZERO (&Err);
FD_SET (s->fd, &Write);
FD_SET (s->fd, &Err);
select (0, NULL, &Write, &Err, &Timeout);
if (FD_ISSET (s->fd, &Write)) {
return true;
}
return false;
#elif __UNIX__
int ret;
struct addrinfo hints = {0};
struct addrinfo *res, *rp;
if (!proto) {
proto = R_SOCKET_PROTO_TCP;
}
r_sys_signal (SIGPIPE, SIG_IGN);
if (proto == R_SOCKET_PROTO_UNIX) {
if (!__connect_unix (s, host)) {
return false;
}
} else {
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_protocol = proto;
int gai = getaddrinfo (host, port, &hints, &res);
if (gai != 0) {
eprintf ("r_socket_connect: Error in getaddrinfo: %s (%s:%s)\n",
gai_strerror (gai), host, port);
return false;
}
for (rp = res; rp != NULL; rp = rp->ai_next) {
int flag = 1;
s->fd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (s->fd == -1) {
perror ("socket");
continue;
}
ret = setsockopt (s->fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof (flag));
if (ret < 0) {
perror ("setsockopt");
close (s->fd);
s->fd = -1;
continue;
}
r_socket_block_time (s, 0, 0, 0);
ret = connect (s->fd, rp->ai_addr, rp->ai_addrlen);
if (ret == 0) {
freeaddrinfo (res);
return true;
}
if (errno == EINPROGRESS) {
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
if ((ret = select (s->fd + 1, NULL, NULL, NULL, &tv)) != -1) {
if (r_socket_is_connected (s)) {
freeaddrinfo (res);
return true;
}
} else {
perror ("connect");
}
}
r_socket_close (s);
}
freeaddrinfo (res);
if (!rp) {
eprintf ("Could not resolve address '%s' or failed to connect\n", host);
return false;
}
}
#endif
#if HAVE_LIB_SSL
if (s->is_ssl) {
s->ctx = SSL_CTX_new (SSLv23_client_method ());
if (!s->ctx) {
r_socket_free (s);
return false;
}
s->sfd = SSL_new (s->ctx);
SSL_set_fd (s->sfd, s->fd);
if (SSL_connect (s->sfd) != 1) {
r_socket_free (s);
return false;
}
}
#endif
return true;
}
/* close the file descriptor associated with the RSocket s */
R_API int r_socket_close_fd(RSocket *s) {
#ifdef _MSC_VER
return s->fd != INVALID_SOCKET ? closesocket (s->fd) : false;
#else
return s->fd != -1 ? close (s->fd) : false;
#endif
}
/* shutdown the socket and close the file descriptor */
R_API int r_socket_close(RSocket *s) {
int ret = false;
if (!s) {
return false;
}
if (s->fd != R_INVALID_SOCKET) {
#if __UNIX__
shutdown (s->fd, SHUT_RDWR);
#endif
#if __WINDOWS__
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms740481(v=vs.85).aspx
shutdown (s->fd, SD_SEND);
if (r_socket_ready (s, 0, 250)) {
do {
char buf = 0;
ret = recv (s->fd, &buf, 1, 0);
} while (ret != 0 && ret != SOCKET_ERROR);
}
ret = closesocket (s->fd);
#else
ret = close (s->fd);
#endif
s->fd = R_INVALID_SOCKET;
}
#if HAVE_LIB_SSL
if (s->is_ssl && s->sfd) {
SSL_free (s->sfd);
s->sfd = NULL;
}
#endif
return ret;
}
/* shutdown the socket, close the file descriptor and free the RSocket */
R_API int r_socket_free(RSocket *s) {
int res = r_socket_close (s);
#if HAVE_LIB_SSL
if (s && s->is_ssl) {
if (s->sfd) {
SSL_free (s->sfd);
}
if (s->ctx) {
SSL_CTX_free (s->ctx);
}
}
#endif
free (s);
return res;
}
R_API int r_socket_port_by_name(const char *name) {
struct servent *p = getservbyname (name, "tcp");
return (p && p->s_port) ? ntohs (p->s_port) : r_num_get (NULL, name);
}
R_API bool r_socket_listen(RSocket *s, const char *port, const char *certfile) {
int optval = 1;
int ret;
struct linger linger = { 0 };
if (s->proto == R_SOCKET_PROTO_UNIX) {
#if __UNIX__
return __listen_unix (s, port);
#endif
return false;
}
if (r_sandbox_enable (0)) {
return false;
}
#if __WINDOWS__
WSADATA wsadata;
if (WSAStartup (MAKEWORD (1, 1), &wsadata) == SOCKET_ERROR) {
eprintf ("Error creating socket.");
return false;
}
#endif
if ((s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == R_INVALID_SOCKET) {
return false;
}
linger.l_onoff = 1;
linger.l_linger = 1;
ret = setsockopt (s->fd, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof (linger));
if (ret < 0) {
return false;
}
{ // fix close after write bug //
int x = 1500; // FORCE MTU
ret = setsockopt (s->fd, SOL_SOCKET, SO_SNDBUF, (void*)&x, sizeof (int));
if (ret < 0) {
return false;
}
}
ret = setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof optval);
if (ret < 0) {
return false;
}
memset (&s->sa, 0, sizeof (s->sa));
s->sa.sin_family = AF_INET;
s->sa.sin_addr.s_addr = htonl (s->local? INADDR_LOOPBACK: INADDR_ANY);
s->port = r_socket_port_by_name (port);
if (s->port < 1) {
return false;
}
s->sa.sin_port = htons (s->port); // TODO honor etc/services
if (bind (s->fd, (struct sockaddr *)&s->sa, sizeof (s->sa)) < 0) {
r_sys_perror ("bind");
#ifdef _MSC_VER
closesocket (s->fd);
#else
close (s->fd);
#endif
return false;
}
#if __UNIX__
r_sys_signal (SIGPIPE, SIG_IGN);
#endif
if (listen (s->fd, 32) < 0) {
#ifdef _MSC_VER
closesocket (s->fd);
#else
close (s->fd);
#endif
return false;
}
#if HAVE_LIB_SSL
if (s->is_ssl) {
s->ctx = SSL_CTX_new (SSLv23_method ());
if (!s->ctx) {
r_socket_free (s);
return false;
}
if (!SSL_CTX_use_certificate_chain_file (s->ctx, certfile)) {
r_socket_free (s);
return false;
}
if (!SSL_CTX_use_PrivateKey_file (s->ctx, certfile, SSL_FILETYPE_PEM)) {
r_socket_free (s);
return false;
}
SSL_CTX_set_verify_depth (s->ctx, 1);
}
#endif
return true;
}
R_API RSocket *r_socket_accept(RSocket *s) {
RSocket *sock;
socklen_t salen = sizeof (s->sa);
if (!s) {
return NULL;
}
sock = R_NEW0 (RSocket);
if (!sock) {
return NULL;
}
//signal (SIGPIPE, SIG_DFL);
sock->fd = accept (s->fd, (struct sockaddr *)&s->sa, &salen);
if (sock->fd == R_INVALID_SOCKET) {
if (errno != EWOULDBLOCK) {
// not just a timeout
r_sys_perror ("accept");
}
free (sock);
return NULL;
}
#if HAVE_LIB_SSL
sock->is_ssl = s->is_ssl;
if (sock->is_ssl) {
sock->sfd = NULL;
sock->ctx = NULL;
sock->bio = NULL;
BIO *sbio = BIO_new_socket (sock->fd, BIO_NOCLOSE);
sock->sfd = SSL_new (s->ctx);
SSL_set_bio (sock->sfd, sbio, sbio);
if (SSL_accept (sock->sfd) <= 0) {
r_socket_free (sock);
return NULL;
}
sock->bio = BIO_new (BIO_f_buffer ());
sbio = BIO_new (BIO_f_ssl ());
BIO_set_ssl (sbio, sock->sfd, BIO_CLOSE);
BIO_push (sock->bio, sbio);
}
#else
sock->is_ssl = 0;
#endif
return sock;
}
R_API RSocket *r_socket_accept_timeout(RSocket *s, unsigned int timeout) {
fd_set read_fds;
fd_set except_fds;
FD_ZERO (&read_fds);
FD_SET (s->fd, &read_fds);
FD_ZERO (&except_fds);
FD_SET (s->fd, &except_fds);
struct timeval t;
t.tv_sec = timeout;
t.tv_usec = 0;
int r = select (s->fd + 1, &read_fds, NULL, &except_fds, &t);
if(r < 0) {
perror ("select");
} else if (r > 0 && FD_ISSET (s->fd, &read_fds)) {
return r_socket_accept (s);
}
return NULL;
}
// Only applies to read in UNIX
R_API int r_socket_block_time(RSocket *s, int block, int sec, int usec) {
#if __UNIX__
int ret, flags;
#endif
if (!s) {
return false;
}
#if __UNIX__
flags = fcntl (s->fd, F_GETFL, 0);
if (flags < 0) {
return false;
}
ret = fcntl (s->fd, F_SETFL, block?
(flags & ~O_NONBLOCK):
(flags | O_NONBLOCK));
if (ret < 0) {
return false;
}
#elif __WINDOWS__
ioctlsocket (s->fd, FIONBIO, (u_long FAR*)&block);
#endif
if (sec > 0 || usec > 0) {
struct timeval tv = {0};
tv.tv_sec = sec;
tv.tv_usec = usec;
if (setsockopt (s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof (tv)) < 0) {
return false;
}
}
return true;
}
R_API int r_socket_flush(RSocket *s) {
#if HAVE_LIB_SSL
if (s->is_ssl && s->bio) {
return BIO_flush (s->bio);
}
#endif
return true;
}
// XXX: rewrite it to use select //
/* waits secs until new data is received. */
/* returns -1 on error, 0 is false, 1 is true */
R_API int r_socket_ready(RSocket *s, int secs, int usecs) {
#if __UNIX__
//int msecs = (1000 * secs) + (usecs / 1000);
int msecs = (usecs / 1000);
struct pollfd fds[1];
fds[0].fd = s->fd;
fds[0].events = POLLIN | POLLPRI;
fds[0].revents = POLLNVAL | POLLHUP | POLLERR;
return poll ((struct pollfd *)&fds, 1, msecs);
#elif __WINDOWS__
fd_set rfds;
struct timeval tv;
if (s->fd == R_INVALID_SOCKET) {
return -1;
}
FD_ZERO (&rfds);
FD_SET (s->fd, &rfds);
tv.tv_sec = secs;
tv.tv_usec = usecs;
return select (s->fd + 1, &rfds, NULL, NULL, &tv);
#else
return true; /* always ready if unknown */
#endif
}
R_API char *r_socket_to_string(RSocket *s) {
#if __WINDOWS__
return r_str_newf ("fd%d", (int)(size_t)s->fd);
#elif __UNIX__
char *str = NULL;
struct sockaddr sa;
socklen_t sl = sizeof (sa);
memset (&sa, 0, sizeof (sa));
if (!getpeername (s->fd, &sa, &sl)) {
struct sockaddr_in *sain = (struct sockaddr_in*) &sa;
ut8 *a = (ut8*) &(sain->sin_addr);
if ((str = malloc (32))) {
sprintf (str, "%d.%d.%d.%d:%d",
a[0], a[1], a[2], a[3], ntohs (sain->sin_port));
}
} else {
eprintf ("getperrname: failed\n"); //r_sys_perror ("getpeername");
}
return str;
#else
return NULL;
#endif
}
/* Read/Write functions */
R_API int r_socket_write(RSocket *s, void *buf, int len) {
int ret, delta = 0;
#if __UNIX__
r_sys_signal (SIGPIPE, SIG_IGN);
#endif
for (;;) {
int b = 1500; //65536; // Use MTU 1500?
if (b > len) {
b = len;
}
#if HAVE_LIB_SSL
if (s->is_ssl) {
if (s->bio) {
ret = BIO_write (s->bio, buf+delta, b);
} else {
ret = SSL_write (s->sfd, buf + delta, b);
}
} else
#endif
{
ret = send (s->fd, (char *)buf+delta, b, 0);
}
//if (ret == 0) return -1;
if (ret < 1) {
break;
}
if (ret == len) {
return len;
}
delta += ret;
len -= ret;
}
return (ret == -1)? -1 : delta;
}
R_API int r_socket_puts(RSocket *s, char *buf) {
return r_socket_write (s, buf, strlen (buf));
}
R_API void r_socket_printf(RSocket *s, const char *fmt, ...) {
char buf[BUFFER_SIZE];
va_list ap;
if (s->fd != R_INVALID_SOCKET) {
va_start (ap, fmt);
vsnprintf (buf, BUFFER_SIZE, fmt, ap);
r_socket_write (s, buf, strlen (buf));
va_end (ap);
}
}
R_API int r_socket_read(RSocket *s, unsigned char *buf, int len) {
if (!s) {
return -1;
}
#if HAVE_LIB_SSL
if (s->is_ssl) {
if (s->bio) {
return BIO_read (s->bio, buf, len);
}
return SSL_read (s->sfd, buf, len);
}
#endif
#if __WINDOWS__
rep:
{
int ret = recv (s->fd, (void *)buf, len, 0);
if (ret == -1) {
goto rep;
}
return ret;
}
#else
return read (s->fd, buf, len);
#endif
}
R_API int r_socket_read_block(RSocket *s, unsigned char *buf, int len) {
int r, ret = 0;
for (ret = 0; ret < len; ) {
r = r_socket_read (s, buf+ret, len-ret);
if (r < 1) {
break;
}
ret += r;
}
return ret;
}
R_API int r_socket_gets(RSocket *s, char *buf, int size) {
int i = 0;
int ret = 0;
if (s->fd == R_INVALID_SOCKET) {
return -1;
}
while (i < size) {
ret = r_socket_read (s, (ut8 *)buf + i, 1);
if (ret == 0) {
if (i > 0) {
return i;
}
return -1;
}
if (ret < 0) {
r_socket_close (s);
return i == 0? -1: i;
}
if (buf[i] == '\r' || buf[i] == '\n') {
buf[i] = 0;
break;
}
i += ret;
}
buf[i]='\0';
return i;
}
R_API RSocket *r_socket_new_from_fd (int fd) {
RSocket *s = R_NEW0 (RSocket);
if (s) {
s->fd = fd;
}
return s;
}
R_API ut8* r_socket_slurp(RSocket *s, int *len) {
int blockSize = 4096;
ut8 *ptr, *buf = malloc (blockSize);
int copied = 0;
if (len) {
*len = 0;
}
for (;;) {
int rc = r_socket_read (s, buf + copied, blockSize);
if (rc > 0) {
copied += rc;
}
ptr = realloc (buf, copied + blockSize);
if (!ptr) {
break;
}
buf = ptr;
if (rc < 1) {
break;
}
}
if (copied == 0) {
R_FREE (buf);
}
if (len) {
*len = copied;
}
return buf;
}
#endif // EMSCRIPTEN