M2Crypto BIO.readlines зависает, python 2.7

Мы находимся в процессе преобразования проекта на основе C++ openssl в python с M2Crypto, и мы столкнулись с несколько необычной проблемой с подпрограммами BIO от M2Crypto. В частности, любой вызов BIO.readlines() навсегда зависает на файловом объекте.

Вот краткий пример того, что мы пробовали:

f = open('test.txt','w')
f.write('hello world\n')
f.close()

import M2Crypto.BIO
bio = M2Crypto.BIO.openfile('test.txt','r')
lines = bio.readlines()
# the above call hangs forever

Чтобы убедиться, что с нашей установкой OpenSSL не произошло ничего ужасного, мы создаем небольшую тестовую программу для чтения только что созданного файла test.txt.

#include <openssl/bio.h>
#include <openssl/err.h>
int main() {
    const int maxrd = 4096;
    char line[maxrd];
    int rd;
    BIO* bio = BIO_new_file("test.txt","r");
    while((rd = BIO_gets(bio, line, maxrd)) > 0) {
        printf("%s",line);
        }
    if (rd == -1) {
        printf("BIO error %ld\n", ERR_get_error());
        }
    }

Без проблем.

Мы изучили файл-оболочку M2Crypto-0.21.1/SWIG/_bio.i и думаем, что у нас есть представление об источнике проблемы. Строка 109 проверяет возвращаемое значение из BIO_gets().

if (r < 0) {
    // return Py_None
    }

НО, справочная страница для BIO_gets() предполагает, что она может возвращать либо 0, либо -1, чтобы указать конец потока.

Я считаю, что это должно быть

if (r < 1) {
    // return Py_None
    }

Но хотел посмотреть, сталкивались ли с этим другие - или мы ошибаемся в нашем понимании системы BIO_gets().

--- Подробности --- Pythong 2.7 M2Crypto 0.21.1 OpenSSL 0.9.8q-fips 2 декабря 2010 г. FreeBSD 8.2-RELEASE-p4


person user590028    schedule 14.02.2012    source источник


Ответы (2)


На случай, если другие наткнутся на это в будущем, я хотел бы поделиться нашим патчем.

--- M2Crypto-0.21.1.orig/SWIG/_bio.i  2011-01-15 14:10:06.000000000 -0500
+++ M2Crypto-0.21.1/SWIG/_bio.i   2012-02-14 11:34:15.000000000 -0500
@@ -106,7 +106,7 @@
     Py_BEGIN_ALLOW_THREADS
     r = BIO_gets(bio, buf, num);
     Py_END_ALLOW_THREADS
-    if (r < 0) {
+    if (r < 1) {
         PyMem_Free(buf);
         if (ERR_peek_error()) {
             PyErr_SetString(_bio_err, ERR_reason_error_string(ERR_get_error()));

ПРИМЕЧАНИЕ. Для тех, кто знаком с внутренностями M2Crypto, существует три решения этой проблемы. Первый - это патч, опубликованный выше. Поскольку мы считаем, что это соответствует замыслу справочной страницы для BIO_gets(), мы выбрали именно это решение.

Вторым решением было пропатчить M2Crypto/BIO.py. В частности, чтобы исправить код, который реализует BIO.readlines(), чтобы проверить возвращаемое значение из m2.bio.gets() либо для None, либо для len(buf) == 0, и рассматривать оба как конец потока.

Третье решение состояло в том, чтобы просто избежать вызова BIO.readlines() и ограничиться вызовом BIO.readline() (примечание: одинарная строка чтения против строк чтения) и проверить возвращаемое значение из BIO.readline() либо для None, либо для лен(баф) == 0.

Третье решение может показаться не самым лучшим вариантом — скорее, избеганием. Но если вы беспокоитесь о развертывании приложения в среде, где M2Crypto не может быть исправлено, этот подход гарантированно будет наиболее совместимым.

Мы отправили наш патч разработчику записи Heikki, но у него еще не было возможности рассмотреть наше предложение. Пока официального ответа так или иначе не сложится, я хотел поделиться нашими мыслями.

person user590028    schedule 15.02.2012
comment
Кстати, я лично не знаком с разработчиком Хейкки... но его проект M2Crypto просто фантастический. Это не только исключительная реализация оболочки Python/OpenSSL, но и пример ПРАВИЛЬНОГО способа создания кода SWIG — чистого, простого, тщательного и т. д. Молодец, сэр. - person user590028; 15.02.2012

Эта проблема связана с ошибкой №717675 в Debian Linux.

Это невозможно воспроизвести в Fedora 21, и я не нашел патчей для Fedora, которые изменяли бы BIO.py или _bio.i.

Вот патч, который был опубликован для Debian:

--- /usr/lib64/python2.7/site-packages/M2Crypto/BIO.py  2011-01-15 20:10:05.000000000 +0100
+++ BIO.py  2015-05-20 09:24:46.600582999 +0200
@@ -73,6 +73,8 @@
             buf=m2.bio_gets(self.bio, 4096)
             if buf is None:
                 break
+       if len(buf)==0:
+       break
             lines.append(buf)
         return lines
person Jessie    schedule 20.05.2015
comment
Ну я понял, почему в Fedora баг не воспроизводится. Существует патч m2crypto-0.21.1-timeouts.patch, который просто переписывает make-файл функции в Connection.py. В этой новой функции вызывается классический класс _fileobject из python socket.py. Это немного странно, так как заставляет избегать всех механик, определенных в BIO.py. Это означает, что объект, возвращаемый open_https, больше не является объектом BIO... Таким образом, часть преимуществ m2crypto теряется, я думаю.... - person Jessie; 22.05.2015
comment
И да, это больше комментарий, чем ответ. - person Jessie; 22.05.2015