Как странице в браузере найти сервер в локальной сети?
От: Basil2 Россия https://starostin.msk.ru
Дата: 31.03.21 20:03
Оценка: 17 (2)
Дано:

Приложение под Windows (сервер на WebSockets).
Веб-страница, открытая в браузере на планшете (WPA).
Оба запущены в одной подсети (роутер).

Задача:

Они должны найти друг друга.

Проблема:

Браузер не умеет в UDP от слова совсем. Следовательно штатный zeroconf отпадает. Пока на ум приходит только перебирать IP-адреса в цикле, и то если получится из под браузера получить локальный IP.

Решение?
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Отредактировано 01.04.2021 13:09 Basil2 . Предыдущая версия . Еще …
Отредактировано 31.03.2021 21:18 Basil2 . Предыдущая версия .
Re: Как наколхозить zeroconf в браузере?
От: cppguard США  
Дата: 31.03.21 20:37
Оценка:
Здравствуйте, Basil2, Вы писали:

B>Решение?


Кросс-платформенного, думаю, не существует. Если завязаться на Хромого, то https://developer.chrome.com/docs/apps/nativeMessaging/.
Re[2]: Как наколхозить zeroconf в браузере?
От: Basil2 Россия https://starostin.msk.ru
Дата: 31.03.21 21:17
Оценка:
Здравствуйте, cppguard, Вы писали:

B>>Решение?


C>Кросс-платформенного, думаю, не существует. Если завязаться на Хромого, то https://developer.chrome.com/docs/apps/nativeMessaging/.


Это же вроде только для хром-приложений (плагинов)?
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Re[3]: Как наколхозить zeroconf в браузере?
От: cppguard США  
Дата: 01.04.21 00:17
Оценка:
Здравствуйте, Basil2, Вы писали:

B>Это же вроде только для хром-приложений (плагинов)?


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

Другой вариант — WebRTC и STUN. Нужен STUN-сервер, можно воспользоваться одним из открытых. Но это тоже не zerconf в чистом виде, а peer discovery, поэтому какую-то из сторон (а, может, обе) придётся модифицировать.
Re[4]: Как наколхозить zeroconf в браузере?
От: Basil2 Россия https://starostin.msk.ru
Дата: 01.04.21 09:12
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Другой вариант — WebRTC и STUN. Нужен STUN-сервер, можно воспользоваться одним из открытых. Но это тоже не zerconf в чистом виде, а peer discovery, поэтому какую-то из сторон (а, может, обе) придётся модифицировать.


Я сейчас думаю просто зарегистрировать ПК на локальном DNS под константным именем, а из браузера к нему обращаться. Пытаюсь понять, умеет ли это встроенный в винду локальный DNS (через который zeroconf и работает, как я понимаю).
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Re: Как странице в браузере найти сервер в локальной сети?
От: flаt  
Дата: 02.04.21 11:40
Оценка:
Сеть своя или чужая? Если своя, то присвоить статический адрес серверу.
Re: Как странице в браузере найти сервер в локальной сети?
От: bnk СССР http://unmanagedvisio.com/
Дата: 02.04.21 11:53
Оценка:
Здравствуйте, Basil2, Вы писали:

B>Решение?


Не знаю но тоже интересно.
Конкретно для случая когда сервер — это raspberry pi, приложение — жаваскриппт в браузере на телефоне. Сеть заранее неизвестна, известно только что они в одной и той же сети (роутер).
Re: Как странице в браузере найти сервер в локальной сети?
От: vsb Казахстан  
Дата: 02.04.21 11:56
Оценка: 4 (1)
Здравствуйте, Basil2, Вы писали:

B>Пока на ум приходит только перебирать IP-адреса в цикле, и то если получится из под браузера получить локальный IP.


Не получится. Раньше был способ, сейчас его пофиксили. http://net.ipcalf.com/ выдаёт "локальный" адрес, например мне он выдал d39362a4-32a0-4883-b4af-1e6f1e13c244.local (который в cmd резолвится в нужный IP, но JS этого сделать не сможет).

