Author Topic: Retrieve your External IP address  (Read 738 times)

Offline AIR

  • BASIC Developer
  • Posts: 684
Retrieve your External IP address
« on: March 18, 2019, 11:49:09 PM »
Typically, when one is looking to retrieve their external IP address programmatically, one resorts to making an html request to a website like https://myexternalip.com/raw

For this challenge, the task is to retrieve your external IP using a *** DNS Query *** to resolver1.opendns.com.

The request you will send to that DNS server is: "myip.opendns.com".  The server is configured to respond to that specific request by returning the EXTERNAL IP address of the machine making the request.

Libraries, third party included, are allowed but shelling to a command line utility (like dig) or another programming language is not allowed.  Neither is scraping a webpage.

Obviously, you should NOT post your IP Address.

To test whether you are retrieving the correct IP address, either check it with the link provided at the begining of this post, or just google "my ip".

You can also check locally at a shell prompt, using:

Windows: nslookup myip.opendns.com. resolver1.opendns.com

Linux/macOS: dig +short myip.opendns.com @resolver1.opendns.com


To summarize:

DNS SERVER:   resolver1.opendns.com
DNS REQUEST: myip.opendns.com

AIR.




Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #1 on: March 18, 2019, 11:52:14 PM »
PYTHON:

Code: Python
  1. #!/usr/bin/env python
  2.  
  3. import dns.resolver,socket
  4.  
  5. res = dns.resolver.Resolver(configure=False)
  6. res.nameservers = [socket.gethostbyname("resolver1.opendns.com")]
  7. print res.query('myip.opendns.com')[0]
  8.  

AIR.

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #2 on: March 18, 2019, 11:55:16 PM »
GO:

Code: Go
  1. package main
  2.  
  3. import (
  4.         "context"
  5.         "fmt"
  6.         "log"
  7.         "net"
  8. )
  9.  
  10. func customDialer(ctx context.Context, network, address string) (net.Conn, error) {
  11.         d := net.Dialer{}
  12.         return d.DialContext(ctx, network, net.JoinHostPort("resolver1.opendns.com", "53"))
  13. }
  14.  
  15. func main() {
  16.  
  17.         r := net.Resolver{
  18.                 PreferGo: true,
  19.                 Dial:     customDialer,
  20.         }
  21.  
  22.         ipaddr, err := r.LookupHost(context.Background(), "myip.opendns.com")
  23.         if err != nil {
  24.                 log.Fatal(err)
  25.         }
  26.  
  27.         fmt.Println("External IP:", ipaddr[0])
  28. }
  29.  

AIR.

Offline jalih

  • Advocate
  • Posts: 52
Re: Retrieve your External IP address
« Reply #3 on: March 19, 2019, 11:25:56 AM »
8th:

Code: [Select]

net:INET4 net:DGRAM net:socket var, socket

[ 0xaa, 0xaa, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  "myip", "opendns", "com", 0x00, 0x00, 0x01, 0x00, 0x01 ] "12b1s1c1s1c1s1c5b" pack var, message


: address-info
  "resolver1.opendns.com" 53 net:getaddrinfo null? if
    t:err? . cr bye
  then ;


: app:main
  socket @ address-info message @ 0 net:sendto null? if
    2drop t:err? . cr bye
  else
    drop
  then

  100 b:new 0 net:recvfrom null? if
    2drop t:err? . cr bye
  then

  \ Last four bytes from the response are IP address.
  dup 4 n:- swap b:slice "4:1B" unpack drop
  ' >s a:map
  "." a:join
  "External IP: " . . cr
  2drop
  bye ;
« Last Edit: March 19, 2019, 01:13:01 PM by jalih »

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #4 on: March 19, 2019, 05:09:48 PM »
Thanks, Jalih!

Works as expected on my Mac.

AIR.


Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Retrieve your External IP address
« Reply #5 on: March 19, 2019, 06:12:48 PM »
I hope this isn't cheating.

Code: Script BASIC
  1. IMPORT curl.bas
  2.  
  3. ch = curl::init()
  4. curl::option(ch,"URL","ipinfo.io")
  5. PRINT curl::perform(ch),"\n"
  6. curl::finish(ch)
  7.  


