Кратко

  • Replaces указывает один созданный INVITE SIP-диалог через Call-ID, to-tag и from-tag. Точное совпадение не аутентифицирует инициатора и не уполномочивает его вытеснить участника.
  • Получающий UA отдельно проверяет полномочия и возможность принять новый INVITE. Лишь после 2xx он посылает BYE подтверждённому старому диалогу или CANCEL подходящему early dialog; при ошибке допуска старый диалог остаётся неизменным.
  • Принятие REFER, происхождение Referred-By, результат INVITE с Replaces, завершение старого leg и фактическая непрерывность медиа, записи и тарификации — разные доказательства. Один статус не может заменить всю цепочку.

В операции перевода нет единственного момента истины

Оператор разговаривает с клиентом и открывает консультационный диалог со специалистом. После консультации оператор отправляет клиентскому устройству REFER. В Refer-To может быть закодирован Replaces, который описывает диалог оператора со специалистом. Клиентское устройство принимает REFER.

Но разговор клиента со специалистом ещё не начался. Устройство клиента должно создать новый INVITE. Этот INVITE должен попасть на нужный экземпляр UA специалиста. Replaces должен указать ровно один местный диалог. Целевой UA должен признать инициатора уполномоченным и затем принять новую сессию с её медиа, ключами и ресурсами.

Любой этап может завершиться отказом, не отрицая предыдущий факт. REFER принят, но пользовательская политика может не одобрить действие. Диалог найден, но инициатор не авторизован. Инициатор авторизован, но медиа несовместимы. Новый диалог принят, но завершение старого leg ещё надо наблюдать.

RFC 3891 не объявляет Replaces универсальной командой. Это явное предложение логически заменить один диалог новым. Окончательное локальное решение остаётся у UA, который хранит состояние и несёт последствие.

Почему замена приходит как новый INVITE

Replaces не переписывает существующий диалог. Заголовок находится в новом INVITE, а будущий диалог получает собственный новый Call-ID.

Так сохраняются обычные семантика и проверки INVITE. Явный заголовок показывает намерение, не заставляя стороны угадывать связь по обычным полям. Отдельный Call-ID не смешивает старую и новую идентичность. Если расширение не поддерживается, работающий диалог не должен измениться случайно.

Поддержка объявляется Supported: replaces. Если отправителю нужен явный отказ при отсутствии поддержки, он может добавить Require: replaces. В реестре параметров SIP IANA зарегистрированы и заголовок, и option tag.

Поддержка — это техническая способность, а не предварительная санкция. Один UA может разрешить замену между устройствами одного пользователя и запретить её между арендаторами или классами звонков.

REFER и Replaces также не тождественны. REFER просит получателя обратиться к ресурсу. Replaces объясняет цели нового INVITE, какой существующий диалог предлагается заменить. В переводе они часто сцеплены, но их решения и результаты раздельны.

Теги имеют смысл только с точки зрения получателя

Replaces содержит Call-ID и ровно по одному to-tag и from-tag. Получающий UAS сравнивает to-tag со своим local tag, а from-tag — со своим remote tag.

Порядок To и From в старом трассировочном сообщении не заменяет эту перспективу. При копировании полей легко получить правильные значения в обратных ролях, особенно после SBC, B2BUA или восстановления состояния.

Реестр errata RFC 3891 содержит проверенную редакционную поправку: в примере early dialog теги были переставлены. Ещё одна похожая редакционная запись имеет статус Held for Document Update. Нормативное правило не менялось; пример показывает, почему local/remote важнее визуального порядка.

Тройка выбирает только один диалог. RFC 3891 исключает несколько диалогов, весь вызов, всю transaction или цепочку proxy forking. Если исходный INVITE создал несколько ранних ветвей, Replaces не охватывает соседние. Если новый INVITE перенаправлен к другому набору Contacts, он не наследует старое дерево развилок.

Бизнес-система, напротив, может объединять под одной «карточкой звонка» legs клиента и оператора, консультацию, очередь, записи и тарификацию. Событие одного SIP-диалога не должно автоматически закрывать весь агрегат.

Идентификатор не содержит действие

Несколько Replaces в одном INVITE, Replaces в другом методе или конфликтующие call-control заголовки приводят к 400. Если совпало более одного диалога, UA действует так, будто совпадений нет.

RFC 3911 даёт полезный контраст. Join тоже находит диалог по Call-ID и двум tags, но предлагает добавить новый диалог в conversation space. Replaces предлагает завершить найденный и заменить его. Один объект допускает разные и даже противоположные операции.

Тройка отвечает «что?». Заголовок — «какое действие предлагается?». Аутентификация — «кто просит?». Авторизация — «имеет ли он право?». Допуск — «можно ли выполнить сейчас?». Последующая сигнализация и медиа — «что получилось?».

Нет совпадения или найден диалог, созданный не INVITE — ответ 481. Уже завершённый диалог следует отклонить 603, чтобы запоздавшая замена не прозвучала как новый обычный вызов.

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

Полномочие проверяется после совпадения

Когда найден активный диалог, RFC 3891 требует проверить, авторизован ли инициатор нового INVITE заменить его. Само совпадение этой проверки не выполняет.

В качестве возможных оснований названы аутентифицированная эквивалентность пользователю, которого заменяют, Referred-By от другого участника в потоке REFER и иная локальная политика. Для нового и старого диалогов политика может различаться.