B>Решение?


Не думаю, что тут есть какое-то решение. Можете перебирать все IP-адреса во всех возможных частных подсетях, начиная от самых употребительных (192.168.0.0 – 192.168.255.255 и тд). Похоже, что у браузера нет ограничения на число одновременных попыток подключения к разным серверам, то бишь одновременно запускаете по несколько десятков тысяч запросов. Но по-моему не очень хороший вариант.

Я бы советовал такой вариант: приложение на Windows определяет свой локальный IP-адрес, делает URL вида http://mysite.com/#192.168.0.1 и кодирует его в QR-код, который показывает в интерфейсе. Пользователь в планшете вызывает приложение "камера", наводит на этот QR-код и переходит на сайт mysite.com с переданным параметром-локальным адресом. Который вы потом в JavaScript используете, как вам нужно.
Отредактировано 02.04.2021 12:00 vsb . Предыдущая версия . Еще …
Отредактировано 02.04.2021 11:59 vsb . Предыдущая версия .
Re[2]: Как странице в браузере найти сервер в локальной сети?
От: Basil2 Россия https://starostin.msk.ru
Дата: 02.04.21 14:34
Оценка:
Здравствуйте, flаt, Вы писали:

F>Сеть своя или чужая? Если своя, то присвоить статический адрес серверу.


Чужая к сожалению.
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Re[2]: Как странице в браузере найти сервер в локальной сети?
От: Basil2 Россия https://starostin.msk.ru
Дата: 02.04.21 14:36
Оценка:
bnk>Конкретно для случая когда сервер — это raspberry pi, приложение — жаваскриппт в браузере на телефоне. Сеть заранее неизвестна, известно только что они в одной и той же сети (роутер).

Я так понял, что в локальной сети не будут работать https и wss, так что в моем случае вопрос отпадает
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Re[3]: Как странице в браузере найти сервер в локальной сети?
От: Vetal_ca Канада http://vetal.ca
Дата: 03.04.21 01:49
Оценка: 8 (1)
Здравствуйте, Basil2, Вы писали:

B>Я так понял, что в локальной сети не будут работать https и wss, так что в моем случае вопрос отпадает


Локальная сеть точно такая же сеть, как и остальные.

Тебе нужен reverse proxy. Который может работать в локальной сети, сидеть на одном приватном адресе (например, 192.168.1.155). Резолвить разных пользователей/адресов, john.basil2.com, bob.basil2.com и т.п., см SNI. При этом работать с TLS (WSS/HTTPS).

Т.е. "nslookup john.basil2.com" => 192.168.1.155, "nslookup john.basil2.bob" => 192.168.1.155

По реверс прокси в Windows я не "доктор", но как-то так.
Re[4]: Как странице в браузере найти сервер в локальной сети?
От: Basil2 Россия https://starostin.msk.ru
Дата: 03.04.21 18:51
Оценка:
Здравствуйте, Vetal_ca, Вы писали:

B>>Я так понял, что в локальной сети не будут работать https и wss, так что в моем случае вопрос отпадает

V_>Локальная сеть точно такая же сеть, как и остальные.
V_>Тебе нужен reverse proxy. Который может работать в локальной сети, сидеть на одном приватном адресе (например, 192.168.1.155). Резолвить разных пользователей/адресов, john.basil2.com, bob.basil2.com и т.п., см SNI. При этом работать с TLS (WSS/HTTPS).

Интересная идея, но я не понял, как это поможет. Чтобы законнектится с прокси-сервером, надо чтобы прокси-сервер имел сертификат. А откуда он его возьмет? Это сервер будут ставить простые пользователи, и я не знаю как у них будут названы компьютеры или какие адреса будут.
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Re[5]: Как странице в браузере найти сервер в локальной сети?
От: Vetal_ca Канада http://vetal.ca
Дата: 03.04.21 20:33
Оценка: 18 (2)
Здравствуйте, Basil2, Вы писали:

B>>>Я так понял, что в локальной сети не будут работать https и wss, так что в моем случае вопрос отпадает

