Свойства selected и selectedIndex

Свойства text и value объекта Option

Свойство text представляет собой отображаемый в меню текст, который соответствует альтернативе. В HTML-коде он расположен между тэгами. Свойство value содержит значение атрибута VALUE тэга

Тогда значение свойства text у соответствующего объекта будет равно "Вариант первый ", а значение свойства value равно " n1 ".

Возникает вопрос, зачем нужны два свойства? Дело в том, что на сервер передается значение value выбранного варианта. В случае же, когда атрибутVALUE у контейнера

Свойство selectedIndex объекта Select возвращает номер выбранного варианта (нумерация начинается с нуля).

Вариант: Выбрали индекс:

Обратите внимание, что в обработчике события onChange мы ссылаемся на второй элемент формы. На данный момент он не определен, но событие произойдет только тогда, когда мы будем выбирать вариант - к этому моменту поле уже будет определено.

Если список вариантов задан как
Выбраны позиции:

Пример 5.5. Обработчик onChange при выборе множественных вариантов (html, txt)

Обратите внимание на то, что событие Change происходит тогда, когда пользователь выбирает или отменяет какой-либо вариант. Исключение составляет тот случай, когда варианты при выборе последовательно отмечаются (нажатие кнопки мыши на одном элементе, ведение мыши до конечного элемента, отпускание кнопки мыши). В этом случае событие происходит в тот момент, когда пользователь отпускает кнопку мыши, и все отмеченные альтернативы становятся выбранными.

SOCKS сокращение от «SOCKetS» (сокеты, гнёзда) разработан для того, чтобы дать возможность приложениям клиент/сервер в доменах TCP и UDP удобно и безопасно пользоваться услугами межсетевого экрана. Он дает пользователям возможность преодолевать межсетевой экран организации и получать доступ к ресурсам, расположенным в сети Интернет. SOCKS является “посредником уровня приложений”: он взаимодействует с общими сетевыми средствами (например, Telnet и браузер Netscape) и с помощью центрального сервера (прокси-сервера) от имени вашего компьютера устанавливает связь с другими центральными компьютерами.


SOCKS был разработан много лет назад Дейвом Кобласом из компании SGI, и сегодня этот код можно бесплатно получить через Интернет. С момента первого выпуска этот код пережил несколько крупных модификаций, но каждая из них распространялась совершенно бесплатно. SOCKS версия 4 решает вопрос незащищенного пересечения межсетевых экранов приложениями клиент/сервер, основанными на протоколе TCP, включая Telnet, FTP и популярные информационные протоколы, такие как HTTP, Wide Area Information Server (WAIS) и GOPHER. SOCKS версия 5, RFC 1928, является дальнейшим расширением четвертой версии SOCKS. Он включает в себя UDP, расширяет общую рамочную структуру, придавая ей возможность использования мощных обобщенных схем аутентификации, и расширяет систему адресации, включая в нее имя домена и адреса IP v6.

В настоящее время предлагается создать механизм управления входящими и исходящими многоадресными сообщениями IP, которые проходят через межсетевой экран. Это достигается определением расширений для существующего протокола SOCKS V.5, что создает основу для аутентифицированного перехода межсетевого экрана одноадресным пользовательским графиком TCP и UDP. Однако ввиду того, что поддержка UDP в текущей версии SOCKS V.5 имеет проблемы с масштабируемостью и другие недостатки (и их обязательно нужно разрешить, прежде чем переходить к многоадресной передаче), расширения определяются двояко: как базовые расширения UDP и как многоадресные расширения UDP.


Функционирование SOCKS заключается в замене стандартных сетевых системных вызовов в приложении их специальными версиями. Эти новые системные вызовы устанавливают связь с прокси-сервером SOCKS (который конфигурируется самим пользователем в приложении или системным файлом конфигурации), подключаясь к хорошо известному порту (обычно это порт 1080/ТСР). После установления связи с сервером SOCKS приложение отправляет серверу имя машины и номер порта, к которому хочет подключиться пользователь. Сервер SOCKS реально устанавливает связь с удаленным центральным компьютером, а затем прозрачно передает данные между приложением и удаленной машиной. При этом пользователь даже не подозревает, что в канале связи присутствует сервер SOCKS.


Трудность с использованием SOCKS состоит в том, что кто-то должен проводить работу по замене сетевых системных вызовов версиями SOCKS (этот процесс обычно называется “SOCKS-ификацией” приложения). К счастью, большинство обычных сетевых приложений (Telnet, FTP, finger, whois) уже SOCKS-ифицированы, и многие производители включают поддержку SOCKS в свои коммерческие приложения. Кроме того, SOCKS V.5 включает эти процедуры в свою общую библиотеку: на некоторых системах (например, на машинах Solaris) можно автоматически SOCKS-ифицировать приложение, поставив общую библиотеку SOCKS перед “shared libc” в вашей строке поиска библиотек (переменная среды LD__LIBRARY_PATH в системах Solaris).

Какое то время тому назад захотелось мне попробовать реализовать прокси сервер для собственных нужд, да такой, который можно было бы в дальнейшем использовать, а также, чтобы размер его был минимален. Естественным вариантом для меня стала реализация с использованием ассемблера. Программка получилась небольшая, удобная и в дальнейшем я очень часто ей пользовался. А вот теперь, по прошествии лет, хотелось бы показать простейшую реализацию одного протокола, SOCKS4. Данный протокол был создан для того, чтобы клиенты, находящиеся в локальной сети за межсетевым экраном могли обращаться во внешнюю сеть. В то же время запросы клиентов в таком случае есть возможность контролировать:) Самым первым, что нужно, при реализации – прочитать документацию с описанием данного протокола, так как мы хотим, чтобы наш протокол понимался стандартными программами, без “подтачивания напильником”. Итак, документация:

Теперь, вооружившись описанием, приступим. Работа прокси сервера состоит в том, чтобы принять от клиента запрос в определенном формате, сформировать сокет и подключить его по адресу запрошенному клиентом, после чего обеспечить обмен данными между двумя сокетами, до их закрытия со стороны сервера, либо клиента. Приступим к реализации.

Макросы и структуры данных, используемые в программе

Сформируем включаемый файл, includes.inc. В данный файл поместим стандартные, при написании Windows программ макросы + структуры для работы с SOCKS4. Здесь я не буду приводить все макросы, я приведу лишь описание и функционал, необходимый для решения основной задачи, все остальное вы найдете в приложенном файле с исходными кодами.
; SOCKS4 – Структура, используемая клиентом, при запросе соединения; на указанный сервер(DSTIP)/порт(DSTPORT) CONNECT_SOCK4 Struc VN Db ? CD Db ? DSTPORT Dw ? DSTIP Dd ? NULL Db ? CONNECT_SOCK4 Ends ; SOCKS4 - ответ прокси-сервера о соединении. RESPONSE_SOCK4 Struc VN Db ? CD Db ? DSTPORT Dw ? DSTIP Dd ? RESPONSE_SOCK4 Ends

По большому счету, структуры CONNECT_SOCK4 и RESPONSE_SOCK4 ничем не отличаются, так как мы реализуем протокол без авторизации. Но я решил все таки оставить их отдельно, чтобы в дальнейшем можно было легко изменить их, для доработки. В самих структурах, в переменной VN – указывается версия протокола, в нашем случае, здесь всегда должно быть 4, в случае с SOCKS5 в данной переменной находится 5 (протокол в принципе похож). Переменная CD используется для возврата клиенту результата запроса прокси сервера к запрошенному клиентом адресу (90 – соединение успешно / 91 – соединение не удалось).
У нас в программе по факту три этапа.
* Первый, инициализируем сокет, слушаем сокет на предмет наличия клиентских запросов, создаем поток обработки.
* Второй этап – анализ запроса клиента, попытка создания и соединения сокета с запрошенным клиентом сервером.
* И окончательный, третий этап – пересылка данных между сокетом клиента и сокетом созданным и соединенным нами с запрошенным адресом.

Реализация первого этапа, инициализация программы:

; Основная процедура, является стартовой для программы WinMain Proc LOCAL ThreadId, hServSock:DWORD LOCAL hostname :BYTE LOCAL _wsa:WSADATA LOCAL _our:sockaddr_in ; Запуск библиотеки работы с сокетами, мы используем функционал версии 1.1, ; запросим его как минимальный invoke WSAStartup, 0101h, ADDR _wsa .if eax == 0 ; Берем свой адрес, подготавливаем структуру, для инициализации серверного сокета invoke gethostname, ADDR hostname, 256 invoke gethostbyname, ADDR hostname .if eax == 0 invoke inet_addr, ADDR hostname .else mov eax, mov eax, mov eax, .endif mov _our.sin_addr, eax invoke inet_ntoa, eax mov _our.sin_family, AF_INET mov _our.sin_addr.S_un.S_addr, INADDR_ANY xor eax, eax ; Вносим порт, на котором хотим слушать входящие сообщения mov ax, SOCKS_PORT invoke htons, eax mov _our.sin_port, ax invoke socket, AF_INET, SOCK_STREAM, 0 .if eax != INVALID_SOCKET ; Сохраняем созданный серверный сокет mov hServSock, eax ; Привязываем серверный сокет к нашему адресу и необходимому порту invoke bind, hServSock, ADDR _our, SIZEOF sockaddr_in .if eax != SOCKET_ERROR @@: ; Инициируем сокет на ожидание invoke listen, hServSock, SOMAXCONN .repeat ; Пришел клиент, получаем сокет с пришедшим клиентом invoke accept, hServSock, NULL, NULL .until eax != INVALID_SOCKET ; Создаем поток, в котором будет обрабатываться текущий клиент xchg eax, ebx invoke CreateThread, NULL, NULL, ADDR socketThread, ebx, NULL, ADDR ThreadId ; Уходим на ожидание клиентов jmp @B .endif .endif invoke closesocket, hServSock .endif invoke ExitProcess, 0 WinMain Endp
Это первая наша процедура, я постарался максимально прокомментировать код, чтобы вы могли разобраться, но если все же что-то не понятно – обращайтесь либо ко мне, либо к MSDN. В принципе весь код написан с использованием синтаксиса MASM и WinAPI. Итогом работы приведенной функции должен быть работающий сокет на одном из сетевых адресов вашей машины (локальный адрес, либо внешний адрес если у вас реальный IP) + по соединению клиента функция создает отдельный поток, используемый для работы с пришедшим клиентом. Теперь пойдем дальше...

Второй этап, анализ запроса клиента

На втором этапе все, что необходимо сделать, это принять структуру CONNECT_SOCK4, создать сокет, попытаться соединить его и отправить клиенту ответ. Реализация:

