2 * Get External IP address by STUN protocol
4 * Based on project Minimalistic STUN client "ministun"
5 * https://code.google.com/p/ministun/
7 * This program is free software, distributed under the terms of
8 * the GNU General Public License Version 2. See the LICENSE file
9 * at the top of the source tree.
11 * STUN is described in RFC3489 and it is based on the exchange
12 * of UDP packets between a client and one or more servers to
13 * determine the externally visible address (and port) of the client
14 * once it has gone through the NAT boxes that connect it to the
16 * The simplest request packet is just the header defined in
17 * struct stun_header, and from the response we may just look at
18 * one attribute, STUN_MAPPED_ADDRESS, that we find in the response.
19 * By doing more transactions with different server addresses we
20 * may determine more about the behaviour of the NAT boxes, of
21 * course - the details are in the RFC.
23 * All STUN packets start with a simple header made of a type,
24 * length (excluding the header) and a 16-byte random transaction id.
25 * Following the header we may have zero or more attributes, each
26 * structured as a type, length and a value (whose format depends
27 * on the type, but often contains addresses).
28 * Of course all fields are in network format.
35 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
54 /*---------------------------------------------------------------------*/
61 /*---------------------------------------------------------------------*/
62 static const int StunSrvListQty = 263; // Must be PRIME!!!!!
64 static struct StunSrv StunSrvList[263] = {
65 {"23.21.150.121", 3478},
66 {"iphone-stun.strato-iphone.de", 3478},
67 {"numb.viagenie.ca", 3478},
68 {"s1.taraba.net", 3478},
69 {"s2.taraba.net", 3478},
70 {"stun.12connect.com", 3478},
71 {"stun.12voip.com", 3478},
72 {"stun.1und1.de", 3478},
73 {"stun.2talk.co.nz", 3478},
74 {"stun.2talk.com", 3478},
75 {"stun.3clogic.com", 3478},
76 {"stun.3cx.com", 3478},
77 {"stun.a-mm.tv", 3478},
78 {"stun.aa.net.uk", 3478},
79 {"stun.acrobits.cz", 3478},
80 {"stun.actionvoip.com", 3478},
81 {"stun.advfn.com", 3478},
82 {"stun.aeta-audio.com", 3478},
83 {"stun.aeta.com", 3478},
84 {"stun.alltel.com.au", 3478},
85 {"stun.altar.com.pl", 3478},
86 {"stun.annatel.net", 3478},
87 {"stun.arbuz.ru", 3478},
88 {"stun.avigora.com", 3478},
89 {"stun.avigora.fr", 3478},
90 {"stun.awa-shima.com", 3478},
91 {"stun.awt.be", 3478},
92 {"stun.b2b2c.ca", 3478},
93 {"stun.bahnhof.net", 3478},
94 {"stun.barracuda.com", 3478},
95 {"stun.bluesip.net", 3478},
96 {"stun.bmwgs.cz", 3478},
97 {"stun.botonakis.com", 3478},
98 {"stun.budgetphone.nl", 3478},
99 {"stun.budgetsip.com", 3478},
100 {"stun.cablenet-as.net", 3478},
101 {"stun.callromania.ro", 3478},
102 {"stun.callwithus.com", 3478},
103 {"stun.cbsys.net", 3478},
104 {"stun.chathelp.ru", 3478},
105 {"stun.cheapvoip.com", 3478},
106 {"stun.ciktel.com", 3478},
107 {"stun.cloopen.com", 3478},
108 {"stun.colouredlines.com.au", 3478},
109 {"stun.comfi.com", 3478},
110 {"stun.commpeak.com", 3478},
111 {"stun.comtube.com", 3478},
112 {"stun.comtube.ru", 3478},
113 {"stun.cope.es", 3478},
114 {"stun.counterpath.com", 3478},
115 {"stun.counterpath.net", 3478},
116 {"stun.cryptonit.net", 3478},
117 {"stun.darioflaccovio.it", 3478},
118 {"stun.datamanagement.it", 3478},
119 {"stun.dcalling.de", 3478},
120 {"stun.decanet.fr", 3478},
121 {"stun.demos.ru", 3478},
122 {"stun.develz.org", 3478},
123 {"stun.dingaling.ca", 3478},
124 {"stun.doublerobotics.com", 3478},
125 {"stun.drogon.net", 3478},
126 {"stun.duocom.es", 3478},
127 {"stun.dus.net", 3478},
128 {"stun.e-fon.ch", 3478},
129 {"stun.easybell.de", 3478},
130 {"stun.easycall.pl", 3478},
131 {"stun.easyvoip.com", 3478},
132 {"stun.efficace-factory.com", 3478},
133 {"stun.einsundeins.com", 3478},
134 {"stun.einsundeins.de", 3478},
135 {"stun.ekiga.net", 3478},
136 {"stun.epygi.com", 3478},
137 {"stun.etoilediese.fr", 3478},
138 {"stun.eyeball.com", 3478},
139 {"stun.faktortel.com.au", 3478},
140 {"stun.freecall.com", 3478},
141 {"stun.freeswitch.org", 3478},
142 {"stun.freevoipdeal.com", 3478},
143 {"stun.fuzemeeting.com", 3478},
144 {"stun.gmx.de", 3478},
145 {"stun.gmx.net", 3478},
146 {"stun.gradwell.com", 3478},
147 {"stun.halonet.pl", 3478},
148 {"stun.hoiio.com", 3478},
149 {"stun.hosteurope.de", 3478},
150 {"stun.ideasip.com", 3478},
151 {"stun.imesh.com", 3478},
152 {"stun.infra.net", 3478},
153 {"stun.internetcalls.com", 3478},
154 {"stun.intervoip.com", 3478},
155 {"stun.ipcomms.net", 3478},
156 {"stun.ipfire.org", 3478},
157 {"stun.ippi.fr", 3478},
158 {"stun.ipshka.com", 3478},
159 {"stun.iptel.org", 3478},
160 {"stun.irian.at", 3478},
161 {"stun.it1.hr", 3478},
162 {"stun.ivao.aero", 3478},
163 {"stun.jappix.com", 3478},
164 {"stun.jumblo.com", 3478},
165 {"stun.justvoip.com", 3478},
166 {"stun.kanet.ru", 3478},
167 {"stun.kiwilink.co.nz", 3478},
168 {"stun.kundenserver.de", 3478},
169 {"stun.l.google.com", 19302},
170 {"stun.linea7.net", 3478},
171 {"stun.linphone.org", 3478},
172 {"stun.liveo.fr", 3478},
173 {"stun.lowratevoip.com", 3478},
174 {"stun.lugosoft.com", 3478},
175 {"stun.lundimatin.fr", 3478},
176 {"stun.magnet.ie", 3478},
177 {"stun.manle.com", 3478},
178 {"stun.mgn.ru", 3478},
179 {"stun.mit.de", 3478},
180 {"stun.mitake.com.tw", 3478},
181 {"stun.miwifi.com", 3478},
182 {"stun.modulus.gr", 3478},
183 {"stun.mozcom.com", 3478},
184 {"stun.myvoiptraffic.com", 3478},
185 {"stun.mywatson.it", 3478},
186 {"stun.nas.net", 3478},
187 {"stun.neotel.co.za", 3478},
188 {"stun.netappel.com", 3478},
189 {"stun.netappel.fr", 3478},
190 {"stun.netgsm.com.tr", 3478},
191 {"stun.nfon.net", 3478},
192 {"stun.noblogs.org", 3478},
193 {"stun.noc.ams-ix.net", 3478},
194 {"stun.node4.co.uk", 3478},
195 {"stun.nonoh.net", 3478},
196 {"stun.nottingham.ac.uk", 3478},
197 {"stun.nova.is", 3478},
198 {"stun.nventure.com", 3478},
199 {"stun.on.net.mk", 3478},
200 {"stun.ooma.com", 3478},
201 {"stun.ooonet.ru", 3478},
202 {"stun.outland-net.de", 3478},
203 {"stun.ozekiphone.com", 3478},
204 {"stun.patlive.com", 3478},
205 {"stun.personal-voip.de", 3478},
206 {"stun.petcube.com", 3478},
207 {"stun.phone.com", 3478},
208 {"stun.phoneserve.com", 3478},
209 {"stun.pjsip.org", 3478},
210 {"stun.poivy.com", 3478},
211 {"stun.powerpbx.org", 3478},
212 {"stun.powervoip.com", 3478},
213 {"stun.ppdi.com", 3478},
214 {"stun.prizee.com", 3478},
215 {"stun.qq.com", 3478},
216 {"stun.qvod.com", 3478},
217 {"stun.rackco.com", 3478},
218 {"stun.rapidnet.de", 3478},
219 {"stun.rb-net.com", 3478},
220 {"stun.remote-learner.net", 3478},
221 {"stun.rixtelecom.se", 3478},
222 {"stun.rockenstein.de", 3478},
223 {"stun.rolmail.net", 3478},
224 {"stun.rounds.com", 3478},
225 {"stun.rynga.com", 3478},
226 {"stun.samsungsmartcam.com", 3478},
227 {"stun.schlund.de", 3478},
228 {"stun.services.mozilla.com", 3478},
229 {"stun.sigmavoip.com", 3478},
230 {"stun.sip.us", 3478},
231 {"stun.sipdiscount.com", 3478},
232 {"stun.sipgate.net", 10000},
233 {"stun.sipgate.net", 3478},
234 {"stun.siplogin.de", 3478},
235 {"stun.sipnet.net", 3478},
236 {"stun.sipnet.ru", 3478},
237 {"stun.siportal.it", 3478},
238 {"stun.sippeer.dk", 3478},
239 {"stun.siptraffic.com", 3478},
240 {"stun.skylink.ru", 3478},
241 {"stun.sma.de", 3478},
242 {"stun.smartvoip.com", 3478},
243 {"stun.smsdiscount.com", 3478},
244 {"stun.snafu.de", 3478},
245 {"stun.softjoys.com", 3478},
246 {"stun.solcon.nl", 3478},
247 {"stun.solnet.ch", 3478},
248 {"stun.sonetel.com", 3478},
249 {"stun.sonetel.net", 3478},
250 {"stun.sovtest.ru", 3478},
251 {"stun.speedy.com.ar", 3478},
252 {"stun.spokn.com", 3478},
253 {"stun.srce.hr", 3478},
254 {"stun.ssl7.net", 3478},
255 {"stun.stunprotocol.org", 3478},
256 {"stun.symform.com", 3478},
257 {"stun.symplicity.com", 3478},
258 {"stun.sysadminman.net", 3478},
259 {"stun.t-online.de", 3478},
260 {"stun.tagan.ru", 3478},
261 {"stun.tatneft.ru", 3478},
262 {"stun.teachercreated.com", 3478},
263 {"stun.tel.lu", 3478},
264 {"stun.telbo.com", 3478},
265 {"stun.telefacil.com", 3478},
266 {"stun.tis-dialog.ru", 3478},
267 {"stun.tng.de", 3478},
268 {"stun.twt.it", 3478},
269 {"stun.u-blox.com", 3478},
270 {"stun.ucallweconn.net", 3478},
271 {"stun.ucsb.edu", 3478},
272 {"stun.ucw.cz", 3478},
273 {"stun.uls.co.za", 3478},
274 {"stun.unseen.is", 3478},
275 {"stun.usfamily.net", 3478},
276 {"stun.veoh.com", 3478},
277 {"stun.vidyo.com", 3478},
278 {"stun.vipgroup.net", 3478},
279 {"stun.virtual-call.com", 3478},
280 {"stun.viva.gr", 3478},
281 {"stun.vivox.com", 3478},
282 {"stun.vline.com", 3478},
283 {"stun.vo.lu", 3478},
284 {"stun.vodafone.ro", 3478},
285 {"stun.voicetrading.com", 3478},
286 {"stun.voip.aebc.com", 3478},
287 {"stun.voip.blackberry.com", 3478},
288 {"stun.voip.eutelia.it", 3478},
289 {"stun.voiparound.com", 3478},
290 {"stun.voipblast.com", 3478},
291 {"stun.voipbuster.com", 3478},
292 {"stun.voipbusterpro.com", 3478},
293 {"stun.voipcheap.co.uk", 3478},
294 {"stun.voipcheap.com", 3478},
295 {"stun.voipfibre.com", 3478},
296 {"stun.voipgain.com", 3478},
297 {"stun.voipgate.com", 3478},
298 {"stun.voipinfocenter.com", 3478},
299 {"stun.voipplanet.nl", 3478},
300 {"stun.voippro.com", 3478},
301 {"stun.voipraider.com", 3478},
302 {"stun.voipstunt.com", 3478},
303 {"stun.voipwise.com", 3478},
304 {"stun.voipzoom.com", 3478},
305 {"stun.voxgratia.org", 3478},
306 {"stun.voxox.com", 3478},
307 {"stun.voys.nl", 3478},
308 {"stun.voztele.com", 3478},
309 {"stun.vyke.com", 3478},
310 {"stun.webcalldirect.com", 3478},
311 {"stun.whoi.edu", 3478},
312 {"stun.wifirst.net", 3478},
313 {"stun.wwdl.net", 3478},
314 {"stun.xs4all.nl", 3478},
315 {"stun.xtratelecom.es", 3478},
316 {"stun.yesss.at", 3478},
317 {"stun.zadarma.com", 3478},
318 {"stun.zadv.com", 3478},
319 {"stun.zoiper.com", 3478},
320 {"stun1.faktortel.com.au", 3478},
321 {"stun1.l.google.com", 19302},
322 {"stun1.voiceeclipse.net", 3478},
323 {"stun2.l.google.com", 19302},
324 {"stun3.l.google.com", 19302},
325 {"stun4.l.google.com", 19302},
326 {"stunserver.org", 3478},
327 {"stun.antisip.com", 3478}
331 /* wrapper to send an STUN message */
332 static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
334 return sendto(s, (const char *)resp, ntohs(resp->msglen) + sizeof(*resp), 0,
335 (struct sockaddr *)dst, sizeof(*dst));
338 /* helper function to generate a random request id */
339 static uint64_t randfiller;
340 static void stun_req_id(struct stun_header *req)
342 const uint64_t *S_block = (const uint64_t *)StunSrvList;
343 req->id.id[0] |= 0x55555555;
344 req->id.id[1] &= 0x55555555;
345 req->id.id[2] |= 0x55555555;
346 req->id.id[3] &= 0x55555555;
347 register char x = 20;
349 uint32_t s_elm = S_block[(uint8_t)randfiller];
350 randfiller ^= (randfiller << 5) | (randfiller >> (64 - 5));
351 randfiller += s_elm ^ x;
352 req->id.id[x & 3] ^= randfiller + (randfiller >> 13);
356 /* callback type to be invoked on stun responses. */
357 typedef int (stun_cb_f)(struct stun_attr *attr, void *arg);
359 /* handle an incoming STUN message.
361 * Do some basic sanity checks on packet size and content,
362 * try to extract a bit of information, and possibly reply.
363 * At the moment this only processes BIND requests, and returns
364 * the externally visible address of the request.
365 * If a callback is specified, invoke it with the attribute.
367 static int stun_handle_packet(int s, struct sockaddr_in *src,
368 unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
370 struct stun_header *hdr = (struct stun_header *)data;
371 struct stun_attr *attr;
375 /* On entry, 'len' is the length of the udp payload. After the
376 * initial checks it becomes the size of unprocessed options,
377 * while 'data' is advanced accordingly.
379 if (len < sizeof(struct stun_header))
382 len -= sizeof(struct stun_header);
383 data += sizeof(struct stun_header);
384 x = ntohs(hdr->msglen); /* len as advertised in the message */
389 if (len < sizeof(struct stun_attr)) {
393 attr = (struct stun_attr *)data;
394 /* compute total attribute length */
395 x = ntohs(attr->len) + sizeof(struct stun_attr);
401 //if (stun_process_attr(&st, attr)) {
405 /* Clear attribute id: in case previous entry was a string,
406 * this will act as the terminator for the string.
412 /* Null terminate any string.
413 * XXX NOTE, we write past the size of the buffer passed by the
414 * caller, so this is potentially dangerous. The only thing that
415 * saves us is that usually we read the incoming message in a
420 /* Now prepare to generate a reply, which at the moment is done
421 * only for properly formed (len == 0) STUN_BINDREQ messages.
427 /* Extract the STUN_MAPPED_ADDRESS from the stun response.
428 * This is used as a callback for stun_handle_response
429 * when called from stun_request.
431 static int stun_get_mapped(struct stun_attr *attr, void *arg)
433 struct stun_addr *addr = (struct stun_addr *)(attr + 1);
434 struct sockaddr_in *sa = (struct sockaddr_in *)arg;
436 if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
437 return 1; /* not us. */
438 sa->sin_port = addr->port;
439 sa->sin_addr.s_addr = addr->addr;
443 /*---------------------------------------------------------------------*/
445 static int StunRequest2(int sock, struct sockaddr_in *server, struct sockaddr_in *mapped) {
447 struct stun_header *req;
448 unsigned char reqdata[1024];
450 req = (struct stun_header *)reqdata;
455 req->msglen = htons(reqlen);
456 req->msgtype = htons(STUN_BINDREQ);
458 unsigned char reply_buf[1024];
460 struct timeval to = { STUN_TIMEOUT, 0 };
461 struct sockaddr_in src;
468 int res = stun_send(sock, server, req);
473 res = select(sock + 1, &rfds, NULL, NULL, &to);
474 if (res <= 0) /* timeout or error */
476 memset(&src, 0, sizeof(src));
477 srclen = sizeof(src);
478 /* XXX pass -1 in the size, because stun_handle_packet might
479 * write past the end of the buffer.
481 res = recvfrom(sock, (char *)reply_buf, sizeof(reply_buf) - 1,
482 0, (struct sockaddr *)&src, &srclen);
485 memset(mapped, 0, sizeof(struct sockaddr_in));
486 return stun_handle_packet(sock, &src, reply_buf, res, stun_get_mapped, mapped);
489 /*---------------------------------------------------------------------*/
490 static int StunRequest(const char *host, uint16_t port, struct sockaddr_in *mapped) {
491 struct hostent *hostinfo = gethostbyname(host);
495 struct sockaddr_in server, client;
496 memset(&server, 0, sizeof(server));
497 memset(&client, 0, sizeof(client));
498 server.sin_family = client.sin_family = AF_INET;
500 server.sin_addr = *(struct in_addr*) hostinfo->h_addr;
501 server.sin_port = htons(port);
503 int sock = socket(AF_INET, SOCK_DGRAM, 0);
507 client.sin_addr.s_addr = htonl(INADDR_ANY);
510 if(bind(sock, (struct sockaddr*)&client, sizeof(client)) >= 0)
511 rc = StunRequest2(sock, &server, mapped);
520 /*---------------------------------------------------------------------*/
521 // Input: two random values (pos, step) for generate uniuqe way over server
523 // Output: populate struct struct mapped
526 int GetExternalIPbySTUN(uint64_t rnd, struct sockaddr_in *mapped, const char **srv) {
531 rnd = (rnd >> 8) | 0xff00000000000000LL;
532 step = rnd % StunSrvListQty;
536 for(attempt = 1; attempt < StunSrvListQty * 2; attempt++) {
537 pos = (pos + step) % StunSrvListQty;
538 int rc = StunRequest(*srv = StunSrvList[pos].name, StunSrvList[pos].port, mapped);
541 // fprintf(stderr, "Lookup: %s:%u\t%s\t%d\n", StunSrvList[pos].name,
542 // StunSrvList[pos].port, inet_ntoa(mapped->sin_addr), rc);