Repository: sabnzbd/sabctools (language: python)

Issue:
Cannot link to OpenSSL on macOS
@animetosho Maybe you know what is going wrong. I thought I could just simply add macOS support, but for some reason it does not seem to search the right folders.
https://github.com/sabnzbd/sabyenc/commit/0146b0ea147515653c903fce923d5c56552de449
Because I located the file as such here, just like on Windows:
<img width="773" alt="Screenshot 2023-01-03 at 14 39 03" src="https://user-images.githubusercontent.com/5703454/210368373-313afca2-4dfb-4cb9-826a-70eff9f300a9.png">



Discussion / hints:
Does macOS use some sort of container/packaging system? If so, maybe it has a private libssl that isn't visible on the system.

Maybe try running the following and using the *libssl* filename printed there.

	otool -L `python3 -c "import ssl; print(ssl._ssl.__file__)"`

Update: checked my macOS 11.6 + Python 3.8 VM, it seems to have `SSL_read` but not `SSL_read_ex`. Checking `ssl.OPENSSL_VERSION` I get *LibreSSL 2.8.3*, so maybe you're not guaranteed to have OpenSSL at all.

It's */usr/lib/libssl.46.dylib* on this VM, and seems like the whole path is needed.
Interesting, here it is:
```
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/lib-dynload/_ssl.cpython-39-darwin.so:
	/Library/Frameworks/Python.framework/Versions/3.9/lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
	/Library/Frameworks/Python.framework/Versions/3.9/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.100.5)
```
Does it work if you use the full path instead of just `libssl.1.1.dylib`?

Can you double check that the function is exported?  
`nm --extern-only /Library/Frameworks/Python.framework/Versions/3.9/lib/libssl.1.1.dylib | grep read`

Also try the following:

	import ctypes
	h=ctypes.CDLL('libssl.1.1.dylib')
	print(h)
	print(h.SSL_read_ex)

(I don't know that much about macOS, so poking in the dark here)
I found a solution by accessing Python runtime information.
Not the most pretty, but it works: 779fdf734b1e4bebc04ac6ed8b5a1e5543042088
Thanks for helping me brainstorm :)

EDIT: Oops, forgot to remove the debug print.
Nice!

Since the full path seems to work, another possibility may be to pass through `ssl._ssl.__file__` to the module - it might work because I think its dependencies get re-exported.
I understood from this comment on that Cpython thread that you linked before, that on macOS the symbols do not get re-exported: https://github.com/python/cpython/issues/88068#issuecomment-1093911787
So that's why I did not try `ssl._ssl.__file__`.

Ah I see.
What happened with this? its not on the openssl_link branch did it get lost in a rebase?

I tested it and it didn't work on my [brew](brew.sh) python@3.10 install.

The extension tried: /usr/local/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/lib/libssl.1.1.dylib

ssl._ssl.\_\_file\_\_: /usr/local/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload/_ssl.cpython-310-darwin.so

There are symlinks going on but based on how it's creating the path it could have found it in:

/usr/local/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload/_ssl.cpython-310-darwin.so

Tested it with that path hardcode and it worked.

edit:

```bash
$ otool -L `python3 -c "import ssl; print(ssl._ssl.__file__)"`
/usr/local/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload/_ssl.cpython-310-darwin.so:
	/usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
	/usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.100.3)
```
I only implemented it for Python from the python.org installer, not homebrew.
Maybe another time. 

Fix patch:
diff --git a/src/sabyenc3.cc b/src/sabyenc3.cc
index 809d73f..7258a70 100644
--- a/src/sabyenc3.cc
+++ b/src/sabyenc3.cc
@@ -71,6 +71,9 @@ static struct PyModuleDef sabyenc3_definition = {
 static bool openssl_init() {
     // TODO: consider adding an extra version check to avoid possible future changes to SSL_read_ex
 
+    PyObject *ssl_module = PyImport_ImportModule("_ssl");
+    if(!ssl_module) return false;
+
 #if defined(_WIN32) || defined(__CYGWIN__)
     HMODULE openssl_handle = GetModuleHandle(TEXT("libssl-1_1.dll"));
 
@@ -79,21 +82,14 @@ static bool openssl_init() {
     *(void**)&SSL_read_ex = GetProcAddress(openssl_handle, "SSL_read_ex");
     *(void**)&SSL_get_error = GetProcAddress(openssl_handle, "SSL_get_error");
 #else
- #if defined(__APPLE__)
-    void* openssl_handle = dlopen("libssl.1.1.dylib", RTLD_LAZY | RTLD_NOLOAD);
- #else
-    void* openssl_handle = dlopen("libssl.so", RTLD_LAZY | RTLD_NOLOAD);
-    if(!openssl_handle)
-        openssl_handle = dlopen("libssl.so.1.1", RTLD_LAZY | RTLD_NOLOAD);
-    if(!openssl_handle)
-        openssl_handle = dlopen("libssl.so.3", RTLD_LAZY | RTLD_NOLOAD);
-    if(!openssl_handle)
-        openssl_handle = dlopen("libssl.so.1.0.2", RTLD_LAZY | RTLD_NOLOAD);
-    if(!openssl_handle)
-        openssl_handle = dlopen("libssl.so.1.0.1", RTLD_LAZY | RTLD_NOLOAD);
-    if(!openssl_handle)
-        openssl_handle = dlopen("libssl.so.1.0.0", RTLD_LAZY | RTLD_NOLOAD);
- #endif
+    // Find library at "import ssl; print(ssl._ssl.__file__)"
+    PyObject *ssl_module_dict = PyModule_GetDict(ssl_module);
+    if(!ssl_module_dict) return false;
+
+    PyObject *ssl_module_path = PyDict_GetItemString(ssl_module_dict, "__file__");
+    if(!ssl_module_path) return false;
+
+    void* openssl_handle = dlopen(PyUnicode_AsUTF8(ssl_module_path), RTLD_LAZY | RTLD_NOLOAD);
 
     if(!openssl_handle) return false;
     *(void**)&SSL_read_ex = dlsym(openssl_handle, "SSL_read_ex");


Test patch:
diff --git a/tests/test_openssl_linked.py b/tests/test_openssl_linked.py
new file mode 100644
index 0000000..b988a77
--- /dev/null
+++ b/tests/test_openssl_linked.py
@@ -0,0 +1,5 @@
+from tests.testsupport import *
+
+
+def test_openssl_linked():
+    assert sabyenc3.openssl_linked == True