diff --git a/src/napi_socket.h b/src/napi_socket.h index f523ca2..b2f7b4b 100644 --- a/src/napi_socket.h +++ b/src/napi_socket.h @@ -9,6 +9,37 @@ namespace NAPI { + static struct { + char host[32]; + uint16 port; + } stunServers[] = { + { "stun.l.google.com", 19302 }, + { "stun1.l.google.com", 19302 }, + { "stun2.l.google.com", 19302 }, + { "stun3.l.google.com", 19302 }, + { "stun4.l.google.com", 19302 }, + }; + + struct stun_header { + uint16 msgtype; + uint16 msglen; + uint32 id[4]; + }; + + struct stun_attr { + uint16 attr; + uint16 len; + }; + + #pragma pack(push, 1) + struct stun_addr { + unsigned char unused; + unsigned char family; + unsigned short port; + unsigned int addr; + }; + #pragma pack(pop) + struct Peer { uint16 port; uint32 ip; @@ -22,6 +53,9 @@ namespace NAPI { sockaddr_in addr; uint16 port; + Peer peer; + bool waitAddress; + void init() { sock = INVALID_SOCKET; @@ -41,6 +75,96 @@ namespace NAPI { WSACleanup(); } + void handleAddress(const uint8 *data, int size) { + waitAddress = false; + if (size < sizeof(stun_header)) + return; + stun_header *hdr = (stun_header*)data; + data += sizeof(stun_header); + size -= sizeof(stun_header); + int i = ntohs(hdr->msglen); + if (i < size) + size = i; + + while (size) { + if (size < sizeof(stun_attr)) + return; + + stun_attr *attr = (stun_attr*)data; + i = ntohs(attr->len) + sizeof(stun_attr); + if (i > size) + return; + + stun_addr *addr = (stun_addr*)(data + sizeof(stun_attr)); + + if (attr->attr == 0x0100 && ntohs(attr->len) == 8) { + peer.ip = addr->addr; + peer.port = addr->port; + LOG("network: acquire UDP tunnel %s:%d\n", inet_ntoa(*(in_addr*)&peer.ip), ntohs(peer.port)); + } + + data += i; + size -= i; + } + } + + int send(const Peer &to, const void *data, int size) { + if (sock == INVALID_SOCKET) return false; + + addr.sin_addr.s_addr = to.ip; + addr.sin_port = to.port; + return sendto(sock, (const char*)data, size, 0, (sockaddr*)&addr, sizeof(addr)); + } + + int recv(Peer &from, void *data, int size) { + if (sock == INVALID_SOCKET) return false; + + int i = sizeof(addr); + int count = recvfrom(sock, (char*)data, size, 0, (sockaddr*)&addr, &i); + if (count > 0) { + from.ip = addr.sin_addr.s_addr; + from.port = addr.sin_port; + } + + if (waitAddress) { + handleAddress((uint8*)data, size); + return 0; + } + + return count; + } + + void broadcast(const void *data, int size) { + Peer peer; + peer.ip = INADDR_BROADCAST; + peer.port = htons(port); + send(peer, data, size); + } + + void requestAddress() { + int stunIndex = 0; + + hostent *hostinfo = gethostbyname(stunServers[stunIndex].host); + if (!hostinfo) + return; + + stun_header req; + + for (int i = 0; i < 4 * 2; i++) + ((uint16*)req.id)[i] = rand(); + + req.msgtype = 0; + req.msglen = 0; + req.msgtype = 0x0100; + + Peer peer; + peer.ip = *(uint32*)hostinfo->h_addr; + peer.port = htons(stunServers[stunIndex].port); + send(peer, &req, sizeof(req)); + + waitAddress = true; + } + void listen(uint16 port) { NAPI::port = port; @@ -64,39 +188,20 @@ namespace NAPI { memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); - if (bind(sock, (sockaddr*)&addr, sizeof(addr))) + if (bind(sock, (sockaddr*)&addr, sizeof(addr)) < 0) { LOG("! network: unable to bind socket on port (%d)\n", (int)port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = 0; + if (bind(sock, (sockaddr*)&addr, sizeof(addr)) < 0) { + LOG("! network: unable to bind socket on ANY port\n"); + } + } on = 1; if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on))) LOG("! network: unable to enable broadcasting\n"); - } - int send(const Peer &to, const void *data, int size) { - if (sock == INVALID_SOCKET) return false; - - addr.sin_addr.s_addr = to.ip; - addr.sin_port = to.port; - return sendto(sock, (const char*)data, size, 0, (sockaddr*)&addr, sizeof(addr)); - } - - int recv(Peer &from, void *data, int size) { - if (sock == INVALID_SOCKET) return false; - - int i = sizeof(addr); - int count = recvfrom(sock, (char*)data, size, 0, (sockaddr*)&addr, &i); - if (count > 0) { - from.ip = addr.sin_addr.s_addr; - from.port = addr.sin_port; - } - return count; - } - - void broadcast(const void *data, int size) { - Peer peer; - peer.ip = INADDR_BROADCAST; - peer.port = htons(port); - send(peer, data, size); + requestAddress(); } }