{"id":2441,"date":"2020-06-27T19:56:23","date_gmt":"2020-06-27T19:56:23","guid":{"rendered":"http:\/\/blog.rastersoft.com\/?p=2441"},"modified":"2020-10-21T12:15:28","modified_gmt":"2020-10-21T12:15:28","slug":"a-ritmo-de-conga-y-4","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=2441","title":{"rendered":"A ritmo de conga (4)"},"content":{"rendered":"\n<p><strong>DISCLAIMER:<\/strong> no ser\u00e9 responsable si alguien decide seguir mis pasos y se carga su aspiradora. En principio todo lo que cuento deber\u00eda ser seguro, pero por motivos obvios no me puedo responsabilizar de lo que hagan otras personas, s\u00f3lo de lo que haga yo.<\/p>\n\n\n\n<p>Hay que reconocer que cuando las cosas se complican, se complican de verdad. No se si lo que ha pasado demuestra que el programador que lo hizo es un genio y meti\u00f3 una sutil protecci\u00f3n antihacking, o un chapuzas que hizo un c\u00f3digo que funciona de milagro.<\/p>\n\n\n\n<p>Me explico: decid\u00ed hacer una prueba r\u00e1pida de la conexi\u00f3n de la aspiradora con un servidor m\u00edo, as\u00ed que escrib\u00ed un miniservidor con <a href=\"https:\/\/www.python.org\/\">Python3<\/a> que escuchaba en el puerto 80, y que cuando recib\u00eda una petici\u00f3n a alguno de los documentos a los que responde el de bona robots (<em>\/baole-web\/common\/sumbitClearTime.do<\/em> y <em>\/baole-web\/common\/getToken.do<\/em>), anotaba los valores que pasaban y respond\u00eda lo que la aspiradora esperaba.<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"250\" style=\"aspect-ratio: 250 \/ 250;\" width=\"250\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/coding.mp4\"><\/video><\/figure>\n\n\n\n<p>Pero claro, la aspiradora se empe\u00f1a en conectarse a <em>bl-app-eu.robotbona.com<\/em> o a <em>bl-im-eu.robotbona.com<\/em>, as\u00ed que ten\u00eda que enga\u00f1arla para que se conectase a mi ordenador y no al servidor chino. La soluci\u00f3n consisti\u00f3 en dos pasos:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>primero, editar el fichero <em>\/etc\/hosts<\/em> de la <a href=\"https:\/\/www.raspberrypi.org\/\">Raspberry Pi<\/a> que usaba como <a href=\"https:\/\/en.wikipedia.org\/wiki\/Wireless_access_point\">Access Point<\/a> para conectar la aspiradora, y definir ah\u00ed esos dos dominios (adem\u00e1s de <em>robotbona.com<\/em>, por si acaso), de manera que apuntasen a mi ordenador. Si la IP del ordenador fuese 192.168.0.89, habr\u00eda que a\u00f1adir:<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">192.168.0.89 bl-app-eu.robotbona.com\n192.168.0.89 bl-im-eu.robotbona.com\n192.168.0.89 robotbona.com<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>relanzar <a href=\"https:\/\/en.wikipedia.org\/wiki\/Dnsmasq\">dnsmasq<\/a> en dicha Raspberry Pi con <em>sudo systemctl restart dnsmasq<\/em>, para que lea los cambios.<\/li><\/ul>\n\n\n\n<p>Y con esto, cuando la aspiradora se conecte a dicha red y pida la direcci\u00f3n IP de esos dominios, \u00e9ste le devolver\u00e1 la de mi equipo, con lo que lo usar\u00e1 como servidor en lugar del real. Por supuesto, cuando termine todo esto habr\u00e1 que montar el servidor en la Raspberry y que todo se conecte a la WiFi normal, pero eso ya lo har\u00e9 en el \u00faltimo cap\u00edtulo.<\/p>\n\n\n\n<p>Ahora bien, como utilic\u00e9 el m\u00f3dulo <a href=\"https:\/\/docs.python.org\/3\/library\/http.server.html\">http.server<\/a>, mi programa no implementaba la parte del puerto 20008 (donde realmente se reciben todos los comandos), y como para ello ten\u00eda que hacer m\u00e1s cosas, decid\u00ed, de momento, no complicarme la vida para una simple prueba, y limitarme lanzar un <a href=\"https:\/\/en.wikipedia.org\/wiki\/Netcat\">netcat<\/a> en dicho puerto en un terminal. As\u00ed, cuando la aspiradora se conectase, ver\u00eda por la pantalla lo que \u00e9sta enviase.<\/p>\n\n\n\n<p>As\u00ed pues, apagu\u00e9 y encend\u00ed la aspiradora, la puse en modo emparejamiento, la emparej\u00e9 desde mi ordenador, \u00e9sta apag\u00f3 su Access Point, se conect\u00f3 a la WiFi, hizo la petici\u00f3n <em>sumbitClearTime.do<\/em>, mi servidor respondi\u00f3, hizo luego la petici\u00f3n <em>getToken.do<\/em>, mi servidor respondi\u00f3 tambi\u00e9n&#8230; y volvi\u00f3 a pedir <em>getToken.do<\/em>&#8230; Y otra vez lo volvi\u00f3 a pedir&#8230; Y el <em>netcat<\/em> sin inmutarse.<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"134\" style=\"aspect-ratio: 200 \/ 134;\" width=\"200\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/say-what-gone-with-the-wind.mp4\"><\/video><\/figure>\n\n\n\n<p>Algo raro ocurr\u00eda, porque despu\u00e9s de pedir <em>getToken.do<\/em>, netcat deber\u00eda mostrar una conexi\u00f3n, pero ah\u00ed no ocurr\u00eda nada. Decid\u00ed probar a quitar la redirecci\u00f3n en el DNS por si acaso le hab\u00eda pasado algo a la aspiradora, de manera que se conectase al servidor real, y entonces funcion\u00f3 de nuevo.<\/p>\n\n\n\n<p>Esto era muy raro, as\u00ed que volv\u00ed a configurar mi DNS y lanc\u00e9 el<a href=\"https:\/\/en.wikipedia.org\/wiki\/Tcpdump\"> tcpdump <\/a>en la <em>Raspberry<\/em> para capturar el tr\u00e1fico que se intercambiaba entre la aspiradora y mi servidor, y ver qu\u00e9 pod\u00eda estar pasando. Ten\u00eda que ser algo en la segunda orden, porque la primera s\u00ed funcionaba bien.<\/p>\n\n\n\n<p>Compar\u00e9 las respuestas de mi servidor simulado con las del servidor real y eran id\u00e9nticas: los mismos campos, el mismo orden&#8230; \u00a1Espera, hab\u00eda una diferencia sutil entre los <a href=\"https:\/\/en.wikipedia.org\/wiki\/JSON\">JSON<\/a> que yo enviaba y los que enviaba el servidor! En mi caso, en <em>getToken.do<\/em> utilizaba las funciones de python para convertir de un diccionario a JSON, y \u00e9ste lo estaba <em>embelleciendo<\/em> a\u00f1adiendo espacios detr\u00e1s de las comas, de los dos puntos, etc. Sin embargo, en <em>sumbitClearTime.do<\/em> generaba la cadena \u00abtal cual\u00bb, por lo que no ten\u00eda espacios, igual que la que enviaba el servidor. Aunque un JSON deber\u00eda funcionar igual con ellos que sin ellos, probablemente quien escribi\u00f3 el c\u00f3digo que lo analizaba en la aspiradora podr\u00eda haber hecho la chapuza de esperarlo de una determinada manera (o bien exigirlo as\u00ed para detectar hackeos), as\u00ed que elimin\u00e9 ese trozo de c\u00f3digo y gener\u00e9 la salida \u00aba pelo\u00bb, juntando cachos de c\u00f3digo. Ahora s\u00ed funcionar\u00eda.<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"180\" style=\"aspect-ratio: 320 \/ 180;\" width=\"320\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/tepille.mp4\"><\/video><\/figure>\n\n\n\n<p>Volv\u00ed a lanzar todo, encend\u00ed la aspiradora, me sent\u00e9 a ver en pantalla las conexiones al servidor&#8230; y volvi\u00f3 a fallar otra vez. Hab\u00eda alguna otra diferencia. \u00bfPero cual?<\/p>\n\n\n\n<p>Volv\u00ed a revisar todo, y me di cuenta de que mi c\u00f3digo estaba enviando una l\u00ednea con el nombre del servidor. En concreto, el campo <em>Server<\/em> val\u00eda <em>BaseHTTP\/0.6 Python\/3.8.3<\/em>, mientras que el servidor real no enviaba dicho campo.<\/p>\n\n\n\n<p>El problema es que el m\u00f3dulo <em>http.server<\/em> no permite eliminar ese campo&#8230; \u00bfComo resolverlo? La soluci\u00f3n consiti\u00f3 en sobrescribir el m\u00e9todo <em>send_header<\/em> del objeto, de manera que ignorase dicho campo. Esto funciona porque el m\u00f3dulo la usa para a\u00f1adir sus campos propios. As\u00ed pues, a\u00f1ad\u00ed esto al servidor:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">def send_header(self, token, data):\n    if token != 'Server':\n        super().send_header(token, data)<\/pre>\n\n\n\n<p>\u00a1Y con esto por fin funcio&#8230;! No, segu\u00eda fallando. No era eso tampoco.<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"162\" style=\"aspect-ratio: 288 \/ 162;\" width=\"288\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/mecagoenmiputavida.mp4\"><\/video><\/figure>\n\n\n\n<p>Hmm&#8230; En el c\u00f3digo de error estoy enviando, despu\u00e9s del 200, el texto OK, mientras que su servidor no env\u00eda nada, s\u00f3lo hay un espacio detr\u00e1s del 200 y ya luego el retorno de carro. \u00a1Seguro que es eso!<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"124\" style=\"aspect-ratio: 168 \/ 124;\" width=\"168\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/banghead.mp4\"><\/video><\/figure>\n\n\n\n<p>\u00a1Agh, tampoco! No lo entiendo, estoy enviando exactamente lo mismo que su servidor: tanto las cabeceras como el contenido es id\u00e9ntico. \u00bfPor qu\u00e9 no funciona? Si abro el wireshark y comparo las capturas reales con las m\u00edas a nivel de byte&#8230; son iguales. \u00bfQu\u00e9 demonios est\u00e1 pasando? Es exactamente lo mismo, con la \u00fanica excepci\u00f3n de&#8230;<\/p>\n\n\n\n<p>No&#8230;<\/p>\n\n\n\n<p>No habr\u00e1n sido capaces&#8230;<\/p>\n\n\n\n<p>No pueden haberlo hecho&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"140\" style=\"aspect-ratio: 220 \/ 140;\" width=\"220\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/sonofabitch.mp4\"><\/video><\/figure>\n\n\n\n<p>Lo fueron. Resulta que su servidor env\u00eda en un solo paquete la cabecera HTTP y el primer bloque de los datos en formato <a href=\"https:\/\/en.wikipedia.org\/wiki\/Chunked_transfer_encoding\">chunk<\/a>, y en un paquete diferente el segundo y \u00faltimo bloque de los datos <em>chunk<\/em>; o sea, esto va en un paquete IP:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">HTTP\/1.1 200 \\r\\n\nDate: Sat, 27 Jun 2020 16:38:08 GMT\\r\\n\nContent-Type: application\/json;charset=UTF-8\\r\\n\nTransfer-Encoding: chunked\\r\\n\nConnection: close\\r\\n\nSet-Cookie: SERVERID=abcdefghijklmnopqrstuvwxyz12345|1234567890|1234567890;Path=\/\\r\\n\n\\r\\n\n2b\\r\\n\n{\"msg\":\"ok\",\"result\":\"0\",\"version\":\"1.0.0\"}\\r\\n<\/pre>\n\n\n\n<p>Y esto va en el siguiente paquete IP:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">0\\r\\n\n\\r\\n<\/pre>\n\n\n\n<p>Sin embargo, mi servidor enviaba en un paquete la cabecera HTTP, y el resto en un paquete diferente. O sea, esto iba en un paquete:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">HTTP\/1.1 200 \\r\\n\nDate: Sat, 27 Jun 2020 16:38:08 GMT\\r\\n\nContent-Type: application\/json;charset=UTF-8\\r\\n\nTransfer-Encoding: chunked\\r\\n\nConnection: close\\r\\n\nSet-Cookie: SERVERID=abcdefghijklmnopqrstuvwxyz12345|1234567890|1234567890;Path=\/\\r\\n\n\\r\\n<\/pre>\n\n\n\n<p>Y esto en el siguiente:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">2b\\r\\n\n{\"msg\":\"ok\",\"result\":\"0\",\"version\":\"1.0.0\"}\\r\\n\n0\\r\\n\n\\r\\n<\/pre>\n\n\n\n<p>Obviamente, en condiciones normales eso dar\u00eda igual, pues TCP es un protocolo orientado a byte y <em>se supone<\/em> que da igual que un trozo viaje en un paquete IP y otro en el siguiente, pero es que <em>esa era la \u00fanica diferencia que quedaba<\/em>, y efectivamente, en cuanto cambi\u00e9 el c\u00f3digo para que lo emitiese de esa manera, por fin funcion\u00f3 y se conect\u00f3 al puerto 20008. \u00bfSer\u00e1 alg\u00fan tipo de protecci\u00f3n antihacking, o simplemente que, a nivel de programaci\u00f3n del microcontrolador hicieron alguna chapuza? Sin analizar el firmware no puedo saberlo, y la verdad, es una tarea que, en este momento, no me llama nada. Pero da igual, porque lo importante es que&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"240\" style=\"aspect-ratio: 320 \/ 240;\" width=\"320\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/victoryismine.mp4\"><\/video><\/figure>\n\n\n\n<p><a href=\"https:\/\/blog.rastersoft.com\/?p=2463\">Parte 5<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>DISCLAIMER: no ser\u00e9 responsable si alguien decide seguir mis pasos y se carga su aspiradora. En principio todo lo que cuento deber\u00eda ser seguro, pero por motivos obvios no me puedo responsabilizar de lo que hagan otras personas, s\u00f3lo de lo que haga yo. Hay que reconocer que cuando las cosas se complican, se complican &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=2441\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">A ritmo de conga (4)<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,16,5,7],"tags":[],"class_list":["post-2441","post","type-post","status-publish","format-standard","hentry","category-cacharreo","category-opendonita","category-programacion","category-tutoriales"],"_links":{"self":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2441","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2441"}],"version-history":[{"count":16,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2441\/revisions"}],"predecessor-version":[{"id":2596,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2441\/revisions\/2596"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2441"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2441"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2441"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}