V_>>Локальная сеть точно такая же сеть, как и остальные.
V_>>Тебе нужен reverse proxy. Который может работать в локальной сети, сидеть на одном приватном адресе (например, 192.168.1.155). Резолвить разных пользователей/адресов, john.basil2.com, bob.basil2.com и т.п., см SNI. При этом работать с TLS (WSS/HTTPS).

B>Интересная идея, но я не понял, как это поможет. Чтобы законнектится с прокси-сервером, надо чтобы прокси-сервер имел сертификат. А откуда он его возьмет? Это сервер будут ставить простые пользователи, и я не знаю как у них будут названы компьютеры или какие адреса будут.


Обычный сервер-сертификат получаешь под каждый хост. Или wildcard. Например, от Let'sEncrypt.

Для моего примера, это обычный TLS сертификат, john.basil2.com, bob.basil2.com => Common name (CN) = *.basil2.com. Можно при добавлении каждого нового пользователя добавлять сертификат, например, CN="john.basil2.com"

Посмотрел, тот же Traefik работает в Windows и может сам получать SSL сертификат, https://doc.traefik.io/traefik/https/acme/

Тогда алгоритм такой, при добавлении пользователя "user-x"

Пререквизит: у тебя работает в локальной сети Traefik, 192.168.1.155, уже есть A-Record, server-entry.basil2.com="192.168.1.155". Это можно сделать в любом месте, например, Cloudflare бесплатно предоставляет DNS сервер

1. Добавляешь DNS records, тип CNAME, web-user-x.basil2.com=server-entry.basil2.com, wss-user-x.basil2.com=server-entry.basil2.com
2. Добавляешь свой микросервис, один, или несколько. Например, они крутятся у тебя на одном сервере, где и Traefik reverse proxy. Для user-x, http сервис запускаешь на 127.0.0.1:8080, Websocket на 127.0.0.1:8081.
3. Регистрируешь на Traefik proxy два сервиса. Один, web-user-x.basil2.com => 127.0.0.1:8080, wss-user-x.basil2.com => 127.0.0.1:8081


После этого, если все сделано правильно, то в этой же сети, вбиваешь в браузер https://web-user-x.basil2.com, все должно работать с "зеленым замком" https соединения, в браузере в локальной сети, в том числе и для сокетов

Между сервисами на localhost и прокси — не https/wss, прокси делает ssl-offloading и направляет на правильный ip-port для заданного запроса. По заголовку hostname, от сервера. Например, https://stackoverflow.com/questions/50279275/curl-how-to-specify-target-hostname-for-https-request для понятности

Из тонкостей, твой раутер может блокировать DNS с приватными адресами. Типа, борется с DNS rebinding attack. Тогда в настройках сети ставь публичный DNS, например, 1.1.1.1

Для внешнего сервиса это не нужно

Проще, наверное, при помощи docker-compose, тогда сервисы будут не на 127.0.0.1 а на внутренней сети

Вообще, будет довольно геморно, если пользователи не продвинутые. А с tls тебе точно понадобятся и DNS и прочее, на IP адресах у тебя TLS будет болью и страданием.

Если пользователи продвинутые — делай helm chart для кубернетесов. Если это домашние пользователи, то это не пойдет, придется отказаться от TLS. Поддержка и настройка убъет весь доход.
Re[5]: Как странице в браузере найти сервер в локальной сети
От: vsb Казахстан  
Дата: 03.04.21 20:59
Оценка: 18 (2)
Проще всего сделать такую схему:

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

2. Твой сервер в интернете генерирует имя вида 12345.localserver.mysoftware.com, с помощью letsencrypt и DNS-верификации получает сертификат на это имя и отправляет закрытый ключ и сертификат серверу в локальной сети. После этого сервер в локальной "владеет" этим именем. Каждому серверу нужно генерировать отдельное имя, это важно.

3. Также локальный сервер должен иметь возможность отправлять твоему серверу запрос на смену IP-адреса. Т.е. после этого запроса имя 12345.localserver.mysoftware.com будет указывать на 192.168.1.31, например.

