...

суббота, 28 марта 2015 г.

Результаты опроса популярности PHP фреймворков от Sitepoint

Месяц назад на Sitepoint был опубликирован опрос популярности PHP фреймворков. За это время всего проголосовало 7800 людей, и вот его результаты:

На работе


image


В персональных проектах

image


Больше различной статистики на самом сайте


Из интересного еще замечу, что в России самым популярным остается Yii2, в Украине же победил PHPixie. Также PHPixie стал самым популярным фреймворком в возрастной групе до 18 лет.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Создание плагина для логической репликации в PostgreSQL 9.4+

Как многие интересующися знают, в PostgreSQL в версии 9.4 появилась (наконец-то) логическая репликация. Теперь, чтобы сделать свою репликацию, необязательно разбираться с форматом бинарных wal файлов или писать триггеры (может были еще способы), а преобразовать данные в удобный для себя формат. Для этого достаточно написать плагин к PostgreSQL, который будет этим заниматься. В статье описывается плагин, который преобразует данные в JSON.


Код плагина находится на гитхабе — http://ift.tt/1HaKcOW. Приветсвуются pull-requests с улучшениями (особенно по части улучшения поддержки типов), багфиксами и просто косметическими улучшениями. JSON был выбран за простоту. Это не окончательный вариант, возможно после тестирования на реальных данных окажется что нужен более производительный формат, и придется переделать. В статье я не буду приводить весь код плагина, а только части про которые мне кажется нужно рассказать.


Необходимые требования для создания плагина: знание С, установленные средства сборки (gcc, cmake), установленные пакеты (в debian-системах) postgresql-9.4, postgresql-server-dev-9.4 и аналогичные в других системах. После установки postgresql, в postgresql.conf надо установить значение max_replication_slots = 1 (или больше) и wal_level = logical.


Сам плагин представляет собой подключаемую библиотеку на C, из которой вызываются callback функции на события postgresql. При инициализации вызывается функция _PG_output_plugin_init со структурой, полям которой полям нужно назначить свои функции:



  • startup_cb — функция, вызываемая при инициализации плагина

  • begin_cb — начало транзакции

  • change_cb — запись данных

  • commit_cb — потверждение транзакции

  • shutdown_cb — деинициализация плагина




Функция которая заполняет структуру:

void _PG_output_plugin_init(OutputPluginCallbacks *cb)
{
cb->startup_cb = decoder_json_startup;
cb->begin_cb = decoder_json_begin_txn;
cb->change_cb = decoder_json_change;
cb->commit_cb = decoder_json_commit_txn;
cb->shutdown_cb = decoder_json_shutdown;
}




Теперь осталось определить эти пять функций. decoder_json_startup вызывается в начале декодирования и используется для задания опций декодирования и создания своего контекста памяти:
Функция decoder_json_startup


/* initialize this plugin */
static void
decoder_json_startup(LogicalDecodingContext *ctx,
OutputPluginOptions *opt,
bool is_init)
{
ListCell *option;
DecoderRawData *data;

data = palloc(sizeof(DecoderRawData));
data->context = AllocSetContextCreate(ctx->context,
"Raw decoder context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
data->include_transaction = false;
data->sort_keys = false;

ctx->output_plugin_private = data;

/* Default output format */
opt->output_type = OUTPUT_PLUGIN_TEXTUAL_OUTPUT;

foreach(option, ctx->output_plugin_options)
{
DefElem *elem = lfirst(option);

Assert(elem->arg == NULL || IsA(elem->arg, String));

if (strcmp(elem->defname, "include_transaction") == 0)
{
/* if option does not provide a value, it means its value is true */
if (elem->arg == NULL)
data->include_transaction = true;
else if (!parse_bool(strVal(elem->arg), &data->include_transaction))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not parse value \"%s\" for parameter \"%s\"",
strVal(elem->arg), elem->defname)));
}
else if (strcmp(elem->defname, "sort_keys") == 0) {
/* if option does not provide a value, it means its value is true */
if (elem->arg == NULL)
data->sort_keys = true;
else if (!parse_bool(strVal(elem->arg), &data->sort_keys))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not parse value \"%s\" for parameter \"%s\"",
strVal(elem->arg), elem->defname)));
}
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("option \"%s\" = \"%s\" is unknown",
elem->defname,
elem->arg ? strVal(elem->arg) : "(null)")));
}
}
}





Здесь парсятся параметры, переданные в плагин и сохраняются в структуру. Созданный контекст памяти используется потом в decoder_json_change чтобы корректно почистить используемые ресурсы. Важные моменты:

  • свои данные сохраняются в ctx->output_plugin_private

  • opt->output_type = OUTPUT_PLUGIN_TEXTUAL_OUTPUT — так задается что вывод плагина будет текстовый




decoder_json_shutdown вызывается в конце декодирования, и используется для чистки ресурсов.
Функция decoder_json_shutdown


/* cleanup this plugin's resources */
static void
decoder_json_shutdown(LogicalDecodingContext *ctx)
{
DecoderRawData *data = ctx->output_plugin_private;

/* cleanup our own resources via memory context reset */
MemoryContextDelete(data->context);
}







Дальше самое интересное. Надо определить функции decoder_json_begin_txn, decoder_json_commit_txn и decoder_json_change которые собственно и генерируют строки, получаемые командами pg_logical_slot_peek_changes и pg_logical_slot_get_changes. Сгенеренную строку надо добавить в слот, это делается командами:

OutputPluginPrepareWrite(ctx, true);
appendStringInfoString(ctx->out, "some string");
OutputPluginWrite(ctx, true);




Функции decoder_json_begin_txn и decoder_json_commit_txn пишут (или просто пропускают, если есть такое условие) команды начала и конца транзакции в слот — строки 'begin' и 'commit' соответсвенно.

Функция decoder_json_change вызывается на событие изменения данных. В этой функции определяется какое именно событие произошло (INSERT, UPDATE, DELETE) и для каждого из них создается своя структура. Для UPDATE и DELETE важно наличие уникального (not null) или первичного ключа в таблице, иначе просто невозможно определить изменяемую (удаляемую) строку. Это зависит от значения параметра REPLICA IDENTITY для таблицы.


Эта функция принимает 4 параметра:



  • LogicalDecodingContext *ctx — контекст

  • ReorderBufferTXN *txn

  • Relation relation — сведения о изменяемой таблице

  • ReorderBufferChange *change — сведения о данных




Кратко про функцию можно сказать что тип операции определяется через change->action. Далее по данным в change (change->data.tp.newtuple и change->data.tp.oldtuple) создается JSON структура. JSON генерируется с помощью библиотеки libjansson.

Вот здесь то и начинаются сложности. Если REPLICA IDENTITY для таблицы установлен в NOTHING или DEFAULT при отсутствующем первичном ключе, то невозможно определить изменяемые строки и в лог попадут только записи добавления. При обновлении или удалении данных с таблицы с DEFAULT, FULL, INDEX и при наличии уникального ключа, то его значение берется из newtuple или из oldtuple (если значение ключа изменяется запросом). При отсутствии уникального ключа и если FULL, то для идентификации используются все значения из oldtuple.


В результате строится JSON структура, вида {"a": 0, "r": "public.some_table", "c": {"id": 1}, "d": {"a": 2}}, где a — это тип действия, r — название таблицы, c — значения для идентификации строки, d — собственно данные.


Проверим работу. Сборка плагина и запуск тестов:



git clone http://ift.tt/1HaKcOZ
cd decoder_json
# разрешаем запись всем в папку с библиотеками постгреса - ни в коем случае нельзя делать на продакшене
sudo chmod a+rw `pg_config --pkglibdir`
chmod a+rwx ./
# скачиваем и собираем libjansson, для генерации JSON
make deps
# переключаемся под юзера postgres
sudo su postgres
make
make test




Тестирование плагина вручную:

# переключаемся под юзера postgres, создаем тестовую бд и открываем консоль работы с бд
sudo su postgres
createdb test_db
psql test_db

# psql консоль
test_db=# create table test1 (id serial primary key, name varchar);
test_db=# SELECT * FROM pg_create_logical_replication_slot('custom_slot', 'decoder_json');
slot_name | xlog_position
-------------+---------------
custom_slot | 0/4D9F870
(1 row)




Здесь мы указываем название слота и подключаемый плагин. В ответе мы видим название слота, и место (xlog позиция) с которого собственно начинается запись данных в слот. То что мы указали наш плагин, не означает что он уже работает, само декодирование начинается только когда мы забираем данные. Для этого используются функции pg_logical_slot_peek_changes и pg_logical_slot_get_changes. Они отличаются тем что get функция после получения данных чистит очередь.

Добавление данных:



test_db=# insert into test1 values (1, 'bb');
INSERT 0 1
test_db=# insert into test1 values (2, 'bb');
INSERT 0 1
test_db=# select * from pg_logical_slot_get_changes('custom_slot', NULL, NULL, 'include_transaction', 'on');
location | xid | data
-----------+-------+-----------------------------------------------------
0/BAB0968 | 48328 | begin
0/BAB0968 | 48328 | {"a":0,"r":"public.test1","d":{"id":1,"name":"bb"}}
0/BAB09F0 | 48328 | commit
0/BAB09F0 | 48329 | begin
0/BAB09F0 | 48329 | {"a":0,"r":"public.test1","d":{"id":2,"name":"bb"}}
0/BAB0A78 | 48329 | commit
(6 rows)




Изменение данных

test_db=# update test1 set name = 'dd' where id=2;
UPDATE 1
test_db=# select * from pg_logical_slot_get_changes('custom_slot', NULL, NULL, 'include_transaction', 'on');
location | xid | data
-----------+-------+------------------------------------------------------------------
0/BB4C700 | 48338 | begin
0/BB4C700 | 48338 | {"c":{"id":2},"a":1,"r":"public.test1","d":{"id":2,"name":"dd"}}
0/BB4C798 | 48338 | commit
(3 rows)




Удаление данных:

