Дзвони мені

written on Saturday, October 27, 2012

Бібліотека re продовжує радувати загадками. Кривий фреймінг я подолав у попередній серії — пакети перестали застрягати в буфері, але дзвінок так нормально і не встановився.

Відбувається наступне безчинство:

ACK на пряму адресу, вже не використовуючи проксю

При цьому ніяких натів між абонентами немає, вони взагалі живуть на одній машині. Якщо дзвонити не через проксю, а одразу лізти напряму — все спрацьовує, ACK доходить, обидві сторони вважають, що з'єднання встановилося.

Хтось тут бреше

Дзвінок через проксю — це такий же дзвінок, як напряму, але через проксю. Прокся дані не підмінює, тільки форвардить. Третя сторона і тупе мережеве обладнання втрутитися не можуть, бо трафік закруглений у SSL.

Звіряти пакети на вході проксі і на виході я не став, а вирішив одразу з'ясувати, куди намагається слати ACK абонент, який дзвонить. Начебто нічого підозрілого — коннект на локальну адресу, якийсь рендомний порт.

Починаю думати на SSL — може, сертифікат кудись не туди? Підвищую дебаг, але ніякої ругані в логах не бачу, більше того, на стороні, якій дзвонять, навіть не спрацьовує accept.

Починаю уважно дивитися на номер порту, в який лізе коннектом сторона, яка дзвонить. Порт існує, але раптом виявляється у стані ESTABLISHED, а не LISTEN, причому іншою стороною цього з'єднання виявляється прокся, а в стані LISTEN знаходиться зовсім інший сокет.

WTF?

Починаю розбиратися, звідки взявся цей номер. Оскільки він рендомний, сторона, яка дзвонить, його сама не придумала, а отримала від сторони, яка відповідає, яка висить на цьому порту до проксі. Чому замість адреси порту, на якому очікуються коннекти, шлеться якась ліва фігня?

Лізу дивитися, як формується хедер Contact, і знаходжу феєричне гівно. Якщо спростити, то хедер формується так:

fmt("Contact: <sip:%s@%J%s>rn", sess->cuser, &msg->dst)

У msg->dst вказана адреса, на яку прийшло повідомлення, а повідомлення прийшло даунстримом по прямому коннекту від проксі — половинки дупи зімкнулися, і настала істина. Замість того, щоб бігати і з'ясовувати, яка у нас там у глобальному стейті була запитана адреса, автори вирішили схалявити. У випадку з UDP це б навіть спрацювало, бо там для всього використовується один сокет — і для отримання датаграм від інших абонентів, і для посилки повідомлень регістрару, і для отримання відповідей. Якщо на UDP-шний порт щось уже прийшло, то можна сміливо стверджувати, що будь-який інший пір теж може туди щось послати, і воно так само дійде.

Для TCP це не вірно — для прийому коннектів один порт, для зв'язку з кожним із пірів — інший.

Чому працювало напряму? А напряму весь час реюзалося одне встановлене з'єднання — по ньому ходила і встановлення зв'язку, і наступний ACK.

This entry was tagged