Делаем свою систему наблюдения и видеорегистрации
пост написан и отправлен в печать 2011-12-08 примерно в 10:11
Кроме всех известных и неизвестных науке способов заработка в интернете (вентиляторы москва кондиционеры купить ильхамзюлькорнеев download free), у меня есть и оффлайновое место работы. Невообразимо! Причем расположено оно в, мягко выражаясь, неблагоприятном спальном районе, населенным продуктами генной мутации, родившимися и выросшими под воздействием пивных и сигаретных продуктов. И даже учитывая то, что в помещении практически постоянно дежурит охранник с татуировкой Пятак на пальцах левой руки, хотелось дополнительно перестраховаться. Хотя бы от того же Пятака. В итоге было решено своими силами сделать какую-то простую систему видеонаблюдения с регистрацией посторонних, которые шароебятся у дверей в неположенное на то время. Конечно, идеальным вариантом могла быть стать какая-то проводная DVR система, но помещение сравнительно большое и тянуть провод не представлялось возможным по куче разных причин. Именно поэтому я и начал поглядывать в сторону беспроводных IP камер, опыт покупки которых я уже имел. По сути, а IP камера - микрокомпьютер на ARM процессоре с embedded linux, wi-fi адаптером и web-мордой. Разумеется, искать что-либо в пределах Украины не представляется возможным, тут даже пластмассовый прыщеблядский длинк-хуелинк с одним только ethernet портом стоит каких-то дурных неадекватных денег. А вот на ебее или дилэкстриме предложений хоть жопой жуй. Я остановил свой капризный и требовательный выбор на вот таком варианте. Сделан он достаточно хорошо, качество видеопотока тоже радует глаз, но давайте обо всем по порядку.

Прежде всего, забрав свои камеры с почты, продумайте место их крепления. Я советую прикрутить их где-то под навесом, потому что место соединения провода с самой камерой вызывает уж очень сильные подозрения в обещаниях 100% гидроизоляции. Рекомендую залить туда немного силикона, которым обычно герметизируют швы в ванной (водой заливать не будет, а гибкость сохранится), либо, за неимением последнего, замотать это основание провода изолентой. Одним из минусов IP камер является необходимость их питания, обычно это что-то около 9-12 вольт на 1-1,5 ампера. Ну в любом случае вам бы пришлось тащить провод до видео регистратора, так что ищите перфоратор с хорошим сверлом и, смахнув скупую мужскую слезу по красивому оформлению внутренних стен, начинайте бурить. К слову, сотрудники вашего покорного слуги узнали много новых выражений и их сочетаний, ведь непосредственно мне выпала огромная честь перфорировать стену из 50 сантиметров качественного, теплого, лампового армированного бетона с шильдиком "сделано в СССР с любовью" нахуй. Подобные воспоминание еще долго будут заставлять меня кричать во сне. Скорее всего, вам понадобится распределительная коробка (я не знаю, как это называется эта пластмассовая хрень, детальней увидите на фотографиях в конце поста). На конце подобной камеры есть такая вот кишка с эзернет портом и бурить дыру с диаметром 10 см. вам конечно же не захочется. Еще одним конструктивным недостатком является наличие очень слабой ноги крепления. В частности в месте ее соединения с диском, который прикручивается к стене. Китайцы сэкономили и банально сделали две гайки, которые с двух сторон прижимают этот диск. Конечно же эти гайки крутятся в одну и ту же сторону, и, без каких-либо сложностей, камера откручивается от стены и уносится в неизвестном направлении с целью перепродажи или надругательства над вашей частной собственностью. В этом моменте советую деформировать резьбу после закручивания гайки и запаять ее. После этого садится как опытная шлюха на моряка дальнего плаванья, и без стремянки с шуруповертом вашу систему охраны спиздить становится достаточно проблематично. Важным моментом является ограничение свободного доступа к заднице камеры. Ведь если кто и заберется к вам "на огонек", то ему ничего не будет стоить ловким движением руки перерубить кабель питания, оставшись при этом незамеченным. Вообще, защита это достаточно хлипкая. Достаточно купить или собрать направленную шумелку в частотном диапазоне на 2,4Ггц, забить wifi канал и спокойно обойти данную систему наблюдения. Но тут вопрос уже совершенно другого порядка: лично я устанавливал камеры больше для психологического эффекта. Сами понимаете, если захотят залезть и что-то спиздить, то сумеют пробраться даже в Форт Нокс, а всяких малолеток и патологических придурков серьезная железная камера, мигающая светодиодом, хорошо отпугивает. Между прочим, китайцы лепят на свои камеры ик-диоды, ночью все видно великолепно, как днем прямо, но в ЧБ. За это им отдельный пламенный респект. И еще, если у вас есть другие способы защиты, то можете просто поискать на ебее по запросу "fake camera". Выглядят некоторые экземпляры даже покруче настоящих, мигают светодиодом и умеют пищать при приближении к ним. 

Но нас подделки не интересуют, мы любим все настоящее, поэтому продолжим. После подключения ваших камер к эзернет порту для настройки, вы можете впасть в состояние когнитивного диссонанса. На камерах в заводских настройках установлен статический IP 192.168.1.100 и, если для своей домашней сети вы выбрали что-то в диапазоне 10.255.255.255, то кукиш вам, а не веб морда. Веб интерфейс выглядит стандартно практически на всех камерах. Хороших фишек в нем много, но, как и все китайское, работает оно очень эммммм по-особенному. В идеале, если отличная функция Alarm, которая определяет шевеление в зоне видимости камеры и умеет отправлять картинки негодяя вам в почтовый ящик или же загружать их на FTP, а что самое важное - разрешает делать это по расписанию. В идеале. В жизни все работает хуй как пойми. Шедулер там просто для красоты, на FTP в пассивном режиме ничего не загружается, а на почту письма падают постоянно одни и те же.  И да, если собираетесь подключать данную камеру к wifi, то не забудьте в настройках вашего роутера Encription для WPA2-PSK выставить в значение TKIP. Это поможет китайскому говну не терять подключение к сети каждые надцать минут. Не стоит забывать и про то что wifi - радиосигнал, который имеет неприятное свойство теряться в куче железобетонных конструкций. Для этой цели советую докупить для вашего рутера дополнительную усиливающую антенну, прыщеблядский длинк продает подобное. После подключения и настройки всех жизненно важных параметров мы можем приступать к настройке и разработке ПО.

Так как веб морда камеры своей функциональностью нас тотально разочаровала, то писать свою обезьянку, следящую за камерами и отлавливающую праздно шатающихся, мы будет на python. Для меня самым сложным в этой всей затее казалось распознавания движения на изображении, что, впрочем, решилось тремя строками кода благодаря великолепной библиотеке PIL. Все эти ваши GD и ImageMagic-и и в подметки не годятся. Сначала я сделал это на анализе кадров из mjpeg потока, но подобная трата ресурсов оказалось излишеством. В двух словах, сейчас мы просто сравниваем два изображения с камеры с секундным интервалом и получаем разница уровней по R, G и B каналам. При нормальных условиях отсутствие движения дает значение около тройки, при попадании чего-либо крупного в кадр значение возрастает где-то до 15-50 в зависимости от размеров объекта. Все перепады освещения, тараканы и миниатюрные цирковые карлики легко отрубаются повышением уровня чувствительности. Код демона можно посмотреть. Логика состоит в том, что в "неположенное" время при обнаружении движения мы сохраняем изображение и каждые 5 минут отсылаем 10 последних картинок со злыми криминальными рожами на электронную почту "Большого брата", ну в смысле вам. Код не комментирован, но думаю, что сложностей возникнуть не должно. И да, советую запроксировать выдачу jpeg через тот же proxy-pass в nginx с включенной буферизацией. Сам поток камеры любит умирать, если его нагрузить двумя-тремя запросами одновременно. Издержки цены, так сказать. С радостью отвечу на все ваши вопросы, отправленные по адресам в шапке блога. Спасибопожалуйста.

Фотографии сабжа можно посмотреть в пикасе.
Делаем свой Google: NodeJS + ElasticSearch
пост написан и отправлен в печать 2011-08-07 примерно в 06:45

