Friday, February 13, 2009

sslerror: (8, 'EOF occurred in violation of protocol')

I have a program in the field that performs SOAP calls to a server over HTTPS. The program works fine at most places. However, for one customer, the relevant call gives an error of

sslerror: (8, 'EOF occurred in violation of protocol')

I started by taking a look at the stack trace on this guy:

Traceback (most recent call last):
File "/roadware/tire/custom/rt/dcpr", line 257, in main
products = q.getPrices(dealer=opts.dealer, customer_id=opts.customer, document_date=opts.date, pricing_type_id="1", products=opts.products)
File "/roadware/tire/custom/rt/dcpr", line 149, in getPrices
response = self.call(fullaction, gpelem, headerelem)
File "/usr/lib/python2.3/site-packages/elementsoap/ElementSOAP.py", line 209, in call
parser=namespace_parse
File "/usr/lib/python2.3/site-packages/elementsoap/HTTPClient.py", line 147, in do_request
h.endheaders()
File "/opt/K/SCO/python/2.3.4/usr/lib/python2.3/httplib.py", line 712, in endheaders
self._send_output()
File "/opt/K/SCO/python/2.3.4/usr/lib/python2.3/httplib.py", line 597, in _send_output
self.send(msg)
File "/opt/K/SCO/python/2.3.4/usr/lib/python2.3/httplib.py", line 564, in send
self.connect()
File "/opt/K/SCO/python/2.3.4/usr/lib/python2.3/httplib.py", line 985, in connect
ssl = socket.ssl(sock, self.key_file, self.cert_file)
File "/opt/K/SCO/python/2.3.4/usr/lib/python2.3/socket.py", line 73, in ssl
return _realssl(sock, keyfile, certfile)
sslerror: (8, 'EOF occurred in violation of protocol')


Googling around for this problem suggests that proxies can result in this problem, which is likely caused by the HTTP header send being terminated prematurely. A few people suggested using a different SSL library (M2Crypto), but my solution makes use of ElementSOAP, and I'd really rather not have to mess with that much of the underlying code. So I took a look at the source of these Python packages:

In HTTPClient.py:

h.putrequest(method, path)
h.putheader("User-Agent", self.user_agent)
h.putheader("Host", self.host)
h.putheader("Content-Type", content_type)
h.putheader("Content-Length", str(len(body)))
if extra_headers:
for key, value in extra_headers:
h.putheader(key, value)
h.endheaders()

h.send(body)

# fetch the reply
errcode, errmsg, headers = h.getreply()


It looks like the header send is blowing up before the body of the SOAP message goes--we're not going to be able to just suppress the exception and take our response from the server.

In ElementSOAP.py:
# call the server
try:
response = self.__client.do_request(
ET.tostring(envelope),
extra_headers=[("SOAPAction", action)],
parser=namespace_parse
)
except HTTPError, v:
if v[0] == 500:
# might be a SOAP fault
response = namespace_parse(v[3])


Well, this is interesting. I'm not an expert on proxies or HTTP headers, but I see that SOAPAction extra header getting sent, and I can envision a proxy stripping out that header before sending it on to it's final destination. Result? Headers in violation of protocol (according to IIS)? Maybe. However, the customer claims there is no proxy.

My next thought? This post suggests changing the defaultHttpsTransport in client.py from
httplib.HTTPConnection to M2Crypto.httpslib.HTTPSConnection. Unfortunately, I'm working on SCO Openserver, so I don't know if I'll be able to build M2Crypto for this to work.

Instead, I tried to investigate the problem further. On the non-functioning machine, I just tried to access the URL I am targeting using curl. The result?

curl: (1) libcurl was built with SSL disabled, https: not supported!


Really? Well, just be be sure this would work at all, I tried it on a machine that didn't have the problem.

curl: (60) error setting certificate verify locations:
CAfile: /usr/share/curl/curl-ca-bundle.crt
CApath: none

More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). The default
bundle is named curl-ca-bundle.crt; you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.


Coincidence? Or are we onto something? I find another machine that hasn't been running this program, and try the curl command.

curl: (1) libcurl was built with SSL disabled, https: not supported!


And the program gives sslerror: (8, 'EOF occurred in violation of protocol') on this system.

Maybe SSL isn't built the same on the failing machines?

One more time, but on a different machine that is working. This time, the curl command works.

It turns out the failing systems are all running SCO Openserver 5.0.6, while the working systems are running SCO Openserver 5.0.7. Maybe there's a problem with the SSL library installed on 5.0.6? Or the Python installation on those? Or something being used didn't have SSL compiled in, like curl? So maybe the python wrapper calling the SSL library isn't getting what it should be getting out of the library, and then is sending something wrong to the server instead of notifying that something went wrong earlier.

At this point, we've decided we are going to require 5.0.7 for this program, so I'm no longer actively researching a solution.

No comments:

Post a Comment