SocketThread Proc sock:DWORD LOCAL lpMem, _csock, ThreadId, dAmount:DWORD LOCAL Remote:sockaddr_in LOCAL wrFds, rdFds:fd_set LOCAL hResp:RESPONSE_SOCK4 ; Готовимся к чтению данных из сокета invoke FdZero, ADDR rdFds invoke FdSet, sock, ADDR rdFds invoke select, NULL, ADDR rdFds, NULL, NULL, NULL ; Получаем размер ожидающих чтения данных invoke ioctlsocket, sock, FIONREAD, ADDR dAmount ; Резервируем память под данные mov lpMem, @Result(LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, dAmount) ; Читаем данные запроса из сокета invoke recv, sock, lpMem, dAmount, 0 ; Запрос пришел lea edi, hResp mov esi, lpMem ; В Esi лежит пользовательский запрос. Мы обрабатываем (здесь) только версию SOCKS4, ; SOCKS5 можно в принципе здесь же обработать, но это позже... Assume Esi: Ptr CONNECT_SOCK4 Assume Edi: Ptr RESPONSE_SOCK4 .if .VN == 4 ; Реализация протокола СОКС 4 .if .CD == 1 invoke socket, AF_INET, SOCK_STREAM, 0 .if eax != INVALID_SOCKET mov _csock, eax ; Берем данные удаленного хоста, с которым хочет соединиться клиент mov Remote.sin_family, AF_INET mov ax, .DSTPORT mov Remote.sin_port, ax mov eax, .DSTIP mov Remote.sin_addr, eax mov cx, .DSTPORT mov edx, .DSTIP ; В Edi лежит ответ пользователю mov .VN, 0 mov .DSTPORT, cx mov .DSTIP, edx ; Пытаемся соединиться с удаленным сервером invoke connect, _csock, ADDR Remote, SIZEOF Remote .if !eax ; Готовим ответ, что мы соединились mov .CD, 90 ; Отправляем клиенту ответ, содержащий результат попытки соединения invoke send, sock, ADDR hResp, SIZEOF RESPONSE_SOCK4, 0 ; Формируем структуру с информацией о серверном и; соединенном клиентском сокетах; - под серверным здесь подразумеваю сокет соединенный с клиентом, ; приславшим запрос; - под клиентским подразумеваю сокет соединенный с сервером, ; данные которого запросил клиент mov ebx, @Result(LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, SIZEOF THREAD_DATA) Assume Ebx: Ptr THREAD_DATA mov eax, _csock mov .Server, eax mov eax, sock mov .Client, eax Assume Ebx: Nothing ; Запускаем поток обработки сокетов (читающий из клиентского и; передающий в серверный сокет) invoke CreateThread, NULL, NULL, ADDR ClientSock, ebx, NULL, ADDR ThreadId .else ; Если соединение не получилось - закрываем клиентский сокет invoke closesocket, _csock ; Говорим, что произошла ошибка соединения mov , 91 ; Отправляем клиенту ответ, содержащий результат попытки соединения invoke send, sock, ADDR hResp, SIZEOF RESPONSE_SOCK4, 0 .endif .endif .endif .endif Assume Edi: Nothing Assume Esi: Nothing ; Высвобождаем память, выделенную под запрос invoke LocalFree, lpMem ret socketThread Endp
Результат выполнения данной процедуры – соединенный сокет, а также созданный поток, реализующий обмен данными между двумя сокетами. Все просто. Стоит только уточнить, здесь используется несколько моментов по адресации внутри структур, которые введены в MASM для облегчения жизни программиста. Первый момент, макрос “Assume”.
Строчка Assume Esi: Ptr CONNECT_SOCK4 говорит компилятору о том, что в данном регистре(Esi) находится адрес структуры CONNECT_SOCK4, что в дальнейшем упрощает обращение к переменным внутри данной структуры. Assume Esi:Nothing отменяет привязку. Чтобы лучше понять, возможно будет проще, если я укажу несколько вариантов адресации:
Assume Esi:Ptr CONNECT_SOCK4 mov al, .VN ; Помещаем в AL значение байта из переменной VN структуры mov al, .CD ; Помещение в AL переменной CD mov ax. .DSTPORT ; Помещение в AX переменной DSTPORT Assume Esi:Nothing
либо
mov al, ; Помещаем в AL значение байта из переменной VN mov al, ; Помещение в AL переменной CD mov ax, ; Помещение в AX переменной DSTPORT
либо
mov al, byte ptr ; Помещение в AL переменной VN mov al, byte ptr ; Помещение в AL переменной CD mov ax, word ptr ; Помещение в AX переменной DSTPORT

Я думаю, вам очевидно так же как и мне, что быстрее, удобней и наглядней использовать первый вариант. Хотя если необходимо обратиться к одной переменной структуры – имеет право на существование и второй вариант. Третий же вариант думаю лучше использовать в случаях, когда данные по адресу не структурированы. Но, как известно, на вкус и цвет, каждый сам себе тамбовский волк. Пользуйтесь тем методом, который вам удобней.
Еще один момент, который стоит уточнить. Макрос Result . Данный макрос написан, чтобы можно было одной строкой вызвать функцию WinAPI и занести результат исполнения в регистр или память. Так строка:
mov lpMem, @Result(LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, dAmount)
Выполняет сначала вызов такого вида:
invoke LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, dAmount
а уже после выполнения данного вызова результат исполнения (Eax) заносит в переменную lpMem. В данном конкретном случае, будет выделена память, а в переменную будет записан адрес, по которому находится выделенный для нас участок.

Этап третий, передача данных