Общие credentials могут законно обслуживать несколько устройств одной идентичности или делегированную роль. Чрезмерно широкие credentials дают сервисному аккаунту власть над вызовами других пользователей и tenants. Аутентификация доказывает контроль ключа или секрета, но не текущую волю человека, должностное право или договорный мандат.

RFC 3892 проводит ту же границу для Referred-By. Referee переносит сведения referrer к refer target и способен видеть или изменять их. Незащищённый URI может быть входом политики, но если от него зависит допуск или интерфейс пользователя, сведения подозрительны без действительного защищённого token.

Даже верный token защищает ограниченные утверждения о referral. Он не удостоверяет автоматически согласие на запись, полномочие в организации, финансовое поручение или право на регулируемую услугу. Это область явной локальной политики.

Знание диалога — условное доказательство

RFC 4538 определяет Target-Dialog для запросов, авторизация которых может зависеть от знания существующего диалога. Если тот создан через SIPS, identifiers непредсказуемы и доверие к исходному пути определено, знание тройки может подтверждать связь с участником или элементом пути.

Условия не декоративны. На незащищённом пути значения мог узнать наблюдатель. И даже при SIPS получатель вправе решить, что доказанная связь недостаточна для запрошенного действия.

Знание контекста имеет доказательную ценность, но не становится собственностью на контекст. Метод, идентичность, защищённость пути и локальный риск задают предел.

Так технически проявляется принцип Heng Lu: участие и осведомлённость могут быть доказательством, но не заменяют принципала, который вправе принять обязательное решение. В данном случае таким местом остаётся UA с диалогом.

Старый диалог — страховка от непринятой новой сессии

После положительной авторизации UAS пытается принять новый INVITE, перевести интерфейс и ресурсы, а затем закрыть найденный диалог. Попытка ещё может не удаться.

RFC 3891 называет невозможность обеспечить требуемые QoS или keying и несовместимые медиа. Возможны и другие обычные ошибки допуска. Тогда UAS возвращает соответствующий ответ и обязан оставить старый диалог неизменным.

Порядок сдерживает ущерб. Завершение при match превращает утёкшие identifiers в оружие. Завершение после аутентификации роняет разговор из-за технически неприемлемой оферты авторизованного лица. Только приём новой сессии создаёт рабочую замену.

Для confirmed dialog без early-only UAS сначала отвечает 2xx на новый INVITE, затем посылает BYE старому. Для early dialog, который этот UA сам инициировал, новый INVITE принимается, а старый INVITE получает CANCEL.

early-only ограничивает намерение ещё звонящей ветвью. Если диалог уже подтверждён, ответ 486. Если ранний диалог инициировал не принимающий UA, ответ 481 и состояние сохраняется: механизм одного диалога не способен воспроизвести чужой forking.

2xx убедительно доказывает приём нового диалога. Он не доказывает сам по себе доставку BYE, закрытие внутренних legs B2BUA, отсутствие соседних forks, продолжение RTP, переключение записи и прекращение старого биллинга.

REFER имеет собственное одобрение и собственный итог

RFC 3515 рекомендует UA, принимающему корректный REFER, получить одобрение пользователя — интерактивно или через настроенную policy. Лишь затем он обращается к Refer-To по обычным правилам указанного URI.

В attended transfer Refer-To часто содержит escaped Replaces. Transferee создаёт новый INVITE, а transfer target самостоятельно выполняет match, authorization и admission. Принявший REFER не может распорядиться решением target.

Состояние transaction REFER и результат referenced action разделены. NOTIFY сообщает прогресс и финал. RFC 7647 заменяет старый 202 на 200 для принятого REFER, уточняет GRUU и требует Target-Dialog, когда надо указать существующий диалог. Но 200 остаётся ответом на REFER, а не сертификатом всей передачи.

RFC 5589 прямо говорит: успешная REFER transaction не завершает сессию между transferor и transferee. Нужен последующий BYE. При busy, no answer или отказе Replaces потоки восстановления снимают удержание и возвращают известную сессию.

Поэтому approval REFER, response REFER, результат нового INVITE, завершение старого диалога и пользовательский результат должны храниться отдельно.

Хронология, которую можно восстановить

Храните Call-ID нового INVITE отдельно от Replaces-тройки. Добавьте local/remote tags с точки зрения получателя, состояние при match, Contact, route, fork branch и early-only.

Зафиксируйте источник решения: аутентифицированная identity, Referred-By, проверка token, версия policy, правило пользователя или tenant, источник approval и причина. Один authorized=true не объясняет власть.

Для admission сохраняйте response code, fingerprint SDP, codecs, directions, keying, QoS, target instance и ACK. После 2xx связывайте BYE/CANCEL старого диалога, ответы, retransmissions и последнюю наблюдаемую сигнализацию и медиа.

REFER входит в ту же timeline с Call-ID, CSeq, Target-Dialog, fingerprint Refer-To, approval, response и NOTIFY, но не перезаписывает результат INVITE. Уведомлённый успех сверяется с реальными legs.

Тесты должны включать обратные tags, неоднозначный match, завершённый dialog, гонку early-only, отсутствие поддержки, отказ authorization после правильного match, недействительный token, ошибки media/key/QoS, busy/no-answer, потерю BYE после 2xx, остаточные forks, retargeting, потерю состояния в failover и невосстановимые B2BUA mappings.

Итоговая запись отдельно отвечает: что попросили, какой диалог назвали, кто был уполномочен, что приняли, что завершили и какую услугу получил человек.

Источники