Емельянов Эдуард Владимирович (eddy_em) wrote,
Емельянов Эдуард Владимирович
eddy_em

Categories:

libonion

В поисках наиболее быстрого решения (ну лень мне пилить свою веб-библиотечку, должно же быть что-то готовое!) нашел две вполне приличных библиотеки: libhttpd и libonion. Во второй понравилось меньшее количество кода и большее количество примеров (хотя, как оказалось, все равно их слишком мало).
Второй день мучаю эту библиотеку.
Документации ужасно мало (html, генерированный doxygen'ом, да несколько примеров), поэтому иной раз приходится для того, чтобы одну строчку кода написать, час-два ковыряться в исходниках библиотеки, чтобы понять, как это сделать...

Итак, для начала я решил попробовать методику сессионной авторизации, которая как бы реализована в libonion. Увидел, что есть поддержка сессий в sqlite и решил воспользоваться этим:
onion_set_session_backend(o,  onion_sessions_sqlite3_new("sqlite.database"));

Выдрал из примеров oterm сессионную авторизацию. Заработало. Однако, сразу смутило, что кука выдается на одну сессию браузера! Ну это же вообще жесть... И как делать сессию длиной в пару лет, чтобы пользователю не нужно было вводить логин/пароль после каждого перезапуска браузера?
Дальше мне в сессионную БД захотелось добавить IP и User-Agent подключенного пользователя. Полчаса ковыряния в исходниках показали, что геттеров для них нет. Но в структуре onion_request есть поля, которые позволят это сделать.
Пробуем с IP:
onion_connection_status oterm_nopam(onion_handler *next, onion_request *req, onion_response *res){
    char host[INET_ADDRSTRLEN];
    struct sockaddr_in* pV4Addr = (struct sockaddr_in*)&req->connection.cli_addr;
    struct in_addr ipAddr = pV4Addr->sin_addr;
    inet_ntop(AF_INET, &ipAddr, host, INET_ADDRSTRLEN);
    onion_dict *session = onion_request_get_session_dict(req);
    const char *username = onion_request_get_session(req, "username");
    if(!username){
   // здесь у нас должна происходить процедура аутентификации, пока забиваем на нее и берем из env
        username = getenv("USER");
        onion_dict_lock_write(session);
        onion_dict_add(session, "username", username, 0);
        onion_dict_add(session, "IP", host, 0);
        //onion_sessions_save(req->connection.listen_point->server->sessions, req->session_id, req->session);
        onion_dict_unlock(session);
    }
    printf("USER IP=%s\n", onion_dict_get(session, "IP"));
...
}

Закомментированной onion_sessions_save вначале не было. Она появилась, когда оказалось, что IP таким образом не сохраняется: в первый раз onion_dict_get показывает мне нормальный адрес, а вот во второй и последующие — мусор! В БД этот же мусор и записан. ОК, добавляем onion_sessions_save и … фигвам! Опять не сохраняет, собака!
Лезу в код. Ковыряюсь час! И не нахожу, как такое может быть: каждый onion_dict_add вызывает аллокатор и копирует данные! Т.е. там неоткуда взяться мусору!! Правда, все подробно изучать было лень. Возможно, где-то на стадии сохранения БД получается этот бред.
Ладно, заменяю char host на const char host, и… Все работает нормально! Вот как так?

Попытка добавить User-Agent была еще интересней: глядя на примеры, где так ловко временные данные добавлялись в "словарь", попытался сделать
const char *UA = onion_request_get_header(req, "User-Agent");
…
onion_dict_add(session, "UA", UA, 0);

Получаю то же самое, что и с IP! А вот если выделяю статическую строку и snprintf'ом в нее пишу полученное, то все ОК.

Кто-то где-то сильно накосячил.

Ладно, сидел утром на работе и, как уже выше писал, обнаружил, что сессионные куки… — сессионные! Да еще и хитрожопые: с флагом, запрещающим их чтение жабоскриптом!
Ну и как, скажите на милость, я в веб-страничке узнаю, что у меня пользователь уже прошел аутентификацию? Отправлять для этого POST-запрос на сервер?
Ладно, буду отправлять POST-запрос для определения сессионного ключа (хотя по-человечески это можно было бы сделать, просто считав данные из куки). Но после того, как пользователь перезапустит браузер, все сессионные куки канут в Лету!

В общем, в libnonion "из коробки" совершенно непригодные методы аутентификации и авторизации. Придется придумывать, как выйти из положения: либо править код самой библиотеки (что чревато: придется делать патчи на каждое обновление), либо на основе их API сделать свои, которые будут сохранять вменяемые сессионные куки (со сроком хотя бы год), которые можно будет читать из жабоскрипта (чтобы сразу же можно было проверить, все ли ОК, и если сессионная кука есть, то открывать вебсокет; если же нет — сначала делать POST-запрос с именем/паролем и получать куку).
В принципе, все равно придется работать с sqlite напрямую: чтобы достать из БД имя пользователя, уровень доступа и SHA512 от пароля, возможно, придется аутентификацию полностью брать на себя — вместе с сессионными ключами.

Второй вариант — попытаться то же самое реализовать на libhttp: вдруг в ней нет этих багов. Поддержка вебсокетов там есть (правда, тоже нет секурных, поэтому имя и пароль тоже придется передавать POST-запросом по https!).


А хочется простейшего: принимать своим демоном клиентские запросы напрямую, без apache или nginx (т.к. большинство наших железок так и работают: веб-сервер ставится на управляющий компьютер тупо ради передачи пользователю двух-трех веб-страничек и пары жабоскриптов с одной-двумя CSSинами). Иметь возможность безопасно передать с клиента на сервер пару логин-пароль, получить сессионную куку и хранить ее хотя бы год — чтобы после перезапуска браузера не нужно было опять аутентификацию проходить. Ну и после того, как подлинность клиента подтверждена, открываем вебсокет и работаем через него.
Tags: c, web, рукожопие
Subscribe

  • Темы-2

    Некоторые испугались, прочитав предыдущие темы. Повторяю: темы для работы в течение всей школы (три года). А вот — их части, которые можно осилить за…

  • Темы для творческих работ школьников

    В связи с возможным проведением очной весенней школы АФШ (детей набрали еще прошлым летом, но пока очно не было возможности встретиться из-за…

  • Бюджетная читалка с алиэкспресса

    Долго искал вменяемые электронные читалки, но за формат примерно А4 просят около $900, вообще озверели. ОК, решил взять мелкую дешевую читалку, чтобы…

promo eddy_em august 17, 2019 12:33 3
Buy for 10 tokens
Юра намедни напечатал корпус для хронометра. Для первого блина получилось неплохо: И еще немного фотографий:
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

  • 1 comment