Итак, выполнены два самых сложных этапа. Клиент пришел, мы соединили его с удаленным сервером и настал черед простейшей “обезьяньей” работы. Передачи данных между двумя сокетами. Сделаем это просто, по-быстрому:
; Поток читающий из клиентского и передающий в серверный сокет.... ClientSock Proc Param:DWORD LOCAL sserver, sclient:DWORD LOCAL rdFds:fd_set LOCAL dAmount, lpBuf: DWORD ; В Param у нас находится информация о сокетах сервера и клиента, ; переносим в локальные переменные mov ebx, Param Assume Ebx: Ptr THREAD_DATA mov eax, .Server mov sserver, eax mov eax, .Client mov sclient, eax Assume Ebx: Nothing ; Не забудем высвободить память invoke LocalFree, Param @@: invoke FdZero, ADDR rdFds invoke FdSet, sserver, ADDR rdFds invoke FdSet, sclient, ADDR rdFds invoke select, NULL, ADDR rdFds, NULL, NULL, NULL ; Проверяем, есть ли данные для чтения.if eax == SOCKET_ERROR || eax == 0 ; Данных нет - выходим jmp @F .endif ; Есть ли данные от сервера, которые нужно передать клиенту? invoke FdIsSet, sserver, ADDR rdFds .if eax ; Получаем размер ожидающих чтения данных invoke ioctlsocket, sserver, FIONREAD, ADDR dAmount ; Резервируем память под данные mov lpBuf, @Result(LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, dAmount) invoke recv, sserver, lpBuf, dAmount, 0 .if eax == SOCKET_ERROR || eax == 0 jmp @F .endif invoke send, sclient, lpBuf, eax, 0 invoke LocalFree, lpBuf .endif ; Есть ли данные от клиента для отправки серверному сокету? invoke FdIsSet, sclient, ADDR rdFds .if eax ; Получаем размер ожидающих чтения данных invoke ioctlsocket, sclient, FIONREAD, ADDR dAmount ; Резервируем память под данные mov lpBuf, @Result(LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, dAmount) invoke recv, sclient, lpBuf, dAmount, 0 .if eax == SOCKET_ERROR || eax == 0 jmp @F .endif invoke send, sserver, lpBuf, eax, 0 invoke LocalFree, lpBuf .endif ; Идем на новый цикл jmp @B @@: ; Закрываем сокеты invoke closesocket, sserver invoke closesocket, sclient ; Выходим из потока invoke ExitThread, 0 ClientSock Endp
Первоначально в данной процедуре производится инициализация внутренних переменных из переданной в поток структуры, чтобы их было удобнее использовать. Затем, в цикле производится проверка, есть ли данные на чтение из сокетов, затем двумя кусками кода (фактически копипаст, здесь я не заморачивался выносом функции и оптимизацией ибо так наглядней) делается чтение из одного сокета и отправка во второй.
Все, ура! Компилируем и пробуем. В принципе самый хороший вариант – FireFox. В настройках подключения указываем, что нужно использовать прокси сервер SOCKS4. Указываем его адрес и порт, на котором он у нас находится. После этого, сохраняем настройки и наслаждаемся интернетом, прошедшим, через наш прокси, размером 3,5 кбайт))) Да, уточню. Для компиляции жлательно наличие установленного пакета

Статья посвящена протоколу SOCKS5 - его внутреннему устройству, практическому применению, а также серверам и клиентам SOCKS, доступным для платформы Unix

[Валентин Синицын (val AT linuxcenter DOT ru)]

- Извини, Пух, - сказала САВА. - Тигра сжевал все провода от почтового сервера, и почта долго никак не приходила...
- Провода, - подумал Пух злобно. - Носки вязать из таких проводов.

Андрей Щербаков «9600 бод и все-все-все...»

В этой статье мы поговорим о протоколе SOCKS[сноска: «socks» - англ. «носки», «чулочки» ]. С его помощью можно решать самые разные задачи: организовывать защищенный доступ к службам, расположенным за межсетевым экраном (firewall), скрывать свой истинный IP-адрес во время работы с недружелюбными сетевыми ресурсами или реализовать универсальный прокси-сервер, поддерживающий любые протоколы прикладного уровня (HTTP, FTP, POP3/SMTP, ICQ и т.д.). К сожалению, несмотря на всю простоту и богатство возможностей SOCKS, многие системные администраторы не достаточно хорошо знакомы с ним и не представляют, чем он может быть полезен. Хочется надеяться, что после прочтения данного материала незаслуженно забытый протокол займет достойное место в их арсенале. Сразу же оговоримся: все последующее изложение будет относиться к пятой версии SOCKS, SOCKS5. Предыдущая, четвертая версия (SOCKS4), все еще имеет хождение в Сети, однако, ее возможности более ограничены.

К слову сказать, название протокола не имеет ничего общего с упомянутыми в эпиграфе чулочными изделиями и является простым сокращением от «SOCK-et-S» - «гнезда», или, в более привычном уху компьютерного специалиста переводе - «сокеты». Термин был предложен создателями в качестве рабочего варианта, да так и прижился. Как известно, сокеты лежат в основе любого API, реализующего сетевое взаимодействие - Unix, Winsock и т.п. Чтобы послать данные по сети, приложению достаточно просто записать их в сокет, подобно тому, как это делается при сохранении информации в локальном файле. И в том, и в другом случае программе не приходится заботиться о том, что происходит «за кулисами» - дополнение пользовательских данных служебной информацией, разбивка на сегменты с последующей их инкапсуляцией в датаграммы и физическая отправка осуществляется другими частями операционной системы - стеком TCP/IP и драйверами устройств, о которых приложению ничего не известно. Такое «разделение труда» позволяет как угодно изменять процедуру доставки сообщений, при условии, что интерфейс прикладного программирования остается постоянным. Именно эта особенность и лежит в основе идеологии SOCKS. Основная задача данного протокола - внедрить в «нормальный» процесс обмена данными некоего посредника, называемого SOCKS-сервером или SOCKS-прокси. Когда клиент (поддерживающее SOCKS приложение: web-браузер Mozilla, клиент ICQ Miranda IM и др., см. ниже) желает отправить какую-либо информацию по сети, он устанавливает соединение не с реальным адресатом, а с SOCKS-сервером, который, в свою очередь, пересылает данные по назначению, но уже от своего имени. С точки зрения «настоящего» сервера (например, web-узла, который пользователь желает просмотреть в Mozilla Firefox) SOCKS-прокси является самым обыкновенным клиентом. Таким образом, сущность (IP-адрес) истинного клиента оказывается скрытой от обслуживающего его сервера. Это весьма удобное обстоятельство таит в себе потенциальную опасность (можете спрятаться вы , но ведь могут и от вас ), поэтому реально существующие SOCKS-сервера имеют развитые схемы контроля доступа (запрет входящих и исходящих соединений по заданному перечню адресов) и поддерживают авторизацию пользователей по паролю (см. ниже).

