Directory
1. Introduction to Intranet Penetration
1.1 The relationship between public network IP ports, NAT forwarding, and private network IP ports
1.2 Principles of NAT forwarding and UDP hole punching
1.3 Core code
1. Introduction to Intranet Penetration
I don’t like to talk about some academic terms in a long way, and I am confused in the end. Just use the simplest vernacular to briefly talk about the principle and function of intranet penetration.
1.1 The relationship between public network IP ports, NAT forwarding, and intranet IP ports
To understand and use intranet penetration, you must first understand how messages are exchanged between them.
For example: Enter www.baidu.com on the webpage to open Baidu. This is because you know the domain name. The domain name is mapped to the corresponding public network IP on the DNS server. The domain name is bound to the ip address. Visit www.baidu.com xxx.com is equivalent to accessing the public network IP, you can use the cmd ping command to see the returned ip address.
When we visit www.baidu.com, Baidu’s server needs to know your public network ip and port to return the information to you (the final result is that the Baidu page is displayed on the browser)
Enter www.baidu.com in the browser to return to the path of the whole process of Baidu page
Intranet IP: port -> public network IP of nat (forwarding) network cable: port -> Baidu server public network IP: port -> public network IP of nat (forwarding) network cable: port -> internal network IP: port
Among them, intranet and nat (forwarding) will refresh the port every time a request is made, and the intranet IP (the ipv4 address that can be viewed by entering ipconfig /all in cmd) is as shown below:
Those who understand the network transmission protocol should know that information transmission on the Internet requires the target address, port and response address, port
One piece of knowledge involved here is nat, because ipv4 addresses are limited, and now most of the network cables of operators are a public network corresponding to users in a region. Simply put, you use a public network ip with other people.
NAT acts as a forwarding function, so your address and port on the local computer cannot be accessed by external network users, but can be accessed by users under the same public network ip. It can be understood that a public network ip corresponds to a nat.
So why do udp hole punching? Because it is necessary to exchange information with users outside the nat.
The previous content is just an introduction, the following is the key point (look carefully)
1.2 NAT forwarding and UDP hole punching principle
What should I do when two intranets A and B want to communicate with each other? First you need to know the nat address and port of A and B
Every time you send information to the Baidu server, the address returned is actually the address and port of nat, and then nat forwards it to your intranet address and port and generates a record. The record is similar to: (nat address: port – intranet address: port ).
The nat address port and the intranet address port are bound 1 to 1 (you can draw an equal sign, The port will be refreshed every time it is called)
Because A and B are both intranets and cannot communicate directly, a public network server is needed to record the nat addresses and ports of A and B.
Then let A send a UDP message to the address of the public network server, so that a record will be left on A’s NAT A IP: port – natA public network IP: port.
Let B send a UDP message to the address of the public network server, so that a record will be left on B’s NAT B IP: port – natB public network IP: port.
In this way, the nat addresses and ports of A and B are known, and then let A and B send UDP requests to each other and leave records in nat, so that A and B can communicate through UDP.
focus! ! ! When using java new DatagramSocket(), you must use the singleton mode. Do not close it, otherwise the port mapped by nat will be different every time. Nat will not be able to obtain the correct address if it changes. I have stepped on the pit before.
1.3 Core Code
import com.alibaba.fastjson.JSON; import lombok. SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static spike.controller.CheckSyncStateController.doGet; /** * @PACKAGE_NAME: spike.schedule * @NAME: NatSchedule * @USER: spike * @DATE: 2023/5/12 10:27 * @PROJECT_NAME: Springcolud_Spike */ @Slf4j @Component public class NatSchedule { @Value("${tarurl}") private String TAR_URL; @Value("${isserver}") private boolean IS_SERVER; @Value("${key}") private String KEY; @Value("${sendport}") private Integer SEND_PORT; @Value("${receiveport}") private Integer RECEIVE_PORT; public static DatagramSocket RECEIVE_SOCKET = null; public static DatagramSocket SEND_SOCKET = null; public static Map<String, Object> NAT_MAP = new HashMap<>(); // Execute every 1 minute @PostConstruct @Scheduled(cron = "0/60 * * * * ?") private void SendUdp() throws IOException { sendUpdMsg(); } // Execute every 1 second @Scheduled(cron = "*/1 * * * * ?") private void receiveUpd() throws IOException { receiveUpdMsg(); } public void sendUpdMsg() throws IOException { if (!IS_SERVER) { if (SEND_SOCKET == null) { //clear key value doGet("http://" + TAR_URL + "/Nat/clearNatMap?key=" + KEY); SEND_SOCKET = new DatagramSocket(SEND_PORT); Thread thread = new Thread() { @SneakyThrows @Override public void run() { log.info("start receiving upd request"); responseSend(); } }; thread. start(); } sendUpdToServer(); } } private void responseSend() throws IOException { byte[] bytes = new byte[1024]; DatagramPacket packet1 = new DatagramPacket(bytes, bytes. length); while (true) { SEND_SOCKET.receive(packet1); String receive = new String(bytes, 0, packet1. getLength(), "utf-8"); if (receive. contains("client")) { String msg = "****************************Drilled successfully**************** ***************"; log.info(msg); } else { log.info("************************** upd information starts************** *************\\ {}", receive); log.info("************************** upd information end************** *************"); } } } private void sendUpdToServer() throws IOException { String ipV4 = InetAddress.getLocalHost().getHostAddress(); String[] ipArr = TAR_URL. split(":"); // The client segment sends an upd request to the server (the server records the client's internal network ip/external network ip/external network port) String text = KEY + ":" + ipV4 + ":" + SEND_SOCKET.getLocalPort(); byte[] buf = text. getBytes(); sendUdp(buf, ipArr[0], RECEIVE_PORT, SEND_SOCKET); //Query the nat information of two client segments with the same key String result = doGet("http://" + TAR_URL + "/Nat/getNatMap?key=" + KEY); if (result. isEmpty()) { return; } List<String> list = JSON. parseObject(result, List. class); // The same key requires nat to penetrate the ip array log.info("Key value: {} to penetrate the ip array {}", KEY, JSON.toJSONString(list)); String client = ""; for (String str : list) { if (str == null || str. isEmpty()) { continue; } String[] strArr = str. split(":"); if (strArr[0].equals(ipV4)) { client = str; } if (!strArr[0].equals(ipV4)) { //upd hole String[] udpArr = str.split("/")[1].split(":"); // 2. Specify the specific data to be sent. String clientMsg = "From client " + client + "upd message"; byte[] data = clientMsg. getBytes(); sendUdp(data, udpArr[0], Integer. valueOf(udpArr[1]), SEND_SOCKET); } } } public static void sendUdp(byte[] data, String ip, int port, DatagramSocket datagramSocket) throws IOException { // 3. Encapsulate the data into a data packet. DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(ip), port); log.info("Send upd information to the server" + ip + ":" + port + ""); datagramSocket. send(packet); } /** * The server receives the upd request * * @throws IOException */ public void receiveUpdMsg() throws IOException { if (IS_SERVER) { //Create a data packet for receiving data byte[] bytes = new byte[1024]; DatagramPacket dp = new DatagramPacket(bytes, bytes. length); //DatagramSocket(int port) constructs a datagram socket and binds it to the specified port on the local host if (RECEIVE_SOCKET == null) { RECEIVE_SOCKET = new DatagramSocket(RECEIVE_PORT); } DatagramSocket ds = RECEIVE_SOCKET; //Call the method of the DatagramSocket object to receive data ds. receive(dp); InetAddress address = dp. getAddress(); int port = dp. getPort(); String url = address. getHostAddress() + ":" + port; //Parse the packet and display the data on the console byte[] data = dp. getData(); //int getLength() returns the length of the data to be sent or the length of the received data int length = dp. getLength(); String dataString = new String(data, 0, length); log.info("\\ Client Nat address: **** {}**** \\ Client intranet address: **** {} **** ", url, dataString) ; String[] clientArr = dataString. split(":"); if (IS_SERVER) { String key = clientArr[0]; putNatMap(key, clientArr[1] + ":" + clientArr[2] + "/" + url); log.info("key :{} mapping array update completed", KEY); } } } private void putNatMap(String key, String value) { if (NAT_MAP. get(KEY) == null) { List<String> tempArr = new ArrayList<>(); tempArr.add(value); NAT_MAP.put(key, tempArr); } else { List<String> tempArr = (List<String>) NAT_MAP.get(key); String refresh = null; for (String str : tempArr) { String uuid = StringUtils. substringBeforeLast(str, ":"); String uuidValue = StringUtils. substringBeforeLast(value, ":"); if (uuid. equals(uuidValue)) { refresh = str; break; } } if (refresh != null) { tempArr. remove(refresh); } tempArr.add(value); NAT_MAP.put(key, tempArr); } } }
import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import spike.schedule.NatSchedule; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.DatagramSocket; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import static spike.schedule.NatSchedule.RECEIVE_SOCKET; import static spike.schedule.NatSchedule.SEND_SOCKET; import static spike.schedule.NatSchedule.sendUdp; /** * @PACKAGE_NAME: com.example.controller * @NAME: CheckSyncState * @USER: spike * @DATE: 2023/5/8 8:57 * @PROJECT_NAME: SendAlertMsg */ @Slf4j @RestController @RequestMapping("Nat") public class CheckSyncStateController { @Value("${isserver}") private boolean IS_SERVER; /** * Query NAT_MAP */ @ApiOperation(value = "Query NAT_MAP", tags = "Nat Upd hole punching") @RequestMapping(value = "/getNatMap", method = RequestMethod.GET) public Object getSyncState(@RequestParam(required = false, value = "key") String key) { if (!key. isEmpty()) { return NatSchedule.NAT_MAP.get(key); } return NatSchedule.NAT_MAP; } /** * Clear the specified NAT_MAP key */ @ApiOperation(value = "Clear the specified NAT_MAP key", tags = "Nat Upd hole punching") @RequestMapping(value = "/clearNatMap", method = RequestMethod.GET) public Object clearNatMap(@RequestParam(required = false, value = "key") String key) { if (NatSchedule. NAT_MAP. get(key) != null) { NatSchedule.NAT_MAP.put(key, null); return NatSchedule.NAT_MAP.get(key); } return NatSchedule.NAT_MAP.get(key); } /** * ask */ @ApiOperation(value = "udp punching", tags = "CheckSyncStateController check synchronization status") @RequestMapping(value = "/sendToUdp", method = RequestMethod.GET) public String sendToUdp(@RequestParam(value = "key") String key, @RequestParam(value = "client_ip") String client_ip, @RequestParam(value = "client_port") Integer client_port) throws IOException { DatagramSocket datagramSocket; if (IS_SERVER) { datagramSocket = RECEIVE_SOCKET; } else { datagramSocket = SEND_SOCKET; } sendUdp(key.getBytes(), client_ip, client_port, datagramSocket); log.info("GET interface sends upd information to server {}:{}: {}", client_ip, client_port, key); return "Drilling completed"; } public static String doGet(String httpurl) { HttpURLConnection connection = null; InputStream is = null; BufferedReader br = null; String result = null;// Return the result string try { // Create remote url connection object URL url = new URL(httpurl); // Open a connection through the remote url connection object and force it into the httpURLConnection class connection = (HttpURLConnection) url. openConnection(); // Set the connection method: get connection.setRequestMethod("GET"); // Set the timeout for connecting to the host server: 15000 milliseconds connection.setConnectTimeout(15000); // Set the time to read the data returned by the remote: 60000 milliseconds connection.setReadTimeout(60000); // send request connection. connect(); // Connect through connection to get the input stream if (connection. getResponseCode() == 200) { is = connection. getInputStream(); // Encapsulate the input stream is, and specify the character set br = new BufferedReader(new InputStreamReader(is, "UTF-8")); // store data StringBuffer sbf = new StringBuffer(); String temp = null; while ((temp = br. readLine()) != null) { sbf.append(temp); sbf.append("\r\\ "); } result = sbf.toString(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // close resource if (null != br) { try { br. close(); } catch (IOException e) { e.printStackTrace(); } } if (null != is) { try { is. close(); } catch (IOException e) { e.printStackTrace(); } } connection.disconnect();// close the remote connection } return result; } }
server: port: 5001 #Server public network address tarurl: xx.xxx.xx.xxx:5001 # Whether the server isserver: false #Client connection unique key value key: define a uuid yourself sendport: 5001 receiveport: 7091
Nat intranet penetration to obtain the map of the connection server
http://server address:5001/Nat/getNatMap?key=unique uuid defined
Return example:
[
“192.168.0.117:50979/x11.xx2.89.xx2:15623”
]
upd send message interface
http://intranet IPV4:5001/Nat/sendToUdp?client_ip=target public network ip & amp;client_port=target nat port & amp;key=send content
Return example:
Drilling completed
The result display: