radare2/libr/socket/socket.c
Anton Kochkov 7e79b198b1 Fix some more Coverity issues
Fix CID 1171365
Fix CID 1211169
Fix CID 1230038
Fix CID 1295063, 1295062, 1295061
Fix CID 1295070
Fix CID 1295068
Fix CID 12950067
2015-04-19 04:53:08 +03:00

608 lines
13 KiB
C

/* radare - LGPL - Copyright 2006-2014 - pancake */
#include <errno.h>
#include <r_types.h>
#include <r_util.h>
#include <r_socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
R_LIB_VERSION(r_socket);
#if EMSCRIPTEN
/* no network */
R_API RSocket *r_socket_new (int is_ssl) { return NULL; }
#endif
#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 int r_socket_is_connected (RSocket *s) {
#if __WINDOWS__
char buf[2];
r_socket_block_time (s, 0, 0);
ssize_t ret = recv (s->fd, (char*)&buf, 1, MSG_PEEK);
r_socket_block_time (s, 1, 0);
return ret? R_TRUE: R_FALSE;
#else
char buf[2];
int ret = recv (s->fd, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
return ret? R_TRUE: R_FALSE;
#endif
}
#if __UNIX__
static int r_socket_unix_connect(RSocket *s, const char *file) {
struct sockaddr_un addr;
int sock = socket (PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
free (s);
return R_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 R_FALSE;
}
s->fd = sock;
s->is_ssl = R_FALSE;
return R_TRUE;
}
R_API int r_socket_unix_listen (RSocket *s, const char *file) {
struct sockaddr_un unix_name;
int sock = socket (PF_UNIX, SOCK_STREAM, 0);
if (sock <0)
return R_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 R_FALSE;
}
signal (SIGPIPE, SIG_IGN);
/* change permissions */
if (chmod (unix_name.sun_path, 0777) != 0) {
close (sock);
return R_FALSE;
}
if (listen (sock, 1)) {
close (sock);
return R_FALSE;
}
s->fd = sock;
return R_TRUE;
}
#endif
R_API RSocket *r_socket_new (int is_ssl) {
RSocket *s = R_NEW (RSocket);
s->is_ssl = is_ssl;
s->port = 0;
#if __UNIX_
signal (SIGPIPE, SIG_IGN);
#endif
s->local = 0;
s->fd = -1;
#if HAVE_LIB_SSL
if (is_ssl) {
s->sfd = NULL;
s->ctx = NULL;
s->bio = NULL;
if (!SSL_library_init ()) {
r_socket_free (s);
return NULL;
}
SSL_load_error_strings ();
}
#endif
return s;
}
R_API int r_socket_connect (RSocket *s, const char *host, const char *port, int proto, int timeout) {
#if __WINDOWS__
struct sockaddr_in sa;
struct hostent *he;
WSADATA wsadata;
if (WSAStartup (MAKEWORD (1, 1), &wsadata) == SOCKET_ERROR) {
eprintf ("Error creating socket.");
return R_FALSE;
}
s->fd = socket (AF_INET, SOCK_STREAM, 0);
if (s->fd == -1)
return R_FALSE;
memset (&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
he = (struct hostent *)gethostbyname (host);
if (he == (struct hostent*)0) {
close (s->fd);
return R_FALSE;
}
sa.sin_addr = *((struct in_addr *)he->h_addr);
s->port = r_socket_port_by_name (port);
sa.sin_port = htons (s->port);
#warning TODO: implement connect timeout on w32
if (connect (s->fd, (const struct sockaddr*)&sa, sizeof (struct sockaddr))) {
close (s->fd);
return R_FALSE;
}
return R_TRUE;
#elif __UNIX__
if (!proto) proto = R_SOCKET_PROTO_TCP;
int gai, ret;
struct addrinfo hints, *res, *rp;
signal (SIGPIPE, SIG_IGN);
if (proto == R_SOCKET_PROTO_UNIX) {
if (!r_socket_unix_connect (s, host))
return R_FALSE;
} else {
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_protocol = proto;
gai = getaddrinfo (host, port, &hints, &res);
if (gai != 0) {
//eprintf ("Error in getaddrinfo: %s\n", gai_strerror (gai));
return R_FALSE;
}
for (rp = res; rp != NULL; rp = rp->ai_next) {
s->fd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (s->fd == -1)
continue;
if (timeout>0)
r_socket_block_time (s, 1, timeout);
//fcntl (s->fd, F_SETFL, O_NONBLOCK, 1);
ret = connect (s->fd, rp->ai_addr, rp->ai_addrlen);
if (timeout<1) {
if (ret == -1) {
close (s->fd);
s->fd = -1;
freeaddrinfo (res);
return R_FALSE;
}
freeaddrinfo (res);
return R_TRUE;
}
if (ret<0) {
close (s->fd);
s->fd = -1;
continue;
}
if (timeout>0) {
struct timeval tv;
fd_set fdset, errset;
FD_ZERO (&fdset);
FD_SET (s->fd, &fdset);
tv.tv_sec = 1; //timeout;
tv.tv_usec = 0;
if (r_socket_is_connected (s)) {
freeaddrinfo (res);
return R_TRUE;
}
if (select (s->fd + 1, NULL, NULL, &errset, &tv) == 1) {
int so_error;
socklen_t len = sizeof so_error;
ret = getsockopt (s->fd, SOL_SOCKET,
SO_ERROR, &so_error, &len);
// fcntl (s->fd, F_SETFL, O_NONBLOCK, 0);
// r_socket_block_time (s, 0, 0);
freeaddrinfo (res);
return R_TRUE;
} else {
// freeaddrinfo (res);
close (s->fd);
s->fd = -1;
continue;
// return R_FALSE;
}
}
close (s->fd);
s->fd = -1;
}
freeaddrinfo (res);
if (rp == NULL) {
eprintf ("Could not resolve address '%s'\n", host);
return R_FALSE;
}
}
#endif
#if HAVE_LIB_SSL
if (s->is_ssl) {
s->ctx = SSL_CTX_new (SSLv23_client_method ());
if (s->ctx == NULL) {
r_socket_free (s);
return R_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 R_FALSE;
}
}
#endif
return R_TRUE;
}
R_API int r_socket_close (RSocket *s) {
int ret = R_FALSE;
if (!s) return R_FALSE;
if (s->fd != -1) {
#if __UNIX__
shutdown (s->fd, SHUT_RDWR);
#endif
ret = close (s->fd);
}
#if HAVE_LIB_SSL
if (s->is_ssl && s->sfd) {
SSL_free (s->sfd);
s->sfd = NULL;
}
#endif
return ret;
}
R_API int r_socket_free (RSocket *s) {
int res = r_socket_close (s);
#if HAVE_LIB_SSL
if (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");
if (p && p->s_port)
return ntohs (p->s_port);
return r_num_get (NULL, name);
}
R_API int r_socket_listen (RSocket *s, const char *port, const char *certfile) {
#if __UNIX__
int optval = 1;
int ret;
struct linger linger = { 0 };
#endif
if (r_sandbox_enable (0))
return R_FALSE;
#if __WINDOWS__
WSADATA wsadata;
if (WSAStartup (MAKEWORD (1, 1), &wsadata) == SOCKET_ERROR) {
eprintf ("Error creating socket.");
return R_FALSE;
}
#endif
if ((s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP))<0)
return R_FALSE;
#if __UNIX__
linger.l_onoff = 1;
linger.l_linger = 1;
ret = setsockopt (s->fd, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof (linger));
if (ret < 0)
return R_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 R_FALSE;
}
ret = setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof optval);
if (ret < 0)
return R_FALSE;
#endif
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 R_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");
close (s->fd);
return R_FALSE;
}
#if __UNIX_
signal (SIGPIPE, SIG_IGN);
#endif
if (listen (s->fd, 32) < 0) {
close (s->fd);
return R_FALSE;
}
#if HAVE_LIB_SSL
if (s->is_ssl) {
s->ctx = SSL_CTX_new (SSLv23_method ());
if (s->ctx == NULL) {
r_socket_free (s);
return R_FALSE;
}
if (!SSL_CTX_use_certificate_chain_file (s->ctx, certfile)) {
r_socket_free (s);
return R_FALSE;
}
if (!SSL_CTX_use_PrivateKey_file (s->ctx, certfile, SSL_FILETYPE_PEM)) {
r_socket_free (s);
return R_FALSE;
}
SSL_CTX_set_verify_depth (s->ctx, 1);
}
#endif
return R_TRUE;
}
R_API RSocket *r_socket_accept(RSocket *s) {
RSocket *sock;
socklen_t salen = sizeof (s->sa);
if (!s) return NULL;
sock = R_NEW (RSocket);
if (!sock) return NULL;
//signal (SIGPIPE, SIG_DFL);
sock->fd = accept (s->fd, (struct sockaddr *)&s->sa, &salen);
if (sock->fd == -1) {
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 int r_socket_block_time (RSocket *s, int block, int sec) {
#if __UNIX__
int ret, flags;
#endif
if (!s) return R_FALSE;
#if __UNIX__
flags = fcntl (s->fd, F_GETFL, 0);
if (flags < 0)
return R_FALSE;
ret = fcntl (s->fd, F_SETFL, block?
(flags & ~O_NONBLOCK):
(flags | O_NONBLOCK));
if (ret < 0)
return R_FALSE;
#elif __WINDOWS__
// HACK: nonblocking io on w32 behaves strange
return R_TRUE;
ioctlsocket (s->fd, FIONBIO, (u_long FAR*)&block);
#endif
if (sec > 0) {
struct timeval tv = {0};
tv.tv_sec = sec;
tv.tv_usec = 0;
if (setsockopt (s->fd, SOL_SOCKET, SO_RCVTIMEO,
(char *)&tv, sizeof (tv)) < 0)
return R_FALSE;
}
return R_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 R_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 = 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__
return 1;
#if XXX_THIS_IS_NOT_WORKING_WELL
fd_set rfds;
struct timeval tv;
if (s->fd==-1)
return -1;
FD_ZERO (&rfds);
FD_SET (s->fd, &rfds);
tv.tv_sec = secs;
tv.tv_usec = usecs;
if (select (s->fd+1, &rfds, NULL, NULL, &tv) == -1)
return -1;
return FD_ISSET (0, &rfds);
#endif
#else
return R_TRUE; /* always ready if unknown */
#endif
}
R_API char *r_socket_to_string(RSocket *s) {
#if __WINDOWS__
char *str = malloc (32);
snprintf (str, 31, "fd%d", s->fd);
return str;
#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__
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, buf+delta, b, 0);
}
//if (ret == 0) return -1;
if (ret<1) break;
if (ret == len)
return len;
r_sys_usleep (100); // take breath, wtf
delta += ret;
len -= ret;
}
if (ret == -1)
return -1;
return 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 >= 0) {
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 != len)
return 0;
//r_sys_perror ("recv");
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) //==-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 == -1)
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);
s->fd = fd;
return s;
}