Прежде чем начать очередное повествование, которое будет абсолютно неинтересно любителям смехуечек (делегация фишкинет встала и молча вышла из зала) - небольшое лирическое отступление. Мне на мыло написал чувак с забавным вопросом, мол где я, везучий сукин сын, нахожу столько проектов под малоизвестный и нераспространенный ноде. Отвечаю: проектов под эту платформу практически нет, языком программирования это назвать нельзя, это скорее server-side runtime environment для javascript, инструментарий для решения определенного круга задач. Меня находят совершенно по другим критериям (мой рост, соотношение размера ступни к диаметру зрачка, могу говорить, как Сталоне, ну вы поняли), я лишь выбираю NodeJS для реализации. А мог бы выбирать ерланговские Mochiweb, Misultin, Cowboy (показывающие даже более быстрые результаты) или пайтоновский Tornadoweb, но мне родной JS как-то ближе к телу, к тому же под него есть отличный репозитарий готовых велосипедов на все случаи жизни под названием NPM. Хотя, возможно, в ближайшем будущем тут появятся посты и про вышеназванные технологии. Интересно? Жми на +1.

Итак, как меня учили в гуманитарном ВУЗе, начнем с постановки проблематики. Ко мне обратился мой знакомый, подрядчик одной крупной фирмы в Британии со следующим вопросом: есть 40 GB документов в HTML формате (при ближайшем рассмотрении, размер сократился до 18 GB, что тоже немало, столько примерно весит html дамп русскоязычной википедии состоянием на 2008 год), информация крайне конфиденциальная (дневники принцесс и королей, ага) и собиралась за столетнюю историю компании, лет 5-6 тому назад фирма перешла на какую-то свою систему хранения документации, но старый дамп остался статичным и требовал индексации для поиска по нему. Интранет не позволял использовать Google, импортировать все в новую систему документооборота никто не хотел, кому нужен информационный мусор при какие-то результаты санитарных анализов воды в Нью-Гэмпшире за 1928 год? Нужно отдать должное сотрудникам этой конторы, они усердно оцифровывали все и толково составляли каталоги вручную, а значит к каждому документу был доступ по какой-то гиперссылке с другой страницы. Готовые решения я даже не искал по определенным техническим причинам, было принято решение делать свой crawler + indexer, что в наше время оказалось вполне тривиальной задачей.

Самого паука, который будет путешествовать по пыльному архиву, написать, как два пальца об асфальт, об этом мы поговорим чуточку позже, остановимся лучше на технологии поиска и хранения индекса. Всякие реляционные базы данных отпали сразу после того, как я выполнил du -sh в директории с каталогом. Хотел было попробовать сделать что-то свое на основе NoSQL, но из всех известных мне движков не позволял мне искать подобным образом (чуть позже я случайно наткнулся на Riak, увы, было уже поздно), только MongoDB мог похвастаться поддержкой регулярок при выборке, а про полноценный же full text и речь не шла, в официальном мануале советовали разбивать текст на ключевые слова, но это ужасное решение. Именно поэтому было принято решение хотя бы здесь не изобретать двухколесные механизмы и использовать готовые удобные решения. Я успел посмотреть на Apache Solr и ElasticSearch, которые построены на Java Lucene и на Sphinx. Последний сразу отпал, так как его преимущества в виде ненужных мне индексации SQL баз данных и своего Query Language для сложных выборок были сведены на нет необходимостью писать отдельную XML pipe для добавления статики. Solr, имеющий великолепный административный интерфейс, большое коммьюнити, возможность индексировать DOC и PDF (ну мы и сами можем использовать Apache Tika в любом другом движке) показался каким-то слишком сложным для быстрого старта и избыточным, плюс ему не хватало внятной документации (доки в wiki это FFFUUUU). Поэтому я и остановился на ElasticSearch, который может похвастаться великолепным REST API на основе JSON, русской морфологией из коробки, различными текстовыми анализаторами и возможностью сочетать их в любой последовательности, либо даже создавать свои собственные, встроенными языками скриптования (js или python) для выборки и фильтрации, многими вариантами storage для нашего индекса, кучей параметров сортировки и ранжирования, подсвечиванием результатов, wildcard запросами и AND, OR ключевыми словами, простым созданием кластера и репликацией между нодами, даже возможность использования поискового движка в качестве key-value хранилища данных при правильном маппинге. И да, все работает без малейшей конфигурации в стиле load`n`run, я только изменил network.host на 127.0.0.1 и после запуска получил работающее хранилище нашего индекса. А еще у еластика есть, если и не идеальная, то хотя бы внятная документация и крутое Java API, которое, впрочем, нам не понадобится.