test_db=# delete from test1 where id=2;
DELETE 1
test_db=# select * from pg_logical_slot_get_changes('custom_slot', NULL, NULL, 'include_transaction', 'on');
location | xid | data
-----------+-------+-----------------------------------------
0/BB4C8A8 | 48339 | begin
0/BB4C8A8 | 48339 | {"c":{"id":2},"a":2,"r":"public.test1"}
0/BB4C9C8 | 48339 | commit
(3 rows)




Использованные и полезные ресурсы:

  • документация PostgresqlSQL — http://ift.tt/15Yxq9x

  • пример плагина из PostgreSQL (http://ift.tt/1a2drsT)

  • michael.otacoo.com/ — очень полезный блог, плагин decoder_raw автора этого блога использовался как основа для моего плагина.

  • http://ift.tt/1HaKfdy — плагин который использует google protocol buffers как выходной формат.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] 10 советов по прототипированию в Sketch


Так как Sketch был создан специально для работы над мобильными и веб-интерфейсами, неудивительно что он стал одним из самых быстрых и гибких инструментов для прототипирования. Эти 10 советов сделают его еще более мощным средством.


1. Поймите зачем и для кого вы делаете прототип




Окей, этот пункт не имеет ничего общего со Sketch, но это невероятно важная часть всего процесса прототипирования, поэтому мы просто обязаны включить его.

Прототипирование является сердцем всего итеративного дизайн-процесса. Оно снижает риски и уровень неопределенности, а также позволяет сохранить время и деньги, которые были бы потрачены на разработку непроверенных решений. Хороший прототип дает вам возможность протестировать предположения, выявить и устранить проблемы юзабилити, описать взаимодействие и многое другое.


Как сделать хороший прототип? Это зависит от того зачем и для кого вы его делаете. Прототип, созданный для тестирования концепции должен использовать немного другие элементы, чем прототип, который объясняет анимацию разработчикам. Ваши цели и люди, для которых вы создаете прототип определяют его внешний вид, а также его точность и функционал.


2. Делайте наброски прежде чем переходить к Sketch




Sketch — изумительный инструмент. Но набросок на бумаге может помочь быстро определить требования и различные состояния для предложенной вами концепции. Выстроенные экран за экраном быстрые наброски из прямоугольников, кружков и стрелочек могут помочь увидеть всю картину целиком и организовать ваши артборды в Sketch. Однако не надейтесь, что это объяснит что-нибудь хоть кому-то кроме вас.

image

Набросок прототипа мобильного приложения до открытия Sketch


3. Используйте cимволы для всех повторяющихся элементов




Ваш прототип может содержать множество артбордов с кучей повторяющихся элементов: одни и те же хедеры, футеры, кнопки, списки и т.д. Изменение расположения и стилей элементов практически неизбежны, особенно на ранних стадиях прототипирования, что означает тонну утомительной и скучной работы, если только вы не используете cимволы.

Давайте предположим, что вы хотите изменить цвет фона хедера и размер кнопки. Если вы сохранили их как cимволы, то потребуется внести изменения лишь в одну из копий, все остальные изменятся автоматически. Или еще круче — вы можете исключить текст из элемента, тогда после изменения цвета кнопки с надписью «Начать» в синий цвет, кнопка «Войти» тоже станет синей без изменения копии.

Чтобы создать символ, просто выделите слой или группу слоев и выберите Layer > Create Symbol. Однажды создав символ, вы можете быстро добавить его в ваш дизайн при помощи кнопки Insert.


image

Обновление и использование символов между страницами и артбордами: Использование символов в Sketch


4. Создавайте артборды и элементы пользовательского интерфейса легко и быстро




Функция Make Grid позволяет вам быстро создать и распределить элементы по артборду. Она особенно эффективноа в паре с символами. Вы можете создать одно отображение с несколькими символами (фон, хедер, футер, кнопки и т.д.) и скопировать его столько раз, сколько вам нужно.

image

Создание элементов и артбордов с использованием функции Make Grid


5. Генерируйте контент в один клик




Плагин Content Generator for Sketch Тимура Карпеева позволяет наполнить ваш дизайн случайными данными в один клик. Это могут быть фотографии и имена людей, фон, геолокационные данные или просто случайный текст. Этот плагин получает данные и графику от популярных сервисов: Unsplash, uiFaces, uinames и Mockaroo. Нужны случайные финансовые данные? Тогда вам подойдет плагин Тайлера Волфа sketchFinDataGen

image

Генерация данных на лету с использованием плагина Content Generator for Sketch


6. Используйте перекрывающий слой для имитации более реалистичного взаимодействия




Один из способов улучшить ваш прототип — добавить перекрывающий слой. Уведомления, всплывающие окна или предпросмотр изображений часто могут использовать перекрывающий слой, чтобы сфокусировать на себе внимание. Вы можете сделать это, экспортировав один из артбордов как .png-изображение напрямую в другой артборд просто перетащив его. После чего настроить заливку фона, настройки смешивания и прозрачность, чтобы добиться нужного эффекта.

image

Создание перекрывающего слоя для более детализированного и реалистичного прототипа


7. Добавляйте клавиатуру на все экраны, где необходим ввод текста




Ориентированный на пользователя дизайн ведет продукт к успеху, а избавление от лишних шагов делает ваших пользователей более счастливыми. Слишком часто пользователю мобильного приложения приходится нажимать на поля ввода (такие как адрес электронной почты или форма логина), чтобы вызвать клавиатуру. Вместо этого убедитесь, что клавиатура будет показана сразу же как только экран загрузится.

image

Избавление от лишних шагов, даже такой мелочи как нажатие на поле ввода для логина, действительно улучшает взаимодействие с пользователем


8. Организовывайте ваши экраны при помощи страниц в Sketch




Очень просто превратить в хаос все ваши артборды и слои. К счастью, Sketch поддерживает многостраничность, что позволяет упорядочить все ваши материалы при этом храня их в одном .sketch-файле. Мы обычно используем страницы для разделения прототипов, сринфлоу, концептов, вайрфреймов и материалов для App Store.

image

Использование страниц для организации прототипов и скринфлоу


9. Создавайте интерактивный прототип прямо из исходного файла




В случае если вы еще не слышали — InVision поддерживает Sketch и умеет синхронизироваться со ним. Достаточно загрузить ваш исходный файл на сайт или облегчить себе жизнь, используя InVision Sync или Dropbox синхронизацию. С двумя последними вариантами обновление прототипа становится не сложнее его сохранения.

10. Используйте наборы элементов интерфейса и ресурсы других дизайнеров




Вы можете получить множество как платных, так и бесплатных наборов элементов интерфейса таких как TETHR и TERACY на сайте Sketch App Sources. Использование наработок других дизайнеров может стать отличным способом улучшить ваш рабочий процесс и научиться новым техникам прототипирования.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Авторизация через китайские соцсети

Любой нормальный сайт\приложение нынче позволяет зарегистрироваться, используя аккаунты третьих служб. И если по англоязычным\русскоязычным информации более чем достаточно, то по китайским(в русском сегменте Интернета) — кот наплакал. Между тем, помимо того, что это около 700 млн пользователей интернета, это еще и более 5 млн китайцев посетивших Россию в 2014 году и около 500 тысяч китайцев, оставшихся в России на временное\постоянное проживание — в первую очередь студентов. Так как интеграция китайцев в местное общество всегда проходит со скрипом, они предпочитают пользоваться программным обеспечением, знакомым с детства — таким, как интернет-мессенджер QQ.

В этой статье я хотел бы осветить процесс интеграции логина через QQ в мобильных приложениях\ на веб-сайтах. Это очень просто. Расскажу на примере нашего приложения для платформы Android





Первое, что нам понадобится — базовый аккаунт QQ. Зарегистрировать его проще простого — переходим по ссылке, заполняем все поля — готово. Тут даже останавливаться не на чем.

Второе — вам понадобится улучшить свой базовый аккаунт до аккаунта разработчика. Переходим на сайт и нажимаем на кнопку 登陆( справа сверху, где логинимся под нашей учетной записью), после чего переходим в центр управления.


Тут нам понадобится заполнить информацию о себе. Иностранцам все очень даже разрешено, если есть друг в Китае — проще на него регистрировать, общаться с техподдержкой будет проще, если нет — тоже ничего страшного, но вам, скорее всего, в общении придется использовать онлайн-переводчик. Если все же хотите зарегистрироваться, как разработчик из Китая, вам понадобится виртуальный китайский номер телефона, его можно сделать тут:

www.wo-call.com (тогда в качестве адреса надо будет указывать провинцию Шицзячжуан), или

www.jego.me(Шанхай)

Советую все же быть правдивым — они потом потребуют ваш паспорт, да и честность всегда была добродетелью-)



После того, как вас одобрят (в QQ сидят не такие злюки, как, например, в Weibo, они обычно не придираются), вам станет доступна панель разработчика. Нам требуется кнопка «Создать приложение».



После этого будет обычный процесс создания приложения. Главное, не забудьте выбрать пункт «бесплатное», так как прием платежей от китайских пользователей — отдельная большая тема, сразу это недоступно, если будет интересно — расскажу в следующей статье. К бесплатным же никто не придирается. Соответственно, не забудьте подготовить apk, если это Андроид-приложение, ссылку на Appstore, если это iOS, или же ваше веб-приложение( в соответствиии с тем, что выбрали).



После того, как приложение пройдет одобрение и будет выпущено, оно автоматически появится в веб-магазине QQ в их соцсети QZone(если это веб-приложение), их андроид-маркете(если это Android-приложение). Как подключиться к их рекламной сетке и получать доход от рекламы, как интегрировать их облачные сервисы, возможности для промо и т.д. — тема слишком обширная, чтобы на ней останавливаться.



Нас сейчас интересует только логин через их приложение. Для этого нам понадобится App ID и App Key. Его мы видим после нажатия на название приложения.



В принципе, все, дело сделано. Теперь осталось только добавить пару строк кода в ваше приложение.

Полный SDK для вашей платформы можно скачать по ссылке

Полное описание добавления логина через QQ тоже находится по ссылке

1) Переходим по ссылке и заполняем анкету на получение возможности для приложения логина через QQ

