diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..6f3cf4b --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,9 @@ +Testing +==== + +To build the API unit tests: + - `make tests` + +All necessary binaries and scripts will be built and copied into `build/tests`. + +Running `build/tests/test.sh` will execute the automatic API unit tests. diff --git a/integrations/simple_app/app.cpp b/integrations/simple_app/app.cpp new file mode 100644 index 0000000..4120cf2 --- /dev/null +++ b/integrations/simple_app/app.cpp @@ -0,0 +1,221 @@ +#include +#include +#include + +#include "SDK.h" + + +int tcp_client(struct sockaddr_in *remote) +{ + printf("\t\t\tperforming TCP CLIENT test\n"); + + if(argc < 3) { + printf("usage: client \n"); + return 1; + } + + int sock, port = atoi(argv[2]); + struct sockaddr_in server; + char message[1000] , server_reply[2000]; + + sock = socket(AF_INET , SOCK_STREAM , 0); + if (sock == -1) { + printf("could not create socket"); + } + server.sin_addr.s_addr = inet_addr(argv[1]); + server.sin_family = AF_INET; + server.sin_port = htons( port ); + + printf("connecting...\n"); + if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) { + perror("connect failed. Error"); + return 1; + } + printf("connected\n"); + + char *msg = "welcome to the machine!"; + // TX + if(send(sock, msg, strlen(msg), 0) < 0) { + printf("send failed"); + return 1; + } + else { + printf("sent message: %s\n", msg); + printf("len = %ld\n", strlen(msg)); + } + close(sock); +} + +int tcp_server(struct sockaddr_in *local) +{ + printf("\t\t\tperforming TCP SERVER test\n"); +if(argc < 2) { + printf("usage: tcp_server \n"); + return 0; + } + + int socket_desc, client_sock, c, read_size, port = atoi(argv[1]); + struct sockaddr_in server , client; + char client_message[2000]; + + socket_desc = socket(AF_INET , SOCK_STREAM , 0); + if (socket_desc == -1) { + printf("could not create socket"); + return 0; + } + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(port); + + printf("binding on port %d\n", port); + if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) { + perror("bind failed. Error"); + return 0; + } + printf("listening\n"); + listen(socket_desc , 3); + printf("waiting to accept\n"); + c = sizeof(struct sockaddr_in); + + client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); + if (client_sock < 0) { + perror("accept failed"); + return 0; + } + printf("connection accepted\n reading...\n"); + + // RX + + int msglen = 1024; + unsigned long count = 0; + while(1) + { + count++; + int bytes_read = read(client_sock, client_message, msglen); + printf("[%lu] RX = (%d): ", count, bytes_read); + for(int i=0; i \n", argv[0]); + exit(0); + } + hostname = argv[1]; + portno = atoi(argv[2]); + + /* socket: create the socket */ + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + error("ERROR opening socket"); + + /* gethostbyname: get the server's DNS entry */ + server = gethostbyname(hostname); + if (server == NULL) { + fprintf(stderr,"ERROR, no such host as %s\n", hostname); + exit(0); + } + + /* build the server's Internet address */ + bzero((char *) &serveraddr, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + bcopy((char *)server->h_addr, + (char *)&serveraddr.sin_addr.s_addr, server->h_length); + serveraddr.sin_port = htons(portno); + + /* get a message from the user */ + char *msg = "A message to the server!\0"; + fcntl(sockfd, F_SETFL, O_NONBLOCK); + long count = 0; + while(1) + { + count++; + printf("\nTX(%lu)...\n", count); + usleep(10000); + //bzero(buf, BUFSIZE); + //printf("\nPlease enter msg: "); + //fgets(buf, BUFSIZE, stdin); + + /* send the message to the server */ + serverlen = sizeof(serveraddr); + printf("A\n"); + n = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr *)&serveraddr, serverlen); + printf("B\n"); + //if (n < 0) + // error("ERROR in sendto"); + + /* print the server's reply */ + printf("C\n"); + memset(buf, 0, sizeof(buf)); + printf("D\n"); + n = recvfrom(sockfd, buf, BUFSIZE, 0, (struct sockaddr *)&serveraddr, (socklen_t *)&serverlen); + printf("E\n"); + //if (n < 0) + // printf("ERROR in recvfrom: %d", n); + printf("Echo from server: %s", buf); + } + return 0; +} + +int udp_server(struct sockaddr_in *local) +{ + printf("\t\t\tperforming UDP SERVER test\n"); + +} + + +int udp_tests(struct sockaddr_in *local, struct sockaddr_in *local) { + printf("\t\tperforming UDP tests\n"); + udp_client(remote); + udp_server(local); +} +int tcp_tests(struct sockaddr_in *local, struct sockaddr_in *local) { + printf("\t\tperforming TCP tests\n"); + tcp_client(remote); + tcp_server(local) +} +int test(struct sockaddr_in *local, struct sockaddr_in *local) { + printf("\tperforming tests\n"); + udp_tests(local, remote); + tcp_tests(local, remote); +} + +int main() +{ + printf("Starting ZeroTier...\n"); + // where you want the zerotier identity/config files for this app to reside + char *path = "/Users/Joseph/code/ZeroTierSDK/build/simple_app/zerotier"; + char *nwid = "565799d8f612388c"; + + // start ZeroTier in separate thread + pid_t pid = fork(); + if(pid) + zt_start_service(path, nwid); + + printf("started!"); + + //zt_join_network(nwid); + //zt_leave_network(nwid); + test(); // run unit tests + + return 0; +} \ No newline at end of file diff --git a/make-mac.mk b/make-mac.mk index c10a1ac..6997ba9 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -50,7 +50,10 @@ endif CXXFLAGS=$(CFLAGS) -fno-rtti # Build everything -all: osx ios android check +all: osx ios android lwip check + +lwip: + make -f make-liblwip.mk # Build all Apple targets apple: osx ios @@ -100,6 +103,13 @@ android_jni_lib: mv -f $(INT)/android/android_jni_lib/java/libs/* $(BUILD)/android_jni_lib cp -R $(BUILD)/android_jni_lib/* $(INT)/android/example_app/app/src/main/jniLibs +# +simple_app: tests osx_service_and_intercept + mkdir -p build/simple_app + mkdir -p build/tests/zerotier + $(CXX) -Isrc $(BUILD)/libztosx.so integrations/simple_app/app.cpp -o $(BUILD)/simple_app/app + cp $(BUILD)/lwip/liblwip.so $(BUILD)/tests/zerotier/liblwip.so + remove_only_intermediates: -find . -type f \( -name '*.o' -o -name '*.so' \) -delete @@ -139,24 +149,28 @@ check: # Tests TEST_OBJDIR := $(BUILD)/tests -TEST_SOURCES := $(wildcard tests/*.c) +TEST_SOURCES := $(wildcard tests/api_test/*.c) TEST_TARGETS := $(addprefix $(BUILD)/tests/$(OSTYPE).,$(notdir $(TEST_SOURCES:.c=.out))) -$(BUILD)/tests/$(OSTYPE).%.out: tests/%.c +$(BUILD)/tests/$(OSTYPE).%.out: tests/api_test/%.c -$(CC) $(CC_FLAGS) -o $@ $< $(TEST_OBJDIR): mkdir -p $(TEST_OBJDIR) -tests: $(TEST_OBJDIR) $(TEST_TARGETS) +tests: $(TEST_OBJDIR) $(TEST_TARGETS) osx_service_and_intercept mkdir -p $(BUILD)/tests; + mkdir -p build/tests/zerotier + cp tests/api_test/test.sh $(BUILD)/tests/test.sh + cp tests/api_test/servers.sh $(BUILD)/tests/servers.sh + cp tests/api_test/clients.sh $(BUILD)/tests/clients.sh + cp tests/cleanup.sh $(BUILD)/tests/cleanup.sh + cp $(BUILD)/lwip/liblwip.so $(BUILD)/tests/zerotier/liblwip.so JAVAC := $(shell which javac) - clean_unity: - clean_android: # android JNI lib project test -s /usr/bin/javac || { echo "Javac not found"; exit 1; } diff --git a/src/SDK.h b/src/SDK.h index a778885..dc29e82 100644 --- a/src/SDK.h +++ b/src/SDK.h @@ -43,8 +43,9 @@ extern "C" { #endif -#define INTERCEPT_ENABLED 111 -#define INTERCEPT_DISABLED 222 +#define INTERCEPT_ENABLED 111 +#define INTERCEPT_DISABLED 222 +#define MAX_DIR_SZ 256 // Max path length used for home dir extern void load_symbols(); extern void zts_init_rpc(const char *path, const char *nwid); @@ -134,6 +135,11 @@ ssize_t zts_recvmsg(RECVMSG_SIG); int zts_set_nonblock(int fd); /* TODO combine with fcntl() */ #endif +#if !defined(__IOS__) + void zt_start_service(const char * path, const char *nwid); + void zt_join_network(const char * nwid); + void zt_leave_network(const char * nwid); +#endif // Android JNI Direct-call API // JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME diff --git a/src/SDK_Service.cpp b/src/SDK_Service.cpp index bd481cd..a45698c 100644 --- a/src/SDK_Service.cpp +++ b/src/SDK_Service.cpp @@ -198,6 +198,22 @@ char *zts_get_homepath() { } #endif +// +#if (defined(__APPLE__) || defined(__linux__)) && (!defined(__IOS__) && !defined(__ANDROID__)) + void zt_start_service(const char * path, const char *nwid) { + printf("path = %s\n", path); + if(path) + homeDir = path; + zts_start_service(NULL); + } + void zt_join_network(const char * nwid) { + zts_join_network(nwid); + } + void zt_leave_network(const char * nwid) { + zts_leave_network(nwid); + } +#endif + // Android JNI wrapper // JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME @@ -345,7 +361,6 @@ char *zts_get_homepath() { // Starts a ZeroTier service in the background void *zts_start_service(void *thread_id) { - #ifdef defined(__ANDROID__) dwr(MSG_DEBUG, "ZTSDK_BUILD_VERSION = %d\n", ZTSDK_BUILD_VERSION); LOGV("ZTSDK_BUILD_VERSION = %d\n", ZTSDK_BUILD_VERSION); @@ -356,7 +371,6 @@ void *zts_start_service(void *thread_id) { #endif #if defined(__UNITY_3D__) - int MAX_DIR_SZ = 256; char current_dir[MAX_DIR_SZ]; getcwd(current_dir, MAX_DIR_SZ); chdir(service_path.c_str()); @@ -375,11 +389,10 @@ void *zts_start_service(void *thread_id) { #endif #if defined(__APPLE__) && !defined(__IOS__) - homeDir = givenHomeDir; - localHomeDir = givenHomeDir; // Used for RPC and *can* differ from homeDir on some platforms + localHomeDir = homeDir; // Used for RPC and *can* differ from homeDir on some platforms #endif - dwr(MSG_DEBUG, "homeDir = %s\n", givenHomeDir.c_str()); + dwr(MSG_DEBUG, "homeDir = %s\n", homeDir.c_str()); // Where network .conf files will be stored netDir = homeDir + "/networks.d"; zt1Service = (ZeroTier::OneService *)0; diff --git a/tests/api_test/clients.sh b/tests/api_test/clients.sh new file mode 100755 index 0000000..3e7497d --- /dev/null +++ b/tests/api_test/clients.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +echo "\n\n\n\nStarting client(s)" + +# test.sh [udp|tcp|all] [nwid] +TEST_EXECUTABLE_PATH="build/tests" +protocol=$1 +NWID=$2 +HOME_DIR=$(pwd)/$TEST_EXECUTABLE_PATH +ZT_HOME_PATH=$(pwd)/$TEST_EXECUTABLE_PATH/zerotier + +BUILD_PATH=$(pwd)/build +localAddr="127.0.0.1" + +export ZT_NC_NETWORK=$ZT_HOME_PATH/nc_$NWID +export DYLD_LIBRARY_PATH=.$HOME_DIR/libztintercept.so + +echo "network = " $NWID +echo "protocol = " $protocol +echo "ZT_NC_NETWORK = " $ZT_NC_NETWORK +echo "ZT_NC_NETWORK = " $ZT_NC_NETWORK + +echo "ZT_HOME_PATH = " $ZT_HOME_PATH +echo "DYLD_LIBRARY_PATH = " $DYLD_LIBRARY_PATH + +# Start ZeroTier service +echo "Starting ZeroTier background service..." +mkdir -p $ZT_HOME_PATH/networks.d +touch $ZT_HOME_PATH/networks.d/$NWID.conf +$BUILD_PATH/zerotier-sdk-service -U -p$RANDOM $ZT_HOME_PATH & +zt_service_pid=$! + +if [ $protocol="tcp" ]; then + echo "Starting TCP test..." + random_tcp_server_port=$RANDOM + ./$TEST_EXECUTABLE_PATH/Darwin.tcp_server.out $random_tcp_server_port & + tcp_server_pid=$! + echo "TCP SERVER AT = " $localAddr ":" $random_tcp_server_port + sleep 3 + ./$TEST_EXECUTABLE_PATH/Darwin.tcp_client.out $localAddr $random_tcp_server_port & + tcp_client_pid=$! +fi + +echo "Waiting for test to conclude..." +sleep 5 +kill -9 $zt_service_PID $tcp_server_pid $tcp_client_pid diff --git a/tests/api_test/servers.sh b/tests/api_test/servers.sh new file mode 100755 index 0000000..a4c1ef9 --- /dev/null +++ b/tests/api_test/servers.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +echo "\n\n\n\nStarting server(s)" + +# test.sh [udp|tcp|all] [nwid] +TEST_EXECUTABLE_PATH="build/tests" +protocol=$1 +NWID=$2 +HOME_DIR=$(pwd)/$TEST_EXECUTABLE_PATH +ZT_HOME_PATH=$(pwd)/$TEST_EXECUTABLE_PATH/zerotier + +BUILD_PATH=$(pwd)/build +TEST_PATH=$(pwd)/build/tests +localAddr="127.0.0.1" + +export ZT_NC_NETWORK=$ZT_HOME_PATH/nc_$NWID +export DYLD_LIBRARY_PATH=.$HOME_DIR/libztintercept.so + +echo "network = " $NWID +echo "protocol = " $protocol +echo "ZT_NC_NETWORK = " $ZT_NC_NETWORK +echo "ZT_NC_NETWORK = " $ZT_NC_NETWORK +echo "ZT_HOME_PATH = " $ZT_HOME_PATH +echo "DYLD_LIBRARY_PATH = " $DYLD_LIBRARY_PATH + +# Start ZeroTier service +echo "Starting ZeroTier background service..." +mkdir -p $ZT_HOME_PATH/networks.d +touch $ZT_HOME_PATH/networks.d/$NWID.conf +$BUILD_PATH/zerotier-sdk-service -U -p$RANDOM $ZT_HOME_PATH & +zt_service_pid=$! + +if [ $protocol="tcp" ]; then + echo "Starting TCP test..." + random_tcp_server_port=$RANDOM + echo $random_tcp_server_port > $TEST_EXECUTABLE_PATH/tcp_server.port + ./$TEST_EXECUTABLE_PATH/Darwin.tcp_server.out $random_tcp_server_port & + tcp_server_pid=$! + + # echo "TCP SERVER AT = " $localAddr ":" $random_tcp_server_port + # sleep 3 + # ./$TEST_EXECUTABLE_PATH/Darwin.tcp_client.out $localAddr $random_tcp_server_port & + # tcp_client_pid=$! +fi + +echo "Waiting for test to conclude..." +echo $random_tcp_server_port +# ,$random_udp_server_port +sleep 10 +echo "Cleaning up" +kill -9 $zt_service_PID $tcp_server_pid $tcp_client_pid diff --git a/tests/tcp_client.c b/tests/api_test/tcp_client.c similarity index 100% rename from tests/tcp_client.c rename to tests/api_test/tcp_client.c diff --git a/tests/tcp_server.c b/tests/api_test/tcp_server.c similarity index 100% rename from tests/tcp_server.c rename to tests/api_test/tcp_server.c diff --git a/tests/api_test/test.sh b/tests/api_test/test.sh new file mode 100755 index 0000000..ec0b767 --- /dev/null +++ b/tests/api_test/test.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +echo "Performing unit tests..." + +chmod 755 build/tests/servers.sh +chmod 755 build/tests/clients.sh + +./build/tests/servers.sh $1 $2 & +sleep 3 +./build/tests/clients.sh $1 $2 & \ No newline at end of file diff --git a/tests/udp_client.c b/tests/api_test/udp_client.c similarity index 100% rename from tests/udp_client.c rename to tests/api_test/udp_client.c diff --git a/tests/udp_server.c b/tests/api_test/udp_server.c similarity index 100% rename from tests/udp_server.c rename to tests/api_test/udp_server.c diff --git a/tests/cleanup.sh b/tests/cleanup.sh new file mode 100755 index 0000000..4a79b7a --- /dev/null +++ b/tests/cleanup.sh @@ -0,0 +1,11 @@ +killall zerotier-sdk-service + +killall Darwin.tcp_server.out +killall Darwin.tcp_client.out +killall Darwin.udp_server.out +killall Darwin.udp_client.out + +killall Linux.tcp_server.out +killall Linux.tcp_client.out +killall Linux.udp_server.out +killall Linux.udp_client.out \ No newline at end of file diff --git a/tests/simple_apps/tcp_client.c b/tests/simple_apps/tcp_client.c new file mode 100644 index 0000000..c8ec00c --- /dev/null +++ b/tests/simple_apps/tcp_client.c @@ -0,0 +1,49 @@ +// TCP Client test program + +#include +#include +#include +#include + +int atoi(const char *str); +int close(int filedes); + +int main(int argc , char *argv[]) +{ + if(argc < 3) { + printf("usage: client \n"); + return 1; + } + + int sock, port = atoi(argv[2]); + struct sockaddr_in server; + char message[1000] , server_reply[2000]; + + sock = socket(AF_INET , SOCK_STREAM , 0); + if (sock == -1) { + printf("could not create socket"); + } + server.sin_addr.s_addr = inet_addr(argv[1]); + server.sin_family = AF_INET; + server.sin_port = htons( port ); + + printf("connecting...\n"); + if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) { + perror("connect failed. Error"); + return 1; + } + printf("connected\n"); + + char *msg = "welcome to the machine!"; + // TX + if(send(sock, msg, strlen(msg), 0) < 0) { + printf("send failed"); + return 1; + } + else { + printf("sent message: %s\n", msg); + printf("len = %ld\n", strlen(msg)); + } + close(sock); + return 0; +} diff --git a/tests/simple_apps/tcp_server.c b/tests/simple_apps/tcp_server.c new file mode 100644 index 0000000..81dd26e --- /dev/null +++ b/tests/simple_apps/tcp_server.c @@ -0,0 +1,67 @@ +// TCP Server test program + +#include +#include +#include +#include +#include + +int atoi(const char *str); + +int main(int argc , char *argv[]) +{ + if(argc < 2) { + printf("usage: tcp_server \n"); + return 0; + } + + int socket_desc, client_sock, c, read_size, port = atoi(argv[1]); + struct sockaddr_in server , client; + char client_message[2000]; + + socket_desc = socket(AF_INET , SOCK_STREAM , 0); + if (socket_desc == -1) { + printf("could not create socket"); + return 0; + } + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(port); + + printf("binding on port %d\n", port); + if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) { + perror("bind failed. Error"); + return 0; + } + printf("listening\n"); + listen(socket_desc , 3); + printf("waiting to accept\n"); + c = sizeof(struct sockaddr_in); + + client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); + if (client_sock < 0) { + perror("accept failed"); + return 0; + } + printf("connection accepted\n reading...\n"); + + // RX + + int msglen = 1024; + unsigned long count = 0; + while(1) + { + count++; + int bytes_read = read(client_sock, client_message, msglen); + printf("[%lu] RX = (%d): ", count, bytes_read); + for(int i=0; i + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 1024 + +/* + * error - wrapper for perror + */ +void error(char *msg) { + perror(msg); + exit(0); +} + +int main(int argc, char **argv) { + int sockfd, portno, n; + int serverlen; + struct sockaddr_in serveraddr; + struct hostent *server; + char *hostname; + char buf[BUFSIZE]; + + /* check command line arguments */ + if (argc != 3) { + fprintf(stderr,"usage: %s \n", argv[0]); + exit(0); + } + hostname = argv[1]; + portno = atoi(argv[2]); + + /* socket: create the socket */ + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + error("ERROR opening socket"); + + /* gethostbyname: get the server's DNS entry */ + server = gethostbyname(hostname); + if (server == NULL) { + fprintf(stderr,"ERROR, no such host as %s\n", hostname); + exit(0); + } + + /* build the server's Internet address */ + bzero((char *) &serveraddr, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + bcopy((char *)server->h_addr, + (char *)&serveraddr.sin_addr.s_addr, server->h_length); + serveraddr.sin_port = htons(portno); + + /* get a message from the user */ + char *msg = "A message to the server!\0"; + fcntl(sockfd, F_SETFL, O_NONBLOCK); + long count = 0; + while(1) + { + count++; + printf("\nTX(%lu)...\n", count); + usleep(10000); + //bzero(buf, BUFSIZE); + //printf("\nPlease enter msg: "); + //fgets(buf, BUFSIZE, stdin); + + /* send the message to the server */ + serverlen = sizeof(serveraddr); + printf("A\n"); + n = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr *)&serveraddr, serverlen); + printf("B\n"); + //if (n < 0) + // error("ERROR in sendto"); + + /* print the server's reply */ + printf("C\n"); + memset(buf, 0, sizeof(buf)); + printf("D\n"); + n = recvfrom(sockfd, buf, BUFSIZE, 0, (struct sockaddr *)&serveraddr, (socklen_t *)&serverlen); + printf("E\n"); + //if (n < 0) + // printf("ERROR in recvfrom: %d", n); + printf("Echo from server: %s", buf); + } + return 0; +} diff --git a/tests/simple_apps/udp_server.c b/tests/simple_apps/udp_server.c new file mode 100755 index 0000000..c6bd4b3 --- /dev/null +++ b/tests/simple_apps/udp_server.c @@ -0,0 +1,83 @@ +// UDP Server test program + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXBUF 1024*1024 + +void echo( int sd ) { + char bufin[MAXBUF]; + struct sockaddr_in remote; + int n; + socklen_t len = sizeof(remote); + long count = 0; + + while (1) { + usleep(50); + count++; + // read a datagram from the socket (put result in bufin) + n=recvfrom(sd,bufin,MAXBUF,0,(struct sockaddr *)&remote,&len); + // print out the address of the sender + printf("DGRAM from %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port)); + + if (n<0) { + perror("Error receiving data"); + } else { + printf("GOT %d BYTES (count = %ld)\n", n, count); + // Got something, just send it back + // sendto(sd,bufin,n,0,(struct sockaddr *)&remote,len); + } + } +} + +int main(int argc, char *argv[]) { + + if(argc < 2) { + printf("usage: udp_server \n"); + return 0; + } + int ld, port = atoi(argv[1]); + socklen_t len; + struct sockaddr_in skaddr; + struct sockaddr_in skaddr2; + + // Create socket + if ((ld = socket( PF_INET, SOCK_DGRAM, 0)) < 0) { + printf("error creating socket\n"); + return 0; + } + // Create address + skaddr.sin_family = AF_INET; + skaddr.sin_addr.s_addr = htonl(INADDR_ANY); + skaddr.sin_port = htons(port); + // Bind to address + if (bind(ld, (struct sockaddr *) &skaddr, sizeof(skaddr))<0) { + printf("error binding\n"); + return 0; + } + // find out what port we were assigned + len = sizeof( skaddr2 ); + if (getsockname(ld, (struct sockaddr *) &skaddr2, &len)<0) { + printf("error getsockname\n"); + return 0; + } + // Display address:port to verify it was sent over RPC correctly + port = ntohs(skaddr2.sin_port); + int ip = skaddr2.sin_addr.s_addr; + unsigned char d[4]; + d[0] = ip & 0xFF; + d[1] = (ip >> 8) & 0xFF; + d[2] = (ip >> 16) & 0xFF; + d[3] = (ip >> 24) & 0xFF; + printf("bound to address: %d.%d.%d.%d : %d\n", d[0],d[1],d[2],d[3], port); + + // RX + echo(ld); + return(0); +}