学习网络编程,调试各种连接和数据很麻烦,也有很多现成工具可以使用,不过还是喜欢再造个轮子来自己玩玩.
header.h 定义了IP和TCP包头格式:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/if.h> /* 定义TCP和IP的包头格式 */ /* IP header */ struct ip { u_char ip_vhl; /* version << 4 | header length >> 2 */ u_char ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; #define IP_HL(ip) (((ip)->ip_vhl) & 0x0f) #define IP_V(ip) (((ip)->ip_vhl) >> 4) /* TCP header */ typedef u_int tcp_seq; struct tcp { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ u_char th_offx2; /* data offset, rsvd */ #define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4) u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ };
main.c定义了主要功能:
#include "header.h" #include <string.h> #include <sys/ioctl.h> int Open_Raw_Socket(void); int Set_Promisc(char *interface, int sock); void dump(const unsigned char*data_buffer, const unsigned int length); int main() { int sock; sock = Open_Raw_Socket(); printf("raw socket is %d ", sock); char buffer[65535]; int bytes_recieved; size_t fromlen; struct sockaddr_in from; struct ip *ip; struct tcp *tcp; // 设置网卡eth0为混杂模式 Set_Promisc("eth0", sock); // 输出TCP/IP报头的长度 printf("IP header is %d ", sizeof(struct ip)); printf("TCP header is %d ", sizeof(struct tcp)); while (1) { fromlen = sizeof(from); bytes_recieved = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&from, &fromlen); printf(" Bytes recieved: %5d ", bytes_recieved); printf("Source address: %s ", inet_ntoa(from.sin_addr)); ip = (struct ip*)buffer; if (ip->ip_p == 6) { printf("Dest address is: %s ", inet_ntoa(ip->ip_dst)); printf("IP header Length is :%d ", ip->ip_len); printf("Protocol: %d ", ip->ip_p); printf("Type of Server: %d ", ip->ip_tos); printf("Time to live is : %d ",ip->ip_ttl); printf("Check Sum is : %d ", ip->ip_sum); tcp = (struct tcp*)(buffer + 20); // IP数据包和TCP数据包有20个字节的距离 printf("Dest port is: %d ", ntohs(tcp->th_dport)); printf("Source port is: %d ", ntohs(tcp->th_sport)); printf("Seq number is : %d ", tcp->th_seq); printf("Ack number is : %d ", tcp->th_ack); printf("Flags is %d ", tcp->th_flags); dump((const unsigned char*)buffer, bytes_recieved); } } return 0; } // 建立一个原始socket句柄 int Open_Raw_Socket(void) { int sock; if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) { perror("raw socket error "); exit(1); } return sock; } // 设置eth0为混杂模式 int Set_Promisc(char *interface, int sock) { struct ifreq ifr; strncpy(ifr.ifr_name, interface, strnlen(interface, sizeof(interface)) + 1); if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { perror("set promisc error one if "); exit(2); } printf("The interface is %s ", interface); printf("Retrieved flags from interface is ok "); ifr.ifr_flags |= IFF_PROMISC; if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { perror("Can not set PROMISC flag:"); exit(-1); } printf("Set Promisc ok "); return 0; } // 输出buffer内容 void dump(const unsigned char*data_buffer, const unsigned int length) { unsigned char byte; unsigned int i, j; for (i = 0; i < length; i++) { byte = data_buffer[i]; printf("%02x ", data_buffer[i]); if ((i % 16 == 15) || (i == length -1)) { for (j = 0; j < 15 -(i % 16); j++) { printf(" "); } printf("|"); for (j = (i - (i % 16)); j <= i; j++) { byte = data_buffer[j]; if (byte > 31 && byte < 127) printf("%c", byte); else printf("."); } printf(" "); } } }
使用时,需要root权限,在Ubuntu下需要sudo一下,使用实例, 利用了twsited的echo服务:
可以用于调试简单的本机socket编程。