Теперь можем переходить непосредственно к написанию нашего crawler-а, который будет переходить по ссылкам и отправлять данные в ElasticSearch. Прежде всего, я написал свою обертку вокруг RESTа еластика для облегчения процесса индексации и поиска, она очень простая и реализует только функционал двух функций - index (плюс update) и search. Код обвязки можно посмотреть. Его мы будем использовать, как в нашем поисковом боте, так и в серверном приложении, возвращающем ответ пользователю. Теперь непосредственно о преимуществах NodeJS в этой сфере. Прежде всего, это мультипоточность из коробки, разумеется, на всех современных языках можно добиться аналогичного результата (ну разве что кроме похапе, но оно там абсолютно не нужно); и, второй по значимости плюс, - jQuery. Да, я не опечатался, мы будем использовать селекторы и модификаторы jquery на серверсайде. Все это возможно благодаря великолепной библиотеки JSDOM, которая позволяет нам получить виртуальный DOM из кода HTML разметки и эмулировать все вызовы к нему. Почему jQuery? Ответ прост - банальная лень в ущерб определенной производительности, конечно же мы можем использовать регулярки для выборки всех ссылкок, но $('a').each() выглядит проще и симпатичней. Столкнулся с необычной проблемой, которая описана в том числе и в коде самого краулера: ElasticSearch почему-то наотрез игнорировал точки, двоеточия, слешы и прочие символы, поэтому сделать проверку статуса индексации ссылки через ее url не получилось, для этой цели используется md5 хэш. В целом, получилось достаточно быстрое решение, данный блог, скормленный своей главной страницей нашему боту, был полностью добавлен в кеш еластика за полторы минуты всего-то в 3 потока. Я старался, по мере возможности, комментировать исходный код, надеюсь, что у вас не возникнет проблем с его чтением. В идеале, я советую использовать 10 потоков с queueInterval около 100, если ваши сетевая подсистема и процессор выдержат подобные высокие нагрузки, с такими значениями мне удавалось индексировать около трехсот страниц википедии в минуту на средненьком железе и канале в 30 mbit\s. Паук проверяет вхождение домена индексируемой ссылки в разрешенный список и ищет дозволенный content-type в ответе, реализует своеобразный robots.txt при помощи массива регулярок indexQueryPath. К тому же возможна переиндексация при достижении определенного возраста индекса, у меня, по умолчанию, это 100 дней. Процесс индексации в вашей консоли будет выглядеть как-то так. Работающий скрипт в memory leaks замечен не был, но вот JSDOM и jQuery очень любят процессор, будьте готовы к высоким нагрузкам. Для демонстрации я прошерстил этот бложек и написал небольшое приложение поиска по всем постам, состоящее из такой вот серверной части. Его работу можно посмотреть в лаборатории им. Бена Ганна по ссылке.

В заключение, хочу выразить благодарность разработчикам и сообществу NodeJS и ElasticSearch. При всей моей нелюбви к красноглазому opensource, два этих великолепных программных продукта, создаваемых на чистом энтузиазме, помогли мне сделать работу в срок, обрадовать вычурных британцев и купить новую дозу героина. Очень сложно затягивать жгут на руке и писать сюда, до новых встреч, друзья.

Продолжаем копать HTML5: Comet, WebSockets, EventSource, NodeJS
пост написан и отправлен в печать 2011-07-18 примерно в 08:01
Все помнят мой гневный (скорее, ироничный вентиляторно-лопаточный вброс, на который многие купились, смотри UPD внизу следующей ссылки) пост про HTML5 и про весь тот шум, который нагнетается вокруг сырой и неготовой к массовому использованию технологии. В какой-то мере, данный пост станет очередным тому доказательством, но, в то же время, если этот эфемерный набор стандартов будет когда-то окончен, то мы, вебразработчики, получим отличный инструментарий, который лишит необходимости собирать велосипеды с квадратными колесами для езды по стеклянным рельсам. Ну вы поняли.

