Hard core recommendation (do you really understand intranet penetration) “intranet penetration”, do you really understand intranet penetration?,

Preface Intranet penetration is one of the commonly used debugging methods for programmers. We can let others access our locally launched services by running peanut shell or frp on a personal computer, and this access can not be restricted by the LAN. When we use ngrok, frp and other open source frameworks, have you ever wondered about its magical role? It is clear that the service is not deployed to the server. How on earth do programmers allow everyone to access their host in this special way? This article will focus on

The frp open source framework, for example, introduces the principle of intranet penetration. The decisive factor of whether the public IP and the intranet IP can access the server in the public network: the role of the public IP address is well-known. IP address is the identity that every Internet user will have. The role of IP address in the Internet is positioning. Through IP address, we can accurately locate the server where the required resources are located, This is for ordinary users, but for programmers, what we need is to let users locate our deployed resources through IP addresses. Since every Internet user has an IP address, why can’t users directly access the services deployed on personal PCs?.

In fact, there are two types of IP addresses: public IP and intranet IP: intranet IP is the IP address assigned by the gateway of the LAN when the user is using the LAN. Each intranet IP can actually be mapped to a port of the gateway of the current LAN (IPV4 address is realized by NAT and port mapping, the specific principle is explained below), Having an intranet IP can be accessed by other devices under the same LAN;

Public IP: If an intranet device wants to access resources under a different LAN, it must use the public IP. The public IP is the most original IP address provided by the Internet provider (ISP) without NAT conversion. Each public IP can be directly located in the Internet.

A simple example (take front-end development as an example): When we use webpack-dev-server to start a node project, in addition to the way of localhost: [port number], we can use

Intranet IP: [port number] is used to access our project, but when we use our own traffic or connect other devices that are not in the local area network of the current development device to access using intranet IP: [port number], we cannot access the reason: the intranet IP address can only be located and accessed under the current local area network, and when we want to access across the local area network, Our access request needs to be mapped to the public IP address of the other LAN, and then to the public IP address of the other LAN. Finally, the gateway of the other LAN maps it to the corresponding LAN device. However, the address we access belongs to the intranet IP address of the LAN, so we cannot locate its corresponding public IP address.

To sum up, when we want to make devices under other LANs access our local resources, what is indispensable is the rarity of public IP and public IP. Compared with intranet IP, public IP is obviously more useful than intranet IP. Why not have a public IP?.

IPV4 and IPV6 Although the concept of IPV6 has been put forward several years ago, the actual popularity is not very high. Now most network users still use IPV4 IP addresses, which is also the biggest reason for limiting the number of public IP addresses.

**IPV4: * * IPV4 consists of 32-bit binary numbers, and there are 2 ^ 32 different IPV4 addresses * * IPV6 * *: IPV6 consists of 128-bit binary numbers, and there are 2 ^ 128 different IPV6 addresses in theory

It can be seen that the number of IPV4 addresses is not enough to meet the current demand for one IP address per person of network users all over the world. Why can the current network allow so many users to surf the network at the same time? NAT (network address translation) technology The core role of network address translation technology is to realize the reuse of public IP addresses, that is, all intranet hosts share the same IP address. There are three ways to realize NAT:

Static conversion: directly convert the intranet IP address to the public IP address to form a one-to-one correspondence

Dynamic conversion: convert intranet IP addresses to public IP addresses. Unlike static conversion, dynamic conversion selects idle IP addresses in the IP pool for conversion, that is, the public IP corresponding to the same intranet IP will change each time

Port multiplexing (PAT technology): map the intranet IP to a port of the public IP, and access the public network through a port of the public IP

It can be seen that the Port Multiplexing (PAT) technology in the above three forms can alleviate the current situation of IPV4 address tension to the greatest extent, and is also the most widely used implementation method. The common point of the three NAT implementation methods is that for intranet users, their corresponding public IP is unknown, just like we can know our own house number but can’t know their own cell, So we can’t tell others our specific address accurately.

After knowing the current working mode of internal and external networks, let’s take a look at internal network penetration as a common technical means for programmers. Before that, many people may have used such methods as peanut shell, ngrok and frp to deploy some services on the network without servers for others to use. What is the principle of internal network penetration?