2) по ссылке скачиваем изображение кнопки логина

3) Добавляем Build Path и настраиваем androidmanifest

4) Добавляем код callback

5) Получаем Access_token

6) Получаем пользовательский OpenID

7) В случае необходимости получаем пользовательские данные, настраиваем шаринг новостей в QZone и т.д.

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

Надеюсь, хоть кому-то это пригодилось.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Реверс-инжениринг драйверов USB-устройств на примере машинки на радиоуправлении

image

Один из аргументов любителей Windows перед любителями Linux – недостаток драйверов для оборудования под эту ОС. С течением времени ситуация выправляется. Сейчас она уже гораздо лучше, чем 10 лет назад. Но иногда можно встретить какое-то устройство, которое не распознаётся вашим любимым дистрибутивом. Обычно это будет какая-нибудь USB-периферия.


Красота свободного софта в том, что эту проблему можно решить самостоятельно (если вы программист). Конечно, всё зависит от сложности оборудования. С трёхмерной веб-камерой у вас может и не получится – зато многие USB-устройства довольно просты, и вам не придётся нырять в глубины ядра или закапываться в С. В этом уроке мы с вами при помощи Python по шагам изготовим драйвер к игрушечной радиоуправляемой машинке.


Процесс по сути будет реверс-инженирингом. Сначала мы подробно изучим устройство, затем сохраним данные, которыми оно обменивается с драйвером в Windows, и попытаемся понять, что они означают. Для нетривиальных протоколов вам может потребоваться как опыт, так и удача.



Знакомство с USB




USB – шина с управлением хостом. Хост (PC) решает, какое устройство отправляет данные по проводам, и когда именно. Даже в случае асинхронного события (нажатие кнопки на клаве) оно не отправляется хосту сейчас же. Поскольку на каждой шине может быть до 127 устройств (и ещё больше, если через хабы), такая схема работы облегчает управление.

Также у USB есть многослойная система протоколов – примерно как у интернета. Самый нижний уровень обычно реализован в кремнии. Транспортный слой работает через туннели (pipe). Потоковые туннели передают разные данные, туннели сообщений – сообщения для управления устройствами. Каждое устройство поддерживает минимум один туннель сообщений. На высшем уровне приложения (или класса) есть протоколы вроде USB Mass Storage (флэшки) или Human Interface Devices (HID), устройства для взаимодействия человека с компьютером.


В проводах




USB-устройство можно рассматривать как набор конечных точек, или буферов ввода/вывода. У каждого есть направление данных (ввод или вывод) и тип передачи. По типам буферы бывают следующие: прерывания, изохронные, управляющие и пакетные.

Прерывания передают данные по чуть-чуть в реальном времени. Если пользователь нажал клавишу, устройство ждёт, пока хост не спросит «не нажимали ли там кнопочки?». Хост не должен тормозить, и эти события не должны потеряться. Изохронные работают примерно так же, но не настолько жёстко – они разрешают передавать больше данных, при этом допуская их потерю, когда это не критично (например, веб-камеры).


Пакетные предназначены для больших объёмов. Чтобы они не забивали канал, им отдаётся всё место, которое сейчас не занято другими данными. Управляющие используются для управления устройствами, и только у них есть жёстко заданный формат запросов и ответов. Набор буферов со связанными с ним метаданными называется интерфейсом.


У любого USB-устройства есть буфер номер ноль – это конечная точка туннеля по умолчанию, который используется для управляющих данных. Но как хост узнаёт, сколько у устройства есть ещё буферов и какого они типа? Для этого используются разные дескрипторы, отправляемые по особым запросам по туннелю по умолчанию. Они могут быть стандартными для всех, особыми для конкретных классов устройств, или проприетарными.


Дескрипторы составляют иерархию, которую можно посмотреть утилитами типа lsusb. Наверху сидит дескриптор устройства, где содержится Vendor ID (VID) и Product ID (PID). Эта пара уникальная для каждого устройства, по ней система находит нужный драйвер. У устройства может быть несколько конфигураций, каждое со своим интерфейсом (например, принтер, сканер и факс в МФУ). Но обычно определяется одна конфигурация с одним интерфейсом. Они описываются соответствующими дескрипторами. У каждой конечной точки есть дескриптор, содержащий её адрес (число), направление (ввод или вывод) и тип передачи.


У спецификаций классов есть свои типы дескрипторов. Спецификация USB HID ожидает передачу данных в виде «отчётов», которые отправляются и принимаются по буферу управления или прерываний. Эти дескрипторы определяют формат отчёта (к примеру, «1 поле длиной 8 бит») и то, как его надо использовать («офсет в направлении Х»). Поэтому HID-устройство описывает само себя и его может поддерживать универсальный драйвер (usbhid в Linux). Иначе пришлось бы писать свой драйвер для каждой мыши.


Не буду пытаться описывать в нескольких абзацах сотни страниц спецификаций. Интересующихся отправляю к книге O’Reilly «USB in a Nutshell», бесплатно лежащей по адресу http://ift.tt/1CCytJt. Займёмся-ка лучше делом.


Разбираемся с разрешениями




По умолчанию с USB-устройствами можно работать только из-под рута. Чтобы не запускать таким образом тестовую программу, добавим правило udev:

SUBSYSTEM=="usb", ATTRS{idVendor}=="0a81", ATTRS{idProduct}=="0702", GROUP="INSERT_HERE", MODE="0660"


Вставьте имя группы, к которой принадлежит ваш пользователь, и добавьте это в /lib/udev/rules.d/99-usbcar.rules.


Под капотом




Посмотрим, как выглядит машинка по USB. lsusb – инструмент для подсчёта устройств и декодирования их дескрипторов. Входит в комплект usbutils.

[val@y550p ~]$ lsusb
Bus 002 Device 036: ID 0a81:0702 Chesen Electronics Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
...


Машинка – это Device 036 (чтобы быть уверенным, её можно отсоединить и снова запустить lsusb). Поле ID – это пара VID:PID. Для чтения дескрипторов запустите lsusb -v:



[val@y550p ~]$ lsusb -vd 0a81:0702
Bus 002 Device 036: ID 0a81:0702 Chesen Electronics Corp.
Device Descriptor:
idVendor 0x0a81 Chesen Electronics Corp.
idProduct 0x0702
...
bNumConfigurations 1
Configuration Descriptor:
...
Interface Descriptor:
...
bInterfaceClass 3 Human Interface Device
...
iInterface 0
HID Device Descriptor:
...
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
...
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
...


Стандартная иерархия. Как и у большинства устройств, у неё только одна конфигурация и интерфейс. Можно заметить одну конечную точку interrupt-in (кроме точки по умолчанию 0, которая есть всегда, и поэтому не выводится в списке). Поле bInterfaceClass сообщает о том, что это устройство HID. Это хорошо – протокол общения с HID открыт. Казалось бы, прочтём дескриптор отчётов, чтобы понять их формат и использование, и дело в шляпе. Однако у него стоит пометочка ** UNAVAILABLE **. ЧЗН? Поскольку машинка – устройство HID, драйвер usbhid присвоил её себе, но не знает, что с ней делать. Надо отвязать его от управления ею.


Для начала надо найти адрес шины. Переподключим её, запустим dmesg | grep usb, и посмотрим в последнюю строчку, начинающуюся с usb X-Y.Z:. X, Y и Z – целые числа, уникальным образом определяющие порты на хосте. Затем запустим



[root@y550p ~]# echo -n X-Y.Z:1.0 > /sys/bus/usb/drivers/usbhid/unbind


1.0 – это конфигурация и интерфейс, которые должен отпустить драйвер usbhid. Чтобы подвязать всё обратно, запишите то же самое в /sys/bus/usb/drivers/usbhid/bind.


Теперь поле Report descriptor выдаёт информацию:



Report Descriptor: (length is 52)
Item(Global): Usage Page, data= [ 0xa0 0xff ] 65440
(null)
Item(Local ): Usage, data= [ 0x01 ] 1
(null)
...
Item(Global): Report Size, data= [ 0x08 ] 8
Item(Global): Report Count, data= [ 0x01 ] 1
Item(Main ): Input, data= [ 0x02 ] 2
...
Item(Global): Report Size, data= [ 0x08 ] 8
Item(Global): Report Count, data= [ 0x01 ] 1
Item(Main ): Output, data= [ 0x02 ] 2
...


Задано два отчёта. Один читает с устройства (ввод), второй пишет (вывод). Оба размером в байт. Однако их использование не очевидно. Для сравнения, вот как выглядит дескриптор отчёта для мышки (не весь, но главные строчки):



Report Descriptor: (length is 75)
Item(Global): Usage Page, data= [ 0x01 ] 1
Generic Desktop Controls
Item(Local ): Usage, data= [ 0x02 ] 2
Mouse
Item(Local ): Usage, data= [ 0x01 ] 1
Pointer
Item(Global): Usage Page, data= [ 0x09 ] 9
Buttons
Item(Local ): Usage Minimum, data= [ 0x01 ] 1
Button 1 (Primary)
Item(Local ): Usage Maximum, data= [ 0x05 ] 5
Button 5
Item(Global): Report Count, data= [ 0x05 ] 5
Item(Global): Report Size, data= [ 0x01 ] 1
Item(Main ): Input, data= [ 0x02 ] 2


Тут всё ясно. С машинкой – непонятно, и нам надо догадаться об использовании битов самостоятельно.


Небольшой бонус




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

Работа для детектива




При анализе сетевого трафика используют снифер. И в нашем случае такая штука пригодится. Бывают специальные USB-мониторы для коммерческого использования, но для нашей задачи подойдёт и Wireshark.

Настроим перехват USB в Wireshark. Сначала разрешим мониторинг USB в ядре. Загрузим модуль usbmon:



[root@y550p ~]# modprobe usbmon


Подмонтируем особую файловую систему debugfs:



[root@y550p ~]# mount -t debugfs none /sys/kernel/debug


Появится директория /sys/kernel/debug/usb/usbmon, которую можно использовать для записи трафика простыми средствами оболочки:



