GolangSOCKS代理设置与认证教程
在IT行业这个发展更新速度很快的行业,只有不停止的学习,才不会被行业所淘汰。如果你是Golang学习者,那么本文《Golang SOCKS代理实现与认证配置方法》就很适合你!本篇内容主要包括##content_title##,希望对大家的知识积累有所帮助,助力实战开发!
答案:SOCKS5认证流程包括客户端发送支持的认证方法,服务器选择用户密码认证(0x02)并响应,随后客户端发送用户名密码进行验证,服务器校验凭据后返回成功或失败状态,认证通过后进入连接请求阶段。

用Golang实现一个支持认证的SOCKS代理,核心在于理解SOCKS5协议的握手、认证和连接请求流程,并用Go的并发特性高效地处理网络连接和数据转发。这不仅仅是代码的堆砌,更是对网络协议细节的一种深度理解和实践。
实现SOCKS5代理,特别是带认证的,主要围绕几个关键步骤展开:监听客户端连接、处理SOCKS5协议的初始握手、根据协议协商进行用户密码认证(如果启用),最后解析客户端的连接请求并建立到目标地址的连接,双向转发数据。
解决方案
package main
import (
"fmt"
"io"
"log"
"net"
"strconv"
"time"
)
// SOCKS5协议常量
const (
SOCKS5Version = 0x05
AuthNoAuth = 0x00
AuthUserPass = 0x02
CmdConnect = 0x01
AtypIPv4 = 0x01
AtypDomain = 0x03
AtypIPv6 = 0x04
ReplySuccess = 0x00
ReplyFailure = 0x01 // General SOCKS server failure
ReplyAuthFail = 0x02 // Connection not allowed by rule set (used for auth fail)
)
// UserCredentials 存储用户凭据
var UserCredentials = map[string]string{
"admin": "password123",
"user": "securepass",
}
func main() {
listener, err := net.Listen("tcp", ":1080")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
defer listener.Close()
log.Println("SOCKS5 proxy listening on :1080")
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("Failed to accept connection: %v", err)
continue
}
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
conn.SetDeadline(time.Now().Add(10 * time.Second)) // 设置握手超时
// 阶段1: 握手
// 客户端发送: VER NMETHODS METHODS...
buf := make([]byte, 257) // VER(1) + NMETHODS(1) + METHODS(255)
n, err := io.ReadFull(conn, buf[:2])
if err != nil || n != 2 {
log.Printf("Failed to read SOCKS5 handshake initial: %v", err)
return
}
if buf[0] != SOCKS5Version {
log.Printf("Unsupported SOCKS version: %x", buf[0])
return
}
nmethods := int(buf[1])
n, err = io.ReadFull(conn, buf[2:2+nmethods])
if err != nil || n != nmethods {
log.Printf("Failed to read SOCKS5 methods: %v", err)
return
}
// 选择认证方法
var authMethod byte = AuthNoAuth // 默认无认证
foundAuthMethod := false
for i := 0; i < nmethods; i++ {
if buf[2+i] == AuthUserPass {
authMethod = AuthUserPass
foundAuthMethod = true
break
}
}
// 如果客户端没提供我们支持的认证方法,我们也不支持无认证,就直接拒绝
if !foundAuthMethod && authMethod == AuthUserPass { // 如果我们期望用户密码认证,但客户端不支持
conn.Write([]byte{SOCKS5Version, 0xFF}) // 0xFF表示无可接受方法
log.Println("Client did not offer supported authentication method.")
return
}
// 服务器响应: VER METHOD
_, err = conn.Write([]byte{SOCKS5Version, authMethod})
if err != nil {
log.Printf("Failed to write SOCKS5 method response: %v", err)
return
}
// 阶段2: 认证 (如果需要)
if authMethod == AuthUserPass {
if !authenticate(conn) {
log.Println("Authentication failed.")
return // 认证失败,连接关闭
}
log.Println("Authentication successful.")
}
conn.SetDeadline(time.Now().Add(10 * time.Second)) // 重置超时
// 阶段3: 请求
// 客户端发送: VER CMD RSV ATYP DST.ADDR DST.PORT
n, err = io.ReadFull(conn, buf[:4])
if err != nil || n != 4 {
log.Printf("Failed to read SOCKS5 request initial: %v", err)
return
}
if buf[0] != SOCKS5Version || buf[2] != 0x00 { // VER 和 RSV 检查
log.Printf("Invalid SOCKS5 request header: %x %x", buf[0], buf[2])
return
}
cmd := buf[1]
atyp := buf[3]
var dstAddr string
var dstPort int
switch atyp {
case AtypIPv4:
n, err = io.ReadFull(conn, buf[:4]) // IPv4地址
if err != nil || n != 4 {
log.Printf("Failed to read IPv4 address: %v", err)
return
}
dstAddr = net.IPv4(buf[0], buf[1], buf[2], buf[3]).String()
case AtypDomain:
n, err = io.ReadFull(conn, buf[:1]) // 域名长度
if err != nil || n != 1 {
log.Printf("Failed to read domain length: %v", err)
return
}
domainLen := int(buf[0])
n, err = io.ReadFull(conn, buf[1:1+domainLen]) // 域名
if err != nil || n != domainLen {
log.Printf("Failed to read domain: %v", err)
return
}
dstAddr = string(buf[1 : 1+domainLen])
case AtypIPv6:
n, err = io.ReadFull(conn, buf[:16]) // IPv6地址
if err != nil || n != 16 {
log.Printf("Failed to read IPv6 address: %v", err)
return
}
dstAddr = net.IP(buf[:16]).String()
default:
log.Printf("Unsupported address type: %x", atyp)
conn.Write([]byte{SOCKS5Version, ReplyFailure, 0x00, AtypIPv4, 0, 0, 0, 0, 0, 0}) // 返回通用失败
return
}
n, err = io.ReadFull(conn, buf[:2]) // 端口
if err != nil || n != 2 {
log.Printf("Failed to read port: %v", err)
return
}
dstPort = int(buf[0])<<8 | int(buf[1])
targetAddr := fmt.Sprintf("%s:%d", dstAddr, dstPort)
log.Printf("Client requests to connect to: %s", targetAddr)
// 只处理CONNECT命令
if cmd != CmdConnect {
log.Printf("Unsupported SOCKS command: %x", cmd)
conn.Write([]byte{SOCKS5Version, ReplyFailure, 0x00, AtypIPv4, 0, 0, 0, 0, 0, 0})
return
}
// 连接目标服务器
targetConn, err := net.DialTimeout("tcp", targetAddr, 5*time.Second)
if err != nil {
log.Printf("Failed to connect to target %s: %v", targetAddr, err)
conn.Write([]byte{SOCKS5Version, ReplyFailure, 0x00, AtypIPv4, 0, 0, 0, 0, 0, 0}) // 连接失败
return
}
defer targetConn.Close()
// 服务器响应: VER REP RSV ATYP BND.ADDR BND.PORT
// 成功连接,返回SOCKS5成功响应
localAddr := targetConn.LocalAddr().(*net.TCPAddr)
bndAddr := localAddr.IP
bndPort := localAddr.Port
resp := []byte{SOCKS5Version, ReplySuccess, 0x00, AtypIPv4, bndAddr[0], bndAddr[1], bndAddr[2], bndAddr[3], byte(bndPort >> 8), byte(bndPort & 0xFF)}
if bndAddr.To4() == nil { // 如果是IPv6地址,需要调整响应格式
// 简单处理,这里假设我们只返回IPv4的绑定地址
// 实际应根据绑定地址类型返回对应的ATYP和地址长度
resp = []byte{SOCKS5Version, ReplySuccess, 0x00, AtypIPv4, 0, 0, 0, 0, 0, 0} // 简化为0.0.0.0
if bndAddr.To16() != nil {
resp = []byte{SOCKS5Version, ReplySuccess, 0x00, AtypIPv6}
resp = append(resp, bndAddr...)
resp = append(resp, byte(bndPort>>8), byte(bndPort&0xFF))
}
}
_, err = conn.Write(resp)
if err != nil {
log.Printf("Failed to write SOCKS5 success response: %v", err)
return
}
// 阶段4: 数据转发
conn.SetDeadline(time.Time{}) // 移除超时
targetConn.SetDeadline(time.Time{})
log.Printf("Relaying data between client and %s", targetAddr)
relay(conn, targetConn)
}
// authenticate 处理SOCKS5用户名/密码认证
func authenticate(conn net.Conn) bool {
// 认证子协议: VER ULEN UNAME PLEN PASSWD
buf := make([]byte, 257) // VER(1) + ULEN(1) + UNAME(255)
n, err := io.ReadFull(conn, buf[:2])
if err != nil || n != 2 {
log.Printf("Auth: Failed to read initial auth header: %v", err)
return false
}
if buf[0] != 0x01 { // 认证子协议版本,固定为0x01
log.Printf("Auth: Unsupported auth sub-negotiation version: %x", buf[0])
return false
}
usernameLen := int(buf[1])
n, err = io.ReadFull(conn, buf[2:2+usernameLen])
if err != nil || n != usernameLen {
log.Printf("Auth: Failed to read username: %v", err)
return false
}
username := string(buf[2 : 2+usernameLen])
// PLEN PASSWD
n, err = io.ReadFull(conn, buf[2+usernameLen : 2+usernameLen+2]) // PLEN(1) + PASSWD_INITIAL_BYTE(1)
if err != nil || n != 2 {
log.Printf("Auth: Failed to read password length initial: %v", err)
return false
}
passwordLen := int(buf[2+usernameLen])
n, err = io.ReadFull(conn, buf[2+usernameLen+1 : 2+usernameLen+1+passwordLen])
if err != nil || n != passwordLen {
log.Printf("Auth: Failed to read password: %v", err)
return false
}
password := string(buf[2+usernameLen+1 : 2+usernameLen+1+passwordLen])
// 验证凭据
if UserCredentials[username] == password {
conn.Write([]byte{0x01, ReplySuccess}) // 认证成功
return true
} else {
conn.Write([]byte{0x01, ReplyAuthFail}) // 认证失败
return false
}
}
// relay 在两个连接之间双向转发数据
func relay(left, right net.Conn) {
ch := make(chan struct{})
go func() {
_, _ = io.Copy(left, right)
ch <- struct{}{}
}()
go func() {
_, _ = io.Copy(right, left)
ch <- struct{}{}
}()
<-ch // 等待一个方向的传输结束
// 另一个方向可能还在传输,但通常一个方向关闭,另一个也会很快关闭
// 简单起见,这里不再等待第二个ch,因为io.Copy遇到EOF或错误都会返回
// 实际生产环境可能需要更精细的关闭逻辑
}SOCKS5协议认证流程是怎样的?
SOCKS5协议的认证流程,说实话,一开始看协议文档可能会觉得有点枯燥,但一旦跑起来,你会发现它其实挺精巧的。它不是一上来就让你输密码,而是先有个“协商”阶段。
具体来说,当客户端尝试连接SOCKS5代理时,第一件事是发送一个初始握手包。这个包里包含了SOCKS版本(固定是0x05),然后是客户端支持的认证方法数量,紧接着就是一串它能接受的认证方法列表。比如,它可能会说:“我支持无认证(0x00),也支持用户名/密码认证(0x02)。”
代理服务器收到这个包后,会从客户端提供的列表中选择一个它也支持且偏好的认证方法。如果代理配置了必须使用用户名/密码认证,而客户端又提供了这个选项,那代理就会选择0x02。选定之后,代理会回一个响应包给客户端,告诉它:“嘿,我们决定用这个方法认证。”
如果协商的结果是“无认证”(0x00),那接下来的事情就简单了,直接进入请求阶段。但如果协商结果是“用户名/密码认证”(0x02),那么协议就会进入一个独立的认证子协议。
在这个子协议里,客户端会发送另一个包,里面包含了用户名和密码。这个包也有自己的格式:一个子协议版本号(通常是0x01),然后是用户名的长度和用户名本身,接着是密码的长度和密码本身。代理服务器拿到这些信息后,就会去验证这对凭据是否有效。
验证成功,代理会回复一个“认证成功”的包;验证失败,则回复“认证失败”。只有认证成功了,客户端才能继续发送它真正的连接请求(比如,我想连接到哪个网站的哪个端口)。这个设计挺好的,它把认证和实际的连接请求分开了,让协议更灵活。
如何在Golang中处理SOCKS5的连接请求和数据转发?
在Golang里处理SOCKS5的连接请求和数据转发,我觉得最棒的地方就是它对并发和网络IO的原生支持。net包和io包简直是为这种场景量身定做的。
当你完成认证(或者跳过认证)之后,客户端就会发送它的连接请求包。这个包里包含了SOCKS版本、命令(通常是CONNECT,也就是连接一个远程地址)、保留字节、地址类型(IPv4、域名还是IPv6),以及目标地址和端口。
在Go里面,你需要做的就是:
- 解析请求: 读入客户端发送的字节流,根据SOCKS5协议的规定,逐字节地解析出目标地址类型、目标地址和目标端口。这里需要注意处理不同地址类型(IPv4、域名、IPv6)的字节长度差异。域名解析的话,Go的
net.Dial通常能自动处理,但你需要先把域名字符串提取出来。 - 建立目标连接: 一旦你拿到了目标地址和端口,就可以使用
net.Dial或者更推荐的net.DialTimeout来尝试连接到这个目标。使用DialTimeout是个好习惯,可以避免因为目标服务器无响应而导致代理线程长时间阻塞。 - 发送响应: 无论连接目标成功还是失败,你都需要根据SOCKS5协议,给客户端发送一个响应包,告诉它连接的结果。成功就是0x00,失败则对应不同的错误码。
- 数据转发: 这是整个代理的核心。一旦客户端和目标服务器都连接上了,你就需要把客户端发来的数据转发给目标服务器,同时把目标服务器返回的数据转发回给客户端。Go的
io.Copy函数在这里简直是神器,它能高效地在两个io.Reader和io.Writer之间复制数据。你通常会启动两个goroutine,一个负责客户端 -> 目标,另一个负责目标 -> 客户端,这样就能实现双向的数据流。当任何一边的连接关闭或出错时,io.Copy会自动返回,从而让对应的goroutine结束,最终导致整个连接的关闭。这种方式简洁高效,避免了手动管理大量缓冲区的复杂性。
当然,整个过程中,错误处理是必不可少的,任何一步出错都应该及时记录日志并关闭连接,避免资源泄露。
如何为SOCKS代理添加用户认证功能?
给SOCKS代理添加用户认证功能,其实就是把前面说的SOCKS5协议的“认证子协议”那部分逻辑实现出来。对我来说,这就像是给你的房子加一道门禁,不是谁都能进来的。
核心思路是:
- 定义用户凭据存储: 最简单的方式,你可以用一个Go的
map[string]string来存储用户名:密码的键值对。例如,map[string]string{"admin": "password123", "user": "securepass"}。当然,在实际生产环境中,这些凭据应该存储在配置文件、环境变量、数据库,甚至更安全的秘密管理服务中,并且密码绝对不能是明文,至少要经过哈希处理(虽然SOCKS5协议本身传输是明文,但代理内部存储要安全)。 - 实现认证逻辑:
- 读取认证请求: 当SOCKS5握手阶段协商确定使用“用户名/密码认证”后,你的代理就需要从客户端连接中读取下一个认证请求包。这个包的格式是固定的:一个版本号(0x01),然后是用户名的长度、用户名本身,接着是密码的长度、密码本身。你需要精确地读取这些字节,并解析出用户名和密码字符串。
- 验证凭据: 拿到用户名和密码后,你就拿它们去和你的预设存储进行比对。如果
UserCredentials[username] == password,那么认证就成功了。 - 发送认证响应: 无论验证结果如何,你都必须按照SOCKS5认证子协议的规定,给客户端发送一个响应:
- 认证成功:发送
0x01(版本号)和0x00(成功状态码)。 - 认证失败:发送
0x01和0x01(失败状态码)。
- 认证成功:发送
- 处理认证失败: 如果认证失败,你的代理应该立即关闭这个客户端连接,不让它进行后续的连接请求。这就像门禁系统,刷卡失败就无法开门。
这个认证功能虽然增加了代码量,但它极大地提升了代理的安全性,避免了任何人都能通过你的代理访问网络,这在很多场景下都是一个基本要求。
到这里,我们也就讲完了《GolangSOCKS代理设置与认证教程》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于golang,认证,数据转发,SOCKS代理,SOCKS5协议的知识点!
微信收款码申请教程:个人如何开通收款功能
- 上一篇
- 微信收款码申请教程:个人如何开通收款功能
- 下一篇
- Java图书借阅查询功能实现教程
-
- Golang · Go教程 | 4小时前 |
- Go语言实现与外部程序持续通信技巧
- 229浏览 收藏
-
- Golang · Go教程 | 4小时前 |
- GolangWeb错误处理技巧分享
- 190浏览 收藏
-
- Golang · Go教程 | 4小时前 |
- Go语言error接口错误返回实例解析
- 324浏览 收藏
-
- Golang · Go教程 | 4小时前 |
- Golang模板方法模式实战解析
- 180浏览 收藏
-
- Golang · Go教程 | 4小时前 | golang dockercompose 健康检查 多阶段构建 启动优化
- Golang优化Docker多容器启动技巧
- 228浏览 收藏
-
- Golang · Go教程 | 5小时前 |
- 优化Golang模块缓存,提升构建效率技巧
- 483浏览 收藏
-
- Golang · Go教程 | 5小时前 |
- Go递归函数返回值处理方法
- 353浏览 收藏
-
- Golang · Go教程 | 5小时前 |
- Golang微服务容器化部署指南
- 226浏览 收藏
-
- Golang · Go教程 | 5小时前 |
- Golang静态资源管理实战指南
- 186浏览 收藏
-
- Golang · Go教程 | 5小时前 | golang 自定义函数 模板渲染 html/template 模板语法
- Golang模板渲染教程与使用详解
- 104浏览 收藏
-
- Golang · Go教程 | 5小时前 |
- Go模块版本管理全攻略
- 268浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3182次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3393次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3424次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4528次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3802次使用
-
- Golangmap实践及实现原理解析
- 2022-12-28 505浏览
-
- go和golang的区别解析:帮你选择合适的编程语言
- 2023-12-29 503浏览
-
- 试了下Golang实现try catch的方法
- 2022-12-27 502浏览
-
- 如何在go语言中实现高并发的服务器架构
- 2023-08-27 502浏览
-
- 提升工作效率的Go语言项目开发经验分享
- 2023-11-03 502浏览

