注意
由于系列文章仅是我在阅读BHG时进行的一些记录,并不是一个很严谨的教程,且本章标题会跟随书的章节目录。
TCP协议
传输控制协议(Transmission Control Protocel,TCP)是面向连接的可靠通信的主要标准,以及现代网络的基础。BHG要求我们能够从TCP的工作原理入手,开发可以识别打开/关闭的端口,通过端口转发绕过出口限制。
TCP的握手机制
分为以下三种情况
- 端口开放
如果端口是开放的,就会进行经典的三次握手:syn->syn-ack->ack; - 端口关闭
如果端口关闭,服务器会以rst数据包而非syn-ack数据包进行响应; - 防火墙过滤
如果流量被防火墙过滤,客户端通常不会从服务器收到任何响应;
编写TCP扫描器
通过编写一个端口扫描器去理解TCP握手过程以及3种端口状态,以确定TCP端口是否可用,或者已关闭和使用过滤状态进行响应
Go的net包
通过net.Dial(network, address string),启动客户端到服务端的连接
network参数:字符串类型,用于标识要启动的连接的类型。Dial不仅适用于TCP,还可以用于创建UNIX套接字、UDP和第4层协议的连接
address参数:字符串类型,标识要连接的主机。对于IPv4/TCP连接,使用host:port的形式
返回值:Conn,err;若连接成功,err为nil
示例:基本的单端口扫描器
package main
import (
"fmt"
"net"
)
func main() {
_, err := net.Dial("tcp", "scanme.namp.org:80")
if err == nil {
fmt.Println("Connection successful")
}
}
示例:非并发多端口扫描器
package main
import (
"fmt"
"net"
)
func main() {
for i := 1; i <= 1024; i++ {
address := fmt.Sprintf("scanme.nmap.org:%d", i)
connm err := net.Dial("tcp", address)
if err != nil {
continue
}
conn.Close()
fmt.Println("%d open\n", i)
}
}
示例:并发扫描器
package main
import (
"fmt"
"net"
"sync"
)
func main() {
//同步计数器
var wg sync.WaitGroup
//扫描前1024个端口
for i := 1; i < 1024; i++ {
wg.Add(1)
//启动goroutine
go func(j int) {
//延后至外层函数返回时执行
defer wg.Done()
address := fmt.Sprintf("scanme.nmap.org:%d", j)
conn, err := net.Dial("tcp", address)
if err != nil {
return
}
conn.Close()
fmt.Printf("%d open\n", j)
}(i)
}
//函数阻塞,等待同步计数器为0
wg.Wait()
}
缺点:扫描过多的主机或端口,可能会导致网络或系统限制,造成结果不正确
使用goroutine池管理正在进行的并发工作,通过for循环创建一定数量的worker goroutine作为资源池
示例:worker
import (
"fmt"
"net"
"sort"
)
//parameter:
//ports: int类型的通道,用于接收工作
//results: int类型的通道,用于接收结果
func worker(ports, results chan int){
for p := range ports {
address := fmt.Sprintf("scanme.nmap.org:%d", p)
conn, err = net.Dial("tcp", address)
if err != nil {
// 端口关闭,接收0
results <- 0
continue
}
conn.Close()
// 端口打开,接收端口号
results <- p
}
}
func main() {
//使用make函数创建通道
ports := make(chan int, 100)
results := make(chan int)
var openports []int
// cap函数计算通道的容量
// for循环启动所需数量的worker goroutine
for i := 0; i < cap(ports); i++ {
go worker(ports, results)
}
// 匿名函数
go func() {
for i := 1; i <= 1024; i++ {
ports <- i
}
}()
for i := 0; i < 1024; i++ {
port := <-results
if port != 0 {
openports = append(openports, port)
}
}
close(ports)
close(results)
// 切片排序
sort.Ints(openports)
for _, port := range openports {
fmt.Printf("%d open\n", port)
}
}
构造TCP代理
构建回显服务器(仅回显给定响应到服务器)
io.Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
io.Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
net.Conn:Go面向流的网络连接
Conn实现了针对接口Reader和Writer定义的函数Read([]byte)和Write([]byte),即Conn即是Reader也是Writer---->TCP是双向连接,可以发送或接收数据
回显服务器构建流程
1.使用net.Listen(network, address string)在特定端口上打开TCP监听器
2.listener.Accpet()等待客户端连接成功后,创建并返回一个Conn对象,使用该对象接收和发送数据
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。