Analysis of the principle of intranet penetration The principle of the mainstream intranet penetration tools on the market is as follows:

It can be seen that the core principle of intranet penetration is to establish a connection between the external IP address and the internal IP address. The core principle of the commonly used peanut shell tool on the market is to rely on a server with the public IP as the transfer station of the request to achieve the purpose of accessing the intranet host from the public network. When we start the peanut shell service, the peanut shell will map the locally configured port and the port on the server, Inform the server of the request forwarding path, and the public network server of peanut shell will listen to the request of the corresponding port. When the user accesses the IP address provided by peanut shell, the public network host of the corresponding IP address of peanut shell will map to the corresponding intranet host according to the access port, and forward the request through the preconfigured service port to achieve the effect of accessing the corresponding service of the intranet host.

More C++background development technology points include C/C++, Linux, Nginx, ZeroMQ, MySQL, Redis, MongoDB, ZK, streaming media, audio and video development, Linux kernel, TCP/IP, protocols, and DPDK.

Free learning address for C/C++background development architect: the opening of C/C++Linux server/the back platform structure, the electronic teaching layer, the video data on the left side, the video data on the right side, and the communication information on the right side [article benefits]. In addition, it also collates some relevant learning materials, interview questions, teaching videos, and learning roadmap for C++background development architect. Free sharing can be clicked if necessary

“Link” free of charge

As a commercial product, the peanut shell of intranet penetration is encapsulated in a series of work such as configuring ports, so that users can use intranet penetration more quickly. However, after understanding the principle, we can fully implement the corresponding intranet penetration function through some open source frameworks and a public server. We use frp

For example, how to set up the simplest frp service server settings (frps. ini): [common] bind_ Port=7000//Fill in the server port number that the client listens to vhost_ http_ port = 8080。

//Fill in the user’s access port number here Client configuration (frpc. ini): [common] server_ Addr=x.x.x.x//Fill in the server IP address here server_ Port=7000//fill in the bind configured on the server here_ port

[web] Type=http//The protocol type of forwarding request is specified here local_ Port=80//The address of the local service startup is specified here custom_ domains = www.example.com

//You can fill in the custom domain name here (you need to configure the domain name resolution under the IP address). After we configure the above file, the user’s access request will go through the following steps:

The user’s request will be successfully sent to the corresponding intranet service through three steps: domain name resolution, public port forwarding and intranet host monitoring. Of course, frp provides more customized configuration items than peanut shell, which will not be explained in detail here. Interested readers can visit: frp Chinese document

When we use frp to configure our own intranet penetration service, we can use a server to provide public network access for a large number of intranet hosts, so as to achieve the multiplexing of public IP. The principle is similar to the PAT port multiplexing technology mentioned above. When we need to use a server temporarily, we only need to apply for two idle ports from friends who have public network servers.

Frp core code analysis This article takes http requests as an example to analyze what steps will be taken when a public network request is sent to the frp server. frps initializes funcrunServer (cfg config. ServerCommonConf)

(err error) { log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, cfg.LogMaxDays, cfg.DisableLogColor)

if cfgFile != “” { log.Info(“frps uses config file: %s”, cfgFile) } else { log.Info(“frps uses command line arguments for config”

) } // ! Important core code 1 svr, err := server.NewService(cfg) if err != nil { return err } log.Info(

“frps started successfully”) // ! Important core code 2 svr.Run() return }In frp/cmd/frps/root. go, the core code 1: server. NewService () method is

The configuration in frps is parsed, and the core code of the initialization frp server is 2: the server. Run() method starts the frp service frpc initialization for{ // ! Important core code 3 conn, session, err := svr.login()

if err != nil { xl.Warn(“login to server failed: %v”, err) // if login_ fail_ exit is true, just exit this program

// otherwise sleep a while and try again to connect to serverif svr.cfg.LoginFailExit { return

err } util.RandomSleep(10*time.Second, 0.9, 1.1) } else { // login success// ! Important core code 4

ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter) ctl.Run() svr.ctlMu.Lock() svr.ctl = ctl svr.ctlMu.Unlock()

