summaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
authorSimon Rettberg2015-11-24 12:30:46 +0100
committerSimon Rettberg2015-11-24 12:30:46 +0100
commit7b51c287a60d2f202fb131eeed9d1bf19b65a7a3 (patch)
tree031573c708b50aeafe9a6fe6f0992b3ae6456e7c /src/shared
parent[SERVER] Fix race condition potentially leading to use after release (diff)
downloaddnbd3-7b51c287a60d2f202fb131eeed9d1bf19b65a7a3.tar.gz
dnbd3-7b51c287a60d2f202fb131eeed9d1bf19b65a7a3.tar.xz
dnbd3-7b51c287a60d2f202fb131eeed9d1bf19b65a7a3.zip
[FUSE] Mid-refactoring, does not compile
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/signal.h1
-rw-r--r--src/shared/sockhelper.c287
-rw-r--r--src/shared/sockhelper.h93
3 files changed, 380 insertions, 1 deletions
diff --git a/src/shared/signal.h b/src/shared/signal.h
index 0e2f85f..6fd2765 100644
--- a/src/shared/signal.h
+++ b/src/shared/signal.h
@@ -46,4 +46,3 @@ int signal_clear(int signalFd);
void signal_close(int signalFd);
#endif
-
diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c
new file mode 100644
index 0000000..0b7a1db
--- /dev/null
+++ b/src/shared/sockhelper.c
@@ -0,0 +1,287 @@
+#include "sockhelper.h"
+//#include "log.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h> // inet_ntop
+#include <netdb.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdlib.h>
+
+#define MAXLISTEN 20
+
+struct _poll_list {
+ int count;
+ struct pollfd entry[MAXLISTEN];
+};
+
+int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const int rw_ms)
+{
+ // TODO: Move out of here, this unit should contain general socket functions
+ // TODO: Rework the dnbd3_host_t to not use AF_* as these could theoretically change
+ // TODO: Abstract away from sockaddr_in* like the rest of the functions here do,
+ // so WITH_IPV6 can finally be removed as everything is transparent.
+ struct sockaddr_storage ss;
+ int proto, addrlen;
+ memset( &ss, 0, sizeof ss );
+ if ( addr->type == AF_INET ) {
+ // Set host (IPv4)
+ struct sockaddr_in *addr4 = (struct sockaddr_in*)&ss;
+ addr4->sin_family = AF_INET;
+ memcpy( &addr4->sin_addr, addr->addr, 4 );
+ addr4->sin_port = addr->port;
+ proto = PF_INET;
+ addrlen = sizeof *addr4;
+ }
+#ifdef WITH_IPV6
+ else if ( addr->type == AF_INET6 ) {
+ // Set host (IPv6)
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)&ss;
+ addr6->sin6_family = AF_INET6;
+ memcpy( &addr6->sin6_addr, addr->addr, 16 );
+ addr6->sin6_port = addr->port;
+ proto = PF_INET6;
+ addrlen = sizeof *addr6;
+ }
+#endif
+ else {
+ logadd( LOG_DEBUG1, "Unsupported address type: %d\n", (int)addr->type );
+ return -1;
+ }
+ int client_sock = socket( proto, SOCK_STREAM, IPPROTO_TCP );
+ if ( client_sock == -1 ) return -1;
+ // Apply connect timeout
+ sock_setTimeout( client_sock, connect_ms );
+ if ( connect( client_sock, (struct sockaddr *)&ss, addrlen ) == -1 ) {
+ close( client_sock );
+ return -1;
+ }
+ // Apply read/write timeout
+ sock_setTimeout( client_sock, rw_ms );
+ return client_sock;
+}
+
+// TODO: Pretty much same as in server/*
+int sock_resolveToDnbd3Host(const char * const address, dnbd3_host_t * const dest, const int count)
+{
+ if ( count <= 0 )
+ return 0;
+ const int on = 1;
+ int sock = -1;
+ struct addrinfo hints, *res, *ptr;
+ char bufferAddr[100], bufferPort[6];
+ char *addr = bufferAddr;
+ const char *portStr = NULL;
+ int addCount = 0;
+
+ // See if we have a port
+ snprintf( bufferAddr, sizeof bufferAddr, "%s", address );
+ const char *c1, *c2;
+ c1 = strchr( addr, ':' );
+ if ( c1 != NULL ) {
+ c2 = strchr( c1 + 1, ':' );
+ if ( c2 == NULL ) {
+ *c1 = '\0';
+ portStr = c1 + 1;
+ } else if ( *addr == '[' ) {
+ // IPv6 - support [1:2::3]:123
+ do {
+ c1 = strchr( c2 + 1, ':' );
+ if ( c1 != NULL ) c2 = c1;
+ } while ( c1 != NULL );
+ if ( c2[-1] == ']' ) {
+ c2[-1] = '\0';
+ *c2 = '\0';
+ addr += 1;
+ portStr = c2 + 1;
+ }
+ }
+ }
+ if ( portStr == NULL ) {
+ portStr = bufferPort;
+ snprintf( bufferPort, sizeof bufferPort, "%d", (int)PORT );
+ }
+
+ // Set hints for local addresses.
+ memset( &hints, 0, sizeof( hints ) );
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if ( getaddrinfo( addr, portStr, &hints, &res ) != 0 || res == NULL ) {
+ return 0;
+ }
+ for ( ptr = res; ptr != NULL && count > 0; ptr = ptr->ai_next ) {
+ // TODO: AF->DNBD3
+ if ( ptr->ai_addr->sa_family == AF_INET ) {
+ // Set host (IPv4)
+ struct sockaddr_in *addr4 = (struct sockaddr_in*)ptr->ai_addr;
+ dest[addCount].type = AF_INET;
+ dest[addCount].port = addr4->sin_port;
+ memcpy( dest[addCount].addr, &addr4->sin_addr, 4 );
+ addCount += 1;
+#ifdef WITH_IPV6
+ } else if ( ptr->ai_addr->sa_family == AF_INET6 ) {
+ // Set host (IPv6)
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)ptr->ai_addr;
+ dest[addCount].type = AF_INET6;
+ dest[addCount].port = addr6->sin6_port;
+ memcpy( dest[addCount].addr, &addr6->sin6_addr, 16 );
+ addCount += 1;
+#endif
+ }
+ }
+
+ freeaddrinfo( res );
+ return addCount;
+}
+
+void sock_setTimeout(const int sockfd, const int milliseconds)
+{
+ struct timeval tv;
+ tv.tv_sec = milliseconds / 1000;
+ tv.tv_usec = (milliseconds * 1000) % 1000000;
+ setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
+ setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv) );
+}
+
+poll_list_t* sock_newPollList()
+{
+ poll_list_t *list = (poll_list_t*)malloc( sizeof( poll_list_t ) );
+ list->count = 0;
+ return list;
+}
+
+void sock_destroyPollList(poll_list_t *list)
+{
+ for ( int i = 0; i < list->count; ++i ) {
+ if ( list->entry[i].fd >= 0 ) close( list->entry[i].fd );
+ }
+ free( list );
+}
+
+bool sock_printable(struct sockaddr *addr, socklen_t addrLen, char *output, int len)
+{
+ char host[100], port[10];
+ int ret = getnameinfo( addr, addrLen, host, 100, port, 10, NI_NUMERICHOST | NI_NUMERICSERV );
+ if ( ret == 0 ) snprintf( output, len, "[%s]:%s", host, port );
+ return ret == 0;
+}
+
+bool sock_listen(poll_list_t* list, char* bind_addr, uint16_t port)
+{
+ if ( list->count >= MAXLISTEN ) return false;
+ struct addrinfo hints, *res, *ptr;
+ char portStr[6];
+ const int on = 1;
+ int openCount = 0;
+ // Set hints for local addresses.
+ memset( &hints, 0, sizeof(hints) );
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf( portStr, sizeof portStr, "%d", (int)port );
+ if ( getaddrinfo( bind_addr, portStr, &hints, &res ) != 0 || res == NULL ) return false;
+ // Attempt to bind to all of the addresses as long as there's room in the poll list
+ for( ptr = res; ptr != NULL; ptr = ptr->ai_next ) {
+ char bla[100];
+ if ( !sock_printable( (struct sockaddr*)ptr->ai_addr, ptr->ai_addrlen, bla, 100 ) ) snprintf( bla, 100, "[invalid]" );
+ logadd( LOG_DEBUG1, "Binding to %s...", bla );
+ int sock = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol );
+ if ( sock < 0 ) {
+ logadd( LOG_WARNING, "(Bind to %s): cannot socket(), errno=%d", bla, errno );
+ continue;
+ }
+ setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
+ if ( ptr->ai_family == PF_INET6 ) setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on) );
+ if ( bind( sock, ptr->ai_addr, ptr->ai_addrlen ) == -1 ) {
+ logadd( LOG_WARNING, "(Bind to %s): cannot bind(), errno=%d", bla, errno );
+ close( sock );
+ continue;
+ }
+ if ( listen( sock, 20 ) == -1 ) {
+ logadd( LOG_WARNING, "(Bind to %s): cannot listen(), errno=%d", errno );
+ close( sock );
+ continue;
+ }
+ list->entry[list->count].fd = sock;
+ list->entry[list->count].events = POLLIN | POLLRDHUP;
+ list->count++;
+ openCount++;
+ if ( list->count >= MAXLISTEN ) break;
+ }
+ freeaddrinfo( res );
+ return openCount > 0;
+}
+
+int sock_listenAny(poll_list_t* list, uint16_t port)
+{
+ return sock_listen( list, NULL, port );
+}
+
+int sock_accept(poll_list_t *list, struct sockaddr_storage *addr, socklen_t *length_ptr)
+{
+ int ret = poll( list->entry, list->count, -1 );
+ if ( ret < 0 ) {
+ return -1;
+ }
+ for ( int i = list->count - 1; i >= 0; --i ) {
+ if ( list->entry[i].revents == 0 ) continue;
+ if ( list->entry[i].revents == POLLIN ) return accept( list->entry[i].fd, (struct sockaddr *)addr, length_ptr );
+ if ( list->entry[i].revents & ( POLLNVAL | POLLHUP | POLLERR | POLLRDHUP ) ) {
+ logadd( LOG_DEBUG1, "poll fd revents=%d for index=%d and fd=%d", (int)list->entry[i].revents, i, list->entry[i].fd );
+ if ( ( list->entry[i].revents & POLLNVAL ) == 0 ) close( list->entry[i].fd );
+ if ( i != list->count ) list->entry[i] = list->entry[list->count];
+ list->count--;
+ }
+ }
+ return -1;
+}
+
+void sock_set_nonblock(int sock)
+{
+ int flags = fcntl( sock, F_GETFL, 0 );
+ if ( flags == -1 ) flags = 0;
+ fcntl( sock, F_SETFL, flags | O_NONBLOCK );
+}
+
+void sock_set_block(int sock)
+{
+ int flags = fcntl( sock, F_GETFL, 0 );
+ if ( flags == -1 ) flags = 0;
+ fcntl( sock, F_SETFL, flags & ~(int)O_NONBLOCK );
+}
+
+bool sock_append(poll_list_t *list, const int sock, bool wantRead, bool wantWrite)
+{
+ if ( sock == -1 || list->count >= MAXLISTEN ) return false;
+ list->entry[list->count++].fd = sock;
+ list->entry[list->count++].events = ( wantRead ? POLLIN : 0 ) | ( wantWrite ? POLLOUT : 0 ) | POLLRDHUP;
+ list->count++;
+ return true;
+}
+
+ssize_t sock_sendAll(int sock, void *buffer, size_t len, int maxtries)
+{
+ size_t done = 0;
+ ssize_t ret = 0;
+ while ( done < len ) {
+ if ( maxtries >= 0 && --maxtries == -1 ) break;
+ ret = write( sock, (char*)buffer + done, len - done );
+ if ( ret < 0 ) {
+ if ( errno == EINTR ) continue;
+ if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
+ usleep( 1000 );
+ continue;
+ }
+ break;
+ }
+ if ( ret == 0 ) break;
+ done += ret;
+ }
+ if ( done == 0 ) return ret;
+ return done;
+}
+
diff --git a/src/shared/sockhelper.h b/src/shared/sockhelper.h
new file mode 100644
index 0000000..3a4ab6c
--- /dev/null
+++ b/src/shared/sockhelper.h
@@ -0,0 +1,93 @@
+#ifndef SOCKHELPER_H_
+#define SOCKHELPER_H_
+
+/*
+ * Helper functions for dealing with sockets. These functions should
+ * abstract from the IP version by using getaddrinfo() and thelike.
+ */
+
+#include <stdint.h>
+#include "../types.h"
+#include <sys/socket.h>
+#include <string.h>
+
+typedef struct _poll_list poll_list_t;
+
+/**
+ * Connect to given dnbd3_host_t.
+ * @param addr - address of host to connect to
+ * @param connect_ms - timeout in milliseconds after which the connection attempt fails
+ * @param rw_ms - read/write timeout in milliseconds to apply on successful connect
+ * @return socket file descriptor, or -1 on error
+ */
+int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const int rw_ms);
+
+/**
+ * Resolve/parse given address and put the result(s) into passed dnbd3_host_t array,
+ * but only up to count entries.
+ * @return Number of items added to array
+ */
+int sock_resolveToDnbd3Host(const char * const address, dnbd3_host_t * const dest, const int count);
+
+void sock_setTimeout(const int sockfd, const int milliseconds);
+
+/**
+ * Create new poll list.
+ */
+poll_list_t* sock_newPollList();
+
+/**
+ * Delete a poll list, closing all sockets first if necessary.
+ */
+void sock_destroyPollList(poll_list_t *list);
+
+/**
+ * Listen on all interfaces/available IP addresses, using the given protocol.
+ * IPv4 and IPv6 are supported.
+ * @param protocol_family PF_INET or PF_INET6
+ * @param port port to listen on
+ * @return the socket descriptor if successful, -1 otherwise.
+ */
+int sock_listenAny(poll_list_t* list, uint16_t port);
+
+/**
+ * Listen on a specific address and port.
+ * @param addr pointer to a properly filled sockaddr_in or sockaddr_in6
+ * @param addrlen length of the passed struct
+ */
+bool sock_listen(poll_list_t* list, char* bind_addr, uint16_t port);
+
+/**
+ * This is a multi-socket version of accept. Pass in an array of listening sockets.
+ * If any of the sockets has an incoming connection, accept it and return the new socket's fd.
+ * On error, return -1, just like accept().
+ * @param sockets array of listening socket fds
+ * @param socket_count number of sockets in that array
+ * @return fd of new client socket, -1 on error
+ */
+int sock_accept(poll_list_t *list, struct sockaddr_storage *addr, socklen_t *length_ptr);
+
+void sock_set_nonblock(int sock);
+
+void sock_set_block(int sock);
+
+/**
+ * Add given socket to array. Take an existing empty slot ( == -1) if available,
+ * append to end otherwise. Updates socket count variable passed by reference.
+ *
+ * @param poll_list_t list the poll list to add the socket to
+ * @param sock socket fd to add
+ * @param wantRead whether to set the EPOLLIN flag
+ * @param wantWrite whether to set the EPOLLOUT flag
+ * @return true on success, false iff the array is already full or socket is < 0
+ */
+bool sock_append(poll_list_t *list, const int sock, bool wantRead, bool wantWrite);
+
+/**
+ * Send the whole buffer, calling write() multiple times if neccessary.
+ * Give up after calling write() maxtries times.
+ * Set maxtries < 0 to try infinitely.
+ */
+ssize_t sock_sendAll(int sock, void *buffer, size_t len, int maxtries);
+
+#endif /* SOCKHELPER_H_ */