| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /* |
| Socks.cc |
| |
| |
| |
| This file contains the Socks client functionality. Previously this code was |
| duplicated in UnixNet.cc and NTNetProcessor.cc |
| */ |
| |
| #include "P_Net.h" |
| #include "tscore/I_Layout.h" |
| #include "tscore/ink_sock.h" |
| #include "tscore/InkErrno.h" |
| #include "tscore/IpMapConf.h" |
| |
| socks_conf_struct *g_socks_conf_stuff = nullptr; |
| |
| ClassAllocator<SocksEntry> socksAllocator("socksAllocator"); |
| |
| void |
| SocksEntry::init(Ptr<ProxyMutex> &m, SocksNetVC *vc, unsigned char socks_support, unsigned char ver) |
| { |
| mutex = m; |
| buf = new_MIOBuffer(); |
| reader = buf->alloc_reader(); |
| |
| socks_cmd = socks_support; |
| |
| if (ver == SOCKS_DEFAULT_VERSION) { |
| version = netProcessor.socks_conf_stuff->default_version; |
| } else { |
| version = ver; |
| } |
| |
| SET_HANDLER(&SocksEntry::startEvent); |
| |
| ats_ip_copy(&target_addr, vc->get_remote_addr()); |
| |
| #ifdef SOCKS_WITH_TS |
| req_data.hdr = nullptr; |
| req_data.hostname_str = nullptr; |
| req_data.api_info = nullptr; |
| req_data.xact_start = time(nullptr); |
| |
| assert(ats_is_ip4(&target_addr)); |
| ats_ip_copy(&req_data.dest_ip, &target_addr); |
| |
| // we dont have information about the source. set to destination's |
| ats_ip_copy(&req_data.src_ip, &target_addr); |
| |
| server_params = SocksServerConfig::acquire(); |
| #endif |
| |
| nattempts = 0; |
| findServer(); |
| |
| timeout = this_ethread()->schedule_in(this, HRTIME_SECONDS(netProcessor.socks_conf_stuff->server_connect_timeout)); |
| write_done = false; |
| } |
| |
| void |
| SocksEntry::findServer() |
| { |
| nattempts++; |
| unsigned int fail_threshold = server_params->policy.FailThreshold; |
| unsigned int retry_time = server_params->policy.ParentRetryTime; |
| |
| #ifdef SOCKS_WITH_TS |
| if (nattempts == 1) { |
| ink_assert(server_result.result == PARENT_UNDEFINED); |
| server_params->findParent(&req_data, &server_result, fail_threshold, retry_time); |
| } else { |
| socks_conf_struct *conf = netProcessor.socks_conf_stuff; |
| if ((nattempts - 1) % conf->per_server_connection_attempts) { |
| return; // attempt again |
| } |
| |
| server_params->markParentDown(&server_result, fail_threshold, retry_time); |
| |
| if (nattempts > conf->connection_attempts) { |
| server_result.result = PARENT_FAIL; |
| } else { |
| server_params->nextParent(&req_data, &server_result, fail_threshold, retry_time); |
| } |
| } |
| |
| switch (server_result.result) { |
| case PARENT_SPECIFIED: |
| // Original was inet_addr, but should hostnames work? |
| // ats_ip_pton only supports numeric (because other clients |
| // explicitly want to avoid hostname lookups). |
| if (0 == ats_ip_pton(server_result.hostname, &server_addr)) { |
| ats_ip_port_cast(&server_addr) = htons(server_result.port); |
| } else { |
| Debug("SocksParent", "Invalid parent server specified %s", server_result.hostname); |
| } |
| break; |
| |
| default: |
| ink_assert(!"Unexpected event"); |
| // fallthrough |
| case PARENT_DIRECT: |
| case PARENT_FAIL: |
| memset(&server_addr, 0, sizeof(server_addr)); |
| } |
| #else |
| if (nattempts > netProcessor.socks_conf_stuff->connection_attempts) |
| memset(&server_addr, 0, sizeof(server_addr)); |
| else |
| ats_ip_copy(&server_addr, &g_socks_conf_stuff->server_addr); |
| #endif // SOCKS_WITH_TS |
| |
| char buff[INET6_ADDRSTRLEN]; |
| Debug("SocksParents", "findServer result: %s:%d", ats_ip_ntop(&server_addr.sa, buff, sizeof(buff)), |
| ats_ip_port_host_order(&server_addr)); |
| } |
| |
| void |
| SocksEntry::free() |
| { |
| MUTEX_TRY_LOCK(lock, action_.mutex, this_ethread()); |
| // Socks continuation share the user's lock |
| // so acquiring a lock shouldn't fail |
| ink_release_assert(lock.is_locked()); |
| |
| if (timeout) { |
| timeout->cancel(this); |
| } |
| |
| #ifdef SOCKS_WITH_TS |
| if (!lerrno && netVConnection && server_result.retry) { |
| server_params->markParentUp(&server_result); |
| } |
| #endif |
| |
| if ((action_.cancelled || lerrno) && netVConnection) { |
| netVConnection->do_io_close(); |
| } |
| |
| if (!action_.cancelled) { |
| if (lerrno || !netVConnection) { |
| Debug("Socks", "retryevent: Sent errno %d to HTTP", lerrno); |
| NET_INCREMENT_DYN_STAT(socks_connections_unsuccessful_stat); |
| action_.continuation->handleEvent(NET_EVENT_OPEN_FAILED, (void *)static_cast<intptr_t>(-lerrno)); |
| } else { |
| netVConnection->do_io_read(this, 0, nullptr); |
| netVConnection->do_io_write(this, 0, nullptr); |
| netVConnection->action_ = action_; // assign the original continuation |
| netVConnection->con.setRemote(&server_addr.sa); |
| Debug("Socks", "Sent success to HTTP"); |
| NET_INCREMENT_DYN_STAT(socks_connections_successful_stat); |
| action_.continuation->handleEvent(NET_EVENT_OPEN, netVConnection); |
| } |
| } |
| #ifdef SOCKS_WITH_TS |
| SocksServerConfig::release(server_params); |
| #endif |
| |
| free_MIOBuffer(buf); |
| action_ = nullptr; |
| mutex = nullptr; |
| socksAllocator.free(this); |
| } |
| |
| int |
| SocksEntry::startEvent(int event, void *data) |
| { |
| if (event == NET_EVENT_OPEN) { |
| netVConnection = static_cast<SocksNetVC *>(data); |
| |
| if (version == SOCKS5_VERSION) { |
| auth_handler = &socks5BasicAuthHandler; |
| } |
| |
| SET_HANDLER((SocksEntryHandler)&SocksEntry::mainEvent); |
| mainEvent(NET_EVENT_OPEN, data); |
| } else { |
| if (timeout) { |
| timeout->cancel(this); |
| timeout = nullptr; |
| } |
| |
| char buff[INET6_ADDRPORTSTRLEN]; |
| Debug("Socks", "Failed to connect to %s", ats_ip_nptop(&server_addr.sa, buff, sizeof(buff))); |
| |
| findServer(); |
| |
| if (!ats_is_ip(&server_addr)) { |
| Debug("Socks", "Unable to open connection to the SOCKS server"); |
| lerrno = ESOCK_NO_SOCK_SERVER_CONN; |
| free(); |
| return EVENT_CONT; |
| } |
| |
| if (timeout) { |
| timeout->cancel(this); |
| timeout = nullptr; |
| } |
| |
| if (netVConnection) { |
| netVConnection->do_io_close(); |
| netVConnection = nullptr; |
| } |
| |
| timeout = this_ethread()->schedule_in(this, HRTIME_SECONDS(netProcessor.socks_conf_stuff->server_connect_timeout)); |
| |
| write_done = false; |
| |
| NetVCOptions options; |
| options.socks_support = NO_SOCKS; |
| netProcessor.connect_re(this, &server_addr.sa, &options); |
| } |
| |
| return EVENT_CONT; |
| } |
| |
| int |
| SocksEntry::mainEvent(int event, void *data) |
| { |
| int ret = EVENT_DONE; |
| int n_bytes = 0; |
| unsigned char *p; |
| |
| switch (event) { |
| case NET_EVENT_OPEN: |
| buf->reset(); |
| unsigned short ts; |
| p = reinterpret_cast<unsigned char *>(buf->start()); |
| ink_assert(netVConnection); |
| |
| if (auth_handler) { |
| n_bytes = invokeSocksAuthHandler(auth_handler, SOCKS_AUTH_OPEN, p); |
| } else { |
| // Debug("Socks", " Got NET_EVENT_OPEN to SOCKS server"); |
| |
| p[n_bytes++] = version; |
| p[n_bytes++] = (socks_cmd == NORMAL_SOCKS) ? SOCKS_CONNECT : socks_cmd; |
| ts = ats_ip_port_cast(&target_addr); |
| |
| if (version == SOCKS5_VERSION) { |
| p[n_bytes++] = 0; // Reserved |
| if (ats_is_ip4(&target_addr)) { |
| p[n_bytes++] = 1; // IPv4 addr |
| memcpy(p + n_bytes, &target_addr.sin.sin_addr, 4); |
| n_bytes += 4; |
| } else if (ats_is_ip6(&target_addr)) { |
| p[n_bytes++] = 4; // IPv6 addr |
| memcpy(p + n_bytes, &target_addr.sin6.sin6_addr, TS_IP6_SIZE); |
| n_bytes += TS_IP6_SIZE; |
| } else { |
| Debug("Socks", "SOCKS supports only IP addresses."); |
| } |
| } |
| |
| memcpy(p + n_bytes, &ts, 2); |
| n_bytes += 2; |
| |
| if (version == SOCKS4_VERSION) { |
| if (ats_is_ip4(&target_addr)) { |
| // for socks4, ip addr is after the port |
| memcpy(p + n_bytes, &target_addr.sin.sin_addr, 4); |
| n_bytes += 4; |
| |
| p[n_bytes++] = 0; // nullptr |
| } else { |
| Debug("Socks", "SOCKS v4 supports only IPv4 addresses."); |
| } |
| } |
| } |
| |
| buf->fill(n_bytes); |
| |
| if (!timeout) { |
| /* timeout would be already set when we come here from StartEvent() */ |
| timeout = this_ethread()->schedule_in(this, HRTIME_SECONDS(netProcessor.socks_conf_stuff->socks_timeout)); |
| } |
| |
| netVConnection->do_io_write(this, n_bytes, reader, false); |
| // Debug("Socks", "Sent the request to the SOCKS server"); |
| |
| ret = EVENT_CONT; |
| break; |
| |
| case VC_EVENT_WRITE_READY: |
| |
| ret = EVENT_CONT; |
| break; |
| |
| case VC_EVENT_WRITE_COMPLETE: |
| if (timeout) { |
| timeout->cancel(this); |
| timeout = nullptr; |
| write_done = true; |
| } |
| |
| buf->reset(); // Use the same buffer for a read now |
| |
| if (auth_handler) { |
| n_bytes = invokeSocksAuthHandler(auth_handler, SOCKS_AUTH_WRITE_COMPLETE, nullptr); |
| } else if (socks_cmd == NORMAL_SOCKS) { |
| n_bytes = (version == SOCKS5_VERSION) ? SOCKS5_REP_LEN : SOCKS4_REP_LEN; |
| } else { |
| Debug("Socks", "Tunnelling the connection"); |
| // let the client handle the response |
| free(); |
| break; |
| } |
| |
| timeout = this_ethread()->schedule_in(this, HRTIME_SECONDS(netProcessor.socks_conf_stuff->socks_timeout)); |
| |
| netVConnection->do_io_read(this, n_bytes, buf); |
| |
| ret = EVENT_DONE; |
| break; |
| |
| case VC_EVENT_READ_READY: |
| ret = EVENT_CONT; |
| |
| if (version == SOCKS5_VERSION && auth_handler == nullptr) { |
| VIO *vio = static_cast<VIO *>(data); |
| p = reinterpret_cast<unsigned char *>(buf->start()); |
| |
| if (vio->ndone >= 5) { |
| int reply_len; |
| |
| switch (p[3]) { |
| case SOCKS_ATYPE_IPV4: |
| reply_len = 10; |
| break; |
| case SOCKS_ATYPE_FQHN: |
| reply_len = 7 + p[4]; |
| break; |
| case SOCKS_ATYPE_IPV6: |
| Debug("Socks", "Who is using IPv6 Addr?"); |
| reply_len = 22; |
| break; |
| default: |
| reply_len = INT_MAX; |
| Debug("Socks", "Illegal address type(%d) in Socks server", (int)p[3]); |
| } |
| |
| if (vio->ndone >= reply_len) { |
| vio->nbytes = vio->ndone; |
| ret = EVENT_DONE; |
| } |
| } |
| } |
| |
| if (ret == EVENT_CONT) { |
| break; |
| } |
| // Fall Through |
| case VC_EVENT_READ_COMPLETE: |
| if (timeout) { |
| timeout->cancel(this); |
| timeout = nullptr; |
| } |
| // Debug("Socks", "Successfully read the reply from the SOCKS server"); |
| p = reinterpret_cast<unsigned char *>(buf->start()); |
| |
| if (auth_handler) { |
| SocksAuthHandler temp = auth_handler; |
| |
| if (invokeSocksAuthHandler(auth_handler, SOCKS_AUTH_READ_COMPLETE, p) < 0) { |
| lerrno = ESOCK_DENIED; |
| free(); |
| } else if (auth_handler != temp) { |
| // here either authorization is done or there is another |
| // stage left. |
| mainEvent(NET_EVENT_OPEN, nullptr); |
| } |
| |
| } else { |
| bool success; |
| if (version == SOCKS5_VERSION) { |
| success = (p[0] == SOCKS5_VERSION && p[1] == SOCKS5_REQ_GRANTED); |
| Debug("Socks", "received reply of length %" PRId64 " addr type %d", ((VIO *)data)->ndone, (int)p[3]); |
| } else { |
| success = (p[0] == 0 && p[1] == SOCKS4_REQ_GRANTED); |
| } |
| |
| // ink_assert(*(p) == 0); |
| if (!success) { // SOCKS request failed |
| Debug("Socks", "Socks request denied %d", (int)*(p + 1)); |
| lerrno = ESOCK_DENIED; |
| } else { |
| Debug("Socks", "Socks request successful %d", (int)*(p + 1)); |
| lerrno = 0; |
| } |
| free(); |
| } |
| |
| break; |
| |
| case EVENT_INTERVAL: |
| timeout = nullptr; |
| if (write_done) { |
| lerrno = ESOCK_TIMEOUT; |
| free(); |
| break; |
| } |
| /* else |
| This is server_connect_timeout. So we treat this as server being |
| down. |
| Should cancel any pending connect() action. Important on windows |
| */ |
| // fallthrough |
| |
| case VC_EVENT_ERROR: |
| /*This is mostly ECONNREFUSED on Unix */ |
| SET_HANDLER(&SocksEntry::startEvent); |
| startEvent(NET_EVENT_OPEN_FAILED, nullptr); |
| break; |
| |
| case VC_EVENT_EOS: |
| case VC_EVENT_INACTIVITY_TIMEOUT: |
| case VC_EVENT_ACTIVE_TIMEOUT: |
| Debug("Socks", "VC_EVENT error: %s", get_vc_event_name(event)); |
| lerrno = ESOCK_NO_SOCK_SERVER_CONN; |
| free(); |
| break; |
| default: |
| // BUGBUG:: could be active/inactivity timeout ... |
| ink_assert(!"bad case value"); |
| Debug("Socks", "Bad Case/Net Error Event"); |
| lerrno = ESOCK_NO_SOCK_SERVER_CONN; |
| free(); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| void |
| loadSocksConfiguration(socks_conf_struct *socks_conf_stuff) |
| { |
| int socks_config_fd = -1; |
| ats_scoped_str config_pathname; |
| #ifdef SOCKS_WITH_TS |
| char *tmp; |
| #endif |
| |
| socks_conf_stuff->accept_enabled = 0; // initialize it INKqa08593 |
| socks_conf_stuff->socks_needed = REC_ConfigReadInteger("proxy.config.socks.socks_needed"); |
| if (!socks_conf_stuff->socks_needed) { |
| Debug("Socks", "Socks Turned Off"); |
| return; |
| } |
| |
| socks_conf_stuff->default_version = REC_ConfigReadInteger("proxy.config.socks.socks_version"); |
| Debug("Socks", "Socks Version %d", socks_conf_stuff->default_version); |
| |
| if (socks_conf_stuff->default_version != 4 && socks_conf_stuff->default_version != 5) { |
| Error("SOCKS Config: Unsupported Version: %d. SOCKS Turned off", socks_conf_stuff->default_version); |
| goto error; |
| } |
| |
| socks_conf_stuff->server_connect_timeout = REC_ConfigReadInteger("proxy.config.socks.server_connect_timeout"); |
| socks_conf_stuff->socks_timeout = REC_ConfigReadInteger("proxy.config.socks.socks_timeout"); |
| Debug("Socks", "server connect timeout: %d socks response timeout %d", socks_conf_stuff->server_connect_timeout, |
| socks_conf_stuff->socks_timeout); |
| |
| socks_conf_stuff->per_server_connection_attempts = REC_ConfigReadInteger("proxy.config.socks.per_server_connection_attempts"); |
| socks_conf_stuff->connection_attempts = REC_ConfigReadInteger("proxy.config.socks.connection_attempts"); |
| |
| socks_conf_stuff->accept_enabled = REC_ConfigReadInteger("proxy.config.socks.accept_enabled"); |
| socks_conf_stuff->accept_port = REC_ConfigReadInteger("proxy.config.socks.accept_port"); |
| socks_conf_stuff->http_port = REC_ConfigReadInteger("proxy.config.socks.http_port"); |
| Debug("SocksProxy", |
| "Read SocksProxy info: accept_enabled = %d " |
| "accept_port = %d http_port = %d", |
| socks_conf_stuff->accept_enabled, socks_conf_stuff->accept_port, socks_conf_stuff->http_port); |
| |
| #ifdef SOCKS_WITH_TS |
| SocksServerConfig::startup(); |
| #endif |
| |
| config_pathname = RecConfigReadConfigPath("proxy.config.socks.socks_config_file"); |
| Debug("Socks", "Socks Config File: %s", (const char *)config_pathname); |
| |
| if (!config_pathname) { |
| Error("SOCKS Config: could not read config file name. SOCKS Turned off"); |
| goto error; |
| } |
| |
| socks_config_fd = ::open(config_pathname, O_RDONLY); |
| |
| if (socks_config_fd < 0) { |
| Error("SOCKS Config: could not open config file '%s'. SOCKS Turned off", (const char *)config_pathname); |
| goto error; |
| } |
| #ifdef SOCKS_WITH_TS |
| tmp = Load_IpMap_From_File(&socks_conf_stuff->ip_map, socks_config_fd, "no_socks"); |
| // tmp = socks_conf_stuff->ip_range.read_table_from_file(socks_config_fd, "no_socks"); |
| |
| if (tmp) { |
| Error("SOCKS Config: Error while reading ip_range: %s.", tmp); |
| ats_free(tmp); |
| goto error; |
| } |
| #endif |
| |
| if (loadSocksAuthInfo(socks_config_fd, socks_conf_stuff) != 0) { |
| Error("SOCKS Config: Error while reading Socks auth info"); |
| goto error; |
| } |
| Debug("Socks", "Socks Turned on"); |
| ::close(socks_config_fd); |
| |
| return; |
| error: |
| |
| socks_conf_stuff->socks_needed = 0; |
| socks_conf_stuff->accept_enabled = 0; |
| if (socks_config_fd >= 0) { |
| ::close(socks_config_fd); |
| } |
| } |
| |
| int |
| loadSocksAuthInfo(int fd, socks_conf_struct *socks_stuff) |
| { |
| char c = '\0'; |
| char line[256] = {0}; // initialize all chars to nil |
| char user_name[256] = {0}; |
| char passwd[256] = {0}; |
| |
| if (lseek(fd, 0, SEEK_SET) < 0) { |
| Warning("Can not seek on Socks configuration file\n"); |
| return -1; |
| } |
| |
| bool end_of_file = false; |
| do { |
| int n = 0, rc; |
| while (((rc = read(fd, &c, 1)) == 1) && (c != '\n') && (n < 254)) { |
| line[n++] = c; |
| } |
| if (rc <= 0) { |
| end_of_file = true; |
| } |
| line[n] = '\0'; |
| |
| // coverity[secure_coding] |
| rc = sscanf(line, " auth u %255s %255s ", user_name, passwd); |
| if (rc >= 2) { |
| int len1 = strlen(user_name); |
| int len2 = strlen(passwd); |
| |
| Debug("Socks", "Read user_name(%s) and passwd(%s) from config file", user_name, passwd); |
| |
| socks_stuff->user_name_n_passwd_len = len1 + len2 + 2; |
| |
| char *ptr = static_cast<char *>(ats_malloc(socks_stuff->user_name_n_passwd_len)); |
| ptr[0] = len1; |
| memcpy(&ptr[1], user_name, len1); |
| ptr[len1 + 1] = len2; |
| memcpy(&ptr[len1 + 2], passwd, len2); |
| |
| socks_stuff->user_name_n_passwd = ptr; |
| |
| return 0; |
| } |
| } while (!end_of_file); |
| |
| return 0; |
| } |
| |
| int |
| socks5BasicAuthHandler(int event, unsigned char *p, void (**h_ptr)(void)) |
| { |
| // for more info on Socks5 see RFC 1928 |
| int ret = 0; |
| char *pass_phrase = netProcessor.socks_conf_stuff->user_name_n_passwd; |
| |
| switch (event) { |
| case SOCKS_AUTH_OPEN: |
| p[ret++] = SOCKS5_VERSION; // version |
| p[ret++] = (pass_phrase) ? 2 : 1; //#Methods |
| p[ret++] = 0; // no authentication |
| if (pass_phrase) { |
| p[ret++] = 2; |
| } |
| |
| break; |
| |
| case SOCKS_AUTH_WRITE_COMPLETE: |
| // return number of bytes to read |
| ret = 2; |
| break; |
| |
| case SOCKS_AUTH_READ_COMPLETE: |
| |
| if (p[0] == SOCKS5_VERSION) { |
| switch (p[1]) { |
| case 0: // no authentication required |
| Debug("Socks", "No authentication required for Socks server"); |
| // make sure this is ok for us. right now it is always ok for us. |
| *h_ptr = nullptr; |
| break; |
| |
| case 2: |
| Debug("Socks", "Socks server wants username/passwd"); |
| if (!pass_phrase) { |
| Debug("Socks", "Buggy Socks server: asks for username/passwd " |
| "when not supplied as an option"); |
| ret = -1; |
| *h_ptr = nullptr; |
| } else { |
| *reinterpret_cast<SocksAuthHandler *>(h_ptr) = &socks5PasswdAuthHandler; |
| } |
| |
| break; |
| |
| case 0xff: |
| Debug("Socks", "None of the Socks authentications is acceptable " |
| "to the server"); |
| *h_ptr = nullptr; |
| ret = -1; |
| break; |
| |
| default: |
| Debug("Socks", "Unexpected Socks auth method (%d) from the server", (int)p[1]); |
| ret = -1; |
| break; |
| } |
| } else { |
| Debug("Socks", "authEvent got wrong version %d from the Socks server", (int)p[0]); |
| ret = -1; |
| } |
| |
| break; |
| |
| default: |
| // This should be impossible |
| ink_assert(!"bad case value"); |
| ret = -1; |
| break; |
| } |
| return ret; |
| } |
| |
| int |
| socks5PasswdAuthHandler(int event, unsigned char *p, void (**h_ptr)(void)) |
| { |
| // for more info see RFC 1929 |
| int ret = 0; |
| char *pass_phrase; |
| int pass_len; |
| |
| switch (event) { |
| case SOCKS_AUTH_OPEN: |
| pass_phrase = netProcessor.socks_conf_stuff->user_name_n_passwd; |
| pass_len = netProcessor.socks_conf_stuff->user_name_n_passwd_len; |
| ink_assert(pass_phrase); |
| |
| p[0] = 1; // version |
| memcpy(&p[1], pass_phrase, pass_len); |
| |
| ret = 1 + pass_len; |
| break; |
| |
| case SOCKS_AUTH_WRITE_COMPLETE: |
| // return number of bytes to read |
| ret = 2; |
| break; |
| |
| case SOCKS_AUTH_READ_COMPLETE: |
| // NEC thinks it is 5 RFC seems to indicate 1. |
| switch (p[1]) { |
| case 0: |
| Debug("Socks", "Username/Passwd succeeded"); |
| *h_ptr = nullptr; |
| break; |
| |
| default: |
| Debug("Socks", "Username/Passwd authentication failed ret_code: %d", (int)p[1]); |
| ret = -1; |
| } |
| |
| break; |
| |
| default: |
| ink_assert(!"bad case value"); |
| ret = -1; |
| break; |
| } |
| return ret; |
| } |