Т.к. в моей пред.теме о socks никто ничем помочь не смог, создаю еще одну. Проблема заключается в некорректности работы сокс-сервера через соксификаторы. Для наглядности, чтоб было максимально понятно написал простейший socks4 сервер с подробнейшими комментариями (Delphi, WinSock + WinApi). Качаем соксификатор, настраиваем в нем socks4 (Сервер: 127.0.0.1, порт: 8080). Качаем исходник socks4-сервера (залил для большей наглядности) Компилируем, запускаем, заходим на mail.ru - страница грузится очень долго (конца загрузки дождаться мне так и не удалось), хотя тот же гугл, да и вконтакт (по крайней мере на "Поиске людей") работают норм. Так вот, нужна подсказка, из-за чего это происходит и как это исправить. Код: Code: program Socks4; uses Windows, WinSock; const IpToListen = '127.0.0.1'; function OurBrowserListening(p: pointer): DWORD; stdcall; var BSock: TSocket; vn, cd, UserID: byte; answer: array[1..8] of byte; buf: pointer; addr: sockaddr_in; TargetSock: TSocket; fset: tfdset; i: integer; avail_bytes: integer; time_out: timeval; label 2, 3; begin BSock:= Cardinal(p); //BSock - браузер (подключеный клиент) TargetSock:= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //TargetSock - таргет (цель, конечный хост) if TargetSock = INVALID_SOCKET then ExitThread(0); ZeroMemory(@addr, sizeof(addr)); addr.sin_family:= PF_INET; //Теперь нужно принять данные от браузера (клиента): // +----+----+----+----+----+----+----+----+----+----+....+----+ // | VN | CD | DSTPORT | DSTIP | USERID |NULL| // +----+----+----+----+----+----+----+----+----+----+....+----+ // 1 1 2 4 variable 1 recv(BSock, vn, sizeof(vn), 0); //принимаем версию socks - 4 recv(BSock, cd, sizeof(cd), 0); //принимаем команду - 1 (connect) if cd <> $01 then goto 2; //если не connect, то перейти к метке 2 recv(BSock, addr.sin_port, 2, 0); //принимаем порт таргета (цели) recv(BSock, addr.sin_addr.S_addr, 4, 0); //принимаем ip таргета (цели) repeat recv(BSock, UserID, sizeof(UserID), 0); until UserID = $00; //принимаем UserID, оканчивающийся NULL-байтом //пытаемся соединиться с таргетом if connect(TargetSock, addr, sizeof(addr)) <> 0 then begin //если не удалось, то: 2:ZeroMemory(@answer, sizeof(answer)); answer[1]:= $00; //должен быть 0 answer[2]:= $5B; //$5B - ошибка send(BSock, answer, sizeof(answer), 0); //посылаем ответ браузеру (клиенту) goto 3; end; //Теперь нужно послать ответ браузеру (клиенту) // +----+----+----+----+----+----+----+----+ // | VN | CD | DSTPORT | DSTIP | // +----+----+----+----+----+----+----+----+ // 1 1 2 4 ZeroMemory(@answer, sizeof(answer)); answer[1]:= $00; //должен быть 0 answer[2]:= $5A; //$5A - коннект к таргету успешен send(BSock, answer, sizeof(answer), 0); //посылаем ответ браузеру (клиенту) //Теперь следует непосредственно перенаправление данных от браузера к //таргету и наоборот while true do begin //while true do FD_ZERO(fset); FD_SET(BSock, fset); FD_SET(TargetSock, fset); time_out.tv_sec:= 8; //установим таймаут на select в 8 секунд time_out.tv_usec:= 0; if select(0, @fset, nil, nil, @time_out) <= 0 then goto 3; //если есть данные от браузера и/или таргета, то: if FD_IsSet(BSock, fset) then //если есть данные от браузера, то: begin avail_bytes:= 0; ioctlsocket(BSock, FIONREAD, avail_bytes); //читаем кол-во пришедших байт buf:= VirtualAlloc(nil, avail_bytes, MEM_COMMIT, PAGE_READWRITE); //освобождаем под них память i:= recv(BSock, buf^, avail_bytes, 0); //принимаем их от браузера if i <= 0 then //если ошибка/соединение закрыто, то: begin VirtualFree(buf, 0, MEM_RELEASE); goto 3; end; send(TargetSock, buf^, i, 0); //передаем клиенту VirtualFree(buf, 0, MEM_RELEASE); //освобождаем память end; if FD_IsSet(TargetSock, fset) then //если есть данные от таргета, то: begin avail_bytes:= 0; ioctlsocket(TargetSock, FIONREAD, avail_bytes); //читаем кол-во пришедших байт buf:= VirtualAlloc(nil, avail_bytes, MEM_COMMIT, PAGE_READWRITE); //освобождаем под них память i:= recv(TargetSock, buf^, avail_bytes, 0); //принимаем их от таргета if i <= 0 then //если ошибка/соединение закрыто, то: begin VirtualFree(buf, 0, MEM_RELEASE); goto 3; end; send(BSock, buf^, i, 0); VirtualFree(buf, 0, MEM_RELEASE); //освобождаем память end; end; //while true do 3: CloseSocket(BSock); //закрываем сокет браузера CloseSocket(TargetSock); //закрываем сокет таргета Result:= 0; ExitThread(0); //выходим из потока end; var sock, ClientSock: TSocket; wdata: TWSAData; saddr: sockaddr_in; id: DWORD; label 1; begin if WSAStartup(MAKEWORD(1, 1), wdata) <> 0 then ExitProcess(0); sock:= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if sock = INVALID_SOCKET then ExitProcess(0); ZeroMemory(@saddr, sizeof(saddr)); saddr.sin_family:= PF_INET; saddr.sin_port:= htons(8080); //будем слушать на 8080 порту saddr.sin_addr.S_addr:= inet_addr(IpToListen); //127.0.0.1 if bind(sock, saddr, sizeof(sockaddr_in)) <> 0 then ExitProcess(0); if listen(sock, SOMAXCONN) <> 0 then ExitProcess(0); 1: ClientSock:= accept(sock, nil, nil); CreateThread(nil, 0, @OurBrowserListening, pointer(ClientSock), 0, id); goto 1; //Если подключился, ждем подключения снова. WSACleanup(); end. Я так думаю, что проблема зарыта где-то уже на этапе перенаправления данных от браузера к таргету и наоборот, но где именно - не могу найти никак, скоро на стену полезу. Надеюсь на вашу помощь. Тому, кто поможет, скромное вознаграждение размером в 20$. Очень надеюсь на вас, а то я уже сломал мозг.
попробуй на васме спросить. здесь мало кто таким программингом занимается. В основм хттпвебреквест, на делфе сделать проверку 20 радиобатонов через ифы, итп.
Faost, как вариант можно разбить обработчик сессии на 2 потока, в одном передавать данные от браузера к таргету, в другом от таргета к браузеру
Ra$cal, спасибо, попробую. Только не побьют ли меня там за делфийский код...) А то билдер долго качать и устанавливать, чтоб проверить, так ли переписал на C. fluffylion, я и так пробовал, проблема аналогичная.
не побьют, но помогать будут медленнее =) накидай алгоритм словесный примерный лучше. я вот тоже даже не пытался смотретоь на код, ибо делфи.
компилить и отлаживать код не пытался, так что просто опишу что я заметил, просматривая это: - когда принимаешь заголовок socks не проверяешь количество принятых байт - неправльная работа с select. Проблемы с mail.ru полагаю из-за этого. Ты не проверяешь возможность того что селект возвратил управление по таймауту. Ты не обновляешь набор сокетов перед использованием селекта. Короче, читай маны... - зачем-то использованы одновременно потоки и select - VirtualAlloc для выделения буфера памяти каждый раз при приеме данных очень круто и оптимально... вообще выделяемая этой функцией память округляется до страницы(64 кб). Почему бы не использовать буфер в стеке? - когда таргет закрывает соединение, надо сначала отправить все данные в его буфере клиенту, а потом уже корректно завершить соединение с помощью shutdown а не обрывать его. стиль: - зачем goto? тут можно было бы и без него - нет отступов адекватных дополнительные советы: если будут еще проблемы, попробуй использовать сниффер для отладки... PS за такой код на васме точно побьют
Ну это само собой, что нужно проверять, это все отлажено - тут проблем не возникает, поэтому для большей минимальности убрал проверку. Насчет таймаута - я ставил даже на "бесконечное" ожидание - select(0, @fset, nil, nil, nil), проблема абсолютно идентична. Кроме того, маил.ру ну полюбасу ответит в течении 8сек на 1 запрос. FD_ZERO(fset); FD_SET(BSock, fset); FD_SET(TargetSock, fset); В главном потоке сервер слушает подключившихся клиентов, причем слушать след.клиента надо сразу после подключения первого. Учту. А какие данные то отправлять, если они передаются ему по мере поступления от таргета? Как только таргет отправил данные, они отправились клиенту (браузеру). Если таргет вместо того, чтобы отправить данные, разрывает соединение, то что еще остается передать браузеру? Насчет shutdown - пробовал, результат тот же( можно было. Мне кажется, для форумчан использование goto очень-очень наглядно. Иначе, с множеством begin;end было бы жесть. Насчет оформления - да тут и оформлять то собственно нечего, в коде нет и 200 строк. Насчет кода - поправь меня, скажи где что КРИТИЧЕСКИ криво (не учитывая goto, проверку того, в чем я уверен и что ошибочным в ходе ПРАВИЛЬНОГО теста быть не может - например, заголовков сокс. Также не считаю критически кривым кучу ExitProcess в начале, т.к. мне кажется, что это поспособствовало бы лучшему пониманию, чем множество begin;end). Ребята, вы же понимаете, что это не исходник на продажу/не для всеобщего использования в повседневной жизни, поэтому о каких программерских тонкостях типа goto может идти речь? P.S. Лицо у меня далеко не радостное