Собственно всё. Пользователь использует адрес https://12345.localserver.mysoftware.com по которому будет открываться сервер 192.168.1.31 который будет иметь возможность отдать рабочий сертификат для 12345.localserver.mysoftware.com.

Чтобы всё совсем было по феншую, в шаге 1 можно генерировать закрытый ключ прям на локальном сервере и на сервер в интернете отправлять CSR. Но это усложнит реализацию. В принципе можно даже letsencrypt-ом пользоваться напрямую с локального сервера, просто отправлять данные для верификации на сервер в интернете, который уже будет обновлять DNS-записи. Но это тоже усложнит реализацию, придётся разбираться с деталями работы letsencrypt-а. А по описанной простой схеме достаточно пользоваться любым готовым софтом для работы с letsencrypt и каким-нибудь Cloudflare DNS API.
Отредактировано 03.04.2021 21:03 vsb . Предыдущая версия . Еще …
Отредактировано 03.04.2021 21:02 vsb . Предыдущая версия .
Re[6]: Как странице в браузере найти сервер в локальной сети
От: Basil2 Россия https://starostin.msk.ru
Дата: 05.04.21 13:09
Оценка:
Здравствуйте, Vetal_ca, Вы писали:

V_>Если пользователи продвинутые — делай helm chart для кубернетесов. Если это домашние пользователи, то это не пойдет, придется отказаться от TLS. Поддержка и настройка убъет весь доход.


Домашние, это продукт для геймеров. Увы, отказаться от TLS нельзя, т.к. (гребаные) браузеры не дают в простой вебсокет если https. А отказаться от https тоже нельзя, ибо без https не работает PWA. Будь проклят тот день, когда я решил делать на PWA ((( (а еще раньше — на этом долбанном PhoneGap).

А с local DNS это может прокатить? Например, сервер регистрирует в локальном DNS (mDNS) адрес server.local, и веб-клиент всегда обращается к адресу wss://server.local. Тогда сертификат нужен только один.
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Отредактировано 05.04.2021 13:28 Basil2 . Предыдущая версия .
Re[7]: Как странице в браузере найти сервер в локальной сети
От: Vetal_ca Канада http://vetal.ca
Дата: 05.04.21 14:42
Оценка: 12 (1)
Здравствуйте, Basil2, Вы писали:

B>А с local DNS это может прокатить? Например, сервер регистрирует в локальном DNS (mDNS) адрес server.local, и веб-клиент всегда обращается к адресу wss://server.local. Тогда сертификат нужен только один.



Можно, конечно. Локально можно крутить весь свой интернет с DNS, подсетями и TLS


Проблема в том, что у тебя будет самоподписанный certificate authority (CA), не включенный в глобальный, для *.local. Браузеры идут с существующими trusted root CA. И не только браузеры, еще клиентский код. Тот же curl.

См https://security.stackexchange.com/questions/158997/how-does-my-browser-inherently-trust-a-ca

В твоем случае, ты создаешь собственную PKI Infrastructure. Я использую для внутренних CA Vault PKI. Но можно при помощи openssl, конкретный лучший тьюториал я не подскажу.

Первый попавшийся: https://pki-tutorial.readthedocs.io/en/latest/

После этого, чтобы у пользователей не ругался браузер или клиентская часть, твой созданный CA нужно добавить в trusted root CA

https://en.wikipedia.org/wiki/Chain_of_trust
https://www.google.com/search?q=windows+add+trusted+root+ca
https://www.google.com/search?q=Linux+add+trusted+root+ca

Т.е.,
— или свой CA и пользователи должны добавлять CA.
— Или DNS + сертификат внутри глобальной CA Trust Chain — "настоящий" сертификат. Для настоящего поддомена, типа <some-user/prefix>.basil2.com

Cертификат еще обновлять нужно. Let's Encrypt — 3 месяца.

Кстати, раздачу сертификатов для второго случая можно через свой web сервис сделать, как часть платной подписки. Ну и, локальные DNS + Cert раздавать через его. Т.е., пользователь дает тебе внутренний IP (192.168. ....). А ты добавляешь Public DNS record сам (john123.basil2.com => 192.168...), генерируешь сертификат через Let'sEncrypt, с DNS Challenge, и отдаешь пользователю

При этом наверняка будет еще проблема с DNS rebinding attack, многие раутеры по умолчанию будут препятствовать Public DNS Record -> Private IP

Концептуально, весь этот гемор потому, что случай у тебя нестандартный: "инфраструктура на стороне пользователя". У классического, SaaS, инфраструктура с другой стороны.
Это и игры, и всякие домашние системы видеонаблюдения и прочие жирные потоки, которые не должны пересекать раутер
Отредактировано 05.04.2021 15:07 Vetal_ca . Предыдущая версия . Еще …
Отредактировано 05.04.2021 15:00 Vetal_ca . Предыдущая версия .
Отредактировано 05.04.2021 14:43 Vetal_ca . Предыдущая версия .
Re[8]: Как странице в браузере найти сервер в локальной сети
От: Basil2 Россия https://starostin.msk.ru
Дата: 07.04.21 09:35
Оценка:
Здравствуйте, Vetal_ca, Вы писали:

V_>Можно, конечно. Локально можно крутить весь свой интернет с DNS, подсетями и TLS

V_>В твоем случае, ты создаешь собственную PKI Infrastructure. Я использую для внутренних CA Vault PKI. Но можно при помощи openssl, конкретный лучший тьюториал я не подскажу.

Не могу понять зачем так сложно? В Windows 10 есть встроенный mDNS. Более того, компьютер пользователя уже автоматически зарегистрирован в сети под именем CompName.local. Все, что мне требуется, это зарегистрировать компьютер еще под одним именем (чтобы у всех пользолвателей был единый адрес, и на него получить сертификат). И все для этого в винде уже есть.

Вопрос — как? В параметрах mDSN сервиса dns-sd я вижу только регистрацию сервиса, а мне нужно не сервис, а сам ip зарегистрировать.
Проект Minimap Zoom — маппим кусочек экрана компа на планшет.
Проект Levelbuddy — играем в WoW за двух персонажей одновременно.
Re[9]: Как странице в браузере найти сервер в локальной сети
От: Vetal_ca Канада http://vetal.ca
Дата: 07.04.21 15:30
Оценка: 3 (1)
Здравствуйте, Basil2, Вы писали:

B>Здравствуйте, Vetal_ca, Вы писали:


V_>>Можно, конечно. Локально можно крутить весь свой интернет с DNS, подсетями и TLS

V_>>В твоем случае, ты создаешь собственную PKI Infrastructure. Я использую для внутренних CA Vault PKI. Но можно при помощи openssl, конкретный лучший тьюториал я не подскажу.

B>Не могу понять зачем так сложно? В Windows 10 есть встроенный mDNS. Более того, компьютер пользователя уже автоматически зарегистрирован в сети под именем CompName.local. Все, что мне требуется, это зарегистрировать компьютер еще под одним именем (чтобы у всех пользолвателей был единый адрес, и на него получить сертификат). И все для этого в винде уже есть.


B>Вопрос — как? В параметрах mDSN сервиса dns-sd я вижу только регистрацию сервиса, а мне нужно не сервис, а сам ip зарегистрировать.


Компьютеры в сети получают адрес DNS сервера. По DHCP.

Обычно это DNS на internet gateway, IG (A.K.A. раутер). Windows компьютер получает свой IP, отдает свой host name и DNS на раутере знает про эту связку, win-host-name:IP.

Есть еще варианты с разными autodiscovery, но я с в них не очень, может, кто другой подскажет. Например, для затравки, https://en.wikipedia.org/wiki/Bonjour_(software)

Компьютеры на локальной сети не ходят на mDNS конкретного Win PC. Этот DNS сервис — внутренний для данной рабочей станции. Если туда добавить DNS records, они будут исключительно внутри этого компьютера

Или, например, WinServer держит полноценный DNS сервер. И остальные компьютеры через DHCP получают адрес этого сервера. Тогда это работает. Можешь, например, поставить Unbound DNS на конкретный компьютер. Прописать там свой хост со всеми альтернативными именами. И настроить сеть так, что другие компьютеры ходят на этот DNS.

Правильнее же, не привязываться к локальному *.local, а к частному домену. Например, *.lan.basil2.com. Дальше, ставишь DNS сервер на этот PC для обслуживания исключительно *.lan.basil2.com. И в публичный DNS добавляешь NS record, ссылающийся на этот DNS сервер, так что все запросы к *.lan.basil2.com будут идти через этот сервер, из-за публичной NS record. Так оно будет работать, даже если на других компьютерах прописаны вручную публичные DNS сервера, типа 1.1.1.1 или 8.8.8.8

Это все частные варианты DNS извращений, чтобы понять, почему mDNS не работает для других компьютеров

----------------------------

По PKI. *.local не принадлежит тебе. Поэтому все что в этой TLD не может иметь публичной trust chain. Так что браузеры или клиентский код ей не доверяют. "Зеленый замок", из интернета, может быть только для твоих частных сервисов, по *.basil2.com, при условии что ты можешь менять DNS records под basil2.com. Как доказательство владения. И DNS Challenge в Let's Encrypt как раз так и работает.

Ты не можешь добавить в интернет твои DNS Records для *.local. Поэтому не можешь получить публичный сертификат.

Самоподписанный, да, можно, но тогда самосозданный CA должен быть занесен во все клиенты как доверенный. Геморно, да. Но, тоже, есть варианты. Если клиент — не GUI, то можно запаковать киентское приложение в Image (Docker) с CA, включающем твой CA внутри этого image/контейнера. Возможно, как-то поставлять клиента с рядом лежащим CA roots, как поставляется браузер с предустановленными доверенными корневыми сертификатами. Но, это уже другой доктор
Отредактировано 07.04.2021 15:33 Vetal_ca . Предыдущая версия .
Re[9]: Как странице в браузере найти сервер в локальной сети
От: bnk СССР http://unmanagedvisio.com/
Дата: 07.04.21 16:23
Оценка: 4 (1)
Здравствуйте, Basil2, Вы писали:

B>Вопрос — как? В параметрах mDSN сервиса dns-sd я вижу только регистрацию сервиса, а мне нужно не сервис, а сам ip зарегистрировать.


На твой DNS-сервер установленный на локальном компьютере телефон ходить не будет, а к роутеру (который у пользователя, и на котором надо бы изменить DNS) у тебя доступа нет.
Выше vsb привел по виду отличный вариант всё сделать автоматически, "зарегистрировать" новый домен (cname) и автоматически выдать на него не-самоподписанный сертификат через letsencrpypt и cloudflare api.
Только по виду это чертова куча работы. Вариант со сканированием бар-кода телефоном с экрана компьютера выглядит на порядок менее трудозатратным, imho.
Отредактировано 07.04.2021 16:31 bnk . Предыдущая версия .
Re[8]: Как странице в браузере найти сервер в локальной сети
От: bnk СССР http://unmanagedvisio.com/
Дата: 07.04.21 16:29
Оценка:
Здравствуйте, Vetal_ca, Вы писали:

V_>Кстати, раздачу сертификатов для второго случая можно через свой web сервис сделать, как часть платной подписки. Ну и, локальные DNS + Cert раздавать через его. Т.е., пользователь дает тебе внутренний IP (192.168. ....). А ты добавляешь Public DNS record сам (john123.basil2.com => 192.168...), генерируешь сертификат через Let'sEncrypt, с DNS Challenge, и отдаешь пользователю


Тут же штука именно в том, чтобы не просить НИЧЕГО от пользователя (если пользователь "дает внутренний IP" то задача теряет смысл, потому как весь огород стотся ради этого), все должно происходить в полностью автоматическом режиме, насколько я понимаю.
Отредактировано 07.04.2021 16:34 bnk . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.