/* radare - LGPL - Copyright 2006-2015 - pancake */ #include #include #include #include #include #include #include #include #include #include #include #include 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__ && !defined(__CYGWIN__) && !defined(__MINGW64__) 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? true: false; #else char buf[2]; int ret = recv (s->fd, &buf, 1, MSG_PEEK | MSG_DONTWAIT); return ret? true: false; #endif } #if __UNIX__ || defined(__CYGWIN__) 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 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; } 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 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; } 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 (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, unsigned int timeout) { #if __WINDOWS__ && !defined(__CYGWIN__) && !defined(__MINGW64__) struct sockaddr_in sa; struct hostent *he; WSADATA wsadata; 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 == -1) return 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 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 false; } return true; #elif __UNIX__ || defined(__CYGWIN__) 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 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 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 == 0 && ret == 0) { freeaddrinfo (res); return true; } else if (ret == 0 /* || nonblocking */) { 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 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); if (ret == 0 && so_error == 0) { //fcntl (s->fd, F_SETFL, O_NONBLOCK, 0); //r_socket_block_time (s, 0, 0); freeaddrinfo (res); return true; } } } close (s->fd); s->fd = -1; } freeaddrinfo (res); if (rp == NULL) { eprintf ("Could not resolve address '%s'\n", host); return 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 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) { return s->fd != -1 ? close (s->fd) : false; } /* 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 != -1) { #if __UNIX__ || defined(__CYGWIN__) 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; } /* 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->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__ || defined(__CYGWIN__) int optval = 1; int ret; struct linger linger = { 0 }; #endif if (r_sandbox_enable (0)) return false; #if __WINDOWS__ && !defined(__CYGWIN__) && !defined(__MINGW64__) 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))<0) return false; #if __UNIX__ || defined(__CYGWIN__) 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; #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 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 false; } #if __UNIX__ || defined(__CYGWIN__) signal (SIGPIPE, SIG_IGN); #endif if (listen (s->fd, 32) < 0) { close (s->fd); return 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 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_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__ || defined(__CYGWIN__) int ret, flags; #endif if (!s) return false; #if __UNIX__ || defined(__CYGWIN__) 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__ && !defined(__CYGWIN__) && !defined(__MINGW64__) // HACK: nonblocking io on w32 behaves strange return 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 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__ || defined(__CYGWIN__) 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__ && !defined(__CYGWIN__) && !defined(__MINGW64__) 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 true; /* always ready if unknown */ #endif } R_API char *r_socket_to_string(RSocket *s) { #if __WINDOWS__ && !defined(__CYGWIN__) && !defined(__MINGW64__) char *str = malloc (32); snprintf (str, 31, "fd%d", s->fd); return str; #elif __UNIX__ || defined(__CYGWIN__) 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__ || defined(__CYGWIN__) 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; 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__ && !defined(__CYGWIN__) && !defined(__MINGW64__) 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;retfd == -1) return -1; while (i0) 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; }