$ scriba myip.sb
{
  "ip": "#4.#9.#1.#11",
  "hostname": "c-#4-#9-#1-#11.hsd1.wa.comcast.net",
  "city": "Mount Vernon",
  "region": "Washington",
  "country": "US",
  "loc": "48.4352,-122.2080",
  "postal": "98273",
  "org": "AS33650 Comcast Cable Communications, LLC"
}
$


If you only want the ip, add a /ip to the end of the url.

$ scriba myip.sb
#4.#9.#1.#11

$

« Last Edit: March 19, 2019, 06:22:33 PM by John »

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #6 on: March 19, 2019, 08:11:23 PM »
Well, that's how it's typically done:  find a website that returns your IP. It's very simple to set that up yourself using a single line of PHP.

With that said, the challenge is to retrieve the info by querying the provided DNS server directly.

AIR.

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Retrieve your External IP address
« Reply #7 on: March 19, 2019, 08:42:52 PM »
I figured my approach didn't meet the challenge requirements but it's sure is a quick way to get there.

On a positive note I might add this feature to sbhttpd  as a StatCounter like log.
« Last Edit: March 19, 2019, 08:54:41 PM by John »

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #8 on: March 19, 2019, 09:17:05 PM »
The challenge illustrates a language's ability to do high or low level socket programming by interacting with a DNS server, rather than a WEB server.

The Python, Go, and 8th examples show a more high level (abstracted) approach.

I'm finalizing a low level C example, which I'll post in a bit...

AIR.

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Retrieve your External IP address
« Reply #9 on: March 19, 2019, 09:22:17 PM »
I tried using SB socket OPEN but couldn't figure out how to get it to work.

Port = 53 ?

How is the resolver url passed?


Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #10 on: March 19, 2019, 09:32:11 PM »
The key is being able to tell the socket that you wish to use a custom/specific DNS server for the query rather than the DNS as configured on the system.

I don't know if SB native sockets exposes the functionality required to do this, I haven't looked.

AIR.

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #11 on: March 19, 2019, 09:42:48 PM »
C:

Code: C
  1. #include <resolv.h>
  2. #include <arpa/inet.h>
  3. #include <netdb.h>
  4. #include <string.h>
  5.  
  6. void set_dns_server(res_state res, const char *dns_server);
  7. void get_ip(res_state res, const char *host, char ip[]);
  8. int hostname_to_ip(const char * hostname , char* ip);
  9.  
  10.  
  11. int main (int argc, char **argv) {
  12.     struct __res_state res;
  13.     char ip[16], host_ip[16];
  14.     int result;
  15.  
  16.     result = hostname_to_ip("resolver1.opendns.com", host_ip);
  17.     set_dns_server(&res, host_ip);
  18.     get_ip(&res, "myip.opendns.com", ip);
  19.  
  20.     printf("External IP: %s\n",ip);
  21.  
  22.     return result;
  23. }
  24.  
  25. void set_dns_server(res_state res, const char *dns_server)
  26. {
  27.     res_ninit(res);
  28.     struct in_addr addr;
  29.     inet_aton(dns_server, &addr);
  30.    
  31.     res->nsaddr_list[0].sin_addr = addr;
  32.     res->nsaddr_list[0].sin_family = AF_INET;
  33.     res->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
  34.     res->nscount = 1;
  35. }
  36.  
  37. void get_ip(res_state res, const char *host, char ip[])
  38. {
  39.     u_char answer[NS_PACKETSZ];
  40.     int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
  41.    
  42.     ns_msg handle;
  43.     ns_initparse(answer, len, &handle);
  44.    
  45.    
  46.     if(ns_msg_count(handle, ns_s_an) > 0) {
  47.         ns_rr rr;
  48.         if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
  49.             strcpy(ip, inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
  50.         }
  51.     }
  52. }
  53.  
  54. int hostname_to_ip(const char * hostname , char* ip)
  55. {
  56.         struct hostent *he;
  57.         struct in_addr **addr_list;
  58.         int i;
  59.                
  60.         if ( (he = gethostbyname( hostname ) ) == NULL)
  61.         {
  62.                 // get the host info
  63.                 herror("gethostbyname");
  64.                 return 1;
  65.         }
  66.  
  67.         addr_list = (struct in_addr **) he->h_addr_list;
  68.        
  69.         for(i = 0; addr_list[i] != NULL; i++)
  70.         {
  71.                 //Return the first one;
  72.                 strcpy(ip , inet_ntoa(*addr_list[i]) );
  73.                 return 0;
  74.         }
  75.        
  76.         return 1;
  77. }
  78.  