break } }In frp/cmd/client/service. go, the core code 3: for loop continuously initiates the connection with the server. After the failure, the core code 4: After the connection is successful, the client will use the connection information to call NewControl()

Frpc communicates with frps frps initiates connection func (pxy * BaseProxy) GetWorkConnFromPool (src, dst net. Addr) (workConn net. Conn, err error)

{ xl := xlog.FromContextSafe(pxy.ctx) // try all connections from the poolfor i := 0; i < pxy.poolCount+

1; i++ { // ! Important core code 5if workConn, err=pxy. getWorkConnFn(); err != nil { xl.Warn(“failed to get work connection: %v”

, err) return } xl.Debug(“get a new work connection: [%s]”, workConn.RemoteAddr().String()) xl.Spawn().AppendPrefix(pxy.GetName()) workConn = frpNet.NewContextConn(pxy.ctx, workConn) ……

// ! Important core code 6 err := msg.WriteMsg(workConn, &msg.StartWorkConn{ ProxyName: pxy.GetName(), SrcAddr: srcAddr, SrcPort:

uint16(srcPort), DstAddr: dstAddr, DstPort: uint16(dstPort), Error: “”, }) } }

在frp/server/proxy.go中核心代码5: frps从多个连接中通过依次遍历的方式来获取第一个成功获取到的连接核心代码6:frps通过获取到的连接向 frpc 发出 &msg.StartWorkConn 的消息,告诉

frpc建立连接的相应信息frpc 响应连接func(pxy *TCPProxy)InWorkConn(conn net.Conn, m *msg.StartWorkConn) { // !important 核心代码7

HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter, conn, []

byte(pxy.clientCfg.Token), m) }在frp/client/proxy/proxy.go中核心代码7:frpc接收到frps的信息后发起 TCP 连接frps发送消息func(ctl *Control)

writer() { xl := ctl.xl deferfunc() { if err := recover(); err != nil { xl.Error(“panic error: %v”

, err) xl.Error(string(debug.Stack())) } }() defer ctl.allShutdown.Start() defer ctl.writerShutdown.Done() encWriter, err := crypto.NewWriter(ctl.conn, []

byte(ctl.serverCfg.Token)) if err != nil { xl.Error(“crypto new writer error: %v”, err) ctl.allShutdown.Start()

return } for { m, ok := <-ctl.sendCh if !ok { xl.Info(“control writer is closing”)

return } // !important 核心代码8if err := msg.WriteMsg(encWriter, m); err != nil { xl.Warn(

“write message to control connection error: %v”, err) return } } }在frp/server/control.go中

核心代码8: frps发送信息到 crypto.NewWriter() 创建的 writer 中frpc 接收和响应// !important 核心代码9func(ctl *Control)reader

() { xl := ctl.xl deferfunc() { if err := recover(); err != nil { xl.Error(“panic error: %v”

, err) xl.Error(string(debug.Stack())) } }() defer ctl.readerShutdown.Done() deferclose

(ctl.closedCh) encReader := crypto.NewReader(ctl.conn, []byte(ctl.clientCfg.Token)) for { m, err := msg.ReadMsg(encReader)

if err != nil { if err == io.EOF { xl.Debug(“read from control connection EOF”)

return } xl.Warn(“read error: %v”, err) ctl.conn.Close() return } ctl.readCh <- m } }

核心代码9: frpc 读取 frps 转发的信息到这里,我们的 frps 已经成功将公网中接收到的请求转发到 frpc 相应的端口了,这就是一个最简单的请求通过 frp 进行代理转发的流程总结本文所介绍的内网穿透技术相关的实现方式其实在我们的日常开发生活中有更多的使用场景,当我们深入了解了当前 IP 地址以及内外网的实现方式后,我们不难发现,当我们将内网穿透的图片稍加修改后就成为了我们常用的另一种功能的实现方式(VPN实现原理):。

原文作者:内网穿透你真的了解吗? – 掘金

我的科技记录 » Hard core recommendation (do you really understand intranet penetration) “intranet penetration”, do you really understand intranet penetration?,