Как получить клиентский IP-адрес пользователя в ASP.NET?


387

Мы должны Request.UserHostAddressполучить IP-адрес в ASP.NET, но обычно это IP-адрес интернет-провайдера пользователя, а не IP-адрес компьютера пользователя, который, например, щелкнул ссылку. Как я могу получить реальный IP-адрес?

Например, в профиле пользователя Stack Overflow это: «Последняя активность учетной записи: 4 часа назад с 86.123.127.8» , но IP-адрес моей машины немного отличается. Как Stack Overflow получает этот адрес?

В некоторых веб-системах для некоторых целей выполняется проверка IP-адреса. Например, с определенным IP-адресом, может ли пользователь каждые 5 часов нажимать на ссылки для скачивания всего 5 раз? Этот IP-адрес должен быть уникальным, а не для интернет-провайдера, который имеет огромный круг клиентов или пользователей Интернета.

Я хорошо понял?


4
Они обычно делают то же самое и не работают правильно для общих IP-адресов. В этой области мало что можно сделать.
Мехрдад Афшари,

Какую проблему вы пытаетесь решить здесь, почему вы думаете, что вам нужен IP-адрес?
Стив

3
у меня есть приложение, которое проверяет определенные щелчки по ссылке, и конкретный пользователь (по IP) не может щелкнуть ссылку более 5 раз в день. Проблема заключается в том, что если Request.UserHostAddress предназначен для целого ряда пользователей в рамках интернет-провайдера или сети или один конкретный пользователь?
Мехди

Ответы:


142

Как говорили другие, вы не можете делать то, что просите. Если вы опишите проблему, которую пытаетесь решить, может, кто-то может помочь?

Например

  • Вы пытаетесь однозначно идентифицировать своих пользователей?
  • Не могли бы вы использовать куки или идентификатор сессии, а не IP-адрес?

редактироватьАдрес, который вы видите на сервере, не должен быть адресом интернет-провайдера, поскольку вы говорите, что это будет огромный диапазон. Адрес домашнего пользователя в широкополосной сети будет адресом их маршрутизатора, поэтому каждое устройство в доме будет выглядеть одинаково снаружи, но маршрутизатор использует NAT для обеспечения правильной маршрутизации трафика на каждое устройство. Для пользователей, получающих доступ из офисной среды, адрес может быть одинаковым для всех пользователей. Сайты, использующие IP-адрес для идентификации, рискуют ошибиться - приведенные вами примеры являются хорошими, и они часто терпят неудачу. Например, мой офис находится в Великобритании, точка прорыва (где я «появляюсь» в Интернете) находится в другой стране, где находится наше основное ИТ-подразделение, поэтому из моего офиса мой IP-адрес находится не в Великобритании. По этой причине я не могу получить доступ только к веб-контенту в Великобритании, такой как BBC iPlayer). В любой момент времени в моей компании находились сотни или даже тысячи людей, которые, по-видимому, имеют доступ к Интернету с одного и того же IP-адреса.

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

Когда вы говорите, что ваш машинный адрес отличается от IP-адреса, указанного в StackOverflow, как вы узнаете свой машинный адрес? Если вы просто используете локально ipconfigили что-то в этом роде, я ожидаю, что оно будет другим по причинам, которые я изложил выше. Если вы хотите дважды проверить, что думает внешний мир, взгляните на whatismyipaddress.com/ .

Эта ссылка на Википедию по NAT предоставит вам некоторую информацию об этом.


поэтому я понял, что в серверных приложениях мы не можем быть уверены в IP-адресе. это означает, что программирование на стороне клиента является решением ?? Вы имеете в виду, например, с некоторыми кодами JavaScript, мы можем сделать это ??
Мехди

12
НЕТ, это было бы бессмысленно, IP-адрес, который «думает» клиент, будет внутренним по отношению к дому или офису, он будет бессмысленным во внешнем мире. Например, большинство домашних маршрутизаторов раздают IP-адреса в диапазоне 192.168.1.xxx, поэтому тысячи машин имеют одинаковые адреса в своих собственных сетях.
Стив

12
Нет, это не уникально. Два пользователя за одним и тем же маршрутизатором, использующим NAT, будут иметь один и тот же IP-адрес. Я действительно думаю, что вам нужно прочитать об этом, смотрите ссылку в моем редактировании.
Стив

1
Так почему же такие компании, как AWS, Azure и т. Д., Используют IP-адрес в правилах группы безопасности и разрешают подключаться только к этому IP-адресу к ВМ?

