From 6e9bdffe9ddcda32a6c0431cacdd27d5daf33ce3 Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Thu, 27 Nov 2014 21:43:43 +0400 Subject: [PATCH] Switch GetMyExternalIP from HTTP request to STUN approach Special greetings to EmerCoin project --- novacoin-qt.pro | 2 + src/makefile.bsd | 1 + src/makefile.linux-mingw | 1 + src/makefile.mingw | 1 + src/makefile.osx | 1 + src/makefile.unix | 1 + src/ministun.h | 78 +++++++ src/net.cpp | 107 +--------- src/stun.cpp | 538 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 632 insertions(+), 98 deletions(-) create mode 100644 src/ministun.h create mode 100644 src/stun.cpp diff --git a/novacoin-qt.pro b/novacoin-qt.pro index feef761..5323ea0 100644 --- a/novacoin-qt.pro +++ b/novacoin-qt.pro @@ -209,6 +209,7 @@ HEADERS += src/qt/bitcoingui.h \ src/main.h \ src/miner.h \ src/net.h \ + src/ministun.h \ src/key.h \ src/db.h \ src/txdb.h \ @@ -288,6 +289,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/miner.cpp \ src/init.cpp \ src/net.cpp \ + src/stun.cpp \ src/irc.cpp \ src/checkpoints.cpp \ src/addrman.cpp \ diff --git a/src/makefile.bsd b/src/makefile.bsd index 5a30ebc..79db3f0 100644 --- a/src/makefile.bsd +++ b/src/makefile.bsd @@ -112,6 +112,7 @@ OBJS= \ obj/main.o \ obj/miner.o \ obj/net.o \ + obj/stun.o \ obj/protocol.o \ obj/bitcoinrpc.o \ obj/rpcdump.o \ diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 570bd3b..ebd2fdc 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -75,6 +75,7 @@ OBJS= \ obj/main.o \ obj/miner.o \ obj/net.o \ + obj/stun.o \ obj/protocol.o \ obj/bitcoinrpc.o \ obj/rpcdump.o \ diff --git a/src/makefile.mingw b/src/makefile.mingw index 74006f7..35f80ab 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -66,6 +66,7 @@ OBJS= \ obj/main.o \ obj/miner.o \ obj/net.o \ + obj/stun.o \ obj/protocol.o \ obj/bitcoinrpc.o \ obj/rpcdump.o \ diff --git a/src/makefile.osx b/src/makefile.osx index 1570fbd..72821e1 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -77,6 +77,7 @@ OBJS= \ obj/main.o \ obj/miner.o \ obj/net.o \ + obj/stun.o \ obj/protocol.o \ obj/bitcoinrpc.o \ obj/rpcdump.o \ diff --git a/src/makefile.unix b/src/makefile.unix index bcc102e..7c3b72a 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -118,6 +118,7 @@ OBJS= \ obj/miner.o \ obj/main.o \ obj/net.o \ + obj/stun.o \ obj/protocol.o \ obj/bitcoinrpc.o \ obj/rpcdump.o \ diff --git a/src/ministun.h b/src/ministun.h new file mode 100644 index 0000000..8b19df8 --- /dev/null +++ b/src/ministun.h @@ -0,0 +1,78 @@ +/* + * ministun.h + * Part of the ministun package + * + * STUN support code borrowed from Asterisk -- An open source telephony toolkit. + * Copyright (C) 1999 - 2006, Digium, Inc. + * Mark Spencer + * Standalone remake (c) 2009 Vladislav Grishenko + * + * This software is licensed under the terms of the GNU General + * Public License (GPL). Please see the file COPYING for details. + * + */ + +#define PACKAGE "ministun" +#define VERSION "0.1" + +#define STUN_SERVER "stun.xten.com" +#define STUN_PORT 3478 +#define STUN_COUNT 3 +#define STUN_TIMEOUT 3 + +typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id; + +struct stun_header { + unsigned short msgtype; + unsigned short msglen; + stun_trans_id id; + unsigned char ies[0]; +} __attribute__((packed)); + +struct stun_attr { + unsigned short attr; + unsigned short len; + unsigned char value[0]; +} __attribute__((packed)); + +/* + * The format normally used for addresses carried by STUN messages. + */ +struct stun_addr { + unsigned char unused; + unsigned char family; + unsigned short port; + unsigned int addr; +} __attribute__((packed)); + +#define STUN_IGNORE (0) +#define STUN_ACCEPT (1) + +/* STUN message types + * 'BIND' refers to transactions used to determine the externally + * visible addresses. 'SEC' refers to transactions used to establish + * a session key for subsequent requests. + * 'SEC' functionality is not supported here. + */ + +#define STUN_BINDREQ 0x0001 +#define STUN_BINDRESP 0x0101 +#define STUN_BINDERR 0x0111 +#define STUN_SECREQ 0x0002 +#define STUN_SECRESP 0x0102 +#define STUN_SECERR 0x0112 + +/* Basic attribute types in stun messages. + * Messages can also contain custom attributes (codes above 0x7fff) + */ +#define STUN_MAPPED_ADDRESS 0x0001 +#define STUN_RESPONSE_ADDRESS 0x0002 +#define STUN_CHANGE_REQUEST 0x0003 +#define STUN_SOURCE_ADDRESS 0x0004 +#define STUN_CHANGED_ADDRESS 0x0005 +#define STUN_USERNAME 0x0006 +#define STUN_PASSWORD 0x0007 +#define STUN_MESSAGE_INTEGRITY 0x0008 +#define STUN_ERROR_CODE 0x0009 +#define STUN_UNKNOWN_ATTRIBUTES 0x000a +#define STUN_REFLECTED_FROM 0x000b diff --git a/src/net.cpp b/src/net.cpp index 3b1ef2a..9ce8ec3 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -301,109 +301,20 @@ bool IsReachable(const CNetAddr& addr) return vfReachable[net] && !vfLimited[net]; } -bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) -{ - SOCKET hSocket; - if (!ConnectSocket(addrConnect, hSocket)) - return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); - - send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); - - string strLine; - while (RecvLine(hSocket, strLine)) - { - if (strLine.empty()) // HTTP response is separated from headers by blank line - { - while (true) - { - if (!RecvLine(hSocket, strLine)) - { - closesocket(hSocket); - return false; - } - if (pszKeyword == NULL) - break; - if (strLine.find(pszKeyword) != string::npos) - { - strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); - break; - } - } - closesocket(hSocket); - if (strLine.find("<") != string::npos) - strLine = strLine.substr(0, strLine.find("<")); - strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); - while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) - strLine.resize(strLine.size()-1); - CService addr(strLine,0,true); - printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); - if (!addr.IsValid() || !addr.IsRoutable()) - return false; - ipRet.SetIP(addr); - return true; - } - } - closesocket(hSocket); - return error("GetMyExternalIP() : connection closed"); -} +extern int GetExternalIPbySTUN(uint64_t rnd, struct sockaddr_in *mapped, const char **srv); // We now get our external IP from the IRC server first and only use this as a backup bool GetMyExternalIP(CNetAddr& ipRet) { - CService addrConnect; - const char* pszGet; - const char* pszKeyword; - - for (int nLookup = 0; nLookup <= 1; nLookup++) - for (int nHost = 1; nHost <= 2; nHost++) - { - // We should be phasing out our use of sites like these. If we need - // replacements, we should ask for volunteers to put this simple - // php file on their web server that prints the client IP: - // - if (nHost == 1) - { - addrConnect = CService("91.198.22.70",80); // checkip.dyndns.org - - if (nLookup == 1) - { - CService addrIP("checkip.dyndns.org", 80, true); - if (addrIP.IsValid()) - addrConnect = addrIP; - } - - pszGet = "GET / HTTP/1.1\r\n" - "Host: checkip.dyndns.org\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" - "Connection: close\r\n" - "\r\n"; - - pszKeyword = "Address:"; - } - else if (nHost == 2) - { - addrConnect = CService("74.208.43.192", 80); // www.showmyip.com - - if (nLookup == 1) - { - CService addrIP("www.showmyip.com", 80, true); - if (addrIP.IsValid()) - addrConnect = addrIP; - } - - pszGet = "GET /simple/ HTTP/1.1\r\n" - "Host: www.showmyip.com\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" - "Connection: close\r\n" - "\r\n"; - - pszKeyword = NULL; // Returns just IP address - } - - if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) - return true; + struct sockaddr_in mapped; + uint64 rnd = GetRand(~0LL); + const char *srv; + int rc = GetExternalIPbySTUN(rnd, &mapped, &srv); + if(rc >= 0) { + ipRet = CNetAddr(mapped.sin_addr); + printf("GetExternalIPbySTUN(%"PRI64u") returned %s in attempt %d; Server=%s\n", rnd, ipRet.ToStringIP().c_str(), rc, srv); + return true; } - return false; } diff --git a/src/stun.cpp b/src/stun.cpp new file mode 100644 index 0000000..c5863f0 --- /dev/null +++ b/src/stun.cpp @@ -0,0 +1,538 @@ +/* + * Get External IP address by STUN protocol + * + * Based on project Minimalistic STUN client "ministun" + * https://code.google.com/p/ministun/ + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + * + * STUN is described in RFC3489 and it is based on the exchange + * of UDP packets between a client and one or more servers to + * determine the externally visible address (and port) of the client + * once it has gone through the NAT boxes that connect it to the + * outside. + * The simplest request packet is just the header defined in + * struct stun_header, and from the response we may just look at + * one attribute, STUN_MAPPED_ADDRESS, that we find in the response. + * By doing more transactions with different server addresses we + * may determine more about the behaviour of the NAT boxes, of + * course - the details are in the RFC. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + */ + +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include + + +#include "ministun.h" + +/*---------------------------------------------------------------------*/ + +struct StunSrv { + char name[30]; + uint16_t port; +}; + +/*---------------------------------------------------------------------*/ +static const int StunSrvListQty = 263; // Must be PRIME!!!!! + +static struct StunSrv StunSrvList[263] = { + {"23.21.150.121", 3478}, + {"iphone-stun.strato-iphone.de", 3478}, + {"numb.viagenie.ca", 3478}, + {"s1.taraba.net", 3478}, + {"s2.taraba.net", 3478}, + {"stun.12connect.com", 3478}, + {"stun.12voip.com", 3478}, + {"stun.1und1.de", 3478}, + {"stun.2talk.co.nz", 3478}, + {"stun.2talk.com", 3478}, + {"stun.3clogic.com", 3478}, + {"stun.3cx.com", 3478}, + {"stun.a-mm.tv", 3478}, + {"stun.aa.net.uk", 3478}, + {"stun.acrobits.cz", 3478}, + {"stun.actionvoip.com", 3478}, + {"stun.advfn.com", 3478}, + {"stun.aeta-audio.com", 3478}, + {"stun.aeta.com", 3478}, + {"stun.alltel.com.au", 3478}, + {"stun.altar.com.pl", 3478}, + {"stun.annatel.net", 3478}, + {"stun.arbuz.ru", 3478}, + {"stun.avigora.com", 3478}, + {"stun.avigora.fr", 3478}, + {"stun.awa-shima.com", 3478}, + {"stun.awt.be", 3478}, + {"stun.b2b2c.ca", 3478}, + {"stun.bahnhof.net", 3478}, + {"stun.barracuda.com", 3478}, + {"stun.bluesip.net", 3478}, + {"stun.bmwgs.cz", 3478}, + {"stun.botonakis.com", 3478}, + {"stun.budgetphone.nl", 3478}, + {"stun.budgetsip.com", 3478}, + {"stun.cablenet-as.net", 3478}, + {"stun.callromania.ro", 3478}, + {"stun.callwithus.com", 3478}, + {"stun.cbsys.net", 3478}, + {"stun.chathelp.ru", 3478}, + {"stun.cheapvoip.com", 3478}, + {"stun.ciktel.com", 3478}, + {"stun.cloopen.com", 3478}, + {"stun.colouredlines.com.au", 3478}, + {"stun.comfi.com", 3478}, + {"stun.commpeak.com", 3478}, + {"stun.comtube.com", 3478}, + {"stun.comtube.ru", 3478}, + {"stun.cope.es", 3478}, + {"stun.counterpath.com", 3478}, + {"stun.counterpath.net", 3478}, + {"stun.cryptonit.net", 3478}, + {"stun.darioflaccovio.it", 3478}, + {"stun.datamanagement.it", 3478}, + {"stun.dcalling.de", 3478}, + {"stun.decanet.fr", 3478}, + {"stun.demos.ru", 3478}, + {"stun.develz.org", 3478}, + {"stun.dingaling.ca", 3478}, + {"stun.doublerobotics.com", 3478}, + {"stun.drogon.net", 3478}, + {"stun.duocom.es", 3478}, + {"stun.dus.net", 3478}, + {"stun.e-fon.ch", 3478}, + {"stun.easybell.de", 3478}, + {"stun.easycall.pl", 3478}, + {"stun.easyvoip.com", 3478}, + {"stun.efficace-factory.com", 3478}, + {"stun.einsundeins.com", 3478}, + {"stun.einsundeins.de", 3478}, + {"stun.ekiga.net", 3478}, + {"stun.epygi.com", 3478}, + {"stun.etoilediese.fr", 3478}, + {"stun.eyeball.com", 3478}, + {"stun.faktortel.com.au", 3478}, + {"stun.freecall.com", 3478}, + {"stun.freeswitch.org", 3478}, + {"stun.freevoipdeal.com", 3478}, + {"stun.fuzemeeting.com", 3478}, + {"stun.gmx.de", 3478}, + {"stun.gmx.net", 3478}, + {"stun.gradwell.com", 3478}, + {"stun.halonet.pl", 3478}, + {"stun.hoiio.com", 3478}, + {"stun.hosteurope.de", 3478}, + {"stun.ideasip.com", 3478}, + {"stun.imesh.com", 3478}, + {"stun.infra.net", 3478}, + {"stun.internetcalls.com", 3478}, + {"stun.intervoip.com", 3478}, + {"stun.ipcomms.net", 3478}, + {"stun.ipfire.org", 3478}, + {"stun.ippi.fr", 3478}, + {"stun.ipshka.com", 3478}, + {"stun.iptel.org", 3478}, + {"stun.irian.at", 3478}, + {"stun.it1.hr", 3478}, + {"stun.ivao.aero", 3478}, + {"stun.jappix.com", 3478}, + {"stun.jumblo.com", 3478}, + {"stun.justvoip.com", 3478}, + {"stun.kanet.ru", 3478}, + {"stun.kiwilink.co.nz", 3478}, + {"stun.kundenserver.de", 3478}, + {"stun.l.google.com", 19302}, + {"stun.linea7.net", 3478}, + {"stun.linphone.org", 3478}, + {"stun.liveo.fr", 3478}, + {"stun.lowratevoip.com", 3478}, + {"stun.lugosoft.com", 3478}, + {"stun.lundimatin.fr", 3478}, + {"stun.magnet.ie", 3478}, + {"stun.manle.com", 3478}, + {"stun.mgn.ru", 3478}, + {"stun.mit.de", 3478}, + {"stun.mitake.com.tw", 3478}, + {"stun.miwifi.com", 3478}, + {"stun.modulus.gr", 3478}, + {"stun.mozcom.com", 3478}, + {"stun.myvoiptraffic.com", 3478}, + {"stun.mywatson.it", 3478}, + {"stun.nas.net", 3478}, + {"stun.neotel.co.za", 3478}, + {"stun.netappel.com", 3478}, + {"stun.netappel.fr", 3478}, + {"stun.netgsm.com.tr", 3478}, + {"stun.nfon.net", 3478}, + {"stun.noblogs.org", 3478}, + {"stun.noc.ams-ix.net", 3478}, + {"stun.node4.co.uk", 3478}, + {"stun.nonoh.net", 3478}, + {"stun.nottingham.ac.uk", 3478}, + {"stun.nova.is", 3478}, + {"stun.nventure.com", 3478}, + {"stun.on.net.mk", 3478}, + {"stun.ooma.com", 3478}, + {"stun.ooonet.ru", 3478}, + {"stun.outland-net.de", 3478}, + {"stun.ozekiphone.com", 3478}, + {"stun.patlive.com", 3478}, + {"stun.personal-voip.de", 3478}, + {"stun.petcube.com", 3478}, + {"stun.phone.com", 3478}, + {"stun.phoneserve.com", 3478}, + {"stun.pjsip.org", 3478}, + {"stun.poivy.com", 3478}, + {"stun.powerpbx.org", 3478}, + {"stun.powervoip.com", 3478}, + {"stun.ppdi.com", 3478}, + {"stun.prizee.com", 3478}, + {"stun.qq.com", 3478}, + {"stun.qvod.com", 3478}, + {"stun.rackco.com", 3478}, + {"stun.rapidnet.de", 3478}, + {"stun.rb-net.com", 3478}, + {"stun.remote-learner.net", 3478}, + {"stun.rixtelecom.se", 3478}, + {"stun.rockenstein.de", 3478}, + {"stun.rolmail.net", 3478}, + {"stun.rounds.com", 3478}, + {"stun.rynga.com", 3478}, + {"stun.samsungsmartcam.com", 3478}, + {"stun.schlund.de", 3478}, + {"stun.services.mozilla.com", 3478}, + {"stun.sigmavoip.com", 3478}, + {"stun.sip.us", 3478}, + {"stun.sipdiscount.com", 3478}, + {"stun.sipgate.net", 10000}, + {"stun.sipgate.net", 3478}, + {"stun.siplogin.de", 3478}, + {"stun.sipnet.net", 3478}, + {"stun.sipnet.ru", 3478}, + {"stun.siportal.it", 3478}, + {"stun.sippeer.dk", 3478}, + {"stun.siptraffic.com", 3478}, + {"stun.skylink.ru", 3478}, + {"stun.sma.de", 3478}, + {"stun.smartvoip.com", 3478}, + {"stun.smsdiscount.com", 3478}, + {"stun.snafu.de", 3478}, + {"stun.softjoys.com", 3478}, + {"stun.solcon.nl", 3478}, + {"stun.solnet.ch", 3478}, + {"stun.sonetel.com", 3478}, + {"stun.sonetel.net", 3478}, + {"stun.sovtest.ru", 3478}, + {"stun.speedy.com.ar", 3478}, + {"stun.spokn.com", 3478}, + {"stun.srce.hr", 3478}, + {"stun.ssl7.net", 3478}, + {"stun.stunprotocol.org", 3478}, + {"stun.symform.com", 3478}, + {"stun.symplicity.com", 3478}, + {"stun.sysadminman.net", 3478}, + {"stun.t-online.de", 3478}, + {"stun.tagan.ru", 3478}, + {"stun.tatneft.ru", 3478}, + {"stun.teachercreated.com", 3478}, + {"stun.tel.lu", 3478}, + {"stun.telbo.com", 3478}, + {"stun.telefacil.com", 3478}, + {"stun.tis-dialog.ru", 3478}, + {"stun.tng.de", 3478}, + {"stun.twt.it", 3478}, + {"stun.u-blox.com", 3478}, + {"stun.ucallweconn.net", 3478}, + {"stun.ucsb.edu", 3478}, + {"stun.ucw.cz", 3478}, + {"stun.uls.co.za", 3478}, + {"stun.unseen.is", 3478}, + {"stun.usfamily.net", 3478}, + {"stun.veoh.com", 3478}, + {"stun.vidyo.com", 3478}, + {"stun.vipgroup.net", 3478}, + {"stun.virtual-call.com", 3478}, + {"stun.viva.gr", 3478}, + {"stun.vivox.com", 3478}, + {"stun.vline.com", 3478}, + {"stun.vo.lu", 3478}, + {"stun.vodafone.ro", 3478}, + {"stun.voicetrading.com", 3478}, + {"stun.voip.aebc.com", 3478}, + {"stun.voip.blackberry.com", 3478}, + {"stun.voip.eutelia.it", 3478}, + {"stun.voiparound.com", 3478}, + {"stun.voipblast.com", 3478}, + {"stun.voipbuster.com", 3478}, + {"stun.voipbusterpro.com", 3478}, + {"stun.voipcheap.co.uk", 3478}, + {"stun.voipcheap.com", 3478}, + {"stun.voipfibre.com", 3478}, + {"stun.voipgain.com", 3478}, + {"stun.voipgate.com", 3478}, + {"stun.voipinfocenter.com", 3478}, + {"stun.voipplanet.nl", 3478}, + {"stun.voippro.com", 3478}, + {"stun.voipraider.com", 3478}, + {"stun.voipstunt.com", 3478}, + {"stun.voipwise.com", 3478}, + {"stun.voipzoom.com", 3478}, + {"stun.voxgratia.org", 3478}, + {"stun.voxox.com", 3478}, + {"stun.voys.nl", 3478}, + {"stun.voztele.com", 3478}, + {"stun.vyke.com", 3478}, + {"stun.webcalldirect.com", 3478}, + {"stun.whoi.edu", 3478}, + {"stun.wifirst.net", 3478}, + {"stun.wwdl.net", 3478}, + {"stun.xs4all.nl", 3478}, + {"stun.xtratelecom.es", 3478}, + {"stun.yesss.at", 3478}, + {"stun.zadarma.com", 3478}, + {"stun.zadv.com", 3478}, + {"stun.zoiper.com", 3478}, + {"stun1.faktortel.com.au", 3478}, + {"stun1.l.google.com", 19302}, + {"stun1.voiceeclipse.net", 3478}, + {"stun2.l.google.com", 19302}, + {"stun3.l.google.com", 19302}, + {"stun4.l.google.com", 19302}, + {"stunserver.org", 3478}, + {"stun.antisip.com", 3478} +}; + + +/* wrapper to send an STUN message */ +static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp) +{ + return sendto(s, (const char *)resp, ntohs(resp->msglen) + sizeof(*resp), 0, + (struct sockaddr *)dst, sizeof(*dst)); +} + +/* helper function to generate a random request id */ +static uint64_t randfiller; +static void stun_req_id(struct stun_header *req) +{ + const uint64_t *S_block = (const uint64_t *)StunSrvList; + req->id.id[0] |= 0x55555555; + req->id.id[1] &= 0x55555555; + req->id.id[2] |= 0x55555555; + req->id.id[3] &= 0x55555555; + register char x = 20; + do { + uint32_t s_elm = S_block[(uint8_t)randfiller]; + randfiller ^= (randfiller << 5) | (randfiller >> (64 - 5)); + randfiller += s_elm ^ x; + req->id.id[x & 3] ^= randfiller + (randfiller >> 13); + } while(--x); +} + +/* callback type to be invoked on stun responses. */ +typedef int (stun_cb_f)(struct stun_attr *attr, void *arg); + +/* handle an incoming STUN message. + * + * Do some basic sanity checks on packet size and content, + * try to extract a bit of information, and possibly reply. + * At the moment this only processes BIND requests, and returns + * the externally visible address of the request. + * If a callback is specified, invoke it with the attribute. + */ +static int stun_handle_packet(int s, struct sockaddr_in *src, + unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg) +{ + struct stun_header *hdr = (struct stun_header *)data; + struct stun_attr *attr; + int ret = len; + unsigned int x; + + /* On entry, 'len' is the length of the udp payload. After the + * initial checks it becomes the size of unprocessed options, + * while 'data' is advanced accordingly. + */ + if (len < sizeof(struct stun_header)) + return -20; + + len -= sizeof(struct stun_header); + data += sizeof(struct stun_header); + x = ntohs(hdr->msglen); /* len as advertised in the message */ + if(x < len) + len = x; + + while (len) { + if (len < sizeof(struct stun_attr)) { + ret = -21; + break; + } + attr = (struct stun_attr *)data; + /* compute total attribute length */ + x = ntohs(attr->len) + sizeof(struct stun_attr); + if (x > len) { + ret = -22; + break; + } + stun_cb(attr, arg); + //if (stun_process_attr(&st, attr)) { + // ret = -23; + // break; + // } + /* Clear attribute id: in case previous entry was a string, + * this will act as the terminator for the string. + */ + attr->attr = 0; + data += x; + len -= x; + } // while + /* Null terminate any string. + * XXX NOTE, we write past the size of the buffer passed by the + * caller, so this is potentially dangerous. The only thing that + * saves us is that usually we read the incoming message in a + * much larger buffer + */ + *data = '\0'; + + /* Now prepare to generate a reply, which at the moment is done + * only for properly formed (len == 0) STUN_BINDREQ messages. + */ + + return ret; +} + +/* Extract the STUN_MAPPED_ADDRESS from the stun response. + * This is used as a callback for stun_handle_response + * when called from stun_request. + */ +static int stun_get_mapped(struct stun_attr *attr, void *arg) +{ + struct stun_addr *addr = (struct stun_addr *)(attr + 1); + struct sockaddr_in *sa = (struct sockaddr_in *)arg; + + if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) + return 1; /* not us. */ + sa->sin_port = addr->port; + sa->sin_addr.s_addr = addr->addr; + return 0; +} + +/*---------------------------------------------------------------------*/ + +static int StunRequest2(int sock, struct sockaddr_in *server, struct sockaddr_in *mapped) { + + struct stun_header *req; + unsigned char reqdata[1024]; + + req = (struct stun_header *)reqdata; + stun_req_id(req); + int reqlen = 0; + req->msgtype = 0; + req->msglen = 0; + req->msglen = htons(reqlen); + req->msgtype = htons(STUN_BINDREQ); + + unsigned char reply_buf[1024]; + fd_set rfds; + struct timeval to = { STUN_TIMEOUT, 0 }; + struct sockaddr_in src; +#ifdef WIN32 + int srclen; +#else + socklen_t srclen; +#endif + + int res = stun_send(sock, server, req); + if(res < 0) + return -10; + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + res = select(sock + 1, &rfds, NULL, NULL, &to); + if (res <= 0) /* timeout or error */ + return -11; + memset(&src, 0, sizeof(src)); + srclen = sizeof(src); + /* XXX pass -1 in the size, because stun_handle_packet might + * write past the end of the buffer. + */ + res = recvfrom(sock, (char *)reply_buf, sizeof(reply_buf) - 1, + 0, (struct sockaddr *)&src, &srclen); + if (res <= 0) + return -12; + memset(mapped, 0, sizeof(struct sockaddr_in)); + return stun_handle_packet(sock, &src, reply_buf, res, stun_get_mapped, mapped); +} // StunRequest2 + +/*---------------------------------------------------------------------*/ +static int StunRequest(const char *host, uint16_t port, struct sockaddr_in *mapped) { + struct hostent *hostinfo = gethostbyname(host); + if(hostinfo == NULL) + return -1; + + struct sockaddr_in server, client; + memset(&server, 0, sizeof(server)); + memset(&client, 0, sizeof(client)); + server.sin_family = client.sin_family = AF_INET; + + server.sin_addr = *(struct in_addr*) hostinfo->h_addr; + server.sin_port = htons(port); + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if(sock < 0) + return -2; + + client.sin_addr.s_addr = htonl(INADDR_ANY); + + int rc = -3; + if(bind(sock, (struct sockaddr*)&client, sizeof(client)) >= 0) + rc = StunRequest2(sock, &server, mapped); + + close(sock); + return rc; +} // StunRequest + +/*---------------------------------------------------------------------*/ +// Input: two random values (pos, step) for generate uniuqe way over server +// list +// Output: populate struct struct mapped +// Retval: + +int GetExternalIPbySTUN(uint64_t rnd, struct sockaddr_in *mapped, const char **srv) { + randfiller = rnd; + uint16_t pos = rnd; + uint16_t step; + do { + rnd = (rnd >> 8) | 0xff00000000000000LL; + step = rnd % StunSrvListQty; + } while(step == 0); + + uint16_t attempt; + for(attempt = 1; attempt < StunSrvListQty * 2; attempt++) { + pos = (pos + step) % StunSrvListQty; + int rc = StunRequest(*srv = StunSrvList[pos].name, StunSrvList[pos].port, mapped); + if(rc >= 0) + return attempt; + // fprintf(stderr, "Lookup: %s:%u\t%s\t%d\n", StunSrvList[pos].name, + // StunSrvList[pos].port, inet_ntoa(mapped->sin_addr), rc); + } + return -1; +} -- 1.7.1