在進入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可以。利用原...