diff --git a/README.md b/README.md
index eb198a4..99adf66 100644
--- a/README.md
+++ b/README.md
@@ -2,15 +2,15 @@
Library edition of [ZeroTier](https://github.com/zerotier/ZeroTierOne)
***
-
+
-The ZeroTier SDK is composed of two libraries: `libztcore` which is the platform-agnostic network hypervisor, and `libzt` which is the network hypervisor paired with a userspace network stack. `libzt` is a superset of `libztcore` and is distinguished by the fact that it exposes a standard [socket API](https://en.wikipedia.org/wiki/Berkeley_sockets) and simple network control API. With these libraries the stack and virtual link are exclusive to your app and traffic is fully encrypted via the [Salsa20](https://en.wikipedia.org/wiki/Salsa20) cipher. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml?pk_campaign=github_libzt)
+The ZeroTier SDK is composed of two libraries: `libztcore` which is the platform-agnostic network hypervisor, and `libzt` which is the network hypervisor paired with a userspace network stack. `libzt` is a superset of `libztcore` and is distinguished by the fact that it exposes a standard socket API and simple network control API. With these libraries the stack and virtual link are exclusive to your app and traffic is fully encrypted via the [Salsa20](https://en.wikipedia.org/wiki/Salsa20) cipher. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml)
***
-For a complete example, see [test/simple.cpp](test/simple.cpp), and anything in the [examples](examples) directory. With no error checking, a paraphrased example is as follows:
+For a complete example, see [test/simple.cpp](test/simple.cpp) or [test/example.cpp](test/example.cpp). With no error checking, a paraphrased example is as follows:
```
#include "ZeroTier.h"
@@ -38,6 +38,8 @@ int main()
...
```
+The complete API specification can be found here: [API.md](API.md)
+
***
## Build
diff --git a/test/example.cpp b/test/example.cpp
new file mode 100644
index 0000000..11ff33c
--- /dev/null
+++ b/test/example.cpp
@@ -0,0 +1,280 @@
+#include
+#include
+#include
+#include
+
+#include "ZeroTier.h"
+
+bool node_ready = false;
+bool network_ready = false;
+
+// Example callbacks
+void myZeroTierEventCallback(struct zts_callback_msg *msg)
+{
+ //
+ // Node events
+ //
+ if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) {
+ printf("ZTS_EVENT_NODE_ONLINE, node=%llx\n", msg->node->address);
+ node_ready = true;
+ // ZeroTier service is running and online
+ }
+ if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) {
+ printf("ZTS_EVENT_NODE_OFFLINE\n");
+ node_ready = false;
+ // ZeroTier service is running and online
+ }
+ if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) {
+ printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n");
+ // ZeroTier service has stopped
+ }
+
+ //
+ // Virtual network events
+ //
+ if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) {
+ printf("ZTS_EVENT_NETWORK_NOT_FOUND --- network=%llx\n", msg->network->nwid);
+ // Is your nwid incorrect?
+ }
+ if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) {
+ printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- network=%llx\n", msg->network->nwid);
+ // Node is requesting network config details from controller, please wait
+ }
+ if (msg->eventCode == ZTS_EVENT_NETWORK_ACCESS_DENIED) {
+ printf("ZTS_EVENT_NETWORK_ACCESS_DENIED --- network=%llx\n", msg->network->nwid);
+ // This node is not authorized to join nwid
+ }
+ if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) {
+ printf("ZTS_EVENT_NETWORK_READY_IP4 --- network=%llx\n", msg->network->nwid);
+ network_ready = true;
+ // IPv4 traffic can now be processed for nwid
+ }
+ if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP6) {
+ printf("ZTS_EVENT_NETWORK_READY_IP6 --- network=%llx\n", msg->network->nwid);
+ network_ready = true;
+ }
+ if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) {
+ printf("ZTS_EVENT_NETWORK_DOWN --- network=%llx\n", msg->network->nwid);
+ // Can happen if another thread called zts_leave()
+ }
+
+ //
+ // Network stack events
+ //
+ if (msg->eventCode == ZTS_EVENT_NETIF_UP) {
+ printf("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n",
+ msg->netif->nwid,
+ msg->netif->mac,
+ msg->netif->mtu);
+ network_ready = true;
+ }
+ if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) {
+ printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n",
+ msg->netif->nwid,
+ msg->netif->mac);
+
+ network_ready = true;
+ }
+
+ //
+ // Address events
+ //
+ if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) {
+ char ipstr[INET_ADDRSTRLEN];
+ struct sockaddr_in *in4 = (struct sockaddr_in*)&(msg->addr->addr);
+ inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN);
+ printf("ZTS_EVENT_ADDR_NEW_IP4 --- addr=%s (on network=%llx)\n",
+ ipstr, msg->addr->nwid);
+ }
+ if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) {
+ char ipstr[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(msg->addr->addr);
+ inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN);
+ printf("ZTS_EVENT_ADDR_NEW_IP6 --- addr=%s (on network=%llx)\n",
+ ipstr, msg->addr->nwid);
+ }
+ if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) {
+ char ipstr[INET_ADDRSTRLEN];
+ struct sockaddr_in *in4 = (struct sockaddr_in*)&(msg->addr->addr);
+ inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN);
+ printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- addr=%s (on network=%llx)\n",
+ ipstr, msg->addr->nwid);
+ }
+ if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) {
+ char ipstr[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(msg->addr->addr);
+ inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN);
+ printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- addr=%s (on network=%llx)\n",
+ ipstr, msg->addr->nwid);
+ }
+
+ //
+ // Peer events
+ //
+ if (msg->eventCode == ZTS_EVENT_PEER_P2P) {
+ printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address);
+ // A direct path is known for nodeId
+ }
+ if (msg->eventCode == ZTS_EVENT_PEER_RELAY) {
+ printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address);
+ // No direct path is known for nodeId
+ }
+}
+
+void printPeerDetails(struct zts_peer_details *pd)
+{
+ printf("\npeer=%llx, latency=%d, version=%d.%d.%d, pathCount=%d\n",
+ pd->address,
+ pd->latency,
+ pd->versionMajor,
+ pd->versionMinor,
+ pd->versionRev,
+ pd->pathCount);
+ // Print all known paths for each peer
+ for (int j=0; jpathCount; j++) {
+ char ipstr[INET6_ADDRSTRLEN];
+ int port;
+ struct sockaddr *sa = (struct sockaddr *)&(pd->paths[j].address);
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *in4 = (struct sockaddr_in*)sa;
+ inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN);
+ port = ntohs(in4->sin_port);
+ }
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
+ inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN);
+ }
+ printf("\tpath[%d]=%s, port=%d\n", j, ipstr, port);
+ }
+}
+
+void getSinglePeerDetails(uint64_t peerId)
+{
+ struct zts_peer_details pd;
+ int err = zts_get_peer(&pd, peerId);
+
+ if (err == ZTS_ERR_OK) {
+ printf("(%d) call succeeded\n", err);
+ } if (err == ZTS_ERR_INVALID_ARG) {
+ printf("(%d) invalid argument\n", err);
+ return;
+ } if (err == ZTS_ERR_SERVICE) {
+ printf("(%d) error: service is unavailable\n", err);
+ return;
+ } if (err == ZTS_ERR_INVALID_OP) {
+ printf("(%d) error: invalid API operation\n", err);
+ return;
+ } if (err == ZTS_ERR_NO_RESULT) {
+ printf("(%d) error: object or result not found\n", err);
+ return;
+ }
+ if (err == 0) { // ZTS_ERR_OK
+ printPeerDetails(&pd);
+ }
+}
+
+// Similar to "zerotier-cli listpeers"
+void getAllPeerDetails()
+{
+ struct zts_peer_details pd[128];
+ /* This number should be large enough to handle the
+ expected number of peers. This call can also get
+ expensive for large numbers of peers. Consider using
+ get_peer(struct zts_peer_details *pds, uint64_t peerId)
+ instead */
+ int num = 128;
+ int err;
+ if ((err = zts_get_peers(pd, &num)) < 0) {
+ printf("error (%d)\n", err);
+ return;
+ }
+ if (num) {
+ printf("num=%d\n", num);
+ for (int i=0; i
+#include
+#include
+#include
+
+#include "ZeroTier.h"
+
+bool node_ready = false;
+bool network_ready = false;
+
+void myZeroTierEventCallback(struct zts_callback_msg *msg)
+{
+ switch (msg->eventCode)
+ {
+ case ZTS_EVENT_NODE_ONLINE:
+ printf("ZTS_EVENT_NODE_ONLINE, node=%llx\n", msg->node->address);
+ node_ready = true;
+ break;
+ case ZTS_EVENT_NODE_OFFLINE:
+ printf("ZTS_EVENT_NODE_OFFLINE\n");
+ node_ready = false;
+ break;
+ case ZTS_EVENT_NETWORK_READY_IP4:
+ printf("ZTS_EVENT_NETWORK_READY_IP4 --- network=%llx\n", msg->network->nwid);
+ network_ready = true;
+ break;
+ case ZTS_EVENT_PEER_P2P:
+ printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address);
+ break;
+ case ZTS_EVENT_PEER_RELAY:
+ printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address);
+ break;
+ // ...
+ default:
+ break;
+ }
+}
+
+int main()
+{
+ char *str = "welcome to the machine";
+ char *remoteIp = "11.7.7.223";
+ int remotePort = 8082;
+ int fd, err = 0;
+ struct zts_sockaddr_in addr;
+ addr.sin_family = ZTS_AF_INET;
+ addr.sin_addr.s_addr = inet_addr(remoteIp);
+ addr.sin_port = htons(remotePort);
+
+ // Set up ZeroTier service and wai for callbacks
+ int port = 9994;
+ int nwid = 0x0123456789abcdef;
+ zts_start("test/path", &myZeroTierEventCallback, port);
+ printf("Waiting for node to come online...\n");
+ while (!node_ready) { sleep(1); }
+ zts_join(nwid);
+ printf("Joined virtual network. Requesting configuration...\n");
+ while (!network_ready) { sleep(1); }
+
+ // Socket API example
+ if ((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ printf("error creating socket\n");
+ }
+ if ((err = zts_connect(fd, (const struct sockaddr *)&addr, sizeof(addr))) < 0) {
+ printf("error connecting to remote host\n");
+ }
+ if ((err = zts_write(fd, str, strlen(str))) < 0) {
+ printf("error writing to socket\n");
+ }
+ zts_close(fd);
+ zts_stop();
+ return 0;
+}
\ No newline at end of file