Отметим, что коль скоро SOCKS работает на более низком по сравнению с прикладным (а именно, транспортном) уровне модели OSI, его поддержка не потребует никаких изменений в логике работы клиента, а тем более - сервера. Действительно, все что нужно - это модифицировать реализацию функций, отвечающих за создание сетевого подключения и отправку данных: connect(), bind(), send() и т.п. На практике это обычно достигается перехватом системных вызовов с их последующей подменой поддерживающими SOCKS пользовательскими аналогами. Никаких правок в исходном коде клиентских приложений, а тем более самого доступа к исходным текстам, как правило, не требуется. Эта мощная процедура известна как «соксификация» и будет подробно рассмотрена ниже.

Теперь, когда мы получили общее представление о SOCKS, можно перейти к более детальному рассмотрению данного протокола.

Спецификация SOCKS5

Протокол SOCKS5 подробно описан в RFC1928. В отличие от монстроподобных стандартов вроде «HTTP 1.1», спецификация SOCKS умещается на 9 страниц и может быть без труда разобрана любым желающим. Предлагаемые здесь сведения являются ее кратким конспектом и призваны помочь вам в этом несложном деле.

Как уже отмечалось ранее, SOCKS5 является протоколом транспортного уровня. Его «соседи» - TCP и UDP непосредственно используются для передачи данных, поступающих с прикладного уровня (от пользовательских приложений), а значит, SOCKS-прокси должен уметь корректно работать с каждым из них. Отметим также, что протокол ICMP, используемый утилитами ping и traceroute, находится ниже транспортного уровня, а потому соксификации, к сожалению, не поддается.[Сноска: существуют нестандартные расширения протокола SOCKS, позволяющие работать и с ICMP, однако, в данной статье они рассматриваться не будут ].

Прежде чем отправлять какие-либо данные, клиент должен пройти процедуру авторизации на SOCKS-сервере. Для этого он открывает TCP-соединение с портом 1080 (значение по умолчанию) SOCKS-сервера и отправляет по нему сообщение, содержащее кодовые номера поддерживаемых им методов аутентификации. SOCKS-сервер выбирает один из методов по своему усмотрению и сообщает его номер клиенту. Перечень некоторых из возможных значений приведен в таблице 1. Как легко видеть, аутентификация может отсутствовать (на практике это скорее всего означает, что SOCKS-сервер различает клиентов по их IP-адресам) или производиться на основании имени пользователя и пароля. В последнем случае возможно большое количество различных вариантов, от тривиального «Username/Password Authentication» (RFC 1929) предусматривающего передачу пароля в открытом виде до куда более безопасного CHAP (зашифрованный пароль, открытые данные) и GSSAPI (RFC 1961), которое может использоваться для полной криптографической защиты трафика. После успешной авторизации клиент получает возможность посылать запросы (команды), устанавливать исходящие соединения и даже принимать входящие.

Установка исходящего TCP-соединения

Для установки исходящего TCP-соединения, клиент отправляет SOCKS-серверу запрос «CONNECT», в котором указывает адрес и порт доставки. Для идентификации узла-получателя могут использоваться как IP-адреса (поддерживаются IPv4/IPv6), так и полноценные доменные имена. В последнем случае SOCKS-сервер берет на себя заботу по их разрешению, так что сеть, в которой работает клиент, в принципе может обходиться и без DNS-сервера. В ответном сообщении SOCKS-сервер сообщает код ошибки (как обычно, 0 обозначает, что операция прошла успешно), а также IP-адрес (BND.ADDR) и TCP-порт (BND.PORT), которые будут использоваться для фактической связи с запрошенным узлом. Поскольку SOCKS-сервера, как правило, имеют более одного сетевого интерфейса, данный IP-адрес может отличаться от того, с которым было установлено управляющее соединение. После этого клиент открывает новый TCP-сеанс с BND.ADDR:BND.PORT и осуществляет отправку данных. Исходящее TCP-соединение разрывается одновременно с закрытием управляющей сессии. Заметим, что запрос «CONNECT» может быть отклонен SOCKS-прокси, если адреса источника (клиента) или получателя (сервера) запрещены [Сноска: или явно не разрешены, в зависимости от конкретной реализации и выбранной политики ] к обслуживанию системным администратором.

Установка исходящего UDP-соединения

В отличие от потокового протокола TCP, подразумевающего установку сеанса, протокол UDP является датаграммным, а потому несколько более сложным в обращении. Его поддержка появилась лишь в SOCKS5.

Перед отправкой UDP-датаграмм клиент запрашивает у SOCKS-сервера UDP-ассоциацию , используя для этого команду «UDP ASSOCIATE». UDP-ассоциация - это своего рода виртуальный сеанс между клиентом и SOCKS-сервером. В исходящем запросе клиент указывает предполагаемые адрес и порт, которые будут выступать в качестве источника будущих UDP-датаграмм. Если на момент установки UDP-ассоциации эта информация еще не известна, клиент должен использовать комбинацию 0.0.0.0:0 (или, скажем, x.x.x.x:0, если неизвестен только номер порта). В ответном сообщении SOCKS-сервер указывает IP-адрес (BND.ADDR) и UDP-порт (BND.PORT), на которые следует направлять исходящие датаграммы. При этом адрес и порт их реального получателя указываются прямо в теле (можно сказать, что имеет место UDP-инкапсуляция). Э ти параметры, наряду с адресом и портом отправителя используются для принятия решения о допустимости отправки датаграммы. Как легко видеть, это создает дополнительную нагрузку на SOCKS-сервер: правила фильтрации необходимо применять к каждой UDP-датаграмме, тогда как в случае TCP-соединения его легитимность оценивается один раз, в момент исполнения SOCKS-сервером команды «CONNECT». Согласно требованиям стандарта, SOCKS-сервер должен следить за тем, чтобы IP-адрес отправителя датаграммы совпадал с адресом узла, создавшего UDP-ассоциацию. UDP-ассоциация разрушается одновременно с закрытием управляющего TCP-сеанса, в рамках которого была послана команда «UDP ASSOCIATE».

