written on Saturday, October 27, 2012
Библиотека re продолжает радовать загадками. Кривой фрейминг я поборол в предыдущей серии - пакеты перестали застревать в буфере, но звонок так нормально и не установился.
Происходит следующее безобразие:
При этом никаких натов между абонентами нету, они вообще живут на одной машине. Если звонить не через проксю, а сразу лезть напрямую - все срабатывает, ACK доходит, обе стороны считают, что соединение установилось.
Звонок через проксю - это такой же звонок, как напрямую, но через проксю. Прокся данные не подменяет, только форвардит. Третья сторона и тупое сетевое оборудование вмешаться не могут, потомучто трафик закруглен в SSL.
Сверять пакеты на входе прокси и на выходе я не стал, а решил сразу выяснить, куда пытается слать ACK вызывающий абонент. Вроде бы ничего подозрительного - коннект на локальный адрес, какой-то рендомный порт.
Начинаю думать на SSL - может сертификат куда-то не туда? Повышаю дебаг, но никакой ругани в логах не вижу, более того, на вызываемой стороне даже не срабатывает accept.
Начинаю внимательно смотреть на номер порта, в который ломится коннектом вызывающая сторона. Порт существует, но внезапно оказывается в состоянии ESTABLISHED, а не LISTEN, при чем второй стороной этого соединения оказывается прокся, а в состоянии LISTEN находится совершенно другой сокет.
Начинаю разбираться, откуда взялся этот номер. Поскольку он рандомный, то вызывающая сторона его не сама придумала, а получила от отвечающей, которая не этом порту висит к проксе. Почему вместо адреса порта, на котором ожидаются коннекты, шлется какая-то левая фигня?
Лезу смотреть, как же формируется хедер Contact, нахожу фееричный ад. Если упростить, то хедер формируется так:
fmt("Contact: <sip:%s@%J%s>\r\n", sess->cuser, &msg->dst)
В msg->dst указан адрес, на который пришло сообщение, а сообщение пришло даунстримом по прямому коннекту от прокси - половинки жопы сомкнулись и наступила истина. Вместо того, чтобы бегать и выяснять, какой у нас там в глобальной стейте был запрошен адрес, авторы решили схалявить. В случае UDP это бы даже сработало, потомучто там для всего используется один сокет - и для получения датаграмм от других абонентов, и для посылки сообщений регистару и для получения ответов. Если на UDP-шный порт что-то уже пришло, то можно смело утверждать, что любой другой пир может туда тоже что-то послать и оно так же дойдет.
Для TCP это не верно - для приема коннектов один порт, для связи с каждым из пиров - другой.
Почему работало напрямую? А напрямую вообще всю дорого реюзалось одно установленное соединение - по нему ходила и установка связи и послеюущий ACK.