当前位置:网站首页>Socket to do a simple network sniffer
Socket to do a simple network sniffer
2022-04-22 03:14:00 【Red fog】
SniffDlg.h : The header file
// SniffDlg.h : The header file
//
#pragma once
#include "afxcmn.h"
#include "afxwin.h"
typedef struct _PROTN2T
{
int proto;
char *pprototext;
}PROTN2T;
#define PROTO_NUM 11
/* The IP header */
typedef struct _IPHEADER {
unsigned char header_len : 4;
unsigned char version : 4;
unsigned char tos; // type of service
unsigned short total_len; // length of the packet
unsigned short ident; // unique identifier
unsigned short flags;
unsigned char ttl;
unsigned char proto; // protocol ( IP , TCP, UDP etc)
unsigned short checksum;
unsigned int sourceIP;
unsigned int destIP;
}IPHEADER;
#define UDP_HEAD_LEN 8 /* UDP head length */
#define PSEUDO_HEAD_LEN 12 /* Pseudo head length */
#define ICMP_HEAD_LEN 4 /* ICMP head length */
struct TCPPacketHead {
WORD SourPort;
WORD DestPort;
DWORD SeqNo;
DWORD AckNo;
BYTE HLen;
BYTE Flag;
WORD WndSize;
WORD ChkSum;
WORD UrgPtr;
};
struct ICMPPacketHead {
BYTE Type;
BYTE Code;
WORD ChkSum;
};
struct UDPPacketHead {
WORD SourPort;
WORD DestPort;
WORD Len;
WORD ChkSum;
};
// CSniffDlg Dialog box
class CSniffDlg : public CDialogEx
{
// structure
public:
CSniffDlg(CWnd* pParent = NULL); // Standard constructors
void AddData(CString s0, CString s1, CString s2, CString s3, CString s4, CString s5, CString s6);
// Dialog data
#ifdef AFX_DESIGN_TIME
enum {
IDD = IDD_SNIFF_DIALOG };
#endif
CListCtrl m_ctrList;
CButton m_start;
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV Support
// Realization
protected:
HICON m_hIcon;
DWORD m_ipsource; // IP in net format
DWORD m_iphostsource; // same IP in host format
DWORD m_iphost;
DWORD m_ipcheckedhost;
SOCKET m_s;
DWORD m_threadID;
BOOL m_Multihomed;
BOOL m_Local;
CDWordArray m_IPArr;
friend UINT threadFunc(LPVOID p);
// Generated message mapping function
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedLookup();
afx_msg void OnBnClickedOk();
private:
CComboBox m_cAdapter;
public:
afx_msg void OnSelendokComboAdapter();
};
SniffDlg.cpp : Implementation file
// SniffDlg.cpp : Implementation file
//
#include "stdafx.h"
#include "Sniff.h"
#include "SniffDlg.h"
#include "afxdialogex.h"
#pragma comment(lib, "IPHLPAPI.lib")
#include "Iphlpapi.h"
#include "mstcpip.h"//SIO_RCVALL
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
PROTN2T aOfProto[PROTO_NUM + 1] =
{
{
IPPROTO_IP , "IP" },
{
IPPROTO_ICMP , "ICMP" },
{
IPPROTO_IGMP , "IGMP" },
{
IPPROTO_GGP , "GGP" },
{
IPPROTO_TCP , "TCP" },
{
IPPROTO_PUP , "PUP" },
{
IPPROTO_UDP , "UDP" },
{
IPPROTO_IDP , "IDP" },
{
IPPROTO_ND , "NP" },
{
IPPROTO_RAW , "RAW" },
{
IPPROTO_MAX , "MAX" },
{
NULL , "" }
};
char *get_proto_name(unsigned char proto)
{
BOOL bFound = FALSE;
int i;
for (i = 0; i < PROTO_NUM; i++)
{
if (aOfProto[i].proto == proto)
{
bFound = TRUE;
break;
}
}
if (bFound)
return aOfProto[i].pprototext;
return aOfProto[PROTO_NUM].pprototext;
}
// For applications “ About ” Menu items CAboutDlg Dialog box
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog data
#ifdef AFX_DESIGN_TIME
enum {
IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV Support
// Realization
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CSniffDlg Dialog box
CSniffDlg::CSniffDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_SNIFF_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_threadID = 0;
m_Multihomed = FALSE;
m_Local = TRUE;
}
void CSniffDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST, m_ctrList);
DDX_Control(pDX, IDC_LOOKUP, m_start);
DDX_Control(pDX, IDC_COMBO_ADAPTER, m_cAdapter);
}
BEGIN_MESSAGE_MAP(CSniffDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_LOOKUP, &CSniffDlg::OnBnClickedLookup)
ON_BN_CLICKED(IDOK, &CSniffDlg::OnBnClickedOk)
ON_CBN_SELENDOK(IDC_COMBO_ADAPTER, &CSniffDlg::OnSelendokComboAdapter)
END_MESSAGE_MAP()
// CSniffDlg Message handler
BOOL CSniffDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// take “ About ...” Menu items are added to the system menu .
// IDM_ABOUTBOX Must be within the scope of the system command .
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog . When the application's main window is not a dialog box , The frame will automatically
// Do this
SetIcon(m_hIcon, TRUE); // Set the big icon
SetIcon(m_hIcon, FALSE); // Set up small icons
// TODO: Add additional initialization code here
CHAR szHostName[128] = {
0 };
HOSTENT* pHost = NULL;
CHAR* pszIp = NULL;
int iNum = 0;
if (AfxSocketInit(NULL) == FALSE)
{
AfxMessageBox("Sorry, socket load error!");
return FALSE;
}
if (gethostname(szHostName, 128) == 0)
{
pHost = gethostbyname(szHostName);
if (pHost != NULL)
{
for (pszIp = *pHost->h_addr_list;pszIp != NULL;pszIp = *(pHost->h_addr_list + iNum))
{
m_cAdapter.InsertString(iNum++, inet_ntoa(*(in_addr*)pszIp));
}
pszIp = inet_ntoa(*(in_addr*)pHost->h_addr_list[0]);
m_ipsource = inet_addr(pszIp);
m_cAdapter.SetCurSel(0);
}
else
{
AfxMessageBox("pHost = NULL!");
}
}
else
{
AfxMessageBox("can't find host name!");
}
DWORD dwStyle = GetWindowLong(m_ctrList.GetSafeHwnd(), GWL_STYLE);
dwStyle &= ~LVS_TYPEMASK;
dwStyle |= LVS_REPORT;
SetWindowLong(m_ctrList.GetSafeHwnd(), GWL_STYLE, dwStyle);
m_ctrList.InsertColumn(0, " data ", LVCFMT_LEFT, 525);
m_ctrList.InsertColumn(0, " size ", LVCFMT_LEFT, 80);
m_ctrList.InsertColumn(0, " port ", LVCFMT_LEFT, 40);
m_ctrList.InsertColumn(0, " Destination address ", LVCFMT_LEFT, 100);
m_ctrList.InsertColumn(0, " port ", LVCFMT_LEFT, 40);
m_ctrList.InsertColumn(0, " source address ", LVCFMT_LEFT, 100);
m_ctrList.InsertColumn(0, " agreement ", LVCFMT_LEFT, 50);
::SendMessage(m_ctrList.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,
LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
// Here read all IPs of this host
DWORD dwSize = 0;
GetIpAddrTable(NULL, &dwSize, FALSE);
PMIB_IPADDRTABLE pIpAddrTable = (PMIB_IPADDRTABLE)new BYTE[dwSize];
if (pIpAddrTable)
{
if (GetIpAddrTable((PMIB_IPADDRTABLE)pIpAddrTable, // // buffer for IP table
&dwSize, // size of buffer
FALSE // sort by IP address
) == NO_ERROR)
{
if (pIpAddrTable->dwNumEntries > 2) // Second is MS TCP loopback IP ( 127.0.0.1 )
{
m_Multihomed = TRUE;
char szIP[16];
for (int i = 0; i < (int)pIpAddrTable->dwNumEntries; i++)
{
in_addr ina;
ina.S_un.S_addr = pIpAddrTable->table[i].dwAddr;
char *pIP = inet_ntoa(ina);
strcpy(szIP, pIP);
if (stricmp(szIP, "127.0.0.1"))
m_IPArr.Add(pIpAddrTable->table[i].dwAddr);
}
}
}
delete[] pIpAddrTable;
}
return TRUE; // Unless you set the focus to the control , Otherwise return to TRUE
}
void CSniffDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to the dialog , You need the following code
// To draw the icon . For using documents / View model MFC Applications ,
// This will be done automatically by the framework .
void CSniffDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // Device context for drawing
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center the icon in the workspace rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// draw icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// When the user drags the minimized window, the system calls this function to get the cursor
// Show .
HCURSOR CSniffDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
UINT threadFunc(LPVOID p)
{
CSniffDlg *pDlg = static_cast<CSniffDlg *>(p);
char buf[1000], *bufwork;
MSG msg;
int iRet;
DWORD dwErr;
char *pSource, *pDest;
IPHEADER *pIpHeader;
in_addr ina;
char szSource[16], szDest[16], szErr[50];
char *pLastBuf = NULL;
int HdrLen, totallen;
WORD sourport, destport;
struct TCPPacketHead *pTCPHead;
struct ICMPPacketHead *pICMPHead;
struct UDPPacketHead *pUDPHead;
BYTE *pdata = NULL;
/*---------------------------------------------------------------------*/
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); // Force to make the queue Force the creation of a message queue for this thread
pDlg->m_threadID = GetCurrentThreadId();
while (TRUE)
{
if (PeekMessage(&msg, 0, WM_CLOSE, WM_CLOSE, PM_NOREMOVE))
{
closesocket(pDlg->m_s);
pDlg->m_threadID = 0;
pDlg->m_start.EnableWindow(TRUE);
break;
}
memset(buf, 0, sizeof(buf));
iRet = recv(pDlg->m_s, buf, sizeof(buf), 0);
if (iRet == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error recv() = %ld ", dwErr);
continue;
}
else
{
if (*buf)
{
bufwork = buf;
pIpHeader = (IPHEADER *)bufwork;
WORD iLen = ntohs(pIpHeader->total_len);
while (TRUE)
{
if (iLen <= iRet)
{
ina.S_un.S_addr = pIpHeader->sourceIP;
pSource = inet_ntoa(ina);
strcpy(szSource, pSource);
ina.S_un.S_addr = pIpHeader->destIP;
pDest = inet_ntoa(ina);
strcpy(szDest, pDest);
CString str, strProto, strSourPort, strDestPort, strData, strSize;
strProto = get_proto_name(pIpHeader->proto);
/*-------------------zhuwei add(2002.11.9)-----------------------------*/
HdrLen = pIpHeader->header_len & 0xf;
HdrLen *= 4;
totallen = ntohs(pIpHeader->total_len);
totallen -= HdrLen;
switch (pIpHeader->proto)
{
case IPPROTO_ICMP:
{
pICMPHead = (struct ICMPPacketHead *)(buf + HdrLen);
//strL4.Format(" type:%d code:%d\n",pICMPHead->Type,pICMPHead->Code);
strSourPort = "-";
strDestPort = "-";
pdata = ((BYTE *)pICMPHead) + ICMP_HEAD_LEN;
totallen -= ICMP_HEAD_LEN;
break;
}
case IPPROTO_TCP:
{
pTCPHead = (struct TCPPacketHead *)(buf + HdrLen);
sourport = ntohs(pTCPHead->SourPort);
destport = ntohs(pTCPHead->DestPort);
//strL4.Format(" sour port:%d,dest port:%d",sourport,destport);
strSourPort.Format("%d", sourport);
strDestPort.Format("%d", destport);
HdrLen = (pTCPHead->HLen) >> 4; //in fact only 4 bits
HdrLen *= 4;
pdata = ((BYTE *)pTCPHead) + HdrLen;
totallen -= HdrLen;
break;
}
case IPPROTO_UDP:
{
pUDPHead = (struct UDPPacketHead *)(buf + HdrLen);
sourport = ntohs(pUDPHead->SourPort);
destport = ntohs(pUDPHead->DestPort);
//strL4.Format(" sour port:%d,dest port:%d",sourport,destport);
strSourPort.Format("%d", sourport);
strDestPort.Format("%d", destport);
pdata = ((BYTE *)pUDPHead) + UDP_HEAD_LEN;
totallen -= UDP_HEAD_LEN;
break;
}
}
if (pIpHeader->proto == IPPROTO_ICMP)
strData.Format("type:%d code:%d data:%s", pICMPHead->Type, pICMPHead->Code, pdata);
else strData.Format(" %s", pdata);
strSize.Format("%d", totallen);
pDlg->AddData(strProto, szSource, strSourPort, szDest, strDestPort, strSize, strData);
if (iLen < iRet)
{
iRet -= iLen;
bufwork += iLen;
pIpHeader = (IPHEADER *)bufwork;
}
else
break; // pIpHeader->total_len == iRet and go out
}
else
{
// read last part of buf. I wrote it , but always recv() read exactly
// the lenght of the packet
int iLast = iLen - iRet;
pLastBuf = new char[iLen];
int iReaden = iRet;
memcpy(pLastBuf, bufwork, iReaden);
iRet = recv(pDlg->m_s, pLastBuf + iReaden, iLast, 0);
if (iRet == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error recv() = %ld ", dwErr);
break;
}
else
{
bufwork = pLastBuf;
pIpHeader = (IPHEADER *)bufwork;
if (iRet == iLast)
iRet = iLen;
else
{
// read all last data
iReaden += iRet;
iLast -= iRet;
while (TRUE)
{
iRet = recv(pDlg->m_s, pLastBuf + iReaden, iLast, 0);
if (iRet == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error recv() = %ld ", dwErr);
break;
}
else
{
iReaden += iRet;
iLast -= iRet;
if (iLast <= 0)
{
/*iRet = iReaden;*/
break;
}
}
} // while
}
}
}
} // while
if (pLastBuf)
delete[] pLastBuf;
}
else
{
AfxMessageBox("No data on network");
continue;
}
}
}
return TRUE;
}
void CSniffDlg::OnBnClickedLookup()
{
char szErr[50], szHostName[MAX_PATH];
DWORD dwErr;
SOCKADDR_IN sa;
gethostname(szHostName, sizeof(szHostName));
m_iphostsource = m_ipsource;
m_ipcheckedhost = ntohl(m_iphost);
if (0 == m_threadID)
{
SetDlgItemText(IDC_LOOKUP, " Stop viewing !");
m_cAdapter.EnableWindow(false);
}
else
{
if (m_threadID)
{
PostThreadMessage(m_threadID, WM_CLOSE, 0, 0);
SetDlgItemText(IDC_LOOKUP, " Start looking at !");
m_start.EnableWindow(FALSE);
m_cAdapter.EnableWindow(true);
}
return;
}
DWORD dwBufferLen[10];
DWORD dwBufferInLen = 1;
DWORD dwBytesReturned = 0;
m_s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if (INVALID_SOCKET == m_s)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error socket() = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
int rcvtimeo = 5000;
if (setsockopt(m_s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&rcvtimeo, sizeof(rcvtimeo)) == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error WSAIoctl = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
sa.sin_family = AF_INET;
sa.sin_port = htons(7000);
sa.sin_addr.s_addr = m_iphostsource;
if (bind(m_s, (PSOCKADDR)&sa, sizeof(sa)) == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error bind() = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
if (SOCKET_ERROR != WSAIoctl(m_s, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen),
&dwBufferLen, sizeof(dwBufferLen),
&dwBytesReturned, NULL, NULL))
AfxBeginThread(threadFunc, (LPVOID)this);
else
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error WSAIoctl = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
}
void CSniffDlg::OnBnClickedOk()
{
// TODO: Add control notification handler code here
if (NULL != m_threadID)
{
PostThreadMessage(m_threadID, WM_CLOSE, 0, 0);
}
if (m_IPArr.GetSize())
{
m_IPArr.RemoveAll();
}
CDialogEx::OnOK();
}
void CSniffDlg::AddData(CString s0, CString s1, CString s2, CString s3, CString s4, CString s5, CString s6)
{
int index;
index = m_ctrList.InsertItem(0, s0);
m_ctrList.SetItem(index, 1, LVIF_TEXT, s1, 0, 0, 0, 0);
m_ctrList.SetItem(index, 2, LVIF_TEXT, s2, 0, 0, 0, 0);
m_ctrList.SetItem(index, 3, LVIF_TEXT, s3, 0, 0, 0, 0);
m_ctrList.SetItem(index, 4, LVIF_TEXT, s4, 0, 0, 0, 0);
m_ctrList.SetItem(index, 5, LVIF_TEXT, s5, 0, 0, 0, 0);
m_ctrList.SetItem(index, 6, LVIF_TEXT, s6, 0, 0, 0, 0);
}
void CSniffDlg::OnSelendokComboAdapter()
{
CHAR pszIp[100];
int index=m_cAdapter.GetCurSel();
m_cAdapter.GetLBText(index, pszIp);
m_ipsource = inet_addr(pszIp);
}
DWORD_PTR a;
Sniff.rc
IDD_SNIFF_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "Sniff Sniffer "
FONT 9, "MS Shell Dlg", 0, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,15,31,291,135
PUSHBUTTON " see ",IDC_LOOKUP,109,172,45,14
PUSHBUTTON " determine ",IDOK,178,172,45,14
COMBOBOX IDC_COMBO_ADAPTER,15,14,291,86,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
END
版权声明
本文为[Red fog]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204211350475831.html
边栏推荐
- Saas. Extended field custom field
- Sword finger offer special breakthrough version 93, longest Fibonacci series
- Polyfill是什么?
- 2022-4-21 operation
- 二十四.类的深拷贝与浅拷贝
- Redis事件驱动框架(上):何时使用select、poll、epoll?
- leetcode:297. 二叉树的序列化与反序列化
- 终于看清阿里,美团,京东这些企业的真面目了
- Application packaging or test team?
- Code refactoring removes the assignment of parameters
猜你喜欢

嘉戎技術深交所上市破發:公司市值41億 應收賬款2.8億

Technology sharing | selenium ide use case recording

How to take sonar as an example to create a test step applicable to all enterprises
![[NCTF2019]Fake XML cookbook](/img/55/b0f996fd1db2792c14444bcba39377.png)
[NCTF2019]Fake XML cookbook

MySQL allows you to use the same table multiple times in a select statement

Allegro Gerber file path setting

Redis event driven framework (Part 1): when to use select, poll and epoll?
![[bjdctf2020] cookie is so stable (detailed explanation of vulnerability principle)](/img/ef/e05de7a94dc9c2398d714cbf30c4ec.png)
[bjdctf2020] cookie is so stable (detailed explanation of vulnerability principle)

kerberos認證協議

Network and multimedia knowledge (3)
随机推荐
剑指offer 专项突破版 91、粉刷房子
Jiarong Technology Shenzhen Stock Exchange listed and discovered: The Value of Market of the company is 4.1 billion, and the Accounts receivables are 280 million.
Technology sharing | selenium ide use case recording
What is Polyfill?
Troubleshooting kubernetes - 10s delay
Interview with those stereotyped and boring interviewers
Workplace etiquette How to use foreign enterprise email
Go language -----30-----token mechanism, WeChat official account signature verification method, XML parsing, CDATA parsing, exchange protocol, receive message protocol, passive reply message protocol,
TestNG learning notes
jupyter代码无法运行
Rasa对话机器人连载一 第121课:Rasa对话机器人Debugging项目实战之电商零售对话机器人运行流程调试全程演示-1
剑指offer 专项突破版 93、最长斐波那契数列
面试经 那些八股文和无聊的面试官
Analysis of five data structures of redis
How to run collabora office in openshift
[wustctf2020] plain
Comparison of hex, Base64 and URLEncode coding schemes
Golang dependency injection wire. When executing the wire command, an error is reported: bash: Wire: command not found
Wechat H5 payment (reporting cross domain issues)
Interpretation of Flink's new features of fine-grained resource management