Многие из существующих SOCKS-серверов испытывают серьезные проблемы, если между ними и клиентом, запросившим UDP-ассоциацию, располагается межсетевой экран с функцией NAT (Network Address Translation). Причина этого кроется в изменении адреса и порта отправителя, которое происходит в тот момент, когда UDP-датаграмма пересекает межсетевой экран. Как следствие, сервер и ничего не подозревающее клиентское приложение начинают говорить на разных языках: предполагаемый адрес и порт источника, указанные в команде «UDP ASSOCIATE» перестают соответствовать реальным параметрам получаемых SOCKS-сервером датаграмм. В результате они оказываются отброшенными как не принадлежащие UDP-ассоциации. Проблему можно было бы решить, указав в качестве предполагаемого источника 0.0.0.0:0 (см. выше), что должно интерпретироваться SOCKS-сервером как «любая UDP-датаграмма, пришедшая с того же адреса, что и команда на создание ассоциации». К сожалению, большинство из реально существующих SOCKS-серверов трактуют стандарт более узко и не позволяют одновременно установить в ноль и предполагаемый адрес, и порт отправителя. Из протестированных автором реализаций описанный здесь «фокус с пробросом UDP через NAT» позволяет проделать лишь одна - Dante.

Прием входящих соединений

Эта достаточно оригинальная возможность может оказаться полезной в случаях, когда клиент и «настоящий» сервер в описанной выше схеме меняются местами, что может произойти, например, в протоколах типа FTP. В целях дальнейшего рассмотрения будем предполагать, что между “клиентом” (стороной, собирающейся принять входящее соединение) и “сервером” (стороной, инициирующей входящее соединение) уже установлен “прямой” канал связи при помощи команды “CONNECT”. Для открытия “обратного” канала “клиент” должен послать SOCKS-серверу команду “BIND”, указав в ее параметрах IP-адрес и порт, которые будут использоваться им для приема входящего соединения. В ответ на это SOCKS-сервер сообщает IP-адрес и порт, выделенные им для поддержания “обратного” канала. Предполагается, что «клиент» передаст эти параметры «серверу» , используя средства, предоставляемые протоколами прикладного уровня (например, команду «PORT» протокола FTP). После того, как SOCKS-сервер примет (или отбросит) входящее соединение, он повторно уведомляет об этом «клиента», сообщая ему IP-адрес и порт, используемые “сервером”. Отметим, что прием входящих соединений может осуществлять лишь приложение, разработчики которого позаботились о поддержке SOCKS еще на этапе проектирования. В противном случае (если приложение работает с SOCKS-сервером через программу-соксификатор), оно не сможет предоставить корректную информацию об адресе ожидающего “обратной связи” сокета (т.е. сформирует неверную команду “PORT” в рассмотренном выше примере с FTP).

«Цепочки» SOCKS

Давайте, работайте. Шесть арендованных “на раз” роутеров, через которые пробегает сигнал. И все достаточно стойкие к взлому.

Сергей Лукьяненко “Лабиринт отражений”

Архитектура протокола SOCKS5 позволяет легко объединять SOCKS-сервера в каскады, или, как их еще называют «цепочки» («chains»). Примечательно, что все необходимые для этого действия могут быть произведены на стороне клиента. К «звеньям» цепочки предъявляется единственное требование: они должны «доверять» друг другу (т.е. допускать установку входящих и исходящих соединений). Если образующие каскад SOCKS-сервера не являются анонимными (т.е. используют схемы аутентификации Username/Password, CHAP или подобные), необходимо также, чтобы пользователь мог успешно пройти процедуру авторизации на каждом из них.

Предположим, что у нас имеется набор из N SOCKS-серверов с именами socks1, socks2, ..., socksN, удовлетворяющих всем вышеперечисленным требованиям. Тогда для создания каскада клиент может поступить следующим образом:

    В случае исходящего TCP-соединения: клиент подключается к socks1, проходит (при необходимости) процедуру авторизации и посылает команду «CONNECT», указав в качестве адреса доставки socks2. Выполняя этот запрос, socks1 создаст новое соединение с socks2 и будет исправно передавать всю идущую по нему информацию клиенту, при этом socks2 не будет даже догадываться, с кем он общается на самом деле. Далее процедура повторяется до тех пор, пока не будет установлено соединение между socks(N-1) и socksN. Последний сервер каскада подключается к непосредственно узлу, который интересует клиента. Передача данных происходит в обычном режиме: клиент отправляет пакет на сервер socks1, который, в свою очередь, передает его socks2, ... и так до тех пор, пока не будет достигнут конечный узел.

    В случае исходящего UDP-соединения: клиент подключается к socks1, проходит процедуру авторизации и последовательно посылает две команды: “CONNECT” (адрес доставки - socks2) и “UDP ASSOCIATE”. Таким образом, создаются два новых соединения: виртуальный UDP-канал между клиентом и socks1, а также TCP-сессия между socks1 и socks2. Используя эту TCP-сессию, клиент (от имени socks1) посылает команду “UDP ASSOCIATE” на сервер socks2 (открывает UDP-канал между socks1 и socks2) и “CONNECT” на сервер socks3. Процедура продолжается до тех пор, пока между всеми SOCKS-серверами каскада не будут установлены виртуальные UDP-каналы. Чтобы отослать какие-либо данные, клиент предварительно производит N-кратную инкапсуляцию UDP-датаграммы, указывая в качестве адреса доставки последовательно: socks1, socks2, socks3, socksN и адрес реального получателя, а затем отправляет ее на сервер socks1. Отметим, что на практике данный вариант каскадирования встречается крайне редко. Это связано с тем, что SOCKS-сервера, как и NAT Firewall"ы, могут изменить порт источника датаграммы, что приведет к проблемам, подробно описанным в разделе “Установка исходящего UDP-соединения”.