[root@y550p ~]# ls /sys/kernel/debug/usb/usbmon
0s 0u 1s 1t 1u 2s 2t 2u


Там лежат файлы с загадочными именами. Целое число – номер шины (первая часть адреса шины USB); 0 означает все шины на хосте. s – statistics, t – transfers, u — URBs (USB Request Blocks логические сущности, представляющие происходящие транзакции). Чтобы сохранить все передачи на шине 2, введите:



[root@y550p ~]# cat /sys/kernel/debug/usb/usbmon/2t
ffff88007d57cb40 296194404 S Ii:036:01 -115 1 <
ffff88007d57cb40 296195649 C Ii:036:01 0 1 = 05
ffff8800446d4840 298081925 S Co:036:00 s 21 09 0200 0000 0001 1 = 01
ffff8800446d4840 298082240 C Co:036:00 0 1 >
ffff880114fd1780 298214432 S Co:036:00 s 21 09 0200 0000 0001 1 = 00


Для нетренированного глаза тут ничего непонятно. Хорошо, что Wireshark будет декодировать данные.


Теперь нам нужна Windows, которая будет работать с оригинальным драйвером. Лучше всего установить всё в (с Oracle Extension Pack). Убедитесь, что VirtualBox может использовать устройство, и запустите KeUsbCar, которая управляет машинкой в Windows. Запустите Wireshark, чтобы посмотреть, какие команды драйвер отправляет на устройство. На первом экране выберите интерфейс usbmonX, где X – это шина, к которой подключена машинка. Если Wireshark запускается не из-под рута, убедитесь, что узлы /dev/usbmon* имеют соответствующие разрешения.


image


Нажмём в KeUsbCar кнопочку Forward. Wireshark перехватит несколько исходящих управляющих пакетов. На скриншоте отмечен тот, который нужен нам. Судя по параметрам, это запрос SET_REPORT (bmRequestType = 0x21, bRequest = 0x09), который обычно используется, чтобы изменить статус устройства – такого, как лампочки на клавиатуре. Согласно тому Report Descriptor, что мы видели, длина данных составляет 1 байт, и сам отчёт содержит 0x01 (также подсвечено).


Нажатие кнопки Right выливается в похожий запрос. Но отчёт уже содержит 0х02. Можно догадаться, что это означает направление движение. Таким же образом выясняем, что 0х04 – это правый реверс, 0х08 – задний ход, и т.д. Правило простое: код направления – это двоичная единичка, сдвинутая влево на позицию кнопки в интерфейсе KeUsbCar, если считать их по часовой стрелке.


Также можно отметить периодические запросы прерываний от Endpoint 1 (0x81, 0x80 означает, что это точка ввода; 0x01 это её адрес). Что это? Кроме кнопок, у KeUsbCar есть индикатор заряда, так что это, возможно, данные по батарее. Их значение не меняется (0х05), если машина не выезжает из гаража. В противном случае запросов прерываний не происходит, но они возобновляются, если мы ставим её обратно. Тогда, видимо, 0х05 означает «идёт зарядка» (игрушка простая, поэтому уровень зарядки не передаётся). Когда батарея зарядится, прерывание начнёт возвращать 0x85 (0x05 с установленным 7 битом). Видимо, 7 бит означает «заряжено». Что делают биты 0 и 2, которые составляют 0х05, пока неясно.


Пишем почти настоящий драйвер




