// Stop SIGPIPE annoying us.
if (CB_NOSIGPIPE) {
int i = 1;
- setsockopt(*socketID, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i));
+ setsockopt((evutil_socket_t)*socketID, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i));
}
// Make socket non-blocking
evutil_make_socket_nonblocking((evutil_socket_t)*socketID);
}
return true;
}
-bool CBSocketAcceptIPv4(uint64_t socketID,uint64_t * connectionSocketID,uint8_t * IP,uint16_t * port){
- struct sockaddr_in address;
- socklen_t size = sizeof(address);
- *connectionSocketID = accept((evutil_socket_t)socketID, (struct sockaddr *)&address, &size);
+bool CBSocketAccept(uint64_t socketID,uint64_t * connectionSocketID){
+ *connectionSocketID = accept((evutil_socket_t)socketID, NULL, NULL);
if (*connectionSocketID == -1) {
return false;
}
- // Fill out IPv4 address
- memcpy(IP, &address.sin_addr, 4);
- // Set port which will be some random connection port.
- *port = address.sin_port;
// Make socket non-blocking
evutil_make_socket_nonblocking((evutil_socket_t)*connectionSocketID);
- return true;
-}
-bool CBSocketAcceptIPv6(uint64_t socketID,uint64_t * connectionSocketID,uint8_t * IP,uint16_t * port){
- struct sockaddr_in6 address;
- socklen_t size = sizeof(address);
- *connectionSocketID = accept((evutil_socket_t)socketID, (struct sockaddr *)&address, &size);
- if (*connectionSocketID == -1) {
- return false;
+ // Stop SIGPIPE
+ if (CB_NOSIGPIPE) {
+ int i = 1;
+ setsockopt((evutil_socket_t)*connectionSocketID, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i));
}
- // Fill out IPv6 address
- memcpy(IP, &address.sin6_addr, 16);
- // Set port
- *port = address.sin6_port;
- // Make socket non-blocking
- evutil_make_socket_nonblocking((evutil_socket_t)*connectionSocketID);
return true;
}
bool CBNewEventLoop(uint64_t * loopID,void (*onError)(void *),void (*onDidTimeout)(void *,void *,CBTimeOutType),void * communicator){
}
int32_t CBSocketSend(uint64_t socketID,uint8_t * data,uint32_t len){
ssize_t res = send((evutil_socket_t)socketID, data, len, CB_SEND_FLAGS);
- printf("SENT (%li): ",res);
- for (uint32_t x = 0; x < res; x++) {
- printf("%c",data[x]);
- }
- printf("\n");
if (res >= 0)
return (int32_t)res;
if (errno == EAGAIN)
}
int32_t CBSocketReceive(uint64_t socketID,uint8_t * data,uint32_t len){
ssize_t res = read((evutil_socket_t)socketID, data, len);
- printf("RECEIVED: ");
- for (uint32_t x = 0; x < res; x++) {
- printf("%c",data[x]);
- }
- printf("\n");
if (res > 0)
return (int32_t)res; // OK, read data.
if (NOT res)
@brief This is an implementation of the networking dependencies for cbitcoin. It can be included as as a source file in any projects using cbitcoin which require networking capabilities. This file uses libevent for effecient event-based sockets and POSIX threads. The sockets are POSIX sockets with compatibility functions taken from libevent to try and be compatible with windows.
*/
+#include "CBDependencies.h" // cbitcoin dependencies to implement
+#include <pthread.h> // POSIX threads
+#include <event2/event.h> // libevent events
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+
#ifndef CBLIBEVENTSOCKETSH
#define CBLIBEVENTSOCKETSH
// Define if we have SO_NOSIGPIPE
#ifdef SO_NOSIGPIPE
- #define CB_NOSIGPIPE true
+#define CB_NOSIGPIPE true
#else
- #define CB_NOSIGPIPE false
+#define CB_NOSIGPIPE false
+#define SO_NOSIGPIPE 0
#endif
// Define send flags
#ifdef MSG_NOSIGNAL
- #define CB_SEND_FLAGS MSG_NOSIGNAL
+#define CB_SEND_FLAGS MSG_NOSIGNAL
#else
- #define CB_SEND_FLAGS 0
+#define CB_SEND_FLAGS 0
#endif
-#include "CBDependencies.h" // cbitcoin dependencies to implement
-#include <pthread.h> // POSIX threads
-#include <event2/event.h> // libevent events
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-
void event_base_add_virtual(struct event_base *); // Add virtual event.
void * CBStartEventLoop(void *);
void CBCanAccept(evutil_socket_t socketID,short eventNum,void * arg);
--- /dev/null
+//
+// CBLibEventSockets.c
+// cbitcoin
+//
+// Created by Matthew Mitchell on 30/05/2012.
+// Copyright (c) 2012 Matthew Mitchell
+//
+// This file is part of cbitcoin.
+//
+// cbitcoin 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 3 of the License, or
+// (at your option) any later version.
+//
+// cbitcoin 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 cbitcoin. If not, see <http://www.gnu.org/licenses/>.
+
+#include "CBLibevSockets.h"
+
+// Implementation
+
+CBSocketReturn CBNewSocket(uint64_t * socketID,bool IPv6){
+ *socketID = socket(IPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, 0);
+ if (*socketID == -1) {
+ if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
+ return CB_SOCKET_NO_SUPPORT;
+ }
+ return CB_SOCKET_BAD;
+ }
+ // Stop SIGPIPE annoying us.
+ if (CB_NOSIGPIPE) {
+ int i = 1;
+ setsockopt((int)*socketID, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i));
+ }
+ // Make socket non-blocking
+ fcntl((int)*socketID, F_SETFL, fcntl((int)*socketID, F_GETFL, 0) | O_NONBLOCK);
+ return CB_SOCKET_OK;
+}
+bool CBSocketBind(uint64_t * socketID,bool IPv6,uint16_t port){
+ struct addrinfo hints,*res,*ptr;
+ // Set hints for the computer's addresses.
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = IPv6 ? AF_INET6 : AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ // Get host for listening
+ char portStr[6];
+ sprintf(portStr, "%u",port);
+ if (getaddrinfo(NULL, portStr, &hints, &res))
+ return false;
+ // Attempt to bind to one of the addresses.
+ for(ptr = res; ptr != NULL; ptr = ptr->ai_next) {
+ if ((*socketID = socket(ptr->ai_family, ptr->ai_socktype,ptr->ai_protocol)) == -1)
+ continue;
+ if (bind((int)*socketID, ptr->ai_addr, ptr->ai_addrlen) == -1) {
+ close((int)*socketID);
+ continue;
+ }
+ break; // Success.
+ }
+ freeaddrinfo(res);
+ if (ptr == NULL) // Failure
+ return false;
+ // Prevent SIGPIPE
+ if (CB_NOSIGPIPE) {
+ int i = 1;
+ setsockopt((int)*socketID, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i));
+ }
+ // Make socket non-blocking
+ fcntl((int)*socketID, F_SETFL, fcntl((int)*socketID, F_GETFL, 0) | O_NONBLOCK);
+ return true;
+}
+bool CBSocketConnect(uint64_t socketID,uint8_t * IP,bool IPv6,uint16_t port){
+ // Create sockaddr_in6 information for a IPv6 address
+ int res;
+ if (IPv6) {
+ struct sockaddr_in6 address;
+ memset(&address, 0, sizeof(address)); // Clear structure.
+ address.sin6_family = AF_INET6;
+ memcpy(&address.sin6_addr, IP, 16); // Move IP address into place.
+ address.sin6_port = htons(port); // Port number to network order
+ res = connect((int)socketID, (struct sockaddr *)&address, sizeof(address));
+ }else{
+ struct sockaddr_in address;
+ memset(&address, 0, sizeof(address)); // Clear structure.
+ address.sin_family = AF_INET;
+ memcpy(&address.sin_addr, IP + 12, 4); // Move IP address into place. Last 4 bytes for IPv4.
+ address.sin_port = htons(port); // Port number to network order
+ res = connect((int)socketID, (struct sockaddr *)&address, sizeof(address));
+ }
+ if (NOT res || errno == EINPROGRESS)
+ return true;
+ return false;
+}
+bool CBSocketListen(uint64_t socketID,uint16_t maxConnections){
+ if(listen((int)socketID, maxConnections) == -1){
+ return false;
+ }
+ return true;
+}
+bool CBSocketAccept(uint64_t socketID,uint64_t * connectionSocketID){
+ *connectionSocketID = accept((int)socketID, NULL, NULL);
+ if (*connectionSocketID == -1) {
+ return false;
+ }
+ // Stop SIGPIPE
+ if (CB_NOSIGPIPE) {
+ int i = 1;
+ setsockopt((int)*connectionSocketID, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i));
+ }
+ // Make socket non-blocking
+ fcntl((int)*connectionSocketID, F_SETFL, fcntl((int)*connectionSocketID, F_GETFL, 0) | O_NONBLOCK);
+ return true;
+}
+bool CBNewEventLoop(uint64_t * loopID,void (*onError)(void *),void (*onDidTimeout)(void *,void *,CBTimeOutType),void * communicator){
+ struct ev_loop * base = ev_loop_new(0);
+ // Create arguments for the loop
+ CBEventLoop * loop = malloc(sizeof(*loop));
+ loop->base = base;
+ loop->onError = onError;
+ loop->onTimeOut = onDidTimeout;
+ loop->communicator = communicator;
+ loop->userEvent = malloc(sizeof(*loop->userEvent));
+ ev_async_init((struct ev_async *)loop->userEvent, CBDoRun);
+ ev_async_start(base, (struct ev_async *)loop->userEvent);
+ // Create thread attributes explicitly for portability reasons.
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // May need to be joinable.
+ // Create joinable thread
+ if(pthread_create(&loop->loopThread, &attr, CBStartEventLoop, loop)){
+ // Thread creation failed.
+ pthread_attr_destroy(&attr);
+ ev_loop_destroy(base);
+ free(loop->userEvent);
+ return 0;
+ }
+ pthread_attr_destroy(&attr);
+ *loopID = (uint64_t)loop;
+ return loop;
+}
+void * CBStartEventLoop(void * vloop){
+ CBEventLoop * loop = vloop;
+ // Start event loop
+ ev_run(loop->base, 0);
+ // Break from loop. Free everything.
+ ev_loop_destroy(loop->base);
+ free(loop->userEvent);
+ free(loop);
+ return NULL;
+}
+bool CBSocketCanAcceptEvent(uint64_t * eventID,uint64_t loopID,uint64_t socketID,void (*onCanAccept)(void *,uint64_t)){
+ CBIOEvent * event = malloc(sizeof(*event));
+ event->loop = (CBEventLoop *) loopID;
+ event->onEvent.i = onCanAccept;
+ event->socket = (int)socketID;
+ ev_io_init((struct ev_io *)event, CBCanAccept, (int)socketID, EV_READ);
+ *eventID = (uint64_t)event;
+ return true;
+}
+void CBCanAccept(struct ev_loop * loop,struct ev_io * watcher,int eventID){
+ CBIOEvent * event = (CBIOEvent *)watcher;
+ event->onEvent.i(event->loop->communicator,event->socket);
+}
+bool CBSocketDidConnectEvent(uint64_t * eventID,uint64_t loopID,uint64_t socketID,void (*onDidConnect)(void *,void *),void * node){
+ CBIOEvent * event = malloc(sizeof(*event));
+ event->loop = (CBEventLoop *)loopID;
+ event->onEvent.ptr = onDidConnect;
+ event->node = node;
+ event->timerCallback = CBDidConnectTimeout;
+ ev_io_init((struct ev_io *)event, CBDidConnect, (int)socketID, EV_WRITE);
+ *eventID = (uint64_t)event;
+ return true;
+}
+void CBDidConnect(struct ev_loop * loop,struct ev_io * watcher,int eventID){
+ CBIOEvent * event = (CBIOEvent *)watcher;
+ // Reset timeout
+ if (event->timeout)
+ ev_timer_again(loop, (struct ev_timer *)event->timeout);
+ // This is a one-shot event.
+ ev_io_stop(loop, watcher);
+ // Connection successful
+ event->onEvent.ptr(event->loop->communicator,event->node);
+}
+void CBDidConnectTimeout(struct ev_loop * loop,struct ev_timer * watcher,int eventID){
+ CBTimer * event = (CBTimer *) watcher;
+ event->loop->onTimeOut(event->loop->communicator,event->node,CB_TIMEOUT_CONNECT);
+}
+bool CBSocketCanSendEvent(uint64_t * eventID,uint64_t loopID,uint64_t socketID,void (*onCanSend)(void *,void *),void * node){
+ CBIOEvent * event = malloc(sizeof(*event));
+ event->loop = (CBEventLoop *)loopID;
+ event->onEvent.ptr = onCanSend;
+ event->node = node;
+ event->timerCallback = CBCanSendTimeout;
+ ev_io_init((struct ev_io *)event, CBCanSend, (int)socketID, EV_WRITE);
+ *eventID = (uint64_t)event;
+ return true;
+}
+void CBCanSend(struct ev_loop * loop,struct ev_io * watcher,int eventID){
+ CBIOEvent * event = (CBIOEvent *)watcher;
+ // Reset timeout
+ if (event->timeout)
+ ev_timer_again(loop, (struct ev_timer *)event->timeout);
+ // Can send
+ event->onEvent.ptr(event->loop->communicator,event->node);
+}
+void CBCanSendTimeout(struct ev_loop * loop,struct ev_timer * watcher,int eventID){
+ CBTimer * event = (CBTimer *) watcher;
+ event->loop->onTimeOut(event->loop->communicator,event->node,CB_TIMEOUT_SEND);
+}
+bool CBSocketCanReceiveEvent(uint64_t * eventID,uint64_t loopID,uint64_t socketID,void (*onCanReceive)(void *,void *),void * node){
+ CBIOEvent * event = malloc(sizeof(*event));
+ event->loop = (CBEventLoop *)loopID;
+ event->onEvent.ptr = onCanReceive;
+ event->node = node;
+ event->timerCallback = CBCanReceiveTimeout;
+ ev_io_init((struct ev_io *)event, CBCanReceive, (int)socketID, EV_READ);
+ *eventID = (uint64_t)event;
+ return true;
+}
+void CBCanReceive(struct ev_loop * loop,struct ev_io * watcher,int eventID){
+ CBIOEvent * event = (CBIOEvent *)watcher;
+ // Reset timeout
+ if (event->timeout)
+ ev_timer_again(loop, (struct ev_timer *)event->timeout);
+ // Can receive
+ event->onEvent.ptr(event->loop->communicator,event->node);
+}
+void CBCanReceiveTimeout(struct ev_loop * loop,struct ev_timer * watcher,int eventID){
+ CBTimer * event = (CBTimer *) watcher;
+ event->loop->onTimeOut(event->loop->communicator,event->node,CB_TIMEOUT_RECEIVE);
+}
+bool CBSocketAddEvent(uint64_t eventID,uint16_t timeout){
+ CBIOEvent * event = (CBIOEvent *)eventID;
+ ev_io_start(event->loop->base, (struct ev_io *)event);
+ if (timeout) {
+ // Add timer
+ event->timeout = malloc(sizeof(*event->timeout));
+ event->timeout->loop = event->loop;
+ event->timeout->node = event->node;
+ ev_timer_init((struct ev_timer *)event->timeout, event->timerCallback, 0, timeout);
+ ev_timer_start(event->loop->base, (struct ev_timer *)event->timeout);
+ }else
+ event->timeout = NULL;
+ return true;
+}
+bool CBSocketRemoveEvent(uint64_t eventID){
+ CBIOEvent * event = (CBIOEvent *)eventID;
+ if (event->timeout){
+ ev_timer_stop(event->loop->base, (struct ev_timer *)event->timeout);
+ free(event->timeout);
+ event->timeout = NULL;
+ }
+ ev_io_stop(event->loop->base, (struct ev_io *)event);
+}
+void CBSocketFreeEvent(uint64_t eventID){
+ CBSocketRemoveEvent(eventID);
+ free((CBIOEvent *)eventID);
+}
+int32_t CBSocketSend(uint64_t socketID,uint8_t * data,uint32_t len){
+ ssize_t res = send((int)socketID, data, len, CB_SEND_FLAGS);
+ if (res >= 0)
+ return (int32_t)res;
+ if (errno == EAGAIN)
+ return 0; // False event. Wait again.
+ return CB_SOCKET_FAILURE; // Failure
+}
+int32_t CBSocketReceive(uint64_t socketID,uint8_t * data,uint32_t len){
+ ssize_t res = read((int)socketID, data, len);
+ if (res > 0)
+ return (int32_t)res; // OK, read data.
+ if (NOT res)
+ return CB_SOCKET_CONNECTION_CLOSE; // If read() gives zero it means the connection was closed.
+ if (errno == EAGAIN)
+ return 0; // False event. Wait again. No bytes read.
+ return CB_SOCKET_FAILURE; // Failure
+}
+bool CBStartTimer(uint64_t loopID,uint64_t * timer,uint16_t time,void (*callback)(void *),void * arg){
+ CBTimer * theTimer = malloc(sizeof(*theTimer));
+ theTimer->callback = callback;
+ theTimer->arg = arg;
+ theTimer->loop = (CBEventLoop *)loopID;
+ ev_timer_init((struct ev_timer *)theTimer, CBFireTimer, 0, time);
+ ev_timer_start(((CBEventLoop *)loopID)->base, (struct ev_timer *)theTimer);
+ *timer = (uint64_t)theTimer;
+ return true;
+}
+void CBFireTimer(struct ev_loop * loop,struct ev_timer * watcher,int eventID){
+ CBTimer * theTimer = (CBTimer *)watcher;
+ theTimer->callback(theTimer->arg);
+}
+void CBEndTimer(uint64_t timer){
+ CBTimer * theTimer = (CBTimer *)timer;
+ ev_timer_stop(theTimer->loop->base, (struct ev_timer *)theTimer);
+ free(theTimer);
+}
+void CBDoRun(struct ev_loop * loop,struct ev_async * watcher,int event){
+ CBEventLoop * evloop = (CBEventLoop *)((CBAsyncEvent *)watcher)->loop;
+ evloop->userCallback(evloop->userArg);
+}
+bool CBRunOnNetworkThread(uint64_t loopID,void (*callback)(void *),void * arg){
+ CBEventLoop * loop = (CBEventLoop *) loopID;
+ loop->userCallback = callback;
+ loop->userArg = arg;
+ ev_async_send(loop->base, (struct ev_async *)loop->userEvent);
+}
+void CBCloseSocket(uint64_t socketID){
+ close((int)socketID);
+}
+void CBExitEventLoop(uint64_t loopID){
+ if (NOT loopID)
+ return;
+ CBEventLoop * loop = (CBEventLoop *) loopID;
+ ev_unloop(loop->base, EVUNLOOP_ONE);
+}
--- /dev/null
+//
+// CBLibEventSockets.h
+// cbitcoin
+//
+// Created by Matthew Mitchell on 10/08/2012.
+// Copyright (c) 2012 Matthew Mitchell
+//
+// This file is part of cbitcoin.
+//
+// cbitcoin 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 3 of the License, or
+// (at your option) any later version.
+//
+// cbitcoin 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 cbitcoin. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ @file
+ @brief This is an implementation of the networking dependencies for cbitcoin. It can be included as as a source file in any projects using cbitcoin which require networking capabilities. This file uses libevent for effecient event-based sockets and POSIX threads. The sockets are POSIX sockets with compatibility functions taken from libevent to try and be compatible with windows.
+ */
+
+#include "CBDependencies.h" // cbitcoin dependencies to implement
+#include <pthread.h> // POSIX threads
+#include <ev.h> // libev events
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifndef CBLIBEVENTSOCKETSH
+#define CBLIBEVENTSOCKETSH
+
+// Define if we have SO_NOSIGPIPE
+
+#ifdef SO_NOSIGPIPE
+#define CB_NOSIGPIPE true
+#else
+#define CB_NOSIGPIPE false
+#define SO_NOSIGPIPE 0
+#endif
+
+// Define send flags
+
+#ifdef MSG_NOSIGNAL
+#define CB_SEND_FLAGS MSG_NOSIGNAL
+#else
+#define CB_SEND_FLAGS 0
+#endif
+
+void * CBStartEventLoop(void *);
+void CBCanAccept(struct ev_loop * loop,struct ev_io * watcher,int eventID);
+void CBDidConnect(struct ev_loop * loop,struct ev_io * watcher,int eventID);
+void CBDidConnectTimeout(struct ev_loop * loop,struct ev_timer * watcher,int eventID);
+void CBCanSend(struct ev_loop * loop,struct ev_io * watcher,int eventID);
+void CBCanSendTimeout(struct ev_loop * loop,struct ev_timer * watcher,int eventID);
+void CBCanReceive(struct ev_loop * loop,struct ev_io * watcher,int eventID);
+void CBCanReceiveTimeout(struct ev_loop * loop,struct ev_timer * watcher,int eventID);
+void CBFireTimer(struct ev_loop * loop,struct ev_timer * watcher,int eventID);
+void CBDoRun(struct ev_loop * loop,struct ev_async * watcher,int event);
+/**
+ @brief Runs a callback on the network thread.
+ @param loopID The loop ID
+ @returns true if sucessful, false otherwise.
+ */
+bool CBRunOnNetworkThread(uint64_t loopID,void (*callback)(void *),void * arg);
+
+typedef struct{
+ struct ev_async base;
+ void * loop;
+}CBAsyncEvent;
+
+typedef struct{
+ struct ev_loop * base;
+ void (*onError)(void *);
+ void (*onTimeOut)(void *,void *,CBTimeOutType); /**< Callback for timeouts */
+ void * communicator;
+ pthread_t loopThread; /**< The thread ID for the event loop. */
+ void (*userCallback)(void *);
+ void * userArg;
+ CBAsyncEvent * userEvent;
+}CBEventLoop;
+
+union CBOnEvent{
+ void (*i)(void *,uint64_t);
+ void (*ptr)(void *,void *);
+};
+
+typedef struct{
+ struct ev_timer base;
+ CBEventLoop * loop;
+ void (*callback)(void *);
+ void * arg;
+ void * node;
+}CBTimer;
+
+typedef struct{
+ struct ev_io base; /**< libev event. */
+ CBEventLoop * loop; /**< For getting timeout events */
+ union CBOnEvent onEvent;
+ int socket;
+ void * node;
+ void (*timerCallback)(struct ev_loop *,struct ev_timer *,int);
+ CBTimer * timeout;
+}CBIOEvent;
+
+#endif
#pragma weak CBSocketBind
#pragma weak CBSocketConnect
#pragma weak CBSocketListen
-#pragma weak CBSocketAcceptIPv4
-#pragma weak CBSocketAcceptIPv6
+#pragma weak CBSocketAccept
#pragma weak CBNewEventLoop
#pragma weak CBSocketCanAcceptEvent
#pragma weak CBSocketDidConnectEvent
@brief Accepts an incomming IPv4 connection on a bound socket. This should be non-blocking.
@param socketID The socket id
@param connectionSocketID A socket id for a new socket for the connection.
- @param IP 4 bytes to be set by the function for the IP of the incomming connection.
- @param port Set to the port of the connection.
@returns true if function was sucessful and false otherwise.
*/
-bool CBSocketAcceptIPv4(uint64_t socketID,uint64_t * connectionSocketID,uint8_t * IP,uint16_t * port);
-/**
- @brief Accepts an incomming IPv6 connection on a bound socket. This should be non-blocking.
- @param socketID The socket id
- @param connectionSocketID A socket id for a new socket for the connection.
- @param IP 16 bytes to be set by the function for the IP of the incomming connection.
- @param port Set to the port of the connection.
- @returns true if function was sucessful and false otherwise.
- */
-bool CBSocketAcceptIPv6(uint64_t socketID,uint64_t * connectionSocketID,uint8_t * IP,uint16_t * port);
+bool CBSocketAccept(uint64_t socketID,uint64_t * connectionSocketID);
/**
@brief Starts a event loop for socket events on a seperate thread. Access to the loop id should be thread safe.
@param loopID A uint64_t storing an integer or pointer representation of the new event loop.
CBBucket * bucket = self->buckets + CBAddressManagerGetBucketIndex(self, addr);
// Look in that bucket for the address...
for (uint16_t x = 0; x < bucket->addrNum; x++)
- if (CBByteArrayEquals(addr->ip, bucket->addresses[x]->ip))
- // Compares IPs
+ if (CBByteArrayEquals(addr->ip, bucket->addresses[x]->ip) && addr->port == bucket->addresses[x]->port)
+ // Compares IPs and port
return bucket->addresses[x];
return NULL;
}
CBNode * CBAddressManagerGotNode(CBAddressManager * self,CBNetworkAddress * addr){
for (uint16_t x = 0; x < self->nodesNum; x++)
- if (CBByteArrayEquals(CBGetNetworkAddress(self->nodes[x])->ip, addr->ip))
+ if (CBByteArrayEquals(CBGetNetworkAddress(self->nodes[x])->ip, addr->ip) && addr->port == CBGetNetworkAddress(self->nodes[x])->port)
return self->nodes[x];
return NULL;
}
self->reachablity &= ~type;
}
bool CBAddressManagerSetup(CBAddressManager * self){
- // Allocate buckets
- self->buckets = malloc(sizeof(*self->buckets) * CB_BUCKET_NUM);
+ // Clear buckets
memset(self->buckets, 0, sizeof(*self->buckets) * CB_BUCKET_NUM);
// Create random number generators.
if (CBNewSecureRandomGenerator(&self->rndGen)) {
*/
typedef struct{
CBMessage base; /**< CBMessage base structure */
- CBBucket * buckets; /**< Unconnected nodes stored in the buckets */
+ CBBucket buckets[CB_BUCKET_NUM]; /**< Unconnected nodes stored in the buckets */
CBNode ** nodes; /**< Connected nodes sorted by the time offset. */
uint32_t nodesNum; /**< Number of connected nodes */
int16_t networkTimeOffset; /**< Offset to get from system time to network time. */
*/
uint64_t CBAddressManagerGetNumberOfAddresses(CBAddressManager * self);
/**
- @brief Determines if a CBNetworkAddress already exists in the CBAddressManager. Compares the IP address.
+ @brief Determines if a CBNetworkAddress already exists in the CBAddressManager. Compares the IP address and port.
@param self The CBAddressManager object.
@param addr The address.
@returns If the address already exists, returns the existing object. Else returns NULL.
*/
CBNetworkAddress * CBAddressManagerGotNetworkAddress(CBAddressManager * self,CBNetworkAddress * addr);
/**
- @brief Determines if a CBNetworkAddress is in the "nodes" list. Compares the IP address.
+ @brief Determines if a CBNetworkAddress is in the "nodes" list. Compares the IP address and port.
@param self The CBAddressManager object.
@param addr The address.
@returns If the address already exists as a connected node, returns the existing object. Else returns NULL.
bool CBInitNetworkAddress(CBNetworkAddress * self,uint32_t score,CBByteArray * ip,uint16_t port,uint64_t services,CBEvents * events){
self->score = score;
self->ip = ip;
- // Determine IP type
- self->type = CBGetIPType(CBByteArrayGetData(ip));
if (NOT ip) {
ip = CBNewByteArrayOfSize(16, events);
memset(CBByteArrayGetData(ip), 0, 16);
+ self->type = CB_IP_INVALID;
}else{
+ // Determine IP type
+ self->type = CBGetIPType(CBByteArrayGetData(ip));
CBRetainObject(ip);
}
self->port = port;
bool CBInitNodeByTakingNetworkAddress(CBNode * self){
self->receive = NULL;
- self->pingsSent = 0;
self->versionSent = false;
self->versionAck = false;
self->versionMessage = NULL;
uint64_t receiveEvent; /**< Event for receving data from this node */
uint64_t sendEvent; /**< Event for sending data from this node */
uint64_t connectEvent; /**< Event for connecting to the node. */
- uint16_t pingsSent; /**< Number of pings sent and thus how many responses expected */
bool versionSent; /**< True if the version was sent to this node */
CBVersion * versionMessage; /**< The version message from this node. */
bool versionAck; /**< This node acknowledged the version message. */
// Readjust CBByteArray length for recieving address
data->length = len;
CBReleaseObject(data);
- if (self->version >= 106) {
+ if (self->version >= 106) { // ??? Very old. No need for backwards compatibility? Might as well.
if (bytes->length < 85) {
CBGetMessage(self)->events->onErrorReceived(CB_ERROR_MESSAGE_DESERIALISATION_BAD_BYTES,"Attempting to deserialise a CBVersion with less than 85 bytes required.");
return 0;
// Functions
-void CBNetworkCommunicatorAcceptConnection(void * vself,uint64_t socket, bool IPv6){
+void CBNetworkCommunicatorAcceptConnection(void * vself,uint64_t socket){
CBNetworkCommunicator * self = vself;
- uint8_t * IP = malloc(IPv6 ? 16 : 4);
- uint16_t port;
uint64_t connectSocketID;
- CBByteArray * ipByteArray;
- if (IPv6) {
- if (NOT CBSocketAcceptIPv6(socket, &connectSocketID, IP, &port)) {
- // No luck accepting the connection.
- free(IP);
- return;
- }
- ipByteArray = CBNewByteArrayWithData(IP, 16, self->events);
- }else if (NOT CBSocketAcceptIPv4(socket, &connectSocketID, IP, &port)) {
- // No luck accepting the connection.
- free(IP);
+ printf("%s TRIED ACCEPT\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"));
+ if (NOT CBSocketAccept(socket, &connectSocketID))
return;
- }else{
- // IPv4 Success
- ipByteArray = CBNewByteArrayOfSize(16,self->events);
- // Set first 10 bytes to zero.
- memset(CBByteArrayGetData(ipByteArray),0,10);
- // Next two bytes are 0xFF for an IPv4 mapped address
- CBByteArraySetByte(ipByteArray, 10, 0xFF);
- CBByteArraySetByte(ipByteArray, 11, 0xFF);
- // Set IPv4
- memcpy(CBByteArrayGetData(ipByteArray) + 12, IP, 4);
- }
+ printf("%s MADE ACCEPT\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"));
// Connected, add CBNetworkAddress
- CBNode * node = CBNewNodeByTakingNetworkAddress(CBNewNetworkAddress(0, ipByteArray, port, 0, self->events));
- CBReleaseObject(ipByteArray);
+ CBNode * node = CBNewNodeByTakingNetworkAddress(CBNewNetworkAddress(0, NULL, 0, 0, self->events));
node->socketID = connectSocketID;
node->acceptedTypes = CB_MESSAGE_TYPE_VERSION; // The node connected to us, we want the version from them.
// Set up receive event
node->connectionWorking = true;
self->attemptingOrWorkingConnections++;
self->numIncommingConnections++;
- printf("HAS ACCEPT WITH PORT %u\n",port);
+ printf("%s HAS ACCEPTED OK\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"));
if (self->numIncommingConnections == self->maxIncommingConnections || self->attemptingOrWorkingConnections == self->maxConnections) {
// Reached maximum connections, stop listening.
CBNetworkCommunicatorStopListening(self);
// Failure, release node.
CBCloseSocket(connectSocketID);
CBReleaseObject(node);
-}
-void CBNetworkCommunicatorAcceptConnectionIPv4(void * vself,uint64_t socket){
- CBNetworkCommunicatorAcceptConnection(vself, socket, false);
-}
-void CBNetworkCommunicatorAcceptConnectionIPv6(void * vself,uint64_t socket){
- CBNetworkCommunicatorAcceptConnection(vself, socket, true);
+ printf("%s HAS ACCEPTED FAIL\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"));
}
CBConnectReturn CBNetworkCommunicatorConnect(CBNetworkCommunicator * self,CBNode * node){
if (NOT CBAddressManagerIsReachable(self->addresses, CBGetNetworkAddress(node)->type))
}
return CB_CONNECT_FAIL;
}
- // Add event for connection
- if (CBSocketDidConnectEvent(&node->connectEvent,self->eventLoop, node->socketID, CBNetworkCommunicatorDidConnect, node)) {
- if (CBSocketAddEvent(node->connectEvent, self->connectionTimeOut)) {
- printf("DID CONNECTION EVENT\n");
- // Connect
- if(CBSocketConnect(node->socketID, CBByteArrayGetData(CBGetNetworkAddress(node)->ip), isIPv6, CBGetNetworkAddress(node)->port)){
- printf("CONNECTION FINE\n");
+ // Connect
+ if(CBSocketConnect(node->socketID, CBByteArrayGetData(CBGetNetworkAddress(node)->ip), isIPv6, CBGetNetworkAddress(node)->port)){
+ // Add event for connection
+ if (CBSocketDidConnectEvent(&node->connectEvent,self->eventLoop, node->socketID, CBNetworkCommunicatorDidConnect, node)) {
+ if (CBSocketAddEvent(node->connectEvent, self->connectionTimeOut)) {
+ printf("%s CONNECTION WAIT\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"));
// Connection is fine. Retain for waiting for connect.
CBRetainObject(node);
self->attemptingOrWorkingConnections++;
return CB_CONNECT_OK;
- }else{
+ }else
CBSocketFreeEvent(node->connectEvent);
- CBCloseSocket(node->socketID);
- return CB_CONNECT_BAD;
- }
}
- CBSocketFreeEvent(node->connectEvent);
+ CBCloseSocket(node->socketID);
+ return CB_CONNECT_FAIL;
}
CBCloseSocket(node->socketID);
- return CB_CONNECT_FAIL;
+ return CB_CONNECT_BAD;
}
void CBNetworkCommunicatorDidConnect(void * vself,void * vnode){
- printf("HAS CONNECT\n");
CBNetworkCommunicator * self = vself;
+ printf("%s CONNECTED OK\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"));
CBNode * node = vnode;
CBSocketFreeEvent(node->connectEvent); // No longer need this event.
// Make receive event
// Store addresses.
uint64_t now = time(NULL) + self->addresses->networkTimeOffset;
// Loop through addresses
+ bool didAdd = false; // True when we add an address.
for (uint16_t x = 0; x < addrs->addrNum; x++) {
// Check if we have the address as a connected node
CBNode * nodeB = CBAddressManagerGotNode(self->addresses,addrs->addresses[x]);
if(NOT nodeB){
// Do not already have this address as a node
- if (addrs->addresses[x]->type & CB_IP_INVALID || addrs->addresses[x]->type & CB_IP_LOCAL)
- // Address broadcasts should not contain invalid or local addresses.
+ if (addrs->addresses[x]->type & CB_IP_INVALID)
+ // Address broadcasts should not contain invalid addresses.
+ return true;
+ if (addrs->addresses[x]->type & CB_IP_LOCAL && NOT (CBGetNetworkAddress(node)->type & CB_IP_LOCAL))
+ // Do not allow nodes to send local addresses to non-local nodes.
return true;
// Adjust time priority
if (addrs->addresses[x]->score > now + 600)
// Address is advertised more than ten minutes from now. Give low priority at 5 days ago
addrs->addresses[x]->score = (uint32_t)(now - 432000); // ??? Needs fixing for integer overflow in the year 2106.
- else if (addrs->addresses[x]->score > now - 900) // More than fifteen minutes ago. Give highest priority.
+ else if (addrs->addresses[x]->score > now - 900) // Sooner than fifteen minutes ago. Give highest priority.
addrs->addresses[x]->score = (uint32_t)now;
// Else leave the time
// Check if we have the address as a stored address
addr->score = addrs->addresses[x]->score;
addr->services = addrs->addresses[x]->services;
addr->version = addrs->addresses[x]->version;
- }else{
+ }else if (CBAddressManagerIsReachable(self->addresses, addrs->addresses[x]->type)){
// Do not have the address so add it if we believe we can reach it.
- if (CBAddressManagerIsReachable(self->addresses, addrs->addresses[x]->type)) {
- CBRetainObject(addrs->addresses[x]); // The CBMessage will be released with this CBNetworkAddress.
- CBAddressManagerTakeAddress(self->addresses, addrs->addresses[x]);
- }
+ CBAddressManagerAddAddress(self->addresses, addrs->addresses[x]);
+ didAdd = true;
}
}else
// We have an advertised node. This means it is public and should return to the address store.
CBNetworkCommunicatorSendMessage(self, self->addresses->nodes[x], CBGetMessage(addrs));
}
}
- CBNetworkCommunicatorTryConnections(self); // We have new address information so try connecting to addresses.
- if (node->getAddresses) node->getAddresses = false; // Got addresses.
+ if (didAdd)
+ // We have new address information so try connecting to addresses.
+ CBNetworkCommunicatorTryConnections(self);
+ // Got addresses.
+ node->getAddresses = false;
}else if (node->receive->type == CB_MESSAGE_TYPE_GETADDR) {
- // Give 33 nodes with the highest times with a some randomisation added. Try connected nodes first.
+ // Give 33 nodes with the highest times with a some randomisation added. Try connected nodes first. Do not send empty addr.
CBAddressBroadcast * addrBroadcast = CBNewAddressBroadcast(self->version >= CB_ADDR_TIME_VERSION && node->versionMessage->version >= CB_ADDR_TIME_VERSION, self->events);
CBGetMessage(addrBroadcast)->type = CB_MESSAGE_TYPE_ADDR;
// Try connected nodes. Only send nodes that are selected to return to the addresses list and hence aren't private (private addresses are those which connect to us but haven't relayed their address).
uint16_t nodesSent = 0;
uint16_t y = 0;
- while (nodesSent < 33 && y < self->addresses->nodesNum) {
- if (self->addresses->nodes[y]->returnToAddresses) {
- // Not private
+ while (nodesSent < 28 && y < self->addresses->nodesNum) { // 28 to have room for 5 addresses.
+ if (self->addresses->nodes[y] != node // Not the node we are sending to
+ && self->addresses->nodes[y]->returnToAddresses // Not private
+ && (CBGetNetworkAddress(self->addresses->nodes[y])->type != CB_IP_LOCAL // OK if not local
+ || CBGetNetworkAddress(node)->type == CB_IP_LOCAL)) { // Or if the node we are sending to is local
CBAddressBroadcastAddNetworkAddress(addrBroadcast, CBGetNetworkAddress(self->addresses->nodes[y]));
nodesSent++;
}
y++;
}
- // Now send stored addresses.
+ // Now add stored addresses
CBNetworkAddressLocator * addrs = CBAddressManagerGetAddresses(self->addresses, 33 - nodesSent);
for (u_int8_t x = 0; (addrs + x)->addr != NULL; x++){
- CBAddressBroadcastAddNetworkAddress(addrBroadcast, (addrs + x)->addr);
+ if ((addrs + x)->addr->type != CB_IP_LOCAL
+ || CBGetNetworkAddress(node)->type == CB_IP_LOCAL)
+ // If the address is not local or if the node we are sending to is local, the address is acceptable.
+ CBAddressBroadcastAddNetworkAddress(addrBroadcast, (addrs + x)->addr);
CBReleaseObject((addrs + x)->addr);
}
free(addrs);
- // Send address broadcast
- CBNetworkCommunicatorSendMessage(self, node, CBGetMessage(addrBroadcast));
+ // Send address broadcast, if we have at least one.
+ if (addrBroadcast->addrNum)
+ CBNetworkCommunicatorSendMessage(self, node, CBGetMessage(addrBroadcast));
CBReleaseObject(addrBroadcast);
}
// Use opportunity to discover if we should broadcast our own addresses for recieving incoming connections.
- if (node->timeBroadcast < time(NULL) - 86400) {
+ if (self->numIncommingConnections // Only share address if we allow for incomming connections.
+ && (self->isListeningIPv4 || self->isListeningIPv6) // Share when we are listening for incoming connections.
+ && node->timeBroadcast < time(NULL) - 86400 // Every 24 hours
+ && node->acceptedTypes & CB_MESSAGE_TYPE_ADDR) { // Only share address if they are allowed.
node->timeBroadcast = time(NULL);
CBAddressBroadcast * addrBroadcast = CBNewAddressBroadcast(self->version >= CB_ADDR_TIME_VERSION && node->versionMessage->version >= CB_ADDR_TIME_VERSION, self->events);
CBGetMessage(addrBroadcast)->type = CB_MESSAGE_TYPE_ADDR;
// Copy over reported version and services for the CBNetworkAddress.
CBGetNetworkAddress(node)->version = node->versionMessage->version;
CBGetNetworkAddress(node)->services = node->versionMessage->services;
- CBGetNetworkAddress(node)->score = (uint32_t)time(NULL); // ??? Loss of integer precision.
+ CBGetNetworkAddress(node)->score = (uint32_t)time(NULL); // ??? Loss of integer precisibon.
+ // Change port and IP number to the port and IP the node wants us to recognise them with.
+ CBGetNetworkAddress(node)->port = node->versionMessage->addSource->port;
+ CBGetNetworkAddress(node)->ip = node->versionMessage->addSource->ip;
+ CBGetNetworkAddress(node)->type = CBGetIPType(CBByteArrayGetData(CBGetNetworkAddress(node)->ip));
// Adjust network time
node->timeOffset = node->versionMessage->time - time(NULL); // Set the time offset for this node.
// Disallow version from here.
// Request addresses
CBMessage * getaddr = CBNewMessageByObject(self->events);
getaddr->type = CB_MESSAGE_TYPE_GETADDR;
- getaddr->expectResponse = CB_MESSAGE_TYPE_ADDR; // Expect a response of addresses for this message.
+ // Send the message without an expected response. We do not expect an "addr" message because the node may not send us anything if they do not have addresses to give.
CBNetworkCommunicatorSendMessage(self,node,getaddr);
CBReleaseObject(getaddr);
node->getAddresses = true;
memcpy(node->sendingHeader + 4, "alert\0\0\0\0\0\0\0", 12);
else if (toSend->type == CB_MESSAGE_TYPE_ALT)
memcpy(node->sendingHeader + 4, toSend->altText, 12);
+ printf("%s SENT %s\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"),node->sendingHeader + 4);
// Length
if (toSend->bytes){
node->sendingHeader[16] = toSend->bytes->length;
}
}
}
+ printf("%s RECEIVED %s\n",(self->ourIPv4->port == 45562)? "L1" : ((self->ourIPv4->port == 45563)? "L2" : "CN"),CBByteArrayGetData(typeBytes));
CBReleaseObject(typeBytes);
if (error) {
// Error with the message header type or length
len = CBVersionDeserialise(CBGetVersion(node->receive));
break;
case CB_MESSAGE_TYPE_ADDR:
+ if (self->ourIPv4->port == 45563) {
+ printf("ERR\n");
+ }
node->receive = realloc(node->receive, sizeof(CBAddressBroadcast));
CBGetObject(node->receive)->free = CBFreeAddressBroadcast;
CBGetAddressBroadcast(node->receive)->timeStamps = node->versionMessage->version > 31401; // Timestamps start version 31402 and up.
if (self->flags & CB_NETWORK_COMMUNICATOR_AUTO_DISCOVERY)
// Auto discovery responses
disconnect = CBNetworkCommunicatorProcessMessageAutoDiscovery(self,node);
- if (self->flags & CB_NETWORK_COMMUNICATOR_AUTO_HANDSHAKE)
+ if (self->flags & CB_NETWORK_COMMUNICATOR_AUTO_HANDSHAKE && NOT disconnect)
// Auto handshake responses
disconnect = CBNetworkCommunicatorProcessMessageAutoHandshake(self,node);
- if (self->flags & CB_NETWORK_COMMUNICATOR_AUTO_PING) {
+ if (self->flags & CB_NETWORK_COMMUNICATOR_AUTO_PING && NOT disconnect)
// Auto ping response
disconnect = CBNetworkCommunicatorProcessMessageAutoPingPong(self, node);
- }
}
// Release objects and get ready for next message
if (disconnect)
// No response expected but node did not send any information for a long time.
type = CB_TIMEOUT_NO_DATA;
}
- if (node->returnToAddresses){
+ if (node->returnToAddresses && CBGetNetworkAddress(node)->ip){ // Cannot take node as address in the case where the IP is NULL
CBRetainObject(node); // Retain node for returning to addresses list
CBNetworkCommunicatorDisconnect(self,node, CB_24_HOURS); // Remove CBNetworkAddress. 1 day penalty.
// Convert node to address and add it back to the addresses list.
// Create new bound IPv4 socket
if (CBSocketBind(&self->listeningSocketIPv4, false, self->ourIPv4->port)){
// Add event for accepting connection for both sockets
- if (CBSocketCanAcceptEvent(&self->acceptEventIPv4,self->eventLoop, self->listeningSocketIPv4, CBNetworkCommunicatorAcceptConnectionIPv4)) {
+ if (CBSocketCanAcceptEvent(&self->acceptEventIPv4,self->eventLoop, self->listeningSocketIPv4, CBNetworkCommunicatorAcceptConnection)) {
if(CBSocketAddEvent(self->acceptEventIPv4, 0)) // No timeout for listening for incomming connections.
// Start listening on IPv4
if(CBSocketListen(self->listeningSocketIPv4, self->maxIncommingConnections)){
// Create new bound IPv6 socket
if (CBSocketBind(&self->listeningSocketIPv6, true, self->ourIPv6->port)){
// Add event for accepting connection for both sockets
- if (CBSocketCanAcceptEvent(&self->acceptEventIPv6,self->eventLoop, self->listeningSocketIPv6, CBNetworkCommunicatorAcceptConnectionIPv6)) {
+ if (CBSocketCanAcceptEvent(&self->acceptEventIPv6,self->eventLoop, self->listeningSocketIPv6, CBNetworkCommunicatorAcceptConnection)) {
if(CBSocketAddEvent(self->acceptEventIPv6, 0)) // No timeout for listening for incomming connections.
// Start listening on IPv6
if(CBSocketListen(self->listeningSocketIPv6, self->maxIncommingConnections)){
@brief Accepts an incomming connection.
@param vself The CBNetworkCommunicator object.
@param socket The listening socket for accepting a connection.
- @param IPv6 True if an IPv6 connection, false if an IPv4 connection.
*/
-void CBNetworkCommunicatorAcceptConnection(void * vself,uint64_t socket,bool IPv6);
-/**
- @brief Accepts an incomming IPv4 connection.
- @param vself The CBNetworkCommunicator object.
- @param socket The listening socket for accepting a connection.
- */
-void CBNetworkCommunicatorAcceptConnectionIPv4(void * vself,uint64_t socket);
-/**
- @brief Accepts an incomming IPv6 connection.
- @param vself The CBNetworkCommunicator object.
- @param socket The listening socket for accepting a connection.
- */
-void CBNetworkCommunicatorAcceptConnectionIPv6(void * vself,uint64_t socket);
+void CBNetworkCommunicatorAcceptConnection(void * vself,uint64_t socket);
/**
@brief Returns true if it is beleived the network address can be connected to, otherwise false.
@param self The CBNetworkCommunicator object.
#include <stdio.h>
#include "CBNetworkCommunicator.h"
-#include "CBLibEventSockets.h"
+#include "CBLibevSockets.h"
#include <event2/thread.h>
#include <time.h>
#include "stdarg.h"
typedef enum{
GOTVERSION = 1,
GOTACK = 2,
- SUCCESS = 4
+ GOTPING = 4,
+ GOTPONG = 8,
+ GOTGETADDR = 16,
+}TesterProgress;
+
+typedef struct{
+ TesterProgress prog[6];
+ CBNode * nodeToProg[6];
+ uint8_t progNum;
+ int complete;
+ CBNetworkCommunicator * comms[3];
}Tester;
+
void onTimeOut(void * tester,void * comm,void * node,CBTimeOutType type);
void onTimeOut(void * tester,void * comm,void * node,CBTimeOutType type){
- if (NOT (*(Tester *)tester & SUCCESS)){
- switch (type) {
- case CB_TIMEOUT_CONNECT:
- printf("TIMEOUT FAIL: CONNECT\n");
- break;
- case CB_TIMEOUT_NO_DATA:
- printf("TIMEOUT FAIL: NO DATA\n");
- break;
- case CB_TIMEOUT_RECEIVE:
- printf("TIMEOUT FAIL: RECEIVE\n");
- break;
- case CB_TIMEOUT_RESPONSE:
- printf("TIMEOUT FAIL: RESPONSE\n");
- break;
- case CB_TIMEOUT_SEND:
- printf("TIMEOUT FAIL: SEND\n");
- break;
- }
+ switch (type) {
+ case CB_TIMEOUT_CONNECT:
+ printf("TIMEOUT FAIL: CONNECT\n");
+ break;
+ case CB_TIMEOUT_NO_DATA:
+ printf("TIMEOUT FAIL: NO DATA\n");
+ break;
+ case CB_TIMEOUT_RECEIVE:
+ printf("TIMEOUT FAIL: RECEIVE\n");
+ break;
+ case CB_TIMEOUT_RESPONSE:
+ printf("TIMEOUT FAIL: RESPONSE\n");
+ break;
+ case CB_TIMEOUT_SEND:
+ printf("TIMEOUT FAIL: SEND\n");
+ break;
}
CBNetworkCommunicatorStop((CBNetworkCommunicator *)comm);
}
bool onMessageReceived(void * vtester,void * vcomm,void * vnode);
bool onMessageReceived(void * vtester,void * vcomm,void * vnode){
Tester * tester = vtester;
- CBNetworkCommunicator * comm = vcomm;
CBNode * node = vnode;
CBMessage * theMessage = node->receive;
+ // Assign node to tester progress.
+ uint8_t x = 0;
+ for (; x < tester->progNum; x++) {
+ if (tester->nodeToProg[x] == node) {
+ break;
+ }
+ }
+ if (x == tester->progNum) {
+ tester->nodeToProg[x] = node;
+ tester->progNum++;
+ }
+ TesterProgress * prog = tester->prog + x;
switch (theMessage->type) {
case CB_MESSAGE_TYPE_VERSION:
- if (*tester && NOT (*tester == GOTACK && node->versionSent)) {
+ if (NOT ((node->versionSent && *prog == GOTACK) || (*prog == 0))) {
printf("VERSION FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
if (CBGetVersion(theMessage)->services) {
printf("VERSION SERVICES FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
if (CBGetVersion(theMessage)->version != CB_PONG_VERSION) {
printf("VERSION VERSION FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
if (memcmp(CBByteArrayGetData(CBGetVersion(theMessage)->userAgent),CB_USER_AGENT_SEGMENT,CBGetVersion(theMessage)->userAgent->length)) {
printf("VERSION USER AGENT FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
if (memcmp(CBByteArrayGetData(CBGetVersion(theMessage)->addSource->ip),(uint8_t [16]){0,0,0,0,0,0,0,0,0,0,0xFF,0xFF,127,0,0,1},CBGetVersion(theMessage)->addSource->ip->length)) {
printf("VERSION SOURCE IP FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
if (memcmp(CBByteArrayGetData(CBGetVersion(theMessage)->addRecv->ip),(uint8_t [16]){0,0,0,0,0,0,0,0,0,0,0xFF,0xFF,127,0,0,1},CBGetVersion(theMessage)->addRecv->ip->length)) {
printf("VERSION RECEIVE IP FAIL\n");
- CBNetworkCommunicatorStop(comm);
- }
- if (CBGetVersion(theMessage)->addSource->port != 45562 + (comm->ourIPv4->port == 45562)) {
- printf("VERSION SOURCE PORT FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
- *tester |= GOTVERSION;
+ *prog |= GOTVERSION;
break;
case CB_MESSAGE_TYPE_VERACK:
if ((NOT node->versionSent || node->versionAck)) {
printf("VERACK FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
- *tester |= GOTACK;
+ *prog |= GOTACK;
break;
case CB_MESSAGE_TYPE_PING:
- if (NOT (*tester & GOTVERSION && *tester & GOTACK)) {
+ if (NOT (*prog & GOTVERSION && *prog & GOTACK)) {
printf("PING FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
CBGetPingPong(theMessage)->ID = CBGetPingPong(theMessage)->ID; // Test access to ID.
+ *prog |= GOTPING;
break;
case CB_MESSAGE_TYPE_PONG:
- if (NOT (*tester & GOTVERSION && *tester & GOTACK) || node->pingsSent != 1) {
+ if (NOT (*prog & GOTVERSION && *prog & GOTACK)) {
printf("PONG FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
}
CBGetPingPong(theMessage)->ID = CBGetPingPong(theMessage)->ID; // Test access to ID.
- // At least one of the pongs worked. Stop this communicator and thus allow the other communicator to timeout.
- *tester |= SUCCESS;
- CBNetworkCommunicatorStop(comm);
+ // At least one of the pongs worked.
+ *prog |= GOTPONG;
+ break;
+ case CB_MESSAGE_TYPE_GETADDR:
+ if (NOT (*prog & GOTVERSION && *prog & GOTACK)) {
+ printf("GET ADDR FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ *prog |= GOTGETADDR;
+ break;
+ case CB_MESSAGE_TYPE_ADDR:
+ if (NOT (*prog & GOTVERSION && *prog & GOTACK && node->getAddresses)) {
+ printf("GET ADDR FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ tester->complete++;
break;
default:
printf("MESSAGE FAIL\n");
- CBNetworkCommunicatorStop(comm);
+ exit(EXIT_FAILURE);
break;
}
+ if ((*tester).complete == 10 && *prog == (GOTVERSION | GOTACK | GOTPING | GOTPONG |GOTGETADDR)) { // Connector sends self and other node twice (4). Listeners send self to connector (2). Listeners send self and connector to each other (4). 4 + 2 + 4 = 10
+ // Completed testing
+ CBNetworkCommunicatorStop((CBNetworkCommunicator *)vcomm);
+ }
return false;
}
void onNetworkError(void * foo,void * comm);
void onNetworkError(void * foo,void * comm){
- printf("NETWORK ERROR FAIL\n");
- CBNetworkCommunicatorStop((CBNetworkCommunicator *)comm);
+ printf("DID LOSE LAST NODE\n");
+ exit(EXIT_FAILURE);
}
void onBadTime(void * comm,void * addrMan);
void onBadTime(void * comm,void * addrMan){
printf("BAD TIME FAIL\n");
- CBNetworkCommunicatorStop((CBNetworkCommunicator *)comm);
+ exit(EXIT_FAILURE);
}
int main(){
CBEvents events;
- Tester testerListen = 0, testerConnect = 0;
+ Tester tester;
+ memset(&tester, 0, sizeof(tester));
events.onErrorReceived = err;
events.onBadTime = onBadTime;
events.onMessageReceived = onMessageReceived;
events.onNetworkError = onNetworkError;
events.onTimeOut = onTimeOut;
- // Using multiple threads to access libevent
- evthread_use_pthreads();
- // Create two CBNetworkCommunicators and connect over the loopback address. One will listen, one will connect. Test auto handshake and auto ping between them.
+ // Create three CBNetworkCommunicators and connect over the loopback address. Two will listen, one will connect. Test auto handshake, auto ping and auto discovery.
CBByteArray * altMessages = CBNewByteArrayOfSize(0, &events);
CBByteArray * altMessages2 = CBNewByteArrayOfSize(0, &events);
+ CBByteArray * altMessages3 = CBNewByteArrayOfSize(0, &events);
CBByteArray * loopBack = CBNewByteArrayWithDataCopy((uint8_t [16]){0,0,0,0,0,0,0,0,0,0,0xFF,0xFF,127,0,0,1}, 16, &events);
CBByteArray * loopBack2 = CBByteArrayCopy(loopBack); // Do not use in more than one thread.
CBNetworkAddress * addrListen = CBNewNetworkAddress(0, loopBack, 45562, 0, &events);
- CBNetworkAddress * addrListen2 = CBNewNetworkAddress(0, loopBack, 45562, 0, &events); // Use in second thread.
- CBNetworkAddress * addrConnect = CBNewNetworkAddress(0, loopBack2, 45563, 0, &events); // Different port over loopback to seperate the CBNetworkCommunicators.
+ CBNetworkAddress * addrListenB = CBNewNetworkAddress(0, loopBack2, 45562, 0, &events); // Use in second thread.
+ CBNetworkAddress * addrListen2 = CBNewNetworkAddress(0, loopBack, 45563, 0, &events);
+ CBNetworkAddress * addrListen2B = CBNewNetworkAddress(0, loopBack2, 45563, 0, &events); // Use in second thread.
+ CBNetworkAddress * addrConnect = CBNewNetworkAddress(0, loopBack, 45564, 0, &events); // Different port over loopback to seperate the CBNetworkCommunicators.
CBReleaseObject(loopBack);
CBReleaseObject(loopBack2);
CBByteArray * userAgent = CBNewByteArrayFromString(CB_USER_AGENT_SEGMENT, &events);
- // Listening CBNetworkCommunicator setup.
+ CBByteArray * userAgent2 = CBNewByteArrayFromString(CB_USER_AGENT_SEGMENT, &events);
+ CBByteArray * userAgent3 = CBNewByteArrayFromString(CB_USER_AGENT_SEGMENT, &events);
+ // First listening CBNetworkCommunicator setup.
CBAddressManager * addrManListen = CBNewAddressManager(&events);
- addrManListen->maxAddressesInBucket = 1;
+ addrManListen->maxAddressesInBucket = 2;
CBAddressManagerSetReachability(addrManListen, CB_IP_IPv4 | CB_IP_LOCAL, true);
CBNetworkCommunicator * commListen = CBNewNetworkCommunicator(&events);
addrManListen->callbackHandler = commListen;
commListen->networkID = CB_PRODUCTION_NETWORK_BYTES;
- commListen->flags = CB_NETWORK_COMMUNICATOR_AUTO_HANDSHAKE | CB_NETWORK_COMMUNICATOR_AUTO_PING;
+ commListen->flags = CB_NETWORK_COMMUNICATOR_AUTO_HANDSHAKE | CB_NETWORK_COMMUNICATOR_AUTO_PING | CB_NETWORK_COMMUNICATOR_AUTO_DISCOVERY;
commListen->version = CB_PONG_VERSION;
- commListen->maxConnections = 1;
- commListen->maxIncommingConnections = 1;
+ commListen->maxConnections = 2;
+ commListen->maxIncommingConnections = 2;
commListen->heartBeat = 10;
- commListen->timeOut = 20;
- commListen->sendTimeOut = 1;
- commListen->recvTimeOut = 10;
- commListen->responseTimeOut = 10;
- commListen->connectionTimeOut = 10;
+ commListen->timeOut = 0; //20;
+ commListen->sendTimeOut = 0; //1;
+ commListen->recvTimeOut = 0; //10;
+ commListen->responseTimeOut = 0; //10;
+ commListen->connectionTimeOut = 0; //10;
CBNetworkCommunicatorSetAlternativeMessages(commListen, altMessages, NULL);
CBNetworkCommunicatorSetAddressManager(commListen, addrManListen);
CBNetworkCommunicatorSetUserAgent(commListen, userAgent);
CBNetworkCommunicatorSetOurIPv4(commListen, addrListen);
- commListen->callbackHandler = &testerListen;
+ commListen->callbackHandler = &tester;
+ // Second listenin CBNetworkCommunicator setup.
+ CBAddressManager * addrManListen2 = CBNewAddressManager(&events);
+ addrManListen2->maxAddressesInBucket = 2;
+ CBAddressManagerSetReachability(addrManListen2, CB_IP_IPv4 | CB_IP_LOCAL, true);
+ CBNetworkCommunicator * commListen2 = CBNewNetworkCommunicator(&events);
+ addrManListen2->callbackHandler = commListen2;
+ commListen2->networkID = CB_PRODUCTION_NETWORK_BYTES;
+ commListen2->flags = CB_NETWORK_COMMUNICATOR_AUTO_HANDSHAKE | CB_NETWORK_COMMUNICATOR_AUTO_PING | CB_NETWORK_COMMUNICATOR_AUTO_DISCOVERY;
+ commListen2->version = CB_PONG_VERSION;
+ commListen2->maxConnections = 2;
+ commListen2->maxIncommingConnections = 2;
+ commListen2->heartBeat = 10;
+ commListen2->timeOut = 0; //20;
+ commListen2->sendTimeOut = 0; //1;
+ commListen2->recvTimeOut = 0; //10;
+ commListen2->responseTimeOut = 0; //10;
+ commListen2->connectionTimeOut = 0; //10;
+ CBNetworkCommunicatorSetAlternativeMessages(commListen2, altMessages2, NULL);
+ CBNetworkCommunicatorSetAddressManager(commListen2, addrManListen2);
+ CBNetworkCommunicatorSetUserAgent(commListen2, userAgent2);
+ CBNetworkCommunicatorSetOurIPv4(commListen2, addrListen2);
+ commListen2->callbackHandler = &tester;
// Connecting CBNetworkCommunicator setup.
CBAddressManager * addrManConnect = CBNewAddressManager(&events);
- addrManConnect->maxAddressesInBucket = 1;
+ addrManConnect->maxAddressesInBucket = 2;
CBAddressManagerSetReachability(addrManConnect, CB_IP_IPv4 | CB_IP_LOCAL, true);
- CBAddressManagerAddAddress(addrManConnect, addrListen2); // We are going to connect to the listing CBNetworkCommunicator.
+ // We are going to connect to both listing CBNetworkCommunicators.
+ CBAddressManagerAddAddress(addrManConnect, addrListenB);
+ CBAddressManagerAddAddress(addrManConnect, addrListen2B);
CBNetworkCommunicator * commConnect = CBNewNetworkCommunicator(&events);
addrManConnect->callbackHandler = commConnect;
commConnect->networkID = CB_PRODUCTION_NETWORK_BYTES;
- commConnect->flags = CB_NETWORK_COMMUNICATOR_AUTO_HANDSHAKE | CB_NETWORK_COMMUNICATOR_AUTO_PING;
+ commConnect->flags = CB_NETWORK_COMMUNICATOR_AUTO_HANDSHAKE | CB_NETWORK_COMMUNICATOR_AUTO_PING | CB_NETWORK_COMMUNICATOR_AUTO_DISCOVERY;
commConnect->version = CB_PONG_VERSION;
- commConnect->maxConnections = 1;
+ commConnect->maxConnections = 2;
commConnect->maxIncommingConnections = 0;
commConnect->heartBeat = 10;
- commConnect->timeOut = 20;
- commConnect->sendTimeOut = 1;
- commConnect->recvTimeOut = 1;
- commConnect->responseTimeOut = 10;
- commConnect->connectionTimeOut = 10;
- CBNetworkCommunicatorSetAlternativeMessages(commConnect, altMessages2, NULL);
+ commConnect->timeOut = 0; //20;
+ commConnect->sendTimeOut = 0; //1;
+ commConnect->recvTimeOut = 0; //1;
+ commConnect->responseTimeOut = 0; //10;
+ commConnect->connectionTimeOut = 0; //10;
+ CBNetworkCommunicatorSetAlternativeMessages(commConnect, altMessages3, NULL);
CBNetworkCommunicatorSetAddressManager(commConnect, addrManConnect);
- CBNetworkCommunicatorSetUserAgent(commConnect, userAgent);
+ CBNetworkCommunicatorSetUserAgent(commConnect, userAgent3);
CBNetworkCommunicatorSetOurIPv4(commConnect, addrConnect);
- commConnect->callbackHandler = &testerConnect;
+ commConnect->callbackHandler = &tester;
// Release objects
CBReleaseObject(altMessages);
- CBReleaseObject(addrListen);
- CBReleaseObject(addrListen2);
- CBReleaseObject(addrConnect);
CBReleaseObject(userAgent);
- // Start listening. Can start listening on this thread since the event loop will not be doing anything.
+ // Give tester communicators
+ tester.comms[0] = commListen;
+ tester.comms[1] = commListen2;
+ tester.comms[2] = commConnect;
+ // Start listening on first listener. Can start listening on this thread since the event loop will not be doing anything.
CBNetworkCommunicatorStart(commListen);
pthread_t listenThread = ((CBEventLoop *) commListen->eventLoop)->loopThread;
CBNetworkCommunicatorStartListening(commListen);
+ // Start listening on second listener.
+ CBNetworkCommunicatorStart(commListen2);
+ pthread_t listen2Thread = ((CBEventLoop *) commListen2->eventLoop)->loopThread;
+ CBNetworkCommunicatorStartListening(commListen2);
// Start connection
CBNetworkCommunicatorStart(commConnect);
pthread_t connectThread = ((CBEventLoop *) commConnect->eventLoop)->loopThread;
CBNetworkCommunicatorTryConnections(commConnect);
- // Wait until the network loop ends for both CBNetworkCommunicators.
+ // Wait until the network loop ends for all CBNetworkCommunicators.
pthread_join(listenThread, NULL);
+ pthread_join(listen2Thread, NULL);
pthread_join(connectThread, NULL);
- if (NOT (testerListen & SUCCESS) && NOT (testerConnect & SUCCESS))
+ if (NOT (tester.prog[0] == 127 && tester.prog[1] == 127 && tester.prog[2] == 127)){
printf("NO SUCCESS\n");
- // Release communicators
+ exit(EXIT_FAILURE);
+ }
+ // Check addresses in the first listening CBNetworkCommunicator
+ uint8_t i = CBAddressManagerGetBucketIndex(commListen->addresses, addrListen2);
+ CBBucket * bucket = commListen->addresses->buckets + i;
+ if (bucket->addrNum != 2) {
+ printf("ADDRESS DISCOVERY LISTEN ONE ADDR NUM FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if (i != CBAddressManagerGetBucketIndex(commListen->addresses, addrConnect)) {
+ printf("ADDRESS DISCOVERY LISTEN ONE SAME BUCKET FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if(!CBAddressManagerGotNetworkAddress(commListen->addresses, addrListen2)){
+ printf("ADDRESS DISCOVERY LISTEN ONE LISTEN TWO FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if(!CBAddressManagerGotNetworkAddress(commListen->addresses, addrConnect)){
+ printf("ADDRESS DISCOVERY LISTEN ONE CONNECT FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ // Check the addresses in the second.
+ i = CBAddressManagerGetBucketIndex(commListen2->addresses, addrListen);
+ bucket = commListen2->addresses->buckets + i;
+ if (bucket->addrNum != 2) {
+ printf("ADDRESS DISCOVERY LISTEN TWO ADDR NUM FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if (i != CBAddressManagerGetBucketIndex(commListen2->addresses, addrConnect)) {
+ printf("ADDRESS DISCOVERY LISTEN TWO SAME BUCKET FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if(!CBAddressManagerGotNetworkAddress(commListen2->addresses, addrListen)){
+ printf("ADDRESS DISCOVERY LISTEN TWO LISTEN ONE FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if(!CBAddressManagerGotNetworkAddress(commListen2->addresses, addrConnect)){
+ printf("ADDRESS DISCOVERY LISTEN TWO CONNECT FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ // And lastly the connecting CBNetworkCommunicator
+ i = CBAddressManagerGetBucketIndex(commConnect->addresses, addrListen);
+ bucket = commConnect->addresses->buckets + i;
+ if (bucket->addrNum != 2) {
+ printf("ADDRESS DISCOVERY CONNECT ADDR NUM FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if (i != CBAddressManagerGetBucketIndex(commConnect->addresses, addrListen2)) {
+ printf("ADDRESS DISCOVERY CONNECT SAME BUCKET FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if(!CBAddressManagerGotNetworkAddress(commConnect->addresses, addrListen)){
+ printf("ADDRESS DISCOVERY CONNECT LISTEN ONE FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ if(!CBAddressManagerGotNetworkAddress(commConnect->addresses, addrListen2)){
+ printf("ADDRESS DISCOVERY CONNECT LISTEN TWO FAIL\n");
+ exit(EXIT_FAILURE);
+ }
+ // Release all final objects.
+ CBReleaseObject(addrListen);
+ CBReleaseObject(addrListen2);
+ CBReleaseObject(addrConnect);
CBReleaseObject(commListen);
+ CBReleaseObject(commListen2);
CBReleaseObject(commConnect);
- return 0;
-}
\ No newline at end of file
+ return EXIT_SUCCESS;
+}