Используя цепочки SOCKS-серверов, не требующих аутентификации, клиент может значительно повысить анонимность работы в Интернете (см. эпиграф). В Сети можно найти множество программ, реализующих описанные здесь схемы. Таковыми, например, являются SocksChain (http://www.ufasoft.com/socks/ ) для Windows или ProxyChains ( ) для Unix. Каскадирование SOCKS-серверов является также неотъемлемой частью некоторых соксификаторов, в первую очередь, FreeCap (http://www.freecap.ru/ ).

SOCKS-сервера

Теперь, когда принципы работы SOCKS-сервера нам хорошо знакомы, пора переходить от теории к практике. В мире существует большое количество программ, реализующих протокол SOCKS5. Они охватывают все популярные операционные системы (Unix, Windows, ...) и способы распространения (freeware, shareware, open-source и т.д.). Здесь мы вкратце рассмотрим наиболее известные (или интересные с точки зрения автора) реализации.

Начнем, пожалуй, с SOCKS5 Reference Implementation (http://www.socks.permeo.com/), выполненной компанией NEC и принадлежащей в настоящий момент фирме Permeo. Текущая версия имеет номер 1.0r11 и датирована августом 2000 года. Как легко догадаться по названию, этот сервер является справочной реализацией протокола и, вообще говоря, не предназначен для промышленного использования. Однако, по не очень понятным мне причинам, он был включен в порты FreeBSD, а посему является де-факто стандартом на данной платформе. Продукт имеет поддержку GSSAPI и распространяется в исходных текстах, но по несвободной лицензии. Коммерческое применение данного сервера запрещено.

Dante, разработанный норвежской компанией Inferno Nettverk, особенно популярен среди сторонников Linux. Продукт развивается, хотя и не очень бурно (последняя версия, 1.1.15, датирована 31 января 2005 года) и вполне пригоден для практического применения. Как уже упоминалось ранее, Dante позволяет корректно работать с UDP-ассоциациями даже в том случае, если они проходят через NAT Firewall. Программа распространяется в исходных текстах по лицензии BSD. В состав Dante входит библиотека для прозрачной соксификации Unix-приложений (см. ниже)

Валентин Синицын (val AT linuxcenter DOT ru) - Универсальный прокси-сервер

Анонимность в сети — тема не новая. И вы наверняка устанавливали к себе на комп прогу типа A4Proxy, SocksChain
и им подобные. Лично я не люблю, когда для работы с проксями нужна какая-то отдельная прога. Во-первых
некрасиво, когда много окон на панели задач или значков в трее, во-вторых проги эти требуют кряков, а их
искать лень 🙂 Поэтому я и написал классы для поддержки SOCKS5-серверов, которые я теперь могу заюзать
в какой-нибудь своей проге. И вот теперь хочу всем рассказать, как это делать.

То, к каким серверам и по каким протоколам мы может обращаться через прокси, зависит от
типа этого прокси, т. е. протокола, по которому мы обращаемся к нему. Типов проксей существует нескольно:
HTTP-proxies, SOCKS4, SOCKS5, SSL CONNECT и т.д. HTTP-proxy наиболее распространены, их легче всего найти и инете, но работают они только с HTTP, к тому
же могут вставлять в заголовки запроса адрес клиента, то есть быть
не анонимными. Протокол SOCKS наиболее примечателен тем, что он инкапсулирует протоколы не прикладного, а
транспортного уровня, т.е. TCP/IP и UDP/IP. Поскольку только по этим протоколам возможна работа в Сети,
через SOCKS можно работать с любыми серверами, в том числе и такими же SOCKS и,
таким образом, организовывать цепочки SOCKS-серверов. По этой же причине ВСЕ SOCKS-сервера анонимны — невозможно
на уровне TCP/IP и UDP/IP передать дополнительную информацию, не нарушив работу вышестоящего
протокола.

Мы остановимся на протоколе SOCKS5. Его описание лежит в
. Для SOCKS5 стандартным является порт 1080, но, впрочем, на этот
стандарт никто особого внимания не обращает. Каждое SOCKS-соединение проходит стадию аутентификации, если она требуется, затем клиент
посылает команду. Команда может быть одна из трех:
CONNECT — исходящее TCP-соединение с указанным адресом. Использование этой команды мы рассмотрим
подробнее, так как она нужна наиболее часто. BIND — открыть порт (сервер выбирает порт и посылает клиенту адрес и порт) и принять TCP соединение.
Серверу может понадобится знать, кто будет соннектиться. На этот случай нужно передать эту инфу. UDP ASSOCIATE — открыть UDP-порт (сервер выбирает порт). Данные, предназначенные для конечного
хоста и данные от него идут тоже по UDP. Данные в SOCKS5 передаются в бинарном виде, а не в текстовом, как в HTTP, SMTP, POP3 и др.

Описание протокола

Сконнектившись с сервером, клиент шлет пакет, в котором указана версия протокола и поддерживаемые
методы аутентификации. Этот пакет имеет следующий формат:

BYTE Version;
BYTE nMethods;
BYTE methods

Версия должна быть 5. Каждый элемент methods определяет не только метод аутентификации, но и способ шифрования данных,
если оно используется. Из этих методов сервер выбирает один. Можно указывать любое количество методов, но, если сервер не требует аутентификации, то никакие методы,
кроме 0x00 (не использовать ни аутентификацию, ни шифрование) не потребуются. В ответ сервер шлет пакет следующего содержания:

BYTE Version
BYTE method,

где method — выбранный сервером метод или 0xFF (ни один из предложенных методов не поддерживается). Если метод 0x00, то можно сразу посылать команду.

Пакет команды имеет следующий формат:

BYTE Version; // 5
BYTE Cmd ; // 1 — CONNECT
BYTE Reserved; // 0

BYTE addr;
WORD port; // Байты в сетевом порядке, т. е. htons(Port);

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

Сервер посылает ответ:

BYTE Version; // 5
BYTE Rep ; // 0 — Ok
BYTE Reserved; // 0
BYTE AType; // 1 — IPv4; 3 — domain name; 4 — IPv6
BYTE addr;
WORD port;

Здесь адрес и порт — это адрес и порт, видимые хосту. Возвращается, как правило, IP-адрес, а не доменное
имя. Этот адрес может отличаться от того, по которому мы обращаемся к серверу, особенно, если сервер
используется по назначению, т. е. для выхода из локалки в инет. Если Rep не ноль, т. е. ошибка, то закрываем соединение, в
противном случае работаем с хостом. Мы не используем шифрование, поэтому просто передаем и принимаем данные, как при обычном соединении. Если одна из сторон закроет соединение с socks-сервером, то он сразу же закроет соединение с другой
стороной. Одно socks-соединение инкапсулирует одно TCP-соединение или попытку его установления,
так что если использовать socks для анонимного сканирования портов, то эта
процедура может занять пол дня.

Кодинг

Поскольку socks инкапсулирует TCP, целесообразно сделать класс socks-соединения производным от
класса сокета, но MFCшный CSocket не подходит, т.к. у него все методы
не виртуальные. Напишем свой класс сокета и назовем его, скажем, CTSocket

#include

class CTSocket
{
public:





virtual void Close();
virtual unsigned long GetHost(); // Узнать свой адрес. Это тоже может понадобиться.

private:
SOCKET sock;
};

Реализацию этого класса каждый сможет написать сам (кто не знает как, RTFM MSDN), так что не буду ее
рассматривать. Теперь напишем класс socks-соединения. Он будет поддерживать только самый необходимый набор
функций: поддерживается только команда CONNECT, не поддерживается аутентификация и SOCKS-сервер
задается только IP-адресом, а не доменным именем. Больше в одной статье не поместится.

Class CSocksSocket: public CTSocket
{
public:
virtual BOOL CreateSocket();
virtual BOOL Connect(unsigned long ip, unsigned short port);
virtual BOOL Connect(LPCSTR name, unsigned short port);
virtual int Send(const char* str, int len);
virtual int Recv(char* buf, int max);
virtual BOOL Close();
virtual unsigned long GetHost();

CTSocket* pSocket;
unsigned long socks_ip;
unsigned short socks_port;

private:
char buffer; // Такого размера точно хватит
unsigned long l_ip; // Адрес, возвращаемый функцией
GetHost()

};

// Реализация
BOOL CSocksSocket::CreateSocket()
{
if (!pSocket->CreateSocket()) return FALSE;
if (!pSocket->Connect(socks_ip, socks_port)) return FALSE;
buffer = 5; // Ver
buffer = 1; // 1 method
buffer = 0; // no auth
pSocket->Send(buffer, 3);
int n = pSocket->Recv(buffer, 2);
if (n != 2) return FALSE;
method 0 not supported
return TRUE;
}

BOOL CSocksSocket::Connect(unsigned long ip, unsigned short port)
{
buffer = 5; // Ver
buffer = 1; // CONNECT
buffer = 0; // Reserved
buffer = 1; // IPv4
*((unsigned long*)(buffer + 4)) = ip;
*((unsigned short*)(buffer + 8)) = port;
pSocket->Send(buffer, 10);
int n = pSocket->Recv(buffer, 10);
if (n != 10) return FALSE;
if (buffer != 0) return FALSE; //
Can’t connect

return TRUE;
}

BOOL CSocksSocket::Connect(LPCSTR name, unsigned short port)
{
buffer = 5;
buffer = 1;
buffer = 0;
buffer = 3; // Domain name
int m = strlen(name);
buffer = m; //
Length byte
memcpy(buffer+5, name, m); //
Копируем строку без завершающего нуля
*((unsigned short*)(buffer + 5 + m)) = port;
pSocket->Send(buffer, m + 7);
int n = pSocket->Recv(buffer, 10);
if (n != 10) return FALSE;
if (buffer != 0) return FALSE;
if (buffer != 1) return FALSE; //
Будем требовать, чтобы нам сказали IP, а не что-нибудь другое.
l_ip = *((unsigned long*)(buffer + 4));
return TRUE;
}

int CSocksSocket::Send(const char* str, int len)
{
return pSocket->Send(str, len);
}

int CSocksSocket::Recv(char* buf, int max)
{
return pScoket->Recv(buf, max);
}

void CSocksSocket::Close()
{
pSocket->Close();
}

unsigned long CSocksSocket::GetHost()
{
return l_ip;
}

// Ну, а теперь тестовая прога
void main()
{
WSADATA wsadata;
CTSocket tsock;
CSocksSocket ssock(&tsock);

WSAStartup(MAKEWORD(2,2), &wsadata);

ssock.socks_ip = inet_addr(«10.10.10.10»); // Впишите сюда нужный адрес
ssock.socks_port = 1080; //
Впишите сюда порт

if (!ssock.CreateSocket()) return; // Can’t connect to socks
// or auth required
if (!ssock.Connect(«www.mail.ru», htons(80))) return; //
www.mail.ru
// is inaccessible
LPSTR q = «HEAD / HTTP/1.1\xD\xAHost: www.mail.ru:80\xD\xAUser-Agent: xakep\xD\xA\xD\xA»;
ssock.Send(q, strlen(q));

char buf;
int n = ssock.Recv(buf, 1000);
buf[n] = 0;
printf("%s", buf);