Заставить программу работать с устройством, которое ранее не поддерживалось – это хорошо, но иногда нужно сделать так, чтобы с ним работала и остальная система. Это значит, надо делать драйвер, а это требует программирования на уровне ядра (http://ift.tt/TPGMi7), и вам это вряд ли сейчас нужно. Но возможно, нам удастся обойтись и без этого, если речь идёт про USB.

Если у вас есть USB-сетевуха, можно использовать TUN/TAP для подключения программы PyUSB в сетевой стек Linux. Интерфейсы TUN/TAP работают как обычные сетевые, с именами типа tun0 или tap1, но через них все пакеты становятся доступны в узле /dev/net/tun. Модуль pytun делает работу с TUN/TAP простой. Быстродействие страдает, но можно переписать программу на С с использованием libusb.


Ещё один кандидат – USB-дисплей. В Linux есть модуль vfb, который позволяет обращаться к фреймбуферу как к /dev/fbX. Можно использовать ioctls, чтобы перенаправить консоль на него, и закачивать содержимое /dev/fbX на USB-устройство. Это тоже не быстро, но ведь вы не собираетесь играть в 3D-шутеры через USB.


Пишем код




Сделаем такую же программу, как для Windows. 6 стрелочек и уровень зарядки, который мигает, когда машинка заряжается. Код лежит на GitHub http://ift.tt/1BQPcDz

Как нам работать в USB под Linux? Это возможно делать из пространства пользователя при помощи библиотеки libusb. Она написана на С и требует хороших знаний USB. Простая альтернатива – PyUSB. Для интерфейса пользователя я использовал PyGame.


Исходники PyUSB скачайте с http://ift.tt/1BQPf2b, и установите через setup.py. Ещё вам понадобится установить библиотеку libusb. Поместим функциональность для управления машиной в класс с оригинальным названием USBCar.



import usb.core
import usb.util
class USBCar(object):
VID = 0x0a81
PID = 0x0702
FORWARD = 1
RIGHT = 2
REVERSE_RIGHT = 4
REVERSE = 8
REVERSE_LEFT = 16
LEFT = 32
STOP = 0


Импортируем два главных модуля PyUSB и вставляем значения для управления машинкой, которые мы вычислили при просмотре трафика. VID и PID – это id машинки, взятые из вывода lsusb.



def __init__(self):
self._had_driver = False
self._dev = usb.core.find(idVendor=USBCar.VID, idProduct=USBCar.PID)
if self._dev is None:
raise ValueError("Device not found")


Функция usb.core.find() ищет устройство по его ID. Подробности см. http://ift.tt/1BQPf2f



if self._dev.is_kernel_driver_active(0):
self._dev.detach_kernel_driver(0)
self._had_driver = True


Мы отвязываем драйвер ядра, как мы делали в случае с lsusb. 0 – номер интерфейса. По выходу из программы его надо привязать обратно через release(), если он был активен. Поэтому мы запоминаем начальное состояние в self._had_driver.



self._dev.set_configuration()


Запускаем конфигурацию. Этот код эквивалентен следующему коду, который PyUSB скрывает от программиста:



self._dev.set_configuration(1)
usb.util.claim_interface(0)
def release(self):
usb.util.release_interface(self._dev, 0)
if self._had_driver:
self._dev.attach_kernel_driver(0)


Этот метод надо вызвать перед завершением программы. Мы отпускаем использовавшийся интерфейс и присоединяем драйвер ядра обратно.


Передвижение машинки:



def move(self, direction):
ret = self._dev.ctrl_transfer(0x21, 0x09, 0x0200, 0, [direction])
return ret == 1


direction – одно из значений, определённых в начале класса. ctrl_transfer() передаёт управляющие команды. Данные передаются как строка или как список. Возвращает метод количество записанных байт. Поскольку у нас всего один байт, мы возвращем True в этом случае, и False в ином.


Метод для статуса батареи:



def battery_status(self):
try:
ret = self._dev.read(0x81, 1, timeout=self.READ_TIMEOUT)
if ret:
res = ret.tolist()
if res[0] == 0x05:
return 'charging'
elif res[0] == 0x85:
return 'charged'
return 'unknown'
except usb.core.USBError:
return 'out of the garage'


Метод read() принимает адрес конечной точки и количество байт для чтения. Тип передачи определяется конечной точкой и хранится в дескрипторе. Также мы задаём нестандартное время таймаута, чтобы программа работала быстрее. Device.read() возвращает массив, который мы конвертируем в список. Мы проверяем его первый байт, чтобы определить статус зарядки. Если машинка не в гараже, то вызов read() не выполнится, и выбросит ошибку usb.core.USBError. Мы предполагаем, что эта ошибка происходит именно из-за этого. В остальных случаях мы возвращаем статус ‘unknown’.


Класс UI инкапсулирует интерфейс пользователя. Пройдёмся по основным вещам. Главный цикл находится в UI.main_loop(). Мы задаём фон с картинкой, показываем уровень заряда, если машинка в гараже, и рисуем кнопки управления. Затем ждём события – если это клик, то двигаем машинку в заданном направлении через USBCar.move().


Вся программа, включая GUI, занимает чуть больше 200 строк. Неплохо для устройства без документации.


Конечно, мы специально взяли довольно простое устройство. Но в мире есть довольно много схожих с нашим устройств, и многие используют протоколы, не сильно отличающиеся от того, что мы расковыряли. Реверс-инжениринг сложного устройства – задача непростая, но уже сейчас вы можете добавить в Linux поддержку какой-нибудь безделушки вроде устройства, сообщающего о полученном e-mail. Если это и не сильно полезно – то, по крайней мере, это интересно.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Запускаем свежайшее ядро Linux на Intel Edison

Ко всемирному дню #ArduinoD15 я подготовил материал по конфигурации и запуску свежайшего ядра Linux на плате Intel Edison (Arduino Edition).



Ранее я уже писал про то, как обеспечить загрузку ванильного ядра на плате Intel Galileo, сейчас же настал черёд для Intel Edison.


На текущий день в ванильных ядрах отсутствует драйвер последовательного порта, что практически делает работу с платой невозможной. Сегодня Greg KH, мейнтенер подсистемы tty, утвердил изменения, которые приносят поддержку Intel Edison High Speed UART в ядро. Реально же в основной ветке изменения появятся только в версии v4.1-rc1.


Итак, приступим. Хочу сразу обратить внимание, что я не буду повторно описывать некоторые действия, приведенные в статье Запускаем ванильное ядро на Intel Galileo. Также предполагаю, что у вас используется стоковая прошивка на базе Yocto.


Подготовка ядра и файловой системы




Прежде всего нам нужно ядро, которое с лёгкостью достаётся из репозитория подсистемы tty (нам нужна будет ветка tty-testing):

mkdir ~/devel
cd ~/devel
git clone git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
cd ~/devel/linux
git checkout tty-testing


Необходимо внести несколько минимальных правок в конфигурацию по умолчанию, а именно в файл arch/x86/configs/i386_defconfig. Если по каким-то причинам не хочется трогать этот файл, мы его легко можем скопировать с помощью команды cp arch/x86/configs/{i386,eds}_defconfig, и в таком случае использовать eds_defconfig там, где в статье упоминается i386_defconfig.


Итак, было (удаляем строки из файла):



CONFIG_DRM_I915=y





Стало (добавляем строки в файл):


# CONFIG_DRM_I915 is not set

CONFIG_BACKLIGHT_LCD_SUPPORT=y

CONFIG_USB_XHCI_HCD=y

CONFIG_USB_DWC3=y

CONFIG_USB_DWC3_GADGET=y

CONFIG_GPIOLIB=y

CONFIG_GPIO_INTEL_MID=y

CONFIG_INTEL_MID_WATCHDOG=y

CONFIG_X86_EXTENDED_PLATFORM=y

CONFIG_X86_INTEL_MID=y

CONFIG_EFI_STUB=y

CONFIG_EARLY_PRINTK_EFI=y

CONFIG_HSU_DMA=y

CONFIG_HSU_DMA_PCI=y

CONFIG_SERIAL_8250_DMA=y

CONFIG_SERIAL_8250_PCI=y





Данная конфигурация сразу же включит драйверы USB, watchdog, GPIO, HSU.

Собираем полученное:



make i386_defconfig
make -j4




Результатом будет файл arch/x86/boot/bzImage.

Процесс подготовки образа фаловой системы ничем не отличается от описанного ранее за исключением устройства вывода, которое для Intel Edison будет /dev/ttyS2, и соответственно в параметрах конфигурации Buildroot надо указать именно его:



BR2_TARGET_GENERIC_GETTY_PORT=«ttyS2»





В итоге сборки мы получим файл output/images/rootfs.cpio.bz2.

Копируем результаты на eMMC


Полученные в процессе сборки файлы ~/devel/linux/arch/x86/boot/bzImage и

~/devel/buildroot/output/images/rootfs.cpio.bz2 необходимо скопировать на нашу плату под именами vmlinuz.efi и initrd соответственно.


Запускаем плату на стоковой прошивке предварительно переключив её в режим устройства по USB и подключившись кабелем к компьютеру, на котором мы производили сборку. После запуска платы компьютер найдёт устройство USB диска. Оно-то нам и нужно. Копируем наши файлы прямо в корень подключенного диска.


Конфигурируем загрузчик U-Boot


Самая интересная часть рассказа заключается в магических строках, которые нам необходимо записать в конфигурацию U-Boot. Вот, что нам необходимо сделать.



  1. Загрузить плату в командную строку U-Boot нажатием любой клавиши.

  2. Добавить несколько переменных окружения так, чтобы получилось нижеследующее:

    boot_edsboot=zboot 0x100000 0 0x800000 0x800000
    bootargs_edsboot=console=tty1 console=ttyS2,115200n8 root=/dev/ram0 rw initrd=0x800000,8M
    bootcmd_edsboot=setenv bootargs ${bootargs_edsboot}; run load_edsboot; run boot_edsboot
    load_edsboot=load mmc 0:9 0x100000 vmlinuz.efi; load mmc 0:9 0x800000 initrd




    Делается с помощью команды setenv (подробные примеры можно найти здесь).

  3. Сохранить наши изменения не испортив существующие:

    setenv bootcmd_orig ${bootcmd}
    setenv bootcmd ${bootcmd_edsboot}
    saveenv





Теперь можно загрузить плату, например, вот такой командой:

run bootcmd




или нажав кнопку сброса.

Соответственно возвратится к изначальной конфигурации всегда можно запустив:



run bootcmd_orig


И на этом всё!


Посмотреть, что получилось
Welcome to Buildroot

buildroot login: root

# uname -a

Linux buildroot 4.0.0-rc5+ #1 SMP Fri Mar 27 15:15:28 EET 2015 i686 GNU/Linux

# lspci -kn

00:00.0 0600: 8086:1170 (rev 01)

00:01.0 0805: 8086:1190 (rev 01)

00:01.2 0805: 8086:1190 (rev 01)

00:01.3 0805: 8086:1190 (rev 01)

00:02.0 0380: 8086:1182 (rev 01)

00:04.0 0700: 8086:1191 (rev 01)

00:04.1 0700: 8086:1191 (rev 01)

Kernel driver in use: serial

00:04.2 0700: 8086:1191 (rev 01)

Kernel driver in use: serial

00:04.3 0700: 8086:1191 (rev 01)

Kernel driver in use: serial

00:05.0 0700: 8086:1192 (rev 01)

Kernel driver in use: hsu_dma_pci

00:06.0 0880: 8086:1193 (rev 01)

00:06.1 0880: 8086:1193 (rev 01)

00:07.0 0880: 8086:1194 (rev 01)

00:07.1 0880: 8086:1194 (rev 01)

00:07.2 0880: 8086:1194 (rev 01)

00:08.0 0780: 8086:1195 (rev 01)

00:08.1 0780: 8086:1195 (rev 01)

00:08.2 0780: 8086:1195 (rev 01)

00:08.3 0780: 8086:1195 (rev 01)

00:09.0 0780: 8086:1196 (rev 01)

00:09.1 0780: 8086:1196 (rev 01)

00:09.2 0780: 8086:1196 (rev 01)

00:0a.0 0780: 8086:1197 (rev 01)

00:0b.0 1080: 8086:1198 (rev 01)

00:0c.0 0880: 8086:1199 (rev 01)

Kernel driver in use: intel_mid_gpio

00:0d.0 0401: 8086:119a (rev 01)

00:0e.0 0880: 8086:119b (rev 01)

00:11.0 0c03: 8086:119e (rev 01)

Kernel driver in use: dwc3-pci

00:12.0 1180: 8086:119f (rev 01)

00:13.0 0b40: 8086:11a0 (rev 01)

Kernel driver in use: intel_scu_ipc

00:14.0 0b40: 8086:11a1 (rev 01)

00:15.0 0880: 8086:11a2 (rev 01)

00:16.0 0b40: 8086:11a3 (rev 01)

00:16.1 0b40: 8086:11a4 (rev 01)

00:17.0 0880: 8086:11a5 (rev 01)

00:18.0 0380: 8086:11a6 (rev 01)


# cat /proc/interrupts

CPU0 CPU1

15: 0 0 IO-APIC 15-fasteoi watchdog

31: 3 1 IO-APIC 31-fasteoi hsu_dma_pci

34: 50 49 IO-APIC 34-fasteoi xhci-hcd:usb1

48: 0 0 IO-APIC 48-fasteoi intel_scu_ipc

54: 61 65 IO-APIC 54-fasteoi serial





This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


пятница, 27 марта 2015 г.

Да здравствует мыло душистое, или как исправить неправильное масштабирование Windows программ

Не секрет, что внедрение высокого разрешения экранов идет быстрее, чем адаптация Windows программ под них. Проблемы появляются в первую очередь тогда, когда нам надоедает напрягать глаза и пользоваться увеличительными стеклами, и мы изменяем масштабирование системы. Программы можно разделить на три категории по тому, как они ведут себя при этом:


  • Все элементы программы масштабируются правильно

  • Только некоторые элементы масштабируются правильно

  • Программа говорит Windows, что не умеет масштабироваться, и тогда операционная система сама масштабирует всю программу как умеет (в результате чего мы и видим замыленный интерфейс во многих старых программах)




Самый неприятный вариант для пользователя — это конечно же второй. Он может не только сделать программу неудобной для использования, но и вообще сделать пользование невозможным.

Ниже я покажу на примере QTIPlot как это можно исправить.

QTIPlot — это программа, главная функция которой (как ясно из названия) — это построение графиков. Также она использует Qt, который и говорит операционной системе, что умеет сам масштабировать. Но к сожалению что-то идет не так и в результате графики, созданные и старательно выравненные на системе с масштабированием, теряют всю свою гармонию если их открыть на системе без масштабирования — шрифты начинают расползаться и т.п. Программа эта вроде как open source (хотя и надо обладать некоторыми детективными навыками, чтобы найти ее код), но с наскока мне исправить ее не удалось. Поэтому я расценил, что лучше жить с замыленным интерфейсом, чем искать замену программе, и решил заставить программу говорить Windows, что она не умеет масштабироваться.


Для этого надо создать манифест-файл в той директории, где находится exe-файл, который мы хотим изменить, со следующим содержанием:


qtiplot.exe.manifest


<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"
xmlns:xsi="http://ift.tt/ra1lAU">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker"
uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet"
version="1" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://ift.tt/1p1eUl6">
<dpiAware>false</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</asmv1:assembly>







(главное для нас здесь — это конечно же

<dpiAware>false</dpiAware>


).

После этого надо запустить «Developer Command Prompt» из Visual Studio Tools, перейти в эту директорию и выполнить команду

mt.exe -nologo -manifest qtiplot.exe.manifest -outputresource:qtiplot.exe




(естественно «qtiplot.exe» надо заменить на название того exe-файла, который надо пропатчить).

Вот и всё, можно запускать программу и радоваться теплому замыленному интерфейсу с правильным масштабированием.


Приятный бонус этих танцев с манифестом в том, что так можно исправить даже те программы, к которым у нас нет source кода.


PS После того как написал эту публикацию, заметил, что слово «скалирование» не входит в современные орфографические словари. Пришлось заменить его везде на «масштабирование». По этому поводу ниже опрос.



Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.



This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] Dart 1.9. Релиз, который вы ждали

Новый релиз по большей части посвящен упрощению асинхронного программирования. В современных приложениями оно везде — это взаимодействие с пользователем, с сетью, файловый I/O и т.д. Новый релиз Dart существенно упростил работу с такими сценариями, введя async/await.



Async методы и await выражения сделаны на основе знакомого Future API, вы можете использовать их с циклами, условными выражениями и try/catch блоками для управления сложным асинхронным взаимодействием. Подробнее о async/await можно прочитать в статье Dart Language Asynchrony Support: Phase 1 (перевод).

Так асинхронный код выглядел с использованием Future API:


Так код выглядит с приходом нового релиза и async/await:


Также были введены новые генераторы – sync* and async*, которые упрощают левую генерацию последовательностей и избавляют разработчиков от необходимости создания своих итераторов во многих случаях. Подробнее можно узнать в статье Asynchrony Support: Phase 2.


Среди прочих нововведений:



  • Полная поддержка enum

  • Выделение The Dart Analyzer в отдельный Dart Analysis Server, что упрощает интеграцию с IDE

  • Новый быстрый движок регулярный выражений (в некоторых случаях работает до 150 раз быстрее)

  • Isolate API полностью реализован в Dart VM




Полный список нововведений можно посмотреть в release notes.

Скачать последний релиз Dart можно на сайте.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Запрет bitcoin в РФ этой осенью?



Минэкономразвития дало положительное решение при оценке регулирующего воздействия(ОРВ) проекта закона о денежных суррогатах.

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


Конечно, первый удар пришелся бы по бизнесу: купоны, «авиамили», баллы, подарочные сертификаты и т.п. Это было основной причиной отрицательного решения, после чего в тексте проекта после доработки появился абзац следующего содержания:



Не признаются денежными суррогатами объекты имущественных прав, возникающие в результате исполнения сторонами обязательств по договорам гражданско-правового характера, и используемые в целях стимулирования приобретения товаров, работ, услуг.





После этой корректировки текущий вариант проекта Минфина был одобрен Минэкономразвития с небольшим замечанием, мол перечень функций ЦБ дополняется обязанностью


принимать решения в отношении информации, позволяющей осуществлять выпуск (эмиссию) денежных суррогатов и (или) операции с их использованием. При этом характер и содержание указанных решений в проекте акта не раскрывается.





Хотелось бы, конечно, более четко понимать, что вообще может является информацией, позволяющей осуществлять выпуск (эмиссию) денежных суррогатов и (или) операции с их использованием, но это нигде не раскрывается. С такой формулировкой непонятно, можно ли говорить и писать о том, как работает майнинг криптовалют? Позволяет это информация что-то или нет? А описание реализации функции SHA256(SHA256(Data))?

Абсолютно все предложения и замечания по итогам публичного обсуждения учтены не были. Опубликованная сводка предложений является по сути формальной отпиской. Многие предложения и замечания оставлены вообще без комментариев, а где они есть, текст комментария один и тот же, вне зависимости от сути предложений или замечаний.


Исходя из всего вышеизложенного также логично предположить, что могут возникнуть проблемы у геймдев студий, которые используют внутриигровые валюты и у компаний, использующих другие сущности, не попадающие под определение-исключение из обсуждаемого проекта.


Стоит еще вспомнить замминистра финансов Алексея Моисеева, который на Всероссийской банковской конференции заявил, что закон, который предусматривает наказания за использование денежных суррогатов, в этом году будет все-таки принят, а также блокировки сайтов, посвященных тематике криптовалют.


В Думе этот закон уже ждут, некоторые депутаты даже проявляли инициативу провести его до всех согласований, в частности, «Справедливая Россия», да и ЛДПР считает, что



Все эти криптовалюты созданы американскими спецслужбами как раз для финансирования терроризма и «цветных революций»





Резюмируя все вышесказанное — пока не видно причин, почему этот закон не будет принят на осенней сессии Думы в этом году. Что данное решение хорошо отбросит нашу страну назад уже неоднократно писалось, повторяться смысла нет. Просто несколько досадно наблюдать за тем, что происходит у нас, на фоне процессов в более развитых странах по этому вопросу.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.



This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Анонимности нет

Привет %username%! Хотел тебе показать и рассказать часть моего исследованиярасследования изучения мира сего, которое я рассказывал на конференции Zeronights 2014. Тема была о деанонимизации, но больше вопросов было именно по данным, поэтому я решил рассказать об этом отдельно.

Ну ты же в курсе, что сайты собирают данные о твоём посещении, откуда ты пришёл, куда ты уходишь, твои запросы, ip адреса? Вот ты помнишь, во сколько и какого числа ты искал.

Собственно, вот пример данных

Именно твоих, %username%. Если у тебя пустая страничка, возможно ты используешь плагины Ghostery, Adblock, Noscript — дай ссылку своему другу, надеюсь, ты удивишься.


Если совсем нет данных

Возможно «фичу» прикрыли. Может кто из хабражителей добавит пример данных, которые были по этой ссылке?



Что это было?




Данные. Обычные данные о пользователях, которые собирают множество сайтов. Включают в себя поисковые запросы, браузеры, ip адреса, посещённые сайты. По ним строится возраст, пол, интересы. И это всего лишь один из примеров, который собрала одна из множества компаний.



Зачем эти данные третьим лицам?




Прежде чем показать тебе рекламу, про тебя соберут данные, куда ты ходишь и зачем, потом проанализируют их. Поймут твой примерный возраст, отнесут тебя к определённым интересам, определят платежеспособность. А ведь и правда, если я сижу на форуме велосипедистов — неплохо показать мне рекламу велосипедных аксессуаров, а не розовые трусики с ультра-современной резинкой из новой коллекции европейских модельеров.

Как собираются данные?




Ты заходишь на сайт жизньболь.lol.

Система присваивает тебе идентификатор, например — 0001 и создаёт cookie user=0001. Себе записывает:


15-43 27 марта с ip адреса x.x.x.x зашёл пользователь 0001, User-agent: Calculator 1.2, сайт жизньболь.lol





Потом он заходит на сайт голыепопки.lol, с сайта поиск.lol/?search=голые+но+не+смешные, тут можно создать сразу 3 записи.


20-43 27 марта с ip адреса x.x.x.x зашёл пользователь 0001, сайт поиск.lol/?search=голые,+но+не+смешные






20-43 27 марта с ip адреса x.x.x.x поисковой запрос «голые, но не смешные»






20-43 27 марта с ip адреса x.x.x.x зашёл пользователь 0001, сайт голыепопки.lol





Если на сайте 1 нет жучка, но ты переходишь по ссылке на сайт 2, передав referer, «система» уже будет знать, что ты был на обоих.

Ну это самый примитивный вариант. Только подобных данных в сотни, тысячи раз больше. По мимо этого собираются используемые устройства (правило трёх), твоё перемещение, остальное ты выставил на показ в социальных сетях.

Почему данных так много?




Потому что ими обмениваются. Представь, я крупная социальная сеть. А ты — магазин. Тебе выгодно знать, кто посещает магазин, ты не узнаешь без меня. И мне, чтобы показать рекламу, тоже выгодно знать. Так давай махнёмся неглядя?

Да это же персональные данные! Это незаконно!




Спорно. Информация обезличена. Является ли персональной информация о том, что пользователь с идентификатором 0001 зашёл на сайт жизньболь.lol? А то, что пользователь с идентификатором 0001 посещает голыепопки.lol по выходным? А то, что пользователь с идентификатором 0001 — имеет id53083705 на сайте vk.com? Где грань?

Так кто собирает эти данные?




Все. А кто имеет крупный ресурс, но не собирает — тот дурак. Это современность и никуда от этого не деться. Если ты владелец сайта — скорее всего ты тоже собираешь данные и отправляешь их дядькам, которые покажут тебе рекламу. Нет? Уверен?

А теперь пройдемся по основным источникам данных. Больше всего информации, как я считаю, у корпорации Google. Уверен, что она знает о тебе больше, чем ты сам.
Реклама



Вполне логично, что рекламные баннеры сами являются сборщиками информации.


Счётчики и аналитика



Например Яндекс.Метрика с функцией «Вебвизор» — это полноценный кейлоггер. Поставьте какой-нибудь приватный чат и Яндекс.Метрику и наблюдайте, что пишут пользователи. А счётчикам чисто функционально необходимо собирать данные.

Подключаемые шрифты, библиотеки, картинки



А ты ссылаешься на чужие ресурсы?




У меня суперплагин, который блокирует всё




Не всё. Как минимум — пропускает, как максимум — всё покупается и продаётся.

Я стираю cookie каждые 34 секунды, меняю user-agent и мою клавиатуру




Ну и ладно. Помимо cookie есть ещё куча способов закрепить за браузером пользователя уникальный идентификатор. И это уже используют.

Я вообще смотрю сайты через консоль




Не беда. Данные о посещённых тобой сайтах продадут провайдеры. Вот список провайдеров, данные которых можно купить через систему imaker, о которой уже писал ValdikSS




Хочешь стать сайтом-шпионом? Подключай счётчик на сайт и добро пожаловать в команду!

Я смотрю сайты через консоль, через соседский Wi-Fi, меняю Mac адрес, отключен flash, js, за double-vpn разных стран.




Поздравляю, теперь вы привлекаете к себе внимание! Правда, я не уверен, что заинтересованные в этом службы мониторят подозрительный трафик. И вообще, на месте заинтересованных в этом служб, я бы использовал деанонимизацию на практике :)

Что делать?




Обязательно прочитай, посмотри или послушай выступление Стива Рамбама на конференции Hackers On Planet Earth анонимности нет, смеритесь. Живи и радуйся жизни. Всё равно ничего не изменится.



Остальные примеры на слайдах

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Из песочницы] Мой «Умный дом» на ПЛК и с веб-интерфейсом. Часть 1. Введение

Введение




На Хабре много статей про проекты умных домов, но почти все они были на самодельном оборудовании и китайских примочках. В своей статье я хотел рассказать о другом подходе, который показывает, насколько легче выполнять проекты, используя готовые решения мировых производителей (и выглядит солидней), а так же демонстрирует возможность использования оборудования не только в промышленных объектах, но в частной сфере. Получился симбиоз технологий и направлений автоматики. С одной стороны, используя ПЛК, который в основном разработан для нужд промышленности, позволяющий выполнять задачи любой сложности без ограничений жестких алгоритмов готовых устройств умных домов (например, по технологии KNX) с увязкой веб-технологией html/javascript дает неограниченный полет фантазий для расширения проектов.

Текущие затраты — 170 тысяч рублей (по старому курсу евро).


Начнем.



Что я задумал




Управляем освещением и электроснабжением

— В основном управление будет производиться сигналом включить/выключить, диммирование не хочу и не надо;

— Хочу управление с разных мест, например, зашел в спальню — включил люстру, лег на кровать — нажал выключить люстру. Если забыл выключить свет в зале (или в туалете …) нажал кнопку «выключить свет везде». Удобно;

— При выходе из квартиры нажимаю одну кнопку – выключается во всей квартире свет и т.д.;

— Считывать показание с электросчетчика;

— Бесперебойное питание систем управления и безопасности квартиры;

— Без исключений, все группы освещения должны управляться с ПЛК. В случае пожара розеточные группы должны отключаться с ПЛК в автомате — отключать электроснабжения в квартире в экстренных случаях;

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

— В коридоре предусмотреть датчик движения человека для управления освещением + завязывается в охранную сигнализацию;

— Теплый пол на балконе, в туалете, ванной, в прихожей — с возможностью управления в зависимости от уличной температуры, по времени.

Управляем отоплением и вентиляцией

— Управление отоплением — на каждую батарею устанавливается клапан с приводом (для регулирования температуры покомнатно, для измерения температуры комнаты, необходимо предусмотреть датчики температуры);

— Предусмотреть управление кондиционерами по ИК каналу (текущие решение задачи пока не найдено, выводим витую пару на внутренний блок кондея дальше придумаю);

— Температурные датчики уличной температуры (солнечная и не солнечная сторона);

— В зимний период достаточно часто встречаются две проблемы – это холодно и нечем дышать. Решение установить приточную систему. Далее обеспечить управление приточной системой (температура в канале, уличный датчик температуры, три ступени нагрева, вентилятор);

— Управление вытяжными вентиляторами (сан узел, ванная).


Охрана сигнализация

Сигнализация состояния входной двери (архивация состояния двери – время открытие/ закрытие);

Постановка на охрану через Web интерфейс или через выключатель управления светом.


Видеонаблюдение

Запись с камеры входной двери, уличная место парковки;

Архивация записей на удаленном сервере.


Оборудование




— Так как я привык уже к ПЛК от фирмы ABB, то в качестве мозгов для системы управления квартирой был выбран ПЛК модели AC500-eco (ЦПУ PM554-ETH с поддержкой Ethernet);

— Дальше я уже начал считать деньги и … нужно было выбрать среду отображения информации, с возможностью веб-отображения информации о доме. Существует много вариантов, но в основном все базируется на не кроссплатформенных решениях, что не подходит для меня. Все, что со словами SCADA и WEB, были с запредельным ценником. Пришлось немного попуглить, в результате решено использовать не SCADA систему, а framework с большим функционалом для HTML5. Пришел к CSWorks. Этот фримворк дает возможность бесплатно использовать с ограничением 999 переменных, 1 клиент. Что меня полностью устраивало.

— в качестве выключателей и розеток (орган управления светом) была выбрана фирма JUNG, Serie A. Из плюсов — они могут нести до 4-х кнопок на один кнопочный пост (выключатель без фиксации), так же в них присутствует светодиоды индикации с напряжением 24В (данное напряжение является стандартным промышленное напряжением питания систем автоматики). Данные функции не видел ни у одного из производителей электроустановок (не считая Китая);

Начало работ




1. Способ проводки освещения. Каждая группа приходит напрямую в шкаф электроснабжения. В шкафу стоят контакторы и реле для управления. Катушки реле и контакторов управляются от сигналов ПЛК (DC24V). Для резервирования управления использую контакторы с ручным управлением. Питающий провод сделал проходящий через стаканчик выключателя, чтобы была возможность переделки проводку на обычную схему.

Схема:



Сам контактор с ручным управлением:



Собран и смонтирован шкаф:



Схема:



Как я писал выше, выключатели выбрал без фиксаций с led индикаторами. Максимально 4 кнопки, возможно расключение этих кнопок на 8 групп (см. документацию на выключатель выключатель JUNG 4248TSM.





В следующий частях реализация проекта будет рассмотрен более подробно.


Забегаю вперед, открываю вам скриншоты веб-интерфейса:





This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Английская компания обвиняет Facebook в использовании своего проприетарного проекта дата-центра для Open Compute Project


Интересный иск был подан на днях английской компанией BladeRoom Group к Facebook. Истец обвиняет социальную сеть в использовании проекта дата-центра от BladeRoom Group для Open Compute Project. В частности, BladeRoom Group обвиняет компанию Facebook в использовании своих технологий и интеллектуальной собственности для строительства дата-центра в Лулео, Швеция.


Стоит отметить, что проект Open Compute Project — это нечто вроде Open Source, только не в мире софта, а в мире дата-центров. Компания отдает любую проектную документацию по своим дата-центрам партнерам, также Facebook показывает историю создания различных структурных элементов проекта. Цель Open Compute Project — открыть возможность быстрого строительства унифицированных дата-центров для любой компании в любой точке мира.



Проект стартовал в 2011 с подачи нового технического директора компании Facebook Френка Френковски (Frank Frankovsky). С тех пор проект постоянно дополнялся и совершенствовался. В прошлом году Facebook начала строить второй дата-центр в Лулео, Швеция. Этот дата-центр полностью соответствует стандартам Open Compute Project, в частности, RDDC (rapid deployment data center).


Все структурные элементы дата-центра, технологии, которые используются при строительстве, компания выкладывает в общий доступ. Чертежи, схемы, планы — все это становится достоянием общественности и может быть использовано любой компанией.





Ключевыми моментами текущего проекта являются три фактора:

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


Снижение влияния объекта на окружающую среду. Для строительства дата-центра используются уже готовые модули, на месте строительства остается только собрать все в единое целое. Благодаря этому присутствие строителей и их влияние на местность — минимально.


Более эффективная работа команды. Собственно, из двух предыдущих пунктов вытекает следующий. А именно — работа команды строителей максимально эффективна, экономия человеко-часов очевидна.




Рендер дата-центра Лулео-2


Все бы ничего, но компания BladeRoom утверждает, что Facebook использует ее технологии. До 2011 года компания не хотела ссориться с Facebook. Но теперь, когда социальная сеть начала всем и каждому показывать технологию BladeRoom в деталях, английская компания решила подать на Facebook в суд.


Стоит отметить, что BladeRoom — не какой-нибудь патентный тролль. Эта компания появилась 20 лет назад, начав строить модульные здания для коммерческих столовых. Позже в активе компании появились модульные операционные и технические комплексы для различных производств. В 2008 году компания сменила профиль, и начала строить только модульные дата-центры. В 2013 году BladeRoom заключила партнерское соглашение с Modular Power Solutions, договорившись о формировании своего дочернего подразделения, которое теперь производит модули в Мичигане, США.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Из песочницы] Дешифрация TLS трафика Java приложений с помощью логов


Отладка защищенных по SSL/TLS интеграций у Java приложений порой становится весьма нетривиальной задачей: соединение не ставится/рвется, а прикладные логи могут оказаться скудными, доступа к правке исходных кодов может не быть, перехват трафика Wireshark'ом и попытка дешифрации приватным ключом сервера (даже если он есть) может провалиться, если в канале применялся шифр с PFS; прокси-сервер вроде Fiddler или Burp может не подойти, так как приложение не умеет ходить через прокси или на отрез отказывается верить подсунутому ему сертификату…


Недавно на Хабре появилась публикация от ValdikSS о том, как можно с помощью Wireshark расшифровать любой трафик от браузеров Firefox и Chrome без обладания приватным ключом сервера, без подмены сертификатов и без прокси. Она натолкнула автора нынешней статьи на мысль — можно ли применить такой подход к Java приложениям, использовав вместо файла сессионных ключей отладочные записи JVM? Оказалось — можно, и сегодня, уважаемые однохабряне, я расскажу, как это сделать.


Идея рецепта




С недавних версий браузеры Firefox и Chrome научились выводить в специально задаваемый файлик данные, достаточные для деривации (получения) сессионных ключей, которыми шифруется передаваемый ими трафик (равно как и принимаемый трафик, поскольку внутри SSL/TLS используется симметричное шифрование). Строго говоря, делают это не сами браузеры, а библиотечка NSS в их составе; именно она задает формат записываемых файлов. Этот формат умеет читаться и использоваться Wireshark'ом, чтобы расшифровывать SSL-записи, зашифрованные соответствующими ключами. Идея нашего «блюда» заключается в том, чтобы с той же целью научиться формировать подобные файлы самостоятельно для Java-приложений, опираясь в качестве источника на отладочные логи, которые пишутся в стандартный вывод при наличии JVM-опции javax.net.debug.

Ингредиенты




Нам понадобится:

  • Java-приложение, для которого мы можем задавать параметры запуска (опции JVM).

    Для определенности полагаем, что приложение при установлении соединения выступает в роли клиента.

    Версию JDK (JRE) желательно иметь 1.6 или 1.7, на других (пока) не проверялось;

  • Wireshark версии 1.6.0 или выше;

  • Текстовый редактор, например, Notepad++;

  • Щепотка терпения, внимательности и времени.


Замес




Параметры запуска



Поскольку одним из главных источников информации для нас будут являться логи, перво-наперво нужно правильно настроить их получение. Вполне рабочим вариантом будет опция JVM javax.net.debug=ssl:handshake:data. Сразу оговоримся, что именно такое значение она иметь не обязана, можно (наверно) обойтись и универсальным javax.net.debug=all, но работать с результатами такого выбора может быть сложновато (объем логов может оказаться гигантским). Наш же выбор объясняется следующим:

  1. ssl — чтобы в лог писались сообщения, касающиеся только SSL;

  2. handshake — чтобы видеть каждое сообщение в рамках главного для нас этапа — handshake;

  3. data — для ленивых, чтобы вручную не переводить некоторые значения из десятеричной системы счисления в шестнадцатеричную (hex);




Наличие такой опции должно обеспечить нам вывод в лог (или стандартный вывод) отладочной информации, к которой мы вернемся чуть позже.
Настройка снифера



Снюхивать трафик целевого приложения Wireshark'ом можно лишь после простановки указанной выше опции, так как в противном случае мы не будем обладать сессионными ключами. К тому же надо помнить, что ключи «эфемерны» — они пригодны лишь для одной SSL-сессии, то есть лог от одного сеанса связи не подойдет для дешифрации трафика другого сеанса. Ну и чтобы дышалось совсем легко, рекомендую сразу при старте снифера указать хост, с которым планируется обмен данными; это позволит еще «на берегу» отбросить ненужные пакеты, проходящие через прослушиваемый сетевой интерфейс:

Съём и запись



После задания нужных параметров запуска приложения и настроек снифера, можно стартовать само приложение и включать захват пакетов в Wireshark. Тогда при попытке приложения выйти на (защищенную) связь с каким-либо сервером наши «кастрюльки» начнут заполняться. С точки зрения Wireshark это будет выглядеть примерно так:



Как видно, Wireshark явно определяет некоторые SSL-записи как зашифрованные; такими же являются и записи с типом Application Data.

А с позиций стандартного вывода (логов) приложения — примерно так:



...
*** ClientHello, TLSv1
RandomCookie: GMT: 1427238714 bytes = { 246, 5, 6, 214, 168, 159, ... , 140, 141, 50, 196 }
Session ID: {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, ..., SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA]
Compression Methods: { 0 }
...




На самом деле в лог будет выведено еще много всякого (формат отладочного вывода нигде не специфицирован и меняется от версии Java к версии), но нам пока достаточно увидеть хотя бы это.

«Расстойка»




Обладая отладочными записями от сеанса связи по SSL/TLS, мы можем сформировать файл сессионных ключей в формате NSS. Для этого нам в первую очередь потребуется определить, какой метод распространения сессионных ключей использовался в нашем сеансе связи: метод обмена (aka RSA) или метод генерации (aka DH или PFS, хоть это и разные вещи). В чем их суть и отличия можно почитать в замечательной работе Sally Vandeven'а. Нам же достаточно знать лишь сам метод, а определить его можно, как минимум, двумя путями:


  1. По названию шифра, выведенному в лог или определенному снифером. Например, по такому выводу

    *** ServerHello, TLSv1
    RandomCookie: GMT: 1037995915 bytes = { 168, 183, ... 204, 178 }
    Session ID: {141, 155, ... 214, 36}
    Cipher Suite: SSL_RSA_WITH_RC4_128_SHA
    ...


    видно, что шифр не имеет в названии никаких упоминаний DH, зато явно называет себя RSA. Однако такая ясность присутствует далеко не всегда, поэтому можно воспользоваться планом Б:

  2. По наличию SSL-сообщения ServerKeyExchange в перехвате трафика в Wireshark (см. скриншот в подразделе «Съём и запись») — оно присутствует для методов DH и отсутствует для RSA (объяснение почему — за рамками данной статьи). Безусловно, наличие этого сообщения можно определить и по тем же логам.




Определив метод распространения сессионных ключей, обратимся к описанию формата файлов NSS, который предписывает нам различать строки каждого файла как раз по этим двум методам. Рассмотрим каждый из них по-подробнее.

Для начала предположим, что в нашем сеансе связи использовался какой-либо шифр на основе RSA. Согласно описанию формата, соответствующая строка файла должна начинаться с текста RSA, за которым через пробел должны последовать 16 байт HEX-кодированного шифрованного ключа PreMasterSecret, а еще через пробел — 96 байт HEX-кодированного нешифрованного ключа PreMasterSecret (т.е. его же). Этот ключ является основой для генерации главного ключа — MasterSecret и передается от клиента к серверу в сообщении ClientKeyExchange, будучи зашифрованным публичным ключом сервера. Это значит, что первую часть строки (шифрованное представление этого ключа) должно быть видно в Wireshark. Находим нужное сообщение и убеждаемся — да, он есть:



Заметка для буквоедов

Опытный хабровед вправе прервать повествование вопросом — почему в формате файла NSS требуется лишь 16 байт, в то время как длина зашифрованного ключа составляет 256 байт?

Это объясняется тем, что шифрованное значение используется Wireshark'ом просто как индекс — оно нужно лишь для того, чтобы из потенциального множества строк в NSS-файле найти именно ту запись, в которой содержится подходящий MasterSecret. Сделать это можно путем последовательного сопоставления шифрованной версии этого ключа (взятой из перехваченного трафика) с первым (после «RSA») элементом каждой строки файла. Собственно, это Wireshark и делает, и сопоставлять ключ по всей длине в этом случае вовсе необязательно, вполне хватает и 16-ти байт.





К слову, это же значение можно получить и из логов приложения, и здесь нам как раз пригодится подзначение JVM-опции ":data":


Найденное значение (16 байт) можно вставлять в формируемый NSS-файл. Теперь провернем аналогичную операцию для второго элемента строки — собственного значения ключа PreMasterSecret. Поскольку оно, очевидно, никогда не передается по сети в открытом виде (собственно, поэтому оно и называется ...Secret), выуживать его придется только из логов. Благо, с недвусмысленными подсказками от JVM сделать это не особо сложно:



Теперь нужно добавить это значение к формируемой нами строке NSS-файла и «причесать» строку так, чтобы получилось что-то вроде этого (комментарии в стандартной нотации вполне допустимы):



# SSL/TLS secrets log file, generated by Toparvion
RSA 75ff866e23beca1c 03012aede74befa88233253e3207bb1320935ab206696512674df5c6dee7dfaa2156932bc559631c8f3bb46ae38a71ff




Тем, для кого RSA — «как раз тот случай» (как правило, это приложения до Java 7), уже можно переходить к разделу «Дегустация». Тем же, кому довелось столкнуться с PFS (зачастую это Java 7 и выше), придется читать дальше…

Аналогично методу RSA, строка для дешифрации записей на основе PSF должна состоять из трех элементов, разделенных пробелом:



  1. Тип записи; в данном случае он должен быть равен CLIENT_RANDOM;

  2. 64 байт HEX-кодированного клиентского случайного числа Random;

  3. 96 байт HEX-кодированного главного ключа MasterSecret;




Источники данных для второго и третьего элемента также аналогичны предыдущему методу, но есть некоторые тонкости. Клиентское случайное число является конкатенацией текущего времени в миллисекундах по GMT и собственно случайного значения. В случае обращения к Wireshark это отчетливо видно:

А вот при обращении к логам легко ошибиться, но можно пользоваться вот такой подсказкой:



Здесь также вхождение ":data" в значении JVM-опции javax.net.debug избавляет нас от необходимости ручной конвертации систем счисления. Всего нам потребуется 64 байта случайного числа, то все оно целиком (а не только начало, как было с RSA). Оно будет также играть роль индекса при поиске Wireshark'ом подходящей записи в NSS-файле.

Третий элемент строки — главный секретный ключ MasterSecret — также может быть извлечен из логов приложения:



После извлечения главного ключа из логов, дополняем им формируемую строку, «причесываем» и получаем нечто вроде:



# SSL/TLS secrets log file, generated by Toparvion
CLIENT_RANDOM 551435582740bdc1386b20b7fcb51428fe3042e06c8e6e94c910786f577a2ada 976dc1d54dd74d3c2e715109c8a4fb8e743efc084614abc0e12fdb78e472c30e3590ac5eb383424b2d8fa3de84c8b0f5




Нельзя не заметить, что Wireshark весьма чувствителен к формату NSS-файла, поэтому лучше уже сейчас тщательно перепроверить, сходится ли число байт в каждом элементе строки и нет ли где-либо лишних пробелов; это может сэкономить время в будущем.

Дегустация




Теперь, когда все «ручные» шаги выполнены, пора дать слово Wireshark'у — преподносим ему созданный файл в точности также, как это описано в упомянутой в начале статье:


  1. Открываем в Wireshark контекстное меню на любом SSL/TLS пакете;

  2. Выбираем Protocol Preferences -> Secure Socket Layer Preferences...;

  3. В открывшемся окне в графе (Pre-)Master Secret log filnename указываем путь к сформированному нами NSS-файлу.




Нажимаем OK и смотрим на изменения в перехваченном трафике:

Если дешифрация выполнена успешно, то пакеты, ранее имевшие в названиях слово Encrypted, обретут конкретные имена. Именно так стало с командами Finished, приведенными на рисунке выше.

Кроме того (и это, пожалуй, самый приятный момент) теперь можно выбрать любой пакет с протоколом SSL или TLS и в его контекстном меню щелкнуть на Follow SSL Stream — результат не должен требовать комментариев:



Как видно, несмотря на обращение по HTTPS, мы видим передаваемый трафик и можем экспортировать его для дальнейшего анализа.


Если все же не получилось...




Ошибок на этом скользком пути можно сделать множество. Одним из самых ценных источников информации является лог самого Wireshark'а — он ведется в том случае, если будет указан путь к файлу лога в графе SSL denug file все в том же окне настроек SSL.

Важно отметить, что никакой фильтрации на лог не предусмотрено, а Wireshark весьма подробен, поэтому если держать лог постоянно включенным, он, во-первых, очень быстро вырастет, во-вторых, может привести к торможениям самого Wireshark'а (т.к., видимо, пишется синхронно). В связи с этим, лучше всего указывать файл лога лишь перед самым применением NSS-файла, а по окончании анализа — чистить его (но не удалять).

Заключение




В статье был рассмотрен еще один подход к дешифрации SSL/TLS трафика Java приложений с целью их отладки.

В приведенном виде подход едва ли применим на практике, так как требует существенных затрат времени и наличия определенных знаний и навыков у исполнителя. Однако представленное описание позволит формализовать, а значит, и автоматизировать (запрограммировать) этот подход и, таким образом, поставить его на службу людям. Такая работа уже начата автором. Если эта затея интересна и вам — милости просим! Не забудьте рассказать об этом на Хабре.

Спасибо за чтение!


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.