Link to home
Start Free TrialLog in
Avatar of pizarro
pizarro

asked on

VB or C++ -How to find out country of origin?

hello,

I need to write a CGI script that determines what country the user comes from.  For example,  If a user in Germany runs the CGI script, the CGI script would be able to tell that that person comes from Germany.  I know that the IP address of the user is passed as an environmental variable, but how to make the jump to the actual domain name and/or country?
The CGI script can be in any language, but preferably in VB, C/C++, and/or Pearl.
Please help!!!
Avatar of faster
faster

After you get the ip address, do a inet_addr() to convert it into an unsigned long, then call gethostbyaddr() to get a pointer to a hostent structure. the h_name member of it is the domain name.

Check win32 help for detail about these APIs (winsock API).
Avatar of pizarro

ASKER

This might get the domain name but some domain names don't indicate what the country actually is.  Also, I could not find any reference for the gethostbyaddress function and the h_name structure.
The domain is the only easy way to guess the country, and even then often you can't tell or the information is unreliable. The information as to what country the user is in simply isn't sent to the web server, and there's no master database anywhere where you can look it up. You can often manually tell by doing a traceroute to look at the name of routers are upsteam of the final IP address, but there's no fixed naming convention so this can't be done automatically.

Domain isn't completely reliable, but it's the best you have.


Avatar of pizarro

ASKER

I now agree that the domain will be the only information I will be able to use.  But I still could not get the API functions above to work.
I have some code for VB5 that uses GetHostByName, I think I could modify it to use GetHostByAddr. It's pretty tricky because you need to initialize the winsock properly. I would be willing to make the changes and post the full code if you'll agree to accept the answer if it works.
Or perl would be much easier, actually, when I posted that I reread the question and saw perl would be Ok. It's much easier in perl because perl does the setup for you. Take your pick.
Avatar of pizarro

ASKER

If it works I will accept it for sure.  Even if it quite doesn't work but I feel it gave me a good lead I will accept it.
ASKER CERTIFIED SOLUTION
Avatar of alamo
alamo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
And of course, since you asked about country, from the top level domain:

($TopLevelDomain) = $hostname =~/\.(.*?)$/;
print "Top level domain: $TopLevelDomain\n";
oops, ignore my last comment, the regexp is bad, I don't know what I was thinking. Here's a full script (since you originally asked for a script):

use Socket;
print "Content-type: text/plain\n\n";
$ipnum = inet_aton($ENV{REMOTE_ADDR});
$hostname    = gethostbyaddr($ipnum, AF_INET);
print "Your IP address is $ENV{REMOTE_ADDR}\n";
print "You are at $hostname\n";
($TopLevelDomain) = $hostname =~/\.([^.]+)$/;
print "Your top level domain is $TopLevelDomain\n";
Avatar of pizarro

ASKER

Can you post the VB code?  I don't like using perl on Windows NT servers...
I went ahead and wrote the program in VB to turn an IP address into a hostname, you can figure out how to make it into a CGI:

Private Const WS_VERSION_REQD = &H101
Private Const WS_VERSION_MAJOR = WS_VERSION_REQD \ &H100 And &HFF&
Private Const WS_VERSION_MINOR = WS_VERSION_REQD And &HFF&
Private Const MIN_SOCKETS_REQD = 1
Private Const SOCKET_ERROR = -1
Private Const WSADescription_Len = 256
Private Const WSASYS_Status_Len = 128
Private Const AF_INET = 2
Private Type HOSTENT
    hName As Long
    hAliases As Long
    hAddrType As Integer
    hLength As Integer
    hAddrList As Long
End Type
Private Type WSADATA
    wversion As Integer
    wHighVersion As Integer
    szDescription(0 To WSADescription_Len) As Byte
    szSystemStatus(0 To WSASYS_Status_Len) As Byte
    iMaxSockets As Integer
    iMaxUdpDg As Integer
    lpszVendorInfo As Long
End Type
Private Declare Function WSAGetLastError Lib "WSOCK32.DLL" () As Long
Private Declare Function WSAStartup Lib "WSOCK32.DLL" (ByVal wVersionRequired&, lpWSAData As WSADATA) As Long
Private Declare Function WSACleanup Lib "WSOCK32.DLL" () As Long
Private Declare Sub RtlMoveMemory Lib "KERNEL32" (hpvDest As Any, ByVal hpvSource&, ByVal cbCopy&)
Private Declare Function inet_addr Lib "WSOCK32.DLL" (ByVal ipnum As String) As Long
Private Declare Function gethostbyaddr Lib "WSOCK32.DLL" (ipnum As Long, ByVal iplen As Long, ByVal iptype As Long) As Long

Private Sub Command1_Click()
    host$ = GetHostByAddress("207.114.128.129")
    Debug.Print host$
End Sub

Public Function GetHostByAddress(ipnum As String) As String
Dim hostname As String
Dim host As HOSTENT
Dim hostip_addr As Long
Dim hostent_addr As Long
   
  SocketsInitialize
  hostip_addr = inet_addr(ipnum)
  hostent_addr = gethostbyaddr(hostip_addr, 4, AF_INET)
  If hostent_addr = 0 Then
    MsgBox "Winsock.dll is not responding."
    Exit Function
  End If
  RtlMoveMemory host, hostent_addr, LenB(host)
  hostname = Space$(256)
  RtlMoveMemory ByVal hostname, host.hName, LenB(hostname)
  If InStr(hostname, Chr$(0)) Then hostname = Left$(hostname, InStr(hostname, Chr$(0)) - 1)
  SocketsCleanup
  GetHostByAddress = hostname
End Function

Function hibyte(ByVal wParam As Integer)
   hibyte = wParam \ &H100 And &HFF&
End Function

Function lobyte(ByVal wParam As Integer)
    lobyte = wParam And &HFF&
End Function

Sub SocketsInitialize()
Dim WSAD As WSADATA
Dim iReturn As Integer
Dim sLowByte As String, sHighByte As String, sMsg As String

    iReturn = WSAStartup(WS_VERSION_REQD, WSAD)
    If iReturn <> 0 Then
        MsgBox "Winsock.dll is not responding."
        End
    End If
    If lobyte(WSAD.wversion) < WS_VERSION_MAJOR Or (lobyte(WSAD.wversion) = WS_VERSION_MAJOR And hibyte(WSAD.wversion) < WS_VERSION_MINOR) Then
        sHighByte = Trim$(Str$(hibyte(WSAD.wversion)))
        sLowByte = Trim$(Str$(lobyte(WSAD.wversion)))
        sMsg = "Windows Sockets version " & sLowByte & "." & sHighByte
        sMsg = sMsg & " is not supported by winsock.dll "
        MsgBox sMsg
        End
    End If
    If WSAD.iMaxSockets < MIN_SOCKETS_REQD Then
        sMsg = "This application requires a minimum of "
        sMsg = sMsg & Trim$(Str$(MIN_SOCKETS_REQD)) & " supported sockets."
        MsgBox sMsg
        End
    End If
End Sub

Sub SocketsCleanup()
Dim lReturn As Long
  lReturn = WSACleanup()
  If lReturn <> 0 Then
    MsgBox "Socket error " & Trim$(Str$(lReturn)) & " occurred in Cleanup "
    End
  End If
End Sub
Avatar of pizarro

ASKER

thanks.