Compile on Linux/macOS with:

gcc getip.c -lresolv -o getip

AIR.

Offline jalih

  • Advocate
  • Posts: 52
Re: Retrieve your External IP address
« Reply #12 on: March 19, 2019, 10:39:08 PM »
I don't know if SB native sockets exposes the functionality required to do this, I haven't looked.

If SB supports UDP datagram sockets, you can use the same approach that I used with 8th. You can construct DNS message by hand, send the question and receive the answer.

Offline John

  • Forum Support / SB Dev
  • Posts: 2741
    • ScriptBasic Open Source Project
Re: Retrieve your External IP address
« Reply #13 on: March 20, 2019, 09:15:10 AM »
I would like to see an example using raw sockets without the support libraries hiding what is really going on.

Offline AIR

  • BASIC Developer
  • Posts: 684
Re: Retrieve your External IP address
« Reply #14 on: March 20, 2019, 07:05:23 PM »
Using libraries eliminates the need to reinvent the wheel.

That said, here is an example:

Code: C
  1. //DNS Query Program on Linux
  2. //Author : Silver Moon (m00n.silv3r@gmail.com)
  3. //Dated : 29/4/2009
  4.  
  5. //Modified by Armando I. Rivera (AIR)
  6. //to return public ip via opendns servers
  7. //Date: 03/20/2019
  8.  
  9. //Header Files
  10. #include<stdio.h>       //printf
  11. #include<string.h>      //strlen
  12. #include<stdlib.h>      //malloc
  13. #include<sys/socket.h>  //you know what this is for
  14. #include<arpa/inet.h>   //inet_addr , inet_ntoa , ntohs etc
  15. #include<netinet/in.h>
  16. #include<unistd.h>      //getpid
  17.  
  18. //List of DNS Servers registered on the system
  19. char dns_servers[10][100];
  20. int dns_server_count = 0;
  21.  
  22. //Types of DNS resource records :)
  23. #define T_A 1 //Ipv4 address
  24. #define T_NS 2 //Nameserver
  25. #define T_CNAME 5 // canonical name
  26. #define T_SOA 6 /* start of authority zone */
  27. #define T_PTR 12 /* domain name pointer */
  28. #define T_MX 15 //Mail server
  29.  
  30. //Function Prototypes
  31. void ngethostbyname (unsigned char* , int);
  32. void ChangetoDnsNameFormat (unsigned char*,unsigned char*);
  33. unsigned char* ReadName (unsigned char*,unsigned char*,int*);
  34. void get_dns_servers();
  35.  
  36. //DNS header structure
  37. struct DNS_HEADER
  38. {
  39.         unsigned short id; // identification number
  40.  
  41.         unsigned char rd :1; // recursion desired
  42.         unsigned char tc :1; // truncated message
  43.         unsigned char aa :1; // authoritive answer
  44.         unsigned char opcode :4; // purpose of message
  45.         unsigned char qr :1; // query/response flag
  46.  
  47.         unsigned char rcode :4; // response code
  48.         unsigned char cd :1; // checking disabled
  49.         unsigned char ad :1; // authenticated data
  50.         unsigned char z :1; // its z! reserved
  51.         unsigned char ra :1; // recursion available
  52.  
  53.         unsigned short q_count; // number of question entries
  54.         unsigned short ans_count; // number of answer entries
  55.         unsigned short auth_count; // number of authority entries
  56.         unsigned short add_count; // number of resource entries
  57. };
  58.  
  59. //Constant sized fields of query structure
  60. struct QUESTION
  61. {
  62.         unsigned short qtype;
  63.         unsigned short qclass;
  64. };
  65.  
  66. //Constant sized fields of the resource record structure
  67. #pragma pack(push, 1)
  68. struct R_DATA
  69. {
  70.         unsigned short type;
  71.         unsigned short _class;
  72.         unsigned int ttl;
  73.         unsigned short data_len;
  74. };
  75. #pragma pack(pop)
  76.  
  77. //Pointers to resource record contents
  78. struct RES_RECORD
  79. {
  80.         unsigned char *name;
  81.         struct R_DATA *resource;
  82.         unsigned char *rdata;
  83. };
  84.  
  85. //Structure of a Query
  86. typedef struct
  87. {
  88.         unsigned char *name;
  89.         struct QUESTION *ques;
  90. } QUERY;
  91.  
  92. int main( int argc , char *argv[])
  93. {
  94.         unsigned char hostname[] = "myip.opendns.com";
  95.  
  96.         // Set Custom DNS Servers
  97.         strcpy(dns_servers[0] , "208.67.222.222"); //resolver1.opendns.com
  98.         strcpy(dns_servers[1] , "208.67.220.220"); //resolver2.opendns.com
  99.        
  100.         //Now get the ip of this hostname , A record
  101.         ngethostbyname(hostname , T_A);
  102.  
  103.         return 0;
  104. }
  105.  
  106. /*
  107.  * Perform a DNS query by sending a packet
  108.  * */
  109. void ngethostbyname(unsigned char *host , int query_type)
  110. {
  111.         unsigned char buf[65536],*qname,*reader;
  112.         int i , j , stop , s;
  113.  
  114.         struct sockaddr_in a;
  115.  
  116.         struct RES_RECORD answers[20],auth[20],addit[20]; //the replies from the DNS server
  117.         struct sockaddr_in dest;
  118.  
  119.         struct DNS_HEADER *dns = NULL;
  120.         struct QUESTION *qinfo = NULL;
  121.  
  122.         s = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP); //UDP packet for DNS queries
  123.  
  124.         dest.sin_family = AF_INET;
  125.         dest.sin_port = htons(53);
  126.         dest.sin_addr.s_addr = inet_addr(dns_servers[0]); //dns servers
  127.  
  128.         //Set the DNS structure to standard queries
  129.         dns = (struct DNS_HEADER *)&buf;
  130.  
  131.         dns->id = (unsigned short) htons(getpid());
  132.         dns->qr = 0; //This is a query
  133.         dns->opcode = 0; //This is a standard query
  134.         dns->aa = 0; //Not Authoritative
  135.         dns->tc = 0; //This message is not truncated
  136.         dns->rd = 1; //Recursion Desired
  137.         dns->ra = 0; //Recursion not available! hey we dont have it (lol)
  138.         dns->z = 0;
  139.         dns->ad = 0;
  140.         dns->cd = 0;
  141.         dns->rcode = 0;
  142.         dns->q_count = htons(1); //we have only 1 question
  143.         dns->ans_count = 0;
  144.         dns->auth_count = 0;
  145.         dns->add_count = 0;
  146.  
  147.         //point to the query portion
  148.         qname =(unsigned char*)&buf[sizeof(struct DNS_HEADER)];
  149.  
  150.         ChangetoDnsNameFormat(qname , host);
  151.         qinfo =(struct QUESTION*)&buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)]; //fill it
  152.  
  153.         qinfo->qtype = htons( query_type ); //type of the query , A , MX , CNAME , NS etc
  154.         qinfo->qclass = htons(1); //its internet (lol)
  155.  
  156.         // Send Packet
  157.         if( sendto(s,(char*)buf,sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION),0,(struct sockaddr*)&dest,sizeof(dest)) < 0)
  158.         {
  159.                 perror("sendto failed");
  160.         }
  161.  
  162.        
  163.         //Receive the answer
  164.         i = sizeof dest;
  165.  
  166.         if(recvfrom (s,(char*)buf , 65536 , 0 , (struct sockaddr*)&dest , (socklen_t*)&i ) < 0)
  167.         {
  168.                 perror("recvfrom failed");
  169.         }
  170.  
  171.  
  172.         dns = (struct DNS_HEADER*) buf;
  173.  
  174.         //move ahead of the dns header and the query field
  175.         reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)];
  176.  
  177.         //Start reading answers
  178.         stop=0;
  179.  
  180.         for(i=0;i<ntohs(dns->ans_count);i++)
  181.         {
  182.                 answers[i].name=ReadName(reader,buf,&stop);
  183.                 reader = reader + stop;
  184.  
  185.                 answers[i].resource = (struct R_DATA*)(reader);
  186.                 reader = reader + sizeof(struct R_DATA);
  187.  
  188.                 if(ntohs(answers[i].resource->type) == 1) //if its an ipv4 address
  189.                 {
  190.                         answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len));
  191.  
  192.                         for(j=0 ; j<ntohs(answers[i].resource->data_len) ; j++)
  193.                         {
  194.                                 answers[i].rdata[j]=reader[j];
  195.                         }
  196.  
  197.                         answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0';
  198.  
  199.                         reader = reader + ntohs(answers[i].resource->data_len);
  200.                 }
  201.                 else
  202.                 {
  203.                         answers[i].rdata = ReadName(reader,buf,&stop);
  204.                         reader = reader + stop;
  205.                 }
  206.         }
  207.  
  208.         //print answers
  209.         for(i=0 ; i < ntohs(dns->ans_count) ; i++)
  210.         {
  211.  
  212.                 if( ntohs(answers[i].resource->type) == T_A) //IPv4 address
  213.                 {
  214.                         long *p;
  215.                         p=(long*)answers[i].rdata;
  216.                         a.sin_addr.s_addr=(*p); //working without ntohl
  217.                         printf("External IP: %s\n",inet_ntoa(a.sin_addr));
  218.                 }
  219.         }
  220.  
  221.         return;
  222. }
  223.  
  224. /*
  225.  *
  226.  * */
  227. u_char* ReadName(unsigned char* reader,unsigned char* buffer,int* count)
  228. {
  229.         unsigned char *name;
  230.         unsigned int p=0,jumped=0,offset;
  231.         int i , j;
  232.  
  233.         *count = 1;
  234.         name = (unsigned char*)malloc(256);
  235.  
  236.         name[0]='\0';
  237.  
  238.         //read the names in 3www6google3com format
  239.         while(*reader!=0)
  240.         {
  241.                 if(*reader>=192)
  242.                 {
  243.                         offset = (*reader)*256 + *(reader+1) - 49152; //49152 = 11000000 00000000 ;)
  244.                         reader = buffer + offset - 1;
  245.                         jumped = 1; //we have jumped to another location so counting wont go up!
  246.                 }
  247.                 else
  248.                 {
  249.                         name[p++]=*reader;
  250.                 }
  251.  
  252.                 reader = reader+1;
  253.  
  254.                 if(jumped==0)
  255.                 {
  256.                         *count = *count + 1; //if we havent jumped to another location then we can count up
  257.                 }
  258.         }
  259.  
  260.         name[p]='\0'; //string complete
  261.         if(jumped==1)
  262.         {
  263.                 *count = *count + 1; //number of steps we actually moved forward in the packet
  264.         }
  265.  
  266.         //now convert 3www6google3com0 to www.google.com
  267.         for(i=0;i<(int)strlen((const char*)name);i++)
  268.         {
  269.                 p=name[i];
  270.                 for(j=0;j<(int)p;j++)
  271.                 {
  272.                         name[i]=name[i+1];
  273.                         i=i+1;
  274.                 }
  275.                 name[i]='.';
  276.         }
  277.         name[i-1]='\0'; //remove the last dot
  278.         return name;
  279. }
  280.  
  281. /*
  282.  * This will convert www.google.com to 3www6google3com
  283.  * got it :)
  284.  * */
  285. void ChangetoDnsNameFormat(unsigned char* dns,unsigned char* host)
  286. {
  287.         int lock = 0 , i;
  288.         strcat((char*)host,".");
  289.        
  290.         for(i = 0 ; i < strlen((char*)host) ; i++)
  291.         {
  292.                 if(host[i]=='.')
  293.                 {
  294.                         *dns++ = i-lock;
  295.                         for(;lock<i;lock++)
  296.                         {
  297.                                 *dns++=host[lock];
  298.                         }
  299.                         lock++; //or lock=i+1;
  300.                 }
  301.         }
  302.         *dns++='\0';
  303. }
  304.  

AIR.

EDIT:  In the spirit of full attribution, here is the link to Silver Moon's post: https://www.binarytides.com/dns-query-code-in-c-with-linux-sockets
« Last Edit: March 20, 2019, 07:55:36 PM by AIR »