ECONNREFUSED на recvfrom()

sergey_m

http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=1375
Бля! linux++ Это в современных ядрах так же?

Marinavo_0507

Так уж и быть, ради поддержки флейма, цитата из udp(7):
All fatal errors will be passed to the user as an error return even
when the socket is not connected. This includes asynchronous errors
received from the network. You may get an error for an earlier packet
that was sent on the same socket. This behaviour differs from many
other BSD socket implementations which don't pass any errors unless the
socket is connected. Linux's behaviour is mandated by RFC1122.
For compatibility with legacy code it is possible to set the SO_BSDCOM-
PAT SOL_SOCKET option to receive remote errors only when the socket has
been connected (except for EPROTO and EMSGSIZE). It is better to fix
the code to handle errors properly than to enable this option. Locally
generated errors are always passed.

sergey_m

Понимаешь ли тут три спорных момента.
1. Хорошо, должен донести ошибку до приложения. Только вот нельзя возвращать ошибку одной функции в ответ на вызов другой. А если я никогда не вызову вторую функцию, то я не получу сообщения об ошибке. Стало быть стандарт нарушен, как ни старались.
2. Исторически так сложилось, что RFC описывает протоколы взаимодействия разных независимых систем. А интерфейсы внутри системы описывает POSIX. А в нём нет такого, что бы recvfrom возвращал ECONNREFUSED. Поэтому лучше соблюсти POSIX и наплевать на RFC.
3. Можно вообразить себе такую конфигурацию сети, когда удаленный пир будет отвергать присылаемые ему UDP пакеты, но сам будет посылать пакеты с этого же порта. Тогда такой подход просто не позволит получать данные от такого хоста.

Marinavo_0507

> Хорошо, должен донести ошибку до приложения. Только вот нельзя возвращать ошибку одной функции в ответ на вызов другой.
Ошибка асинхронная.
В момент sendto она прийти не может.
Следовательно, её нужно донести позже.
> Исторически так сложилось, что RFC описывает протоколы взаимодействия разных независимых систем.
Исторически сложилось, что в RFC описано также Socket API для работы с TCP/IP.
Можешь почитать и убедиться, есть также модификации для IPv6.
> А в нём нет такого, что бы recvfrom возвращал ECONNREFUSED.
А какую ошибку возвращает FreeBSD?
Если сокет законнекчен.
> Тогда такой подход просто не позволит получать данные от такого хоста.
Враньё.
Вызываешь recvfrom один раз - получаешь ошибку.
Вызываешь следующий раз - получаешь пакет.
Пакеты из входной очереди никуда не пропадают.

sergey_m

Ошибка асинхронная.
В момент sendto она прийти не может.
Следовательно, её нужно донести позже.
При следующем вызове sendto будет правильно. А Linux её вернет только на recvfrom или на любую другую операцию с данным сокетом?
Исторически сложилось, что в RFC описано также Socket API для работы с TCP/IP.
Но стандартом всё таки является POSIX. А "библией" - Стивенс, который на него ссылается постоянно.
А какую ошибку возвращает FreeBSD? Если сокет законнекчен.
Не возвращаются ошибки от другой функции на recvfrom.
Вызываешь recvfrom один раз - получаешь ошибку.
Вызываешь следующий раз - получаешь пакет.
Пакеты из входной очереди никуда не пропадают.
Да. Если окружить #ifdef __Linux__, тот блок где некоторые ошибки игнорируются. С помощью ifdef можно с любым косяком справиться.

Marinavo_0507

> При следующем вызове sendto будет правильно.
А если не будет его? Или будет, но неизвестно когда?
> А Linux её вернет только на recvfrom или на любую другую операцию с данным сокетом?
Не уверен насчёт любой, но на некоторые вернёт.
Я не разбирался.
>
В любом случае, это общеизвестный факт теперь, и код никто править не будет.
У Socket API есть более мерзкие косяки.
Знаешь ли ты например, зачем bind и ntpd получают список IP-адресов, и делают на каждый адрес по отдельному сокету?

sergey_m

А если не будет его? Или будет, но неизвестно когда?
Я задавал этот же вопрос про recvfrom. Как видишь, донести информацию до приложение невозможно. Поэтому и стандарт соблюсти невозможно. Поэтому не нужно вводить кривости для того, что бы приблизиться к недостижимому кривому стандарту.
В любом случае, это общеизвестный факт теперь, и код никто править не будет.
Ну обратным порядком посылки IP фрагментов тоже гордились некоторое время, а потом исправили.
Знаешь ли ты например, зачем bind и ntpd получают список IP-адресов, и делают на каждый адрес по отдельному сокету?
Не знаю, интересно зачем. Меня такое поведение bind бесило, и я всегда специально ему указываю адрес на котором слушать.

Marinavo_0507

> Не знаю, интересно зачем.
Ну так слушай сюда.
Ответить на UDP-запрос надо с того же адреса, на который он пришёл, иначе клиент ответ проигнорирует.
Чтобы это сделать, сервер должен
1) узнать поле dst ip у пришедшего запроса
2) сделать сокету bind на этот адрес
Если этого не делать, то src ip для ответа выберет система, и это не обязательно будет тем адресом, который нужен.
Основная проблема - с п.1
Обычное Socket API не предусматривает передачу такой информации приложению.
Поэтому приложение открывает по сокету на адрес, и посылает ответ в тот же сокет, с которого пришёл запрос.
Кажется, Advanced Socket API такое дело предусматривает, но я могу ошибаться.

С п.2 тоже проблема.
Если сделать сокету bind , то новый запрос на него может не прийти, если он будет направлен на другой ip-адрес.

sergey_m

Логично. Если бы хорошо подумал, то сам мог бы догадаться.
Оставить комментарий
Имя или ник:
Комментарий: