Connect() с соком unix-domain и полным отставанием

Когда заполнение прослушивания заполнено для сокетов UNIX-домена STREAM, connect(2) завершается сбой в большинстве систем с ECONNREFUSED. Было бы предпочтительнее вернуть EAGAIN.

Причиной является то, что очень полезно различать два случая мертвого сокета (node существует в файловой системе, но больше не прослушивается процесс) и случай полного отставания. Я столкнулся с этой проблемой при переносе некоторого программного обеспечения Linux, которое имеет некоторый код для очистки мертвых сокетов, но это уязвимость системы безопасности, если код может быть обманут при удалении сокетов путем их спама, чтобы заполнить их отставание.

Только Linux возвращает EAGAIN; AIX, Solaris и Darwin следуют поведению BSD (просто тестируются на каждом).

POSIX не отображает EAGAIN как возможный код возврата из connect() (ссылка), поэтому здесь может быть проблема с соблюдением.

Какой лучший способ заставить каждого изменить в соответствии с Linux? Я могу пойти и подать отчет об ошибке с Oracle, Apple, FreeBSD PR и бороться с ним в списках рассылки каждой организации. Или я должен приставать к кому-то в стандартном теле (группа Остина)? Является ли даже целесообразным попытаться заставить всех измениться здесь, хотя преимущество ясно?

Ответ 1

Пытаетесь ли вы изменить стандарт или изменить способ, которым разработчики внедрили connect(), я бы сказал, что с точки зрения программного обеспечения это не будет иметь никакого значения. ECONNREFUSED и EAGAIN должны рассматриваться как ECONNREFUSED EAGAIN.

Различение между этими двумя случаями может позволить вам написать более конкретное диагностическое сообщение на клиенте, но логика повторения должна быть одинаковой. Даже если слушатель в настоящее время не существует, он может в конечном итоге существовать, поэтому следует попытаться повторить попытку.

try_again:
    rc = connect(s, (void *)&addr, sizeof(addr));
    if (rc == 0) return connect_succeeded(s, &addr);
    switch (errno) {
    case EAGAIN:
    case ECONNREFUSED:
        if (should_try_again(retries++)) {
            goto try_again;
        }
        break;
    case EINTR:
        goto try_again;
    default:        
        break;
    }
    return connect_failed(s, errno);