📜 ⬆️ ⬇️

Yandex DNS hosting vs Dynamic IP

In September last 2010, Yandex opened DNS hosting for Mail for domains for public use. There was no limit to the users' joy, the topic was met with a mass of positive comments, and Yandex was declared a good corporation.

Unfortunately, the administration of DNS records was provided only through a web interface. API for administration has not been provided, has not yet appeared, and it may not appear for a long time. This fact saddened many domain owners with dynamic IP no less than translating the excellent free service free.editdns.net on a paid basis (for custom domains), in connection with the purchase of the latter by DynDNS.

After making sure that the miracle did not happen, I picked up a Python file in order to correct this injustice ...

First of all, I went to the all-knowing Google in search of at least some information about the API of Yandex services. First I came across a detailed description of the Mail API for domains. Alas, out of the 32 functions available, there was nothing related to the administration of the DNS hosting, and I continued searching. Having added the magic words python to the request, and then c sharp, I came across an article by Alexey Nemiro with a detailed description of the browser operation when authorizing on Yandex services and code examples on VB and C #.
')
After reading the article and making sure that I still have to imitate the browser, I armed myself with FireBug and HTTP Analyzer . Having spent some time studying the intricacies of authorization and working with DNS hosting, I found out that authorization on Yandex services works quite simply. The authorization procedure starts with the yandexuid cookie, obtained when logging in to any Yandex service:

Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  1. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  2. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  3. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  4. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  5. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  6. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  7. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  8. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid
  9. Copy Source | Copy HTML def initialize (self): connection = httplib .HTTPConnection( 'www.yandex.ru' ) connection.request( 'GET' , '/' ) response = connection.getresponse() cookies = response.getheader( 'set-cookie' , None) response.close() match = re .search( '(?<=yandexuid=)[^;]*' , cookies) self ._yandexuid = match.group( 0 ) print 'yandexuid =' , self ._yandexuid


After receiving the cookie yandexuid, the browser sends a POST request a login, password and timestamp in UNIX format. If there were no problems with the formation of the request, then I spent a long time with the timestamp formula:

Copy Source | Copy HTML
  1. def login (self):
  2. content = 'login = {0} & passwd = {1} & timestamp = {2}'
  3. content = content.format ( self ._login, self ._passwd, self .timestamp ())
  4. connection = httplib .HTTPConnection ( 'passport.yandex.ru' )
  5. connection.request ( 'POST' , '/ passport? mode = auth' , content, { 'Cookie' : self .getcookies ()})
  6. response = connection.getresponse ()
  7. content = response.read ()
  8. response.close ()
  9. match = re .search ( 'idkey \ "\ s. *' , content)
  10. match = re .search ( '(\ d \ w *)' , match.group ( 0 ))
  11. self ._idkey = match.group ( 0 )
  12. print 'idkey =' , self ._idkey


Having received the magic idkey, in response to the request “Set up permanent authorization on this computer”, it was necessary to form a request simulating the click of the “No” button:

Copy Source | Copy HTML
  1. def authenticate (self):
  2. content = 'filled = yes & timestamp = {0} & idkey = {1} & no =% D0% 9D% D0% B5% D1% 82'
  3. content = content.format ( self .timestamp (), self ._idkey)
  4. connection = httplib .HTTPConnection ( 'passport.yandex.ru' )
  5. connection.request ( 'POST' , '/ passport? mode = auth' , content, { 'Cookie' : self .getcookies ()})
  6. response = connection.getresponse ()
  7. cookies = response.getheader ( 'set-cookie' , None)
  8. ... parsing cookies with regexps ...
  9. response.close ()


Now, having all the necessary cookies on hand, to work with the Mail for domains, it is enough to imitate clicking the “Save” button in the DNS record editor via the internal AJAX API:

Copy Source | Copy HTML
  1. def updatedomain (self, ns_record_id):
  2. content = 'domain = {0} & ns_record_id = {1} & ns_rec_type = A & ns_subdomain =% 40 & ns_weight = & ns_port = & ns_content = {2} & ns_priority = 1'
  3. content = content.format ( self ._domain, ns_record_id, self ._externalip)
  4. connection = httplib .HTTPSConnection ( 'pdd.yandex.ru' )
  5. connection.request ( 'POST' , '/ajax/ns_simple_record_edit.ajax.xml' , content, \
  6. { 'Accept' : 'application / json, text / javascript, * / *' , \
  7. 'Cookie' : self .getcookies ()})
  8. response = connection.getresponse ()
  9. response.close ()


As in the previous case, at this stage the Accept header turned out to be mandatory. But this is not the most important. To get ns_record_id, I had to parse the HTML code for the page with the list of DNS records:

Copy Source | Copy HTML
  1. def domainlist (self):
  2. connection = httplib .HTTPSConnection ( 'pdd.yandex.ru' )
  3. connection.request ( 'GET' , '/ domain_ns / {0} /' .format ( self ._domain), None, \
  4. { 'Accept' : 'text / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8' , \
  5. 'Cookie' : self .getcookies (), \
  6. 'Referer' : 'https://pdd.yandex.ru' })
  7. response = connection.getresponse ()
  8. content = response.read ()
  9. block = re .findall ( 'item: \ s \' [\ d] + \ '(. +) value = "[\ w \.] +"' , content)
  10. for item in block:
  11. match = re .search ( '(? <= item: \ s \') [\ d] * ' , item)
  12. ns_record_id = match.group ( 0 )
  13. match = re .search ( 'ns_subdomain (. +?) value = \ "(. +?) \"' , item)
  14. match = re .search ( '(? <= value =) \ ". +? \"' , match.group ( 0 ))
  15. ns_subdomain = match.group ( 0 )
  16. match = re .search ( 'ns_rec_type (. +?) value = \ "(. +?) \"' , item)
  17. match = re .search ( '(? <= value =) \ ". +? \"' , match.group ( 0 ))
  18. ns_rec_type = match.group ( 0 )
  19. match = re .search ( 'ns_content (. +?) value = \ "(. +?) \"' , item)
  20. match = re .search ( '(? <= value =) \ ". +? \"' , match.group ( 0 ))
  21. ns_content = match.group ( 0 )
  22. record = 'ns_record_id = {0} | ns_subdomain = {1} | ns_rec_type = {2} | ns_content = {3} '
  23. print record.format (ns_record_id, ns_subdomain, ns_rec_type, ns_content)
  24. response.close ()


Since this was my first Python program, I limited myself to httplib and manual shaping of cookies. Having added a command line parameter, parsing the config and getting an external IP to this cocktail, I received a simple script to update the DNS records on Yandex DNS hosting.

Download source code and config example .

Source: https://habr.com/ru/post/122877/


All Articles