Давайте по порядку. Сейчас в моде real-time коммуникации, в техническом смысле этого слова. Контакт, лицокнига, гуглоплюсы - все перечисленные социальные сети имею свой WebIM, позволяющий в реальном времени общаться со своими друзьями и подругами. Правда, концепция там немного другая, чаты этих социалок, как правило, представляют собой JavaScript обертку вокруг XMPP протокола, но смысл понятен. Еще, справедливости ради, стоит заметить, что WebSockets и Server-Sent Events в настоящее время выделены из спецификации HTML5 в отдельные доки, но, опять таки, я утверждаю и буду утверждать, что HTML5 - не технология, а маркетинговый бренд и писькомерялка современных браузеров, поэтому отождествление понятий тут допустимо. Передо мной была поставлена задача создать приложение, позволяющее в реальном времени обмениваться информацией между сервером и клиентом с минимальными задержками. Благо, приложение это исключительно интранетовское и платформа использование - Google Chrome, а значит технически я не был ограничен. Вариантов было несколько: long-polling HTTP запросы, HTTP Streaming или fast-polling, все это называется Comet моделью. И у каждой реализации есть свой недостаток. В спецификации HTTP/1.1 четко указано, что браузер не должен создавать одновременно более 2х параллельных соединений к серверу. Конечно, современные браузеры могут поддерживать десяток connections и подобное нарушение стандарта не является фатальным, но в каждом из вариантов решения есть свои недостатки. Fast-polling, который представляет собой многократное повторение запроса (с завершением сессии) создает приличную нагрузку на сервер, а "висящие" запросы - костыли, которые приводят либо к memory-leak (в последнем фаерфоксе сделать garbage collector для буфера запроса не удосужились, в хроме и сафари память чистится, но только для запросов, возвращающих "Transfer-Encoding: chunked"), к тому же, контролировать Comet сессию сложнее, она может отваливаться без какой-либо видимой причины, да и изначально протокол создавался не для этого. Есть еще BOSH, который используется в вышеупомянутом XMPP, но он заточен больше под создание чатов и готовых решений для этой технологии очень мало. В PaaS Google App Engine, на котором расположен этот блог, есть Channel API, что-то вроде Comet-а, я даже пытался попробовать, но возможности тоже сведены до минимума. Конечно можно еще использовать обертки вокруг flash socket или java-апплета, но не этому учила нас партия.

Вот именно таким путем, перепробовав все вышеназванные методы и оценив ресурсоемкость, я решил остановиться на server-sent events и web-sockets, которые вроде даже выведены из статуса эксперементальных в последних версиях Webkit-а. Начнем с SSE, спецификацию можно почитать тут. По сути, это тот же HTTP Streaming на стероидах. Его смысл заключается в том, что в JavaScript коде или DOM создается объект EventSource со скриптом в src атрибуте или методе-конструкторе. Этот скрипт, прежде всего, должен в контексте "висящего" соединения (хотя и не обязательно) возвращать корректный Content-Type: text/event-stream и в дальнейшем push-ить обновление клиенту в виде строки data: any_text_info \n\n. То бишь, протокол абсолютно односторонний, нас это вполне устраивает, общаться обратно мы можем и через милый сердцу XHR. Есть в нем и вкусности, к примеру, ответ event: test-remote-event \n data: success \n\n, если верить стандарту, можно обрабатывать в addEventListener('test-remote-event' ,function(data){}), правда, это пока только на бумаге. В Chrome Canary у меня работал только event onmessage. Со стороны все всегда кажется таким красивым, но тут начались проблемы, бывает, что поток просто висит. Никакой onerror или onclose не вызывался, сервер продолжает исправно посылать данные, что проверяется curl-ом в консоли, но браузеру пофиг, хотя рестарт страницы помогает. Пробовал в Chrome Stable - та же  заморочка. ЧЯДНТ? Вторая и более серьезная проблема - отсутствие поддержки CORS, Access-Control-Allow-Origin: * отсылается, но браузерам на него с большой колокольни, они возвращают DOM Exception 18. Очередная брешь в стандарте, как так - XHR работает, а EventSource нет. Конечно, это все придирки, можно было бы завернуть трафик через proxy_pass в nginx, но в любом случае, осадок остался. К тому же, нужно уже было думать о проверке наличия \n\n в контенте, иначе была бы вероятность получить побитый ответ. Таким вот путем я и пришел к WebSockets. Рассказывать что-либо про клиентскую часть не имеет смысла, есть спецификация, все это делается одной строкой кода, давайте лучше кратко поговорим, как реализовать подобное на сервере. В качестве server-side для подобных проектов я всегда рекомендую брать NodeJS. Сама парадигма event based неблокирующего асинхронного сервера идеально подходит для высокозагруженных API, а этот самый интранет, он очень большой и запросы ресурсоемкие. К тому же, имеющегося в NPM добра с головой хватит для решения любых задач.

Для демонстрации возможностей, я написал небольшое приложение. Прописано оно по адресу. Работать должно в Chrome, Safari (в том числе и на iPad, iPhone с 4.2) и FF. Но в последнем нужно включить поддержку сокетов. Вот вам мануал. Долго думал, откуда бы взять большой поток данных для наглядного примера, как вспомнил про Twitter Streaming API. Код готового приложения можно невозбранно скачать архивом. Если демонстрация перестанет работать - прошу меня простить, мой старенький сервер очень нежный и неторопливый, и больше времени заняла компиляция node, чем прочтение документации или написание самого кода. Разумеется, в server.js нужно вписать свои логин и пароль. Вообще, я использую sample поток, а вы, прочитав документацию по твиттер апи и twitter-node, можете создать какой-то полезный сервис мониторинга и аналитики хештегов, или выводить в форме границ Украины аватары пользователей, написавших что-то на мове. Фантазии ограничены исключительно возможностями API твиттера, а их более чем хватает.

В заключение, хочу сказать, что я постепенно пересматриваю формат блога, если вам понравилась подобная статья - не поленитесь перейти на веб версию, если в РСС читаете, и воспользоваться кнопкой Google +1. Если соберется немного плюсов, то в ближайшее время мы поговорим про WebWorkers, Forms Validation, нарисуем мужской половой хуй в Canvas, напишем вращающийся чайник на WebGL и попробуем накладывать фильтры и получать информацию из тега <video>.

GrandTorrino или второе дыхание Torrero
пост написан и отправлен в печать 2011-05-29 примерно в 15:01
Малопонятный заголовок, согласен, но позвольте мне объясниться: когда-то в прошлом, будучи под воздействием опиатов или каких-то других тяжелых наркотиков, позволяющих мне почувствовать себя чуточку счастливее, я сделал проигрыватель видеофайлов, распространяемых по пиринговым сетям протокола torrent без необходимости их полной загрузки. В двух словах, ждете 10% подгрузки и потом смотрите себе киношку без необходимости ожидания загрузки остального, вполне удобно, особенно для всяких там сериалов. Но, как и всё, что я делаю бесплатно и под воздействием галюциногенов и эйфоретиков, работало это из рук вон хуйово -  были проблемы с линейным алгоритмом загрузки, когда миниатюрные куски по 1-5 килобайт выпадали из piecepicking query, а декодер плеера на этом захлебывался за милую душу. Были проблемы с mplayer-ом, который очень нехотя цеплялся через hid windows forms компонента. Но, как ни странно, нашлись и дорчитатели этого бложика, которым очень даже понравилось. И вот они начали забрасывать меня багрепортами, мол не работают сабтитры, нельзя выбрать звуковую дорожку, нельзя выбрать, какой именно файл из торрента смотреть. На все это я отвечал, что удивительно, что оно вообще работает, любезно предлагая предоставить исходники. Но, увы, энтузиастов, готовых хоть сколько-то помочь в разработке, в наличии не оказалось, а письма все продолжали капать и капать - давать дельные советы, как известно, это вам не мешки ворочать. Ну теперь можно все вышеперечисленное, короче говоря. Открываю вам страшную тайну, для себя месяца 2-3 тому назад я написал отдельную версию, так сказать, исправленное и облегченное издание, лишенное большинства недостатков оригинала. И даже основного - ужасное GUI было начисто заменено ламповым черным консольным окном. К тому же, был полностью переписан алгоритм загрузки, он работает чуточку медленнее по причине необходимости подтягивать куски последовательно, зато файлы не ломает и позволяет даже использовать перемотку в пределах загруженного отрезка видео файла (все еще в экспериментальном режиме, как я не пытался сделать byte by byte, все равно идет разбивка на куски и параллельная загрузка, в этом весь битторрент). Называется эта версия GrandTorrino и, билдани я эту версию году так в 2007, всем мог бы говорить, что фильм Клинта Иствуда назван именно в честь нее. Но, кажись, все наоборот. Использование очень простое - drag and drop torrent файл на exe-шник GrandTorrino, ждем 10% буферизации и готово. Если в торренте 1 файл - все пройдет само собой, если больше 1го - вас спросят, какой именно вы хотели бы посмотреть. Все хоткеи для mplayer можно узнать в прилагаемом файле mplayer.html, они немного необычные. Скачать последнюю версию можно тут. Для работы необходим .net framework 3.5, и запас алчности, позволяющий вам воровать чеcтно заработанные деньги из дырявого кармана нищих правообладателей. 

А теперь, давайте по сути. Мне очень нравятся подобная идея - нарушение авторских прав без ожидания и очень печалит факт отсутствия нормальной реализации. Нет, конечно моя версия работает и с поставленной задачей справляется, но хотелось бы чего-то большего, интуитивно-удобного и приятного, а у меня для для такого не хватает ни знаний, ни времени, ни мотивации - работает же и сейчас, зачем мне что-то большое? Так вот, я отдам все исходники по запросу на email или в скайп, указанный в заголовке этого блога. Вы сможете делать с ними все, что душа пожелает - продавайте, воруйте, убивайте, делайте стартап, но я всегда должен буду иметь доступ к исходникам, природное любопытство и все дела. Написано все на C#, код конечно не ахти какой, но я постараюсь все приаккуратить к передаче, чтобы стыдно не было моим стареньким учителям информатики, которых у меня конечно же не было. В качестве проверки, что вы не какой-то хер с горы, вам нужно будет показать технику гроула рассказать, как можно рассчитать необходимый процент буферизации, имея скорость загрузки за последние 10 секунд, вес файла и его аудио-видео битрейт и как определить, что проигрывание идет быстрее скачивания файла с помощью стандартного mplayer вывода. А вообще, шучу конечно. Без проблем вышлю и так + модифицированную библиотеку MonoTorrent в довесок. Спасибопожалуйста.

Пишем свой caching nameserver с фильтрацией
пост написан и отправлен в печать 2011-04-09 примерно в 10:58
Прежде чем начинать свое повествование, маленькое сообщение для всех, кто прочитал топик поста и закачал убеленной сединой\лысиной головой с неприкрытым укором: да, я знаю о существовании BIND и да, я знаю про MySQL BIND SDB, или даже про MyDNS тоже слышал, и настроить кеширующий child неймсервер умею. Увы, реализация такой комбинации не виделась мне целесообразной по ряду причин о которых я не буду тут писать. В любом случае, мне нужно было сделать DNS с фильтрацией определенных доменных имен с хранением списка этих имен на центральном сервере в MySQL базе данных. Нужно было сделать его не намного медленней родного варианта, что было вполне нетривиальной задачей. Думаю, что для начала мне стоит объяснить, зачем это все задумывалось. Как упоминалось в прошлом посте, я работаю в компании основным направление деятельности которой является предоставление сервиса защищенного VPN туннеля для обхода корпоративных фаерволов, шифрования трафика, защиты от man-in-the-middle и прочих мерзких снифферов, получения своего личного реального IP где-то в странах экваториальной Америки, ну и для защиты от вирусов с помощью решений от тов. Касперского. В качестве серверных площадок используются разнообразные virtual dedicated и root dedicated от разных провайдеров по всему миру. И вот тут возникает основная проблема, которая загубила большинство подобных сервисов - торренты. Да, в большинстве TOS-ов большинства хостеров ясно прописано, что если с вашего сервера будет замечена подозрительная активность по загрузке из интернета нелегального контента, то вы автоматически лишаетесь предоставляемой услуги без какой-либо надежды на материальное возмещение. Звучит достаточно печально, учитывая предоплату на целый год. В итоге, часто получаешь подобные письма, рассылаемые полуавтоматическими системами правозащитных ищеек, что не добавляет хорошего настроения в твой и так не особо веселый рабочий день. Каким образом они отслеживают попытку скачивания торрента? Fake Seed к примеру? Или просто фильтруют запросы к торрент трекерам? Я без понятия, но вот если правильным образом не среагировать на подобное письмо, убедив хостера, что этот мудак, качающий Спанжбоба, физически устранен, а вся его семья стерилизована - можно получить серьезные проблемы. Вплоть даже до судебного разбирательства, все мы новости читаем. Это проблема, теперь поговорим о решении. Любой системный администратор скажет вам, что не существует оптимального решения перекрытия кислорода p2p трафику. Да, можно ограничивать количество udp сессий одного хоста (что достаточно глупое решение, учитывая саму природу протокола с неявным закрытием соединения), можно вообще закрыть udp (что еще глупее, одним махом лишаем пользователя всяких скайпов\сипов, а торрент клиенты могут спокойно и через TCP работать), можно использовать всякие модули фильтрации уровня iptables вроде ipp2p, OpenDPI, l7-filter (на сайте пишут про высокий perfomance, но у меня la вскакивал до 1 при 10 соединениях и иногда всплывали defunct зомби) или вообще пускать весь трафик OpenVPN  в лоб через фильтрующий прокси сервер (мною было опробовано все из списка, увы, все решения крайне требовательны к системным ресурсами из-за необходимости прочесывания регулярками большого потока данных и по этой причине не подошли, не все сервера в сети могли позволить себе такую роскошь). Есть еще решения для дураков: закрытие типичных портов, что не работает в условиях современных клиентов и тот вариант, который выбрал я - DNS блокировка главных торрент трекеров. Защита ужасная, ничто не мешает клиенту после подключения выставить вместо присваиваемого неймсервера какой-то другой, данное решение к тому же не помогает, если пользователь начал скачивание торрента до подключения к нашему VPN-у (хотя моя практика показывает, что в таких случаях правозащитные ищейки молчат, что еще раз служит подтверждением конспирологической теории, что софт копирайтеров просто отслеживает announce запросы к трекерам), и конечно же данный вариант не спасает от DHT, которому вообще ничего не нужно: ни трекеры, ни днсы. Но работает же, количество писем от правообладателей сократилось до 1-2х за 3 месяца, возвращаем NXDOMAIN для всех доменных имен в блеклисте и живем припеваючи. Кстати, если кто имеет идеи, как побороть bittorrent в сети - скиньте мне хоть пару строк на mail@smirnoff.sumy.ua, чтоб я примерно знал, куда копать. С меня благодарности в приемном для вас эквиваленте.

Возвращаемся к теме реализации. Прежде всего, отвечаю на вопрос: зачем мне нужно много неймсерверов, а не один центральный без всех этих заморочек с MySQL и т.д. В качестве master мы будем использовать Google Public DNS, на нем был замечен load balancing для некоторых крупных сайтов, на запрос по тому же ютубу с разных географических точек отдавались разные A-записи. К тому же, проводить NS query с гонконгского сервера через туннель на центральный сервер где-то в Люксембурге или США - очень медленно, поэтому наши DNS-ы будут отвечать по тому же адресу, что и шлюз. Языком реализации стал Perl. Почему? Да просто в нем есть все необходимые нам компоненты. Использовать мы будем Net::DNS для резолвера и Net::DNS::Nameserver для сервера. Разумеется, без кеширования никуда. В качестве решения я советую смотреть в сторону Cache::Bounded, которое является по сути оберткой вокруг Cache::MemoryCache с оптимизацией и обнулением кеша по достижению определенного размера, что спасет нас от переполнения памяти. В результате получилось что-то вроде такого кода. Сразу замечу, что 3 цикла for при чтении ответа из кеша - необъяснимая мною мистика. Казалось бы с помощью тех же элементов исходного массива путем перебора формируем новый, а вот простое присваивание - не работает. И хоть ты тресни. По скорости вполне нормально, первый запрос конечно отнимает определенное время, но в дальнейшем из кеша в памяти все читается очень быстро. В качестве бонуса, держите список из 1800 самых крупных торрент трекеров для блокировки.

Как по мне, сервисы vpn туннелей должны служить исключительно 2м целям: скачивание торрентов в странах, где за это находят и бьют по рукам; и просмотр католическими священниками детской порнографии там, где это тоже не особо приветствуется. Первых случаев становится все больше, а последних - все меньше. Мы же нашим TOS-ом запрещаем основные приоритетные направления. А все остальные истории с  защитой вашей информации, все это сказки для детей. Мы то с вами знаем, что от паяльника еще ни один фаерволл или канал с шифрованием не спасал.