Python module that will handle connections and add in a Proxy? [duplicate] - python-3.x

I'd like to manually (using the socket and ssl modules) make an HTTPS request through a proxy which itself uses HTTPS.
I can perform the initial CONNECT exchange just fine:
import ssl, socket
PROXY_ADDR = ("proxy-addr", 443)
CONNECT = "CONNECT example.com:443 HTTP/1.1\r\n\r\n"
sock = socket.create_connection(PROXY_ADDR)
sock = ssl.wrap_socket(sock)
sock.sendall(CONNECT)
s = ""
while s[-4:] != "\r\n\r\n":
s += sock.recv(1)
print repr(s)
The above code prints HTTP/1.1 200 Connection established plus some headers, which is what I expect. So now I should be ready to make the request, e.g.
sock.sendall("GET / HTTP/1.1\r\n\r\n")
but the above code returns
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Reason: You're speaking plain HTTP to an SSL-enabled server port.<br />
Instead use the HTTPS scheme to access this URL, please.<br />
</body></html>
This makes sense too, since I still need to do an SSL handshake with the example.com server to which I'm tunneling. However, if instead of immediately sending the GET request I say
sock = ssl.wrap_socket(sock)
to do the handshake with the remote server, then I get an exception:
Traceback (most recent call last):
File "so_test.py", line 18, in <module>
ssl.wrap_socket(sock)
File "/usr/lib/python2.6/ssl.py", line 350, in wrap_socket
suppress_ragged_eofs=suppress_ragged_eofs)
File "/usr/lib/python2.6/ssl.py", line 118, in __init__
self.do_handshake()
File "/usr/lib/python2.6/ssl.py", line 293, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [Errno 1] _ssl.c:480: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
So how can I do the SSL handshake with the remote example.com server?
EDIT: I'm pretty sure that no additional data is available before my second call to wrap_socket because calling sock.recv(1) blocks indefinitely.

This should work if the CONNECT string is rewritten as follows:
CONNECT = "CONNECT %s:%s HTTP/1.0\r\nConnection: close\r\n\r\n" % (server, port)
Not sure why this works, but maybe it has something to do with the proxy I'm using. Here's an example code:
from OpenSSL import SSL
import socket
def verify_cb(conn, cert, errun, depth, ok):
return True
server = 'mail.google.com'
port = 443
PROXY_ADDR = ("proxy.example.com", 3128)
CONNECT = "CONNECT %s:%s HTTP/1.0\r\nConnection: close\r\n\r\n" % (server, port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(PROXY_ADDR)
s.send(CONNECT)
print s.recv(4096)
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_verify(SSL.VERIFY_PEER, verify_cb)
ss = SSL.Connection(ctx, s)
ss.set_connect_state()
ss.do_handshake()
cert = ss.get_peer_certificate()
print cert.get_subject()
ss.shutdown()
ss.close()
Note how the socket is first opened and then open socket placed in SSL context. Then I manually initialize SSL handshake. And output:
HTTP/1.1 200 Connection established
<X509Name object '/C=US/ST=California/L=Mountain View/O=Google Inc/CN=mail.google.com'>
It's based on pyOpenSSL because I needed to fetch invalid certificates too and Python built-in ssl module will always try to verify the certificate if it's received.

Judging from the API of the OpenSSL and GnuTLS library, stacking a SSLSocket onto a SSLSocket is actually not straightforwardly possible as they provide special read/write functions to implement the encryption, which they are not able to use themselves when wrapping a pre-existing SSLSocket.
The error is therefore caused by the inner SSLSocket directly reading from the system socket and not from the outer SSLSocket. This ends in sending data not belonging to the outer SSL session, which ends badly and for sure never returns a valid ServerHello.
Concluding from that, I would say there is no simple way to implement what you (and actually myself) would like to accomplish.

Finally I got somewhere expanding on #kravietz and #02strich answers.
Here's the code
import threading
import select
import socket
import ssl
server = 'mail.google.com'
port = 443
PROXY = ("localhost", 4433)
CONNECT = "CONNECT %s:%s HTTP/1.0\r\nConnection: close\r\n\r\n" % (server, port)
class ForwardedSocket(threading.Thread):
def __init__(self, s, **kwargs):
threading.Thread.__init__(self)
self.dest = s
self.oursraw, self.theirsraw = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
self.theirs = socket.socket(_sock=self.theirsraw)
self.start()
self.ours = ssl.wrap_socket(socket.socket(_sock=self.oursraw), **kwargs)
def run(self):
rl, wl, xl = select.select([self.dest, self.theirs], [], [], 1)
print rl, wl, xl
# FIXME write may block
if self.theirs in rl:
self.dest.send(self.theirs.recv(4096))
if self.dest in rl:
self.theirs.send(self.dest.recv(4096))
def recv(self, *args):
return self.ours.recv(*args)
def send(self, *args):
return self.outs.recv(*args)
def test():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(PROXY)
s = ssl.wrap_socket(s, ciphers="ALL:aNULL:eNULL")
s.send(CONNECT)
resp = s.read(4096)
print (resp, )
fs = ForwardedSocket(s, ciphers="ALL:aNULL:eNULL")
fs.send("foobar")
Don't mind custom cihpers=, that only because I didn't want to deal with certificates.
And there's depth-1 ssl output, showing CONNECT, my response to it ssagd and depth-2 ssl negotiation and binary rubbish:
[dima#bmg ~]$ openssl s_server -nocert -cipher "ALL:aNULL:eNULL"
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
-----BEGIN SSL SESSION PARAMETERS-----
MHUCAQECAgMDBALAGQQgmn6XfJt8ru+edj6BXljltJf43Sz6AmacYM/dSmrhgl4E
MOztEauhPoixCwS84DL29MD/OxuxuvG5tnkN59ikoqtfrnCKsk8Y9JtUU9zuaDFV
ZaEGAgRSnJ81ogQCAgEspAYEBAEAAAA=
-----END SSL SESSION PARAMETERS-----
Shared ciphers: [snipped]
CIPHER is AECDH-AES256-SHA
Secure Renegotiation IS supported
CONNECT mail.google.com:443 HTTP/1.0
Connection: close
sagq
�u\�0�,�(�$��
�"�!��kj98���� �m:��2�.�*�&���=5�����
��/�+�'�#�� ����g#32��ED���l4�F�1�-�)�%���</�A������
�� ������
�;��A��q�J&O��y�l

It doesn't sound like there's anything wrong with what you're doing; it's certainly possible to call wrap_socket() on an existing SSLSocket.
The 'unknown protocol' error can occur (amongst other reasons) if there's extra data waiting to be read on the socket at the point you call wrap_socket(), for instance an extra \r\n or an HTTP error (due to a missing cert on the server end, for instance). Are you certain you've read everything available at that point?
If you can force the first SSL channel to use a "plain" RSA cipher (i.e. non-Diffie-Hellman) then you may be able to use Wireshark to decrypt the stream to see what's going on.

Building on #kravietz answer. Here is a version that works in Python3 through a Squid proxy:
from OpenSSL import SSL
import socket
def verify_cb(conn, cert, errun, depth, ok):
return True
server = 'mail.google.com'
port = 443
PROXY_ADDR = ("<proxy_server>", 3128)
CONNECT = "CONNECT %s:%s HTTP/1.0\r\nConnection: close\r\n\r\n" % (server, port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(PROXY_ADDR)
s.send(str.encode(CONNECT))
s.recv(4096)
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_verify(SSL.VERIFY_PEER, verify_cb)
ss = SSL.Connection(ctx, s)
ss.set_connect_state()
ss.do_handshake()
cert = ss.get_peer_certificate()
print(cert.get_subject())
ss.shutdown()
ss.close()
This works in Python 2 also.

Related

OPC-UA: recv(buffersize) function gets stuck without any error/warning message

Some information about my relevant-to-the-thread background:
Client-Server connection domain knowledge: Beginner
Python-programmer: Mid to advanced level
Problem I am facing:
connecting a Client to a Server using the OPCUA protocol (tool I am using: opcua python-library and VS-code editor).
How did the problem start?
I was attempting to use the node.get_value() method (where node = client.get_node("ns=2;i=2")). Then the error is: "None type does not have a method called 'send_request'". This made me think that the socket connection is not established. Therefore, I tried to establish a socket connection first, by using the UAClient.connect_socket() method, which lead me to the origin of the problem, which is the last command line in the following code:
My code:
from opcua import Server
from opcua import Client
from opcua import ua
from ua_client import *
from traceback import print_exc
from opcua import Client
name = "OPC_SIMULATION_SERVER"
addspace = server.register_namespace(name)
node = server.get_objects_node()
Param = node.add_object(addspace, "Parameters")
server = Server()
url = 'opc.tcp://127.0.0.1:4840' # opc server URL (connection works, checked in in command line with "ping")
server.start()
print("Server started at {}".format(url))
client = Client(url) # create client object
ref = client.uaclient # create UAClient object
# establish socket (to my understanding)
ref._uasocket = UASocketClient(ref._timeout, security_policy=ref.security_policy)
host = client.server_url.hostname
port = client.server_url.port
refsock = ref._uasocket
sock = socket.create_connection((host, port), timeout=refsock.timeout)
sock.settimeout(None) # for blocking (doesn’t cause the error)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
refsock._socket = ua.utils.SocketWrapper(sock)
# ERRATIC LINE: The following command gets stuck without an error/warning message
chunk = refsock._socket.socket.recv(8)
# \==================================================
I should note that initially I had no problem with getting the value from the node.

Is there a way to process CRL updates without creating a new socket

I'm trying to create a socket server which includes a CRL, the clients maintain socket connection so restarting or reseting the server socket dissconnects all clients. I would like to maintain a CRL however I dont want to have to restart the server every time the CRL is updated to reset the socket. Does anyone know of a way to update an existing ssl socket?
Server code:
import socket, ssl
from certvalidator import CertificateValidator, ValidationContext
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(cafile=r"path to ca with crl appended")
context.load_cert_chain(r"path to crt", r"path to key", password='pass') ###############
bindsocket = socket.socket()
bindsocket.bind(('', 9000))
bindsocket.listen(5)
while True:
newsocket, fromaddr = bindsocket.accept()
sslsoc = context.wrap_socket(newsocket, server_side=True)
request = sslsoc.read()
print(request)
Client code:
import socket
import ssl
hostname = '127.0.0.1'
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations(cafile="path to CA")
context.load_cert_chain(r"path to cer", r"path to key", password='pass')
context.check_hostname = False
with socket.create_connection((hostname, 8089)) as sock:
with context.wrap_socket(sock) as ssock:
print(ssock.version())
ssock.do_handshake()
print('connected')
ssock.send(b'122222')
while True:
header = ssock.recv(4)
print(header)
data = ssock.recv(10000)
print(header + data)
print(len(data))
print()
The server socket bindsocket is not affected from the change of CRL in the first place, since it is a plain TCP socket. All what needs to be changed is the SSL context context and this can be done at any time without any changes needed to bindsocket. The new SSL context with the changed URL will then be used for the next client when doing the TLS handshake using context.wrap_socket.
restarting or reseting the server socket dissconnects all clients
This is not true. The listener bindsocket and the client connection newsocket are independent from each other, i.e. closing bindsocket has no effect on newsocket. Of course, restarting the full server application will disconnect all clients since close of the application will close all sockets.

TLS/SSL socket python server

I am trying to build a simple HTTPS server with Python3 using the socket and ssl modules.
I have a self signed certificate and a private key files generated by OpenSSL and I tried to use them with the ssl module but every time I try, I get a "ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1076)" error.
My code is
import socket
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.load_cert_chain(certfile='cert.pem', keyfile='my_key.key')
context.verify_mode = ssl.CERT_NONE
sock = socket.socket()
sock.bind(('', 443))
sock.listen(5)
while True:
new_conn, addr = sock.accept()
ssl_conn = context.wrap_socket(new_conn, server_side=True)
print(ssl_conn.recv(1024).decode()) # this is where i get the error
The error I get is:
File "C:\AllInOne\PortableApps\Python374\lib\ssl.py", line 1139, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1076)
Does anyone know why this happens or how to fix it?
Generate server.pem with the following command:
mkdir .ssh
openssl req -new -x509 -keyout .ssh/key.pem -out .ssh/cert.pem -days 365 -nodes
run as follows:
python3 simple-https-server.py
Then in your browser, visit:
https://localhost:4443
Here is the code:
import http.server
from http.server import HTTPServer, BaseHTTPRequestHandler, SimpleHTTPRequestHandler
import ssl
import sys
# This class will handles any incoming request from the browser
class myHandler(BaseHTTPRequestHandler):
# Handler for the GET requests
def do_GET(self):
print(self.requestline)
# print(self.rfile.read(content_length))
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
# Send the html message
self.wfile.write("Hello World !".encode())
return
try:
separator = "-" * 80
server_address = ("", 4443)
# server_address = ('localhost', 4443)
httpd = http.server.HTTPServer(server_address, myHandler)
# httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(
httpd.socket,
server_side=True,
certfile=".ssh/cert.pem",
keyfile=".ssh/key.pem",
ssl_version=ssl.PROTOCOL_TLS,
)
print(separator)
print("Server running on https://localhost:4443")
print(separator)
# Wait forever for incoming htto requests
httpd.serve_forever()
except KeyboardInterrupt:
print("^C received, shutting down the web server")
server.socket.close()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1076)
The client signals your server that it is does not trust your certificate. Which is expected since this is not a certificate issued by a trusted CA and you did not make the client explicit trust this certificate. If the client would not complain it would be insecure since every man in the middle could just use a fake certificate to identify itself as a trusted server.

OSError: [Errno 22] Invalid argument for udp connection

The udp server and client on my local pc.
cat server.py
import socket
MAX_BYTES =65535
def server():
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind(('127.0.0.1',10000))
print('Listening at {}'.format(sock.getsockname()))
while True:
data,address = sock.recvfrom(MAX_BYTES)
text = data.decode('ascii')
print('The client at {} says {!r} '.format(address,text))
if __name__ == "__main__":
server()
Bind port 10000 with localhost-127.0.0.1,and listening to the message send from client.
cat client.py
import socket
import time
from datetime import datetime
MAX_BYTES =65535
def client():
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind(('127.0.0.1',10001))
text = 'The time is {}'.format(datetime.now())
data = text.encode('ascii')
while True:
time.sleep(10)
sock.sendto(data,('127.0.0.1',10000))
print('The OS assinged me the address {}'.format(sock.getsockname()))
if __name__ == "__main__":
client()
Run the server.py and client.py on my local pc,server can receive message send from client.
Now i change 127.0.0.1 in the line in client.py with my remote vps_ip.
sock.sendto(data,('127.0.0.1',10000))
into
sock.sendto(data,('remote_ip',10000))
Push server.py into my vps.Start client.py on my local pc,server.py on remote vps,start them all.
In my client,an error info occurs:
File "client.py", line 13, in client
sock.sendto(data,('remote_ip',10000))
OSError: [Errno 22] Invalid argument
How to make remote ip receive message send from my local client pc?
Two things that could be happening:
You're not passing the remote IP correctly. Make sure that your not passing literally 'remote_ip' and replace it with a valid IPv4 IP address string (IE: '192.168.0.100') for the server. (FYI technically on the server you can just put '0.0.0.0' to listen on all IP addresses)
You could still be binding the client to the local address to (127.0.0.1), but setting the destination to a valid external address (192.168.0.100). Remove the socket.bind line of code in the client to test this, you shouldn't need it.
If these both don't work, then add the results of a ping command running on the client and targeting the server.

what is laddr and raddr in handshake souce code of websocket

I am learning how websocket works in python 3.
I add print(sock) to def handshake of _handshake.py in websocket source to learn what is the message inside sock
And the result is sth like this:
Print out sock:<ssl.SSLSocket fd=508, family=AddressFamily.AF_INET, type=0, proto=0, laddr=('192.168.1.2', 58730), raddr=('202.160.125.211', 443)>
I wonder what laddr and raddr is?
I know that is too basic but without solid background as me it appears complicated to understand
I have searched gg for those keywords but there is no explaination.
def handshake(sock, hostname, port, resource, **options):
headers, key = _get_handshake_headers(resource, hostname, port, options)
header_str = "\r\n".join(headers)
send(sock, header_str)
dump("request header", header_str)
print("Print out sock:{}".format(sock))
status, resp = _get_resp_headers(sock)
if status in SUPPORTED_REDIRECT_STATUSES:
return handshake_response(status, resp, None)
success, subproto = _validate(resp, key, options.get("subprotocols"))
if not success:
raise WebSocketException("Invalid WebSocket Header")
return handshake_response(status, resp, subproto)
laddr means local address and raddr means remote address of the socket. Depending on the context of the process or application, one address becomes the remote to the other socket.

Resources