Search code examples
pythonurllib

Why can't I access the Google Translate API via IP?


I'm trying to access the Google Translate API via IP with the urllib library as follows:

from urllib.request import Request, urlopen
import ssl

ip = '142.250.1.90'

HOST = 'translate.googleapis.com'
URL_FORMAT = 'https://{}/translate_a/single?client=gtx&sl=en&tl=fr&q=a'

def _build_request(ip):
    url = URL_FORMAT.format(ip)
    request = Request(url)
    request.add_header('Host', HOST)
    return request

urlopen(_build_request(ip), timeout=2.5, context=ssl._create_unverified_context()).close()

But I got this error:

urllib.error.URLError: <urlopen error [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:997)>

How to fix it?

All the existing solutions which I found on the web did not work (ChatGPT didn't solve it either).


Solution

  • I suspect that the remote endpoint requires the client to correctly set the SNI value in the SSL request. There's no built-in to do this other than by providing the correct hostname in your request.

    We can monkey patch the SSL context to explicitly set the server hostname by doing something like this:

    from urllib.request import Request, urlopen
    import ssl
    
    ip = "142.250.1.90"
    
    HOST = "translate.googleapis.com"
    URL_FORMAT = "https://{}/translate_a/single?client=gtx&sl=en&tl=fr&q=a"
    
    
    def _build_request(ip):
        url = URL_FORMAT.format(ip)
        request = Request(url)
        request.add_header("Host", HOST)
        return request
    
    
    def patch_context(ctx, server_hostname):
        old_wrap_socket = ctx.wrap_socket
    
        def new_wrap_socket(socket, **kwargs):
            kwargs["server_hostname"] = server_hostname
            return old_wrap_socket(socket, **kwargs)
    
        ctx.wrap_socket = new_wrap_socket
    
    
    req = _build_request(ip)
    ctx = ssl._create_unverified_context()
    patch_context(ctx, HOST)
    with urlopen(req, timeout=2.5, context=ctx) as response:
        print(response.read())
    

    In the above code, we're replacing the wrap_socket method of the SSL context with a new method that sets the server_hostname parameter to our desired value.

    Running this code successfully performs the request; the output of the final print() statement is:

    b'[null,null,"en",null,null,null,null,[]]'