2
@ user5950947: поскольку Azure ожидает, что вы будете компанией со статическим публичным IP-адресом. Можно с уверенностью предположить, что ваша компания будет когда-либо получать доступ только со своего публичного IP-адреса, так что это хорошая дополнительная функция безопасности. Но IP-адреса могут быть поддельными или ваша сеть может быть взломана, поэтому она никогда не должна быть единственной защитой.
Deantwo

449

Часто вам захочется узнать IP-адрес того, кто посещает ваш сайт. Хотя в ASP.NET есть несколько способов сделать это, один из лучших способов, которые мы видели, это использование HTTP_X_FORWARDED_FOR из коллекции ServerVariables.

Вот почему ...

Иногда ваши посетители находятся за прокси-сервером или маршрутизатором, и стандарт Request.UserHostAddressфиксирует только IP-адрес прокси-сервера или маршрутизатора. В этом случае IP-адрес пользователя сохраняется в переменной сервера («HTTP_X_FORWARDED_FOR»).

Итак, что мы хотим сделать, это сначала проверить «HTTP_X_FORWARDED_FOR», и если он пуст, мы просто возвращаемся ServerVariables("REMOTE_ADDR").

Хотя этот метод не является надежным, он может привести к лучшим результатам. Ниже приведен код ASP.NET в VB.NET, взятый из сообщения в блоге Джеймса Кроули «Получил: HTTP_X_FORWARDED_FOR возвращает несколько IP-адресов»

C #

protected string GetIPAddress()
{
    System.Web.HttpContext context = System.Web.HttpContext.Current; 
    string ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

    if (!string.IsNullOrEmpty(ipAddress))
    {
        string[] addresses = ipAddress.Split(',');
        if (addresses.Length != 0)
        {
            return addresses[0];
        }
    }

    return context.Request.ServerVariables["REMOTE_ADDR"];
}

VB.NET

Public Shared Function GetIPAddress() As String
    Dim context As System.Web.HttpContext = System.Web.HttpContext.Current
    Dim sIPAddress As String = context.Request.ServerVariables("HTTP_X_FORWARDED_FOR")
    If String.IsNullOrEmpty(sIPAddress) Then
        Return context.Request.ServerVariables("REMOTE_ADDR")
    Else
        Dim ipArray As String() = sIPAddress.Split(New [Char]() {","c})
        Return ipArray(0)
    End If
End Function

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

4
По вашей ссылке, what we actually needed to be doing was take the last IP addressно ваш код получает первым addresses[0]. Что правильно?
Нельсон Ротермел

4
@NelsonRothermel На основании en.wikipedia.org/wiki/X-Forwarded-For#Format, если вам нужен клиент (а не предыдущий прокси), используйте первый .
Ричард

4
@ dr.evil Так что бы вы предложили вместо этого? Потому что я хочу установить доступ к службам WCF по указанным IP-адресам.
Крипру

7
addresses.Length != 0не обязательно, так как никогда не может быть 0.
Джеймс Уилкинс

78

ОБНОВЛЕНИЕ: Спасибо Бруно Лопесу. Если несколько IP-адресов может прийти, то нужно использовать этот метод:

    private string GetUserIP()
    {
        string ipList = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

        if (!string.IsNullOrEmpty(ipList))
        {
            return ipList.Split(',')[0];
        }

        return Request.ServerVariables["REMOTE_ADDR"];
    }

3
Как отмечено в других ответах, HTTP_X_FORWARDED_FOR может быть списком IP-адресов, разделенных запятыми.
Бруно Лопес

В ответ на функцию я просто получаю :: 1 каждый раз. Я не могу получить полный IP-адрес ???
farhangdon

1
он вернет :: 1 на локальном хосте. попробуйте это в среде productino, и все должно быть в порядке.
Бенни Маргалит

1
@farhangdon, следующий код вернет IP-адрес на локальном хосте, как @Bat_Programmer писал нижеSystem.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[1].ToString();
Бенни Маргалит

НЕ ДЕЛАЙТЕ ЭТОГО! Будьте осторожны, чтобы не просто использовать первый IP-адрес в списке. Вам следует только пропустить известные прокси-IP-адреса, начиная с самого правого входа, чтобы избежать атак «человек посередине» и подделки заголовков.
Jpsy

24

Если это C # см. Это очень просто

string clientIp = (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? 
                   Request.ServerVariables["REMOTE_ADDR"]).Split(',')[0].Trim();

8
Если обе серверные переменные могут быть нулевыми, это может вызвать исключение.
Майкл Фрейдгейм,

Что такое запрос ссылки здесь? I ServerVariables не найден
Brownbagger11

23

Что еще вы считаете IP-адресом пользователя? Если вам нужен IP-адрес сетевого адаптера, я боюсь, что в веб-приложении это невозможно. Если ваш пользователь находится за NAT или другими вещами, вы также не можете получить IP.

Обновление : хотя существуют веб-сайты, которые используют IP для ограничения пользователей (например, rapidshare), они не работают правильно в средах NAT.


22

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

Когда я хочу узнать IP-адрес клиента, я стараюсь выбрать все возможные доказательства, чтобы определить, являются ли они уникальными:

Здесь я нашел другой сервер-var, который мог бы помочь вам всем, если вы хотите получить точный IP-адрес клиентской стороны. поэтому я использую: HTTP_X_CLUSTER_CLIENT_IP

HTTP_X_CLUSTER_CLIENT_IP всегда получает точный IP-адрес клиента. В любом случае, если оно не дает вам значения, вам следует искать HTTP_X_FORWARDED_FOR, поскольку это второй лучший кандидат для получения IP- адреса клиента, а затем переменная REMOTE_ADDR, которая может возвращать или не возвращать вам IP-адрес, но у меня есть все эти три - это то, что я считаю лучшим, чтобы следить за ними.

Я надеюсь, что это помогает некоторым парням.


Надо сказать, что заголовки http_x _... могут быть легко подделаны относительно переменной remote_addr. И поэтому remote_addr остается наиболее надежным источником для IP-адреса клиента.
Чиро Корвино

1
@CiroCorvino вы правы, но когда ваш сайт размещен на сервере, который работает за балансировщиком нагрузки (как я уже упоминал в моем посте), тогда remote_addr не даст вам IP-адрес, который вы ищете. Я испытал то, что вы говорите, но когда я выкопал в решении, то, что я сказал, работает лучше для меня.
KMX

Извините, @KMX, по сути, я также использую комбинацию remote_addr и http_x_forwarded, чтобы вывести некоторые предположения о происхождении запроса. Если вы отредактируете ваше сообщение, я
Сиро Корвино

15

Ты можешь использовать:

System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.GetValue(0).ToString();

1
спасибо, для меня это единственный метод, который действительно возвращает мой IP на локальном хосте.
Бенни Маргалит

На самом деле то, что я использовал, чтобы получить IP-адрес локального хоста,System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[1].ToString();
Бенни Маргалит

1
Этот код работает только для меня, и я могу получить IPv6 и IPv4, используя GetValue (0) и GetValue (1) соответственно. Спасибо Upvoted!
Радж Барал

Тем не менее, я получаю сообщение об ошибке при выполнении этого в скрипке Request for the permission of type 'System.Net.DnsPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
vibs2006

ОП спрашивает, как получить IP-адрес удаленного веб-пользователя, а не адрес его локального хоста.
t.durden

13

IP-адреса являются частью сетевого уровня в «семислойном стеке». Сетевой уровень может делать все, что хочет с IP-адресом. Вот что происходит с прокси-сервером, NAT, реле или чем-то еще.

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


1
Это все хорошо, но что вы делаете, когда один клиент мультитенантной системы требует, чтобы учетные записи его пользователей могли входить только с указанного IP-адреса?
Ронни Оверби

1
Затем они должны сказать вам, какой IP-адрес увидит ваш сервер. Если им нужен конкретный адрес, они не смогут находиться за NAT или чем-то подобным.
Джон Сондерс

@RonnieOverby Я в такой же ситуации. Мне нужно знать, с какого IP-адреса они подключаются, и это должно быть в моем белом списке. Затем приложение может включать или отключать определенные функции в зависимости от их IP-адреса. Это то, что хочет клиент.
t.durden

10

Все ответы до сих пор учитывают нестандартный, но очень распространенный X-Forwarded-Forзаголовок. Существует стандартизированный Forwardedзаголовок, который немного сложнее разобрать. Вот некоторые примеры:

Forwarded: for="_gazonk"
Forwarded: For="[2001:db8:cafe::17]:4711"
Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
Forwarded: for=192.0.2.43, for=198.51.100.17

Я написал класс, который учитывает оба этих заголовка при определении IP-адреса клиента.

using System;
using System.Web;

namespace Util
{
    public static class IP
    {
        public static string GetIPAddress()
        {
            return GetIPAddress(new HttpRequestWrapper(HttpContext.Current.Request));
        }

        internal static string GetIPAddress(HttpRequestBase request)
        {
            // handle standardized 'Forwarded' header
            string forwarded = request.Headers["Forwarded"];
            if (!String.IsNullOrEmpty(forwarded))
            {
                foreach (string segment in forwarded.Split(',')[0].Split(';'))
                {
                    string[] pair = segment.Trim().Split('=');
                    if (pair.Length == 2 && pair[0].Equals("for", StringComparison.OrdinalIgnoreCase))
                    {
                        string ip = pair[1].Trim('"');

                        // IPv6 addresses are always enclosed in square brackets
                        int left = ip.IndexOf('['), right = ip.IndexOf(']');
                        if (left == 0 && right > 0)
                        {
                            return ip.Substring(1, right - 1);
                        }

                        // strip port of IPv4 addresses
                        int colon = ip.IndexOf(':');
                        if (colon != -1)
                        {
                            return ip.Substring(0, colon);
                        }

                        // this will return IPv4, "unknown", and obfuscated addresses
                        return ip;
                    }
                }
            }

            // handle non-standardized 'X-Forwarded-For' header
            string xForwardedFor = request.Headers["X-Forwarded-For"];
            if (!String.IsNullOrEmpty(xForwardedFor))
            {
                return xForwardedFor.Split(',')[0];
            }

            return request.UserHostAddress;
        }
    }
}

Ниже приведены некоторые модульные тесты, которые я использовал для проверки своего решения:

using System.Collections.Specialized;
using System.Web;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UtilTests
{
    [TestClass]
    public class IPTests
    {
        [TestMethod]
        public void TestForwardedObfuscated()
        {
            var request = new HttpRequestMock("for=\"_gazonk\"");
            Assert.AreEqual("_gazonk", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv6()
        {
            var request = new HttpRequestMock("For=\"[2001:db8:cafe::17]:4711\"");
            Assert.AreEqual("2001:db8:cafe::17", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv4()
        {
            var request = new HttpRequestMock("for=192.0.2.60;proto=http;by=203.0.113.43");
            Assert.AreEqual("192.0.2.60", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedIPv4WithPort()
        {
            var request = new HttpRequestMock("for=192.0.2.60:443;proto=http;by=203.0.113.43");
            Assert.AreEqual("192.0.2.60", Util.IP.GetIPAddress(request));
        }

        [TestMethod]
        public void TestForwardedMultiple()
        {
            var request = new HttpRequestMock("for=192.0.2.43, for=198.51.100.17");
            Assert.AreEqual("192.0.2.43", Util.IP.GetIPAddress(request));
        }
    }

    public class HttpRequestMock : HttpRequestBase
    {
        private NameValueCollection headers = new NameValueCollection();

        public HttpRequestMock(string forwarded)
        {
            headers["Forwarded"] = forwarded;
        }

        public override NameValueCollection Headers
        {
            get { return this.headers; }
        }
    }
}

9

Если вы используете CloudFlare, вы можете попробовать этот метод расширения :

public static class IPhelper
{
    public static string GetIPAddress(this HttpRequest Request)
    {
        if (Request.Headers["CF-CONNECTING-IP"] != null) return Request.Headers["CF-CONNECTING-IP"].ToString();

        if (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null) return Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();

        return Request.UserHostAddress;
    }
}

тогда

string IPAddress = Request.GetIPAddress();

И используя F5 или Пало-Альто?
Kiquenet

8
string IP = HttpContext.Current.Request.Params["HTTP_CLIENT_IP"] ?? HttpContext.Current.Request.UserHostAddress;

7

То, что вы можете сделать, это сохранить IP-адрес маршрутизатора вашего пользователя, а также переадресованный IP-адрес и попытаться сделать его надежным, используя как IP-адреса [External Public, так и Internal Private]. Но через несколько дней клиенту может быть назначен новый внутренний IP-адрес от маршрутизатора, но он будет более надежным.


5

Комбинируя ответы от @Tony и @mangokun , я создал следующий метод расширения:

public static class RequestExtensions
{
    public static string GetIPAddress(this HttpRequest Request)
    {
        if (Request.Headers["CF-CONNECTING-IP"] != null) return Request.Headers["CF-CONNECTING-IP"].ToString();

        if (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null)
        {
            string ipAddress = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

            if (!string.IsNullOrEmpty(ipAddress))
            {
                string[] addresses = ipAddress.Split(',');
                if (addresses.Length != 0)
                {
                    return addresses[0];
                }
            }
        }

        return Request.UserHostAddress;
    }
}

Почему вы используете, HTTP_X_FORWARDED_FORно нет X_FORWARDED_FOR? Они одинаковы?
Игорь Яловой

3

использовать в файле Ashx

public string getIP(HttpContext c)
{
    string ips = c.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
    if (!string.IsNullOrEmpty(ips))
    {
        return ips.Split(',')[0];
    }
    return c.Request.ServerVariables["REMOTE_ADDR"];
}

2
public static class Utility
{
    public static string GetClientIP(this System.Web.UI.Page page)
    {
        string _ipList = page.Request.Headers["CF-CONNECTING-IP"].ToString();
        if (!string.IsNullOrWhiteSpace(_ipList))
        {
            return _ipList.Split(',')[0].Trim();
        }
        else
        {
            _ipList = page.Request.ServerVariables["HTTP_X_CLUSTER_CLIENT_IP"];
            if (!string.IsNullOrWhiteSpace(_ipList))
            {
                return _ipList.Split(',')[0].Trim();
            }
            else
            {
                _ipList = page.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                if (!string.IsNullOrWhiteSpace(_ipList))
                {
                    return _ipList.Split(',')[0].Trim();
                }
                else
                {
                    return page.Request.ServerVariables["REMOTE_ADDR"].ToString().Trim();
                }
            }
        }
    }
}

Используйте;

string _ip = this.GetClientIP();

0

Это легко. Попробуйте это:

var remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress;

Просто это :))


1
К вашему сведению, приведенный выше код работает только в Asp.Net Core, а не в обычной ссылке
Добин

-1

Привет, ребята. Большинство кодов, которые вы найдете, будут возвращать вам IP-адрес сервера, а не IP-адрес клиента. Однако этот код возвращает правильный IP-адрес клиента. Попробуйте. Для получения дополнительной информации просто проверьте это

https://www.youtube.com/watch?v=Nkf37DsxYjI

для получения вашего локального IP-адреса с помощью JavaScript вы можете использовать этот код внутри тега скрипта

<script>
    var RTCPeerConnection = /*window.RTCPeerConnection ||*/
     window.webkitRTCPeerConnection || window.mozRTCPeerConnection;

         if (RTCPeerConnection) (function () {
             var rtc = new RTCPeerConnection({ iceServers: [] });
             if (1 || window.mozRTCPeerConnection) {      
                 rtc.createDataChannel('', { reliable: false });
             };

             rtc.onicecandidate = function (evt) {

                 if (evt.candidate)
                     grepSDP("a=" + evt.candidate.candidate);
             };
             rtc.createOffer(function (offerDesc) {
                 grepSDP(offerDesc.sdp);
                 rtc.setLocalDescription(offerDesc);
             }, function (e) { console.warn("offer failed", e); });


             var addrs = Object.create(null);
             addrs["0.0.0.0"] = false;
             function updateDisplay(newAddr) {
                 if (newAddr in addrs) return;
                 else addrs[newAddr] = true;
                 var displayAddrs = Object.keys(addrs).filter(function
(k) { return addrs[k]; });
                 document.getElementById('list').textContent =
displayAddrs.join(" or perhaps ") || "n/a";
             }

             function grepSDP(sdp) {
                 var hosts = [];
                 sdp.split('\r\n').forEach(function (line) { 
                     if (~line.indexOf("a=candidate")) {   
                         var parts = line.split(' '),   
                             addr = parts[4],
                             type = parts[7];
                         if (type === 'host') updateDisplay(addr);
                     } else if (~line.indexOf("c=")) {      
                         var parts = line.split(' '),
                             addr = parts[2];
                         updateDisplay(addr);
                     }
                 });
             }
         })(); else
         {
             document.getElementById('list').innerHTML = "<code>ifconfig| grep inet | grep -v inet6 | cut -d\" \" -f2 | tail -n1</code>";
             document.getElementById('list').nextSibling.textContent = "In Chrome and Firefox your IP should display automatically, by the power of WebRTCskull.";

         }




</script>
<body>
<div id="list"></div>
</body>

и для получения вашего публичного IP-адреса вы можете использовать этот код внутри тега скрипта

  function getIP(json) {
    document.write("My public IP address is: ", json.ip);
  }


<script type="application/javascript" src="https://api.ipify.org?format=jsonp&callback=getIP"></script>


-7

Пытаться:

using System.Net;

public static string GetIpAddress()  // Get IP Address
{
    string ip = "";     
    IPHostEntry ipEntry = Dns.GetHostEntry(GetCompCode());
    IPAddress[] addr = ipEntry.AddressList;
    ip = addr[2].ToString();
    return ip;
}
public static string GetCompCode()  // Get Computer Name
{   
    string strHostName = "";
    strHostName = Dns.GetHostName();
    return strHostName;
}

Это возвращает IP-адрес сервера
Sreekumar P

Речь идет об asp.net, который является веб-приложением, которое работает на сервере, а не на компьютере пользователя.
doekman
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.