原始套接字基礎(原始套接字系列二)

2021-09-27 09:43:14 字數 4575 閱讀 5661

在進入raw socket多種強大的應用之前,我們先講解怎樣建立乙個raw socket及怎樣用建立的raw socket傳送和接收ip包。

建立raw socket

在windows平台上,為了使用raw socket,需先初始化winsock:

// 啟動 winsock

wsadata wsadata;

if (wsastartup(makeword(2, 1), &wsadata) != 0)

makeword(2, 1)組成乙個版本字段,2.1版,同樣的,makeword(2, 2)意味著2.2版。makeword本身定義為:

inline word makeword(const byte whigh, const byte wlow)

因此makeword(2, 1)實際等同於0x0201。同樣地,0x0101可等同於makeword(1, 1)。

與wsastartup()的函式為wsacleanup(),在所有的socket都使用完後呼叫,如:

void sock_cleanup()

接下來,定義乙個socket控制代碼:

socket sd; // raw socket控制代碼

建立socket並將控制代碼賦值給定義的sd,可以使用wsasocket()函式來完成,其原型為:

socket wsasocket(int af, int type, int protocol, lpwsaprotocol_info

lpprotocolinfo, group g, dword dwflags);

其中的引數定義為:

af:位址家族,一般為af_inet,指代ipv4(the internet protocol version 4)位址家族。

type:套接字型別,如果建立原始套接字,應該使用sock_raw;

protocol:協議型別,如ipproto_tcp、ipproto_udp等;

lpprotocolinfo :wsaprotocol_info結構體指標;

dwflags:套接字屬性標誌。

例如,下面的**定義icmp協議型別的原始套接字:

sd = wsasocket(af_inet, sock_raw, ipproto_icmp, 0, 0, 0);

建立socket也可以使用socket()函式:

socket wsaapi socket( int af, int type, int protocol);

引數的定義與wsasocket()函式相同。

為了使用socket()函式建立的socket,還需要將這個socket與sockaddr繫結:

sockaddr_in addr_in;

addr_in.sin_family = af_inet;

addr_in.sin_port = inaddr_any;

addr_in.sin_addr.s_un.s_addr = getlocalip();

nretcode = bind(sd, (struct sockaddr*) &addr_in, sizeof(addr_in));

if (socket_error == nretcode)

其中使用的struct sockaddr_in(即sockaddr_in)為:

struct sockaddr_in

而bind()函式第二個引數的struct sockaddr型別定義為:

struct sockaddr;

實際上,bind()函式採用struct sockaddr是為了考慮相容性,最終struct sockaddr和struct sockaddr_in的記憶體占用是等同的。struct sockaddr_in中的struct in_addr成員占用4個位元組,為32位的ip位址,定義為:

typedef struct in_addr

s_un_b;

struct

s_un_w;

u_long s_addr;

}s_un;

} in_addr, *pin_addr, far *lpin_addr;

把32位的ip位址定義為上述聯合體將使使用者可以以位元組、半字或字方式讀寫同乙個ip位址。同志們,注意了,這個技巧在許多軟體開發中定義資料結構時被廣泛採用。

為了控制包的傳送方式,我們可能會用到如下的這個十分重要的函式來設定套接字選項:

int setsockopt(

socket s, //套接字控制代碼

int level, //選項level,如sol_socket

int optname, //選項名,如so_broadcast

const char* optval, //選項值buffer指標

int optlen //選項buffer長度);

例如,當level為sol_socket時,我們可以設定布林型選項so_broadcast從而控制套接字是否傳送和接收廣播訊息。

下面的**通過設定ipproto_ip level的ip_hdrincl選項為true從而使能程式設計師親自處理ip包報頭

//設定 ip 頭操作選項

bool flag = true;

setsockopt(sd, ipproto_ip, ip_hdrincl, (char*) &flag, sizeof(flag);

下面的函式用於控制套接字:

int ioctlsocket(

socket s,

long cmd, //命令

u_long* argp //命令引數指標);

如下面的**讓socket接收所有報文(sniffer模式):

u_long imode = 1;

ioctlsocket(sd, sio_rcvall, & imode); //讓 sockraw 接受所有的資料

raw socket傳送報文

傳送報文的函式為:

int sendto(

socket s, //套接字控制代碼

const char* buf, //傳送緩衝區

int len, //要傳送的位元組數

int flags, //方式標誌

const struct sockaddr* to, //目標位址

int tolen //目標位址長度);

或int send(

socket s, //已經建立連線的套接字控制代碼

const char* buf,

int len,

int flags);

send()函式的第1個引數只能是乙個已經建立連線的套接字控制代碼,所以這個函式就不再需要目標位址引數輸入。

函式的返回值為實際傳送的位元組數,如果返回socket_error,可以通過wsagetlasterror()獲得錯誤原因。請看下面的示例:

raw socket接收報文

接收報文的函式為:

int recvfrom(

socket s, //套接字控制代碼

char* buf, //接收緩衝區

int len, //緩衝區位元組數

int flags, //方式標誌

struct sockaddr* from, //源位址

int* fromlen );

或int recv(

socket s, //已經建立連線的套接字控制代碼

char* buf,

int len,

int flags);

recv()函式的第1個引數只能是乙個已經建立連線的套接字控制代碼,所以這個函式就不再需要源位址引數輸入。

函式的返回值為實際接收的位元組數,如果返回socket_error,我們可以通過wsagetlasterror()函式獲得錯誤原因。請看下面的示例:

int bread = recvfrom(sd, (char*)recv_buf, packet_size + sizeof(ipheader), 0,

(sockaddr*) &source, &fromlen);

if (bread == socket_error)

return - 1;}

原始套接字按如下規則接收報文:若接收的報文中協議型別和定義的原始套接字匹配,那麼,接收的所有資料拷貝入套接字中;如果套接字繫結了本地位址,那麼只有接收資料ip頭中對應的目的位址等於本地位址,接收到的資料才拷貝到套接字中;如果套接字定義了遠端位址,那麼,只有接收資料ip頭中對應的源位址與遠端位址匹配,接收的資料才拷貝到套接字中。

建立報文

在利用raw socket傳送報文時,報文的ip頭、tcp頭、udp頭等需要程式設計師親自賦值,從而達到極大的靈活性。下面的程式利用raw socket傳送tcp報文,並完全手工建立報頭:

int bwrote = sendto(sd, (char*)send_buf, packet_size, 0, (sockaddr*) &dest,

sizeof(dest));

if (bwrote == socket_error)

return - 1;

}else if (bwrote < packet_size)

原始套接字簡介(原始套接字系列一

大多數程式設計師所接觸到的套接字 socket 為兩類 1 流式套接字 sock stream 一種面向連線的socket,針對於面向連線的tcp服務應用 2 資料報式套接字 sock dgram 一種無連線的socket,對應於無連線的udp服務應用。從使用者的角度來看,sock stream s...

原始套接字

資料出處 實際上,我們常用的網路程式設計都是在應用層的報文的收發操作,也就是大多數程式設計師接觸到的流式套接字 sock stream 和資料報式套接字 sock dgram 而這些資料報都是由系統提供的協議棧實現,使用者只需要填充應用層報文即可,由系統完成底層報文頭的填充並傳送。然而在某些情況下需...

原始套接字

參考1 原始套接字能幹什麼?參考2 原始套接字抓包實踐 參考3 各層頭結構 通過原始套接字,我們可以抓取所有傳送到本機的ip包 包括ip頭和tcp udp icmp包頭 也可以抓取所有本機收到的幀 包括資料鏈路層協議頭 普通的套接字無法處理icmp igmp等網路報文,而sock raw可以。利用原...