У меня есть разветвленный HTTP-прокси, реализованный на моем Ubuntu 14.04 x86_64 со следующей схемой (я сообщаю основной код и псевдокод только для того, чтобы показать концепцию):
socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
;bind(socketClient,(struct sockaddr*)&addr, sizeof(addr))
;listen(socketClient, 50)
;newSocket = accept(socketClient, (struct sockaddr*)&cliAddr, sizeof(cliAddr))
;- получить запрос от клиента, проанализировать его, чтобы разрешить запрошенное имя хоста в IP-адресе;
fork()
, открыть соединение с удаленным сервером и обработать запрос;- дочерний процесс: если это запрос
GET
, отправьте исходный запрос на сервер, а пока сервер отправляет данные, отправьте данные с сервера клиенту; - дочерний процесс: иначе, если это запрос
CONNECT
, отправить строку200 ok
клиенту и опросить как дескриптор сокета клиента, так и дескриптор сокета сервера с помощьюselect()
; если я читаю данные из серверного сокета, отправляю эти данные клиенту; иначе, если я прочитаю данные из клиентского сокета, отправлю эти данные на сервер.
Хорошо, что этот прокси работает, плохо, что теперь я должен собирать статистику; это плохо, потому что я работаю на уровне, на котором я не могу получить интересующие меня данные. Меня не волнует полезная нагрузка, мне просто нужно проверить в заголовках IP и TCP нужные мне флаги.
Например, меня интересует:
- отслеживание соединения;
- количество отправленных и полученных пакетов.
Что касается первого, я бы проверил в заголовке TCP флаг SYN, SYN/ACK и затем последний ACK; что касается второго, я бы просто добавлял +1 к моему счетчику каждый раз, когда char buffer[1500]
заполняется данными, когда я send()
или recv()
полный пакет.
Я понял, что это неправильно: SOCK_STREAM
не имеет понятия пакета, это просто непрерывный поток байтов! char buffer[1500]
, который я использую в пунктах 7 и 8, имеет полезную статистику, я могу установить его емкость на 4096 байт, но я не могу отслеживать отправленные или полученные TCP-пакеты, потому что TCP имеет сегменты , а не пакеты.
Я также не смог разобрать char buffer[]
в поисках флага SYN в заголовке TCP, потому что заголовки IP и TCP удалены из заголовка (из-за уровня, над которым я работаю, указанного с флагом IPPROTO_TCP
) и, если я правильно понял, char buffer[]
содержит только полезную нагрузку, бесполезную для меня.
Так что, если я работаю на слишком высоком уровне, я должен пойти ниже: однажды я видел простой анализатор сокетов raw
, в котором unsigned char buffer[65535]
преобразовывался в struct ethhdr, iphdt, tcphdr
, и он мог видеть все флаги все заголовки, вся статистика, которая меня интересует!
После радости разочарование: поскольку raw
сокеты работают на низком уровне, в них нет некоторых важных для моего прокси понятий; raw
сокеты не могут bind
, listen
и accept
; мой прокси прослушивает фиксированный порт, но raw
сокеты не знают, что такое порт, он принадлежит уровню TCP, и они bind
к указанному интерфейсу с setsockopt
.
Итак, если бы я socket(PF_INET, SOCK_RAW, ntohs(ETH_P_ALL))
, я мог бы разобрать буфер, где я recv()
и send()
в 0,7 и 0,8, но я должен использовать recvfrom()
и sendto()
... но все это звучит довольно грязно, и это включает в себя хороший рефакторинг моего кода.
Как я могу сохранить неповрежденной структуру моего прокси (bind, listen, accept
на фиксированный порт и интерфейс) и расширить свое поле зрения для заголовков IP и TCP?