Linux Raw Socket

/*
说明 
本程序是使用原始套接字编写的简单抓包程序,将网卡设置为混杂模式,可以接收到网络上任意的数据包
可以获取ETHERNET包头/IP包头ARP包头/RARP包头/TCP包头/UDP包头ICMP包头的数据
时间 20111228
邮箱 [email protected]

1、增加filter过滤功能,使用BPF(BSD packet filter)码,类似于tcpdump的过滤机制,直接在底层过滤,减少上层过滤引起的压力
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <errno.h>

#define EXIT_FIILURE -1
#define EXIT_SUCCESS 0

extern int errno;

void DisplayEthernet(struct ether_header *_pEtherheader)
{
	#if 0
	struct ether_header{  
		u_int8_t  ether_dhost[ETH_ALEN];	// destination eth addr	  
		u_int8_t  ether_shost[ETH_ALEN];	// source ether addr
		u_int16_t ether_type;		        // packet type ID field
	} __attribute__ ((__packed__));
	#endif
	char *ethertypeStr = NULL;
	unsigned short ethertype = ntohs(_pEtherheader->ether_type);
	switch(ethertype){
		case 0x0200:
			ethertypeStr = "ETHERTYPE_PUP";
			break;
		case 0x0800:
			ethertypeStr = "ETHERTYPE_IP";
			break;
		case 0x0806:
			ethertypeStr = "ETHERTYPE_ARP";
			break;
		case 0x0835:
			ethertypeStr = "ETHERTYPE_REVARP";
			break;
		default:
			ethertypeStr = "Unrecognized";
			break;
	}
	fprintf(stdout, "------this is a ethernet header------\n");
	fprintf(stdout, "ether_dhost= %02X:%02X:%02X:%02X:%02X:%02X\n", _pEtherheader->ether_dhost[0], _pEtherheader->ether_dhost[1], _pEtherheader->ether_dhost[2], _pEtherheader->ether_dhost[3], _pEtherheader->ether_dhost[4], _pEtherheader->ether_dhost[5]);
	fprintf(stdout, "ether_shost= %02X:%02X:%02X:%02X:%02X:%02X\n", _pEtherheader->ether_shost[0], _pEtherheader->ether_shost[1], _pEtherheader->ether_shost[2], _pEtherheader->ether_shost[3], _pEtherheader->ether_shost[4], _pEtherheader->ether_shost[5]);
	fprintf(stdout, "ether_type	= %#04x(%s)\n", ethertype, ethertypeStr);
	fprintf(stdout, "------end---------------------------\n");
}

void DisplayIp(struct ip *_pIPheader)
{
	#if 0
	struct ip  {
	#if __BYTE_ORDER == __LITTLE_ENDIAN    
	unsigned int ip_hl:4;		/* header length */    
	unsigned int ip_v:4;		/* version */
	#endif
	#if __BYTE_ORDER == __BIG_ENDIAN    
	unsigned int ip_v:4;		/* version */    
	unsigned int ip_hl:4;		/* header length */
	#endif    
	u_int8_t 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_int8_t ip_ttl;			/* time to live */    
	u_int8_t ip_p;				/* protocol */    
	u_short ip_sum;				/* checksum */    
	struct in_addr ip_src, ip_dst;	/* source and dest address */  
	};
	#endif
	char *pStr = NULL;
	unsigned char p = _pIPheader->ip_p;
	switch(p){
		case 0:
			pStr = "IPPROTO_IP";
			break;
		case 1:
			pStr = "IPPROTO_ICMP";
			break;
		case 2:
			pStr = "IPPROTO_IGMP";
			break;
		case 6:
			pStr = "IPPROTO_TCP";
			break;
		case 17:
			pStr = "IPPROTO_UDP";
			break;
		default:
			pStr = "Unrecognized";
			break;
	}
	fprintf(stdout, "------this is a IP header------\n");
	fprintf(stdout, "ip_hl 		= %u\n", _pIPheader->ip_hl << 2);
	fprintf(stdout, "ip_v  		= %u\n", _pIPheader->ip_v);
	fprintf(stdout, "ip_tos  	= %u\n", _pIPheader->ip_tos);
	fprintf(stdout, "ip_len  	= %u\n", ntohs(_pIPheader->ip_len));
	fprintf(stdout, "ip_id  	= %u\n", ntohs(_pIPheader->ip_id));
	fprintf(stdout, "ip_off  	= %u\n", ntohs(_pIPheader->ip_off));
	fprintf(stdout, "ip_ttl  	= %u\n", _pIPheader->ip_ttl);
	fprintf(stdout, "ip_p  		= %u(%s)\n", p, pStr);
	fprintf(stdout, "ip_sum  	= %u\n", ntohs(_pIPheader->ip_sum));
	fprintf(stdout, "ip_src  	= %s\n", inet_ntoa(_pIPheader->ip_src));
	fprintf(stdout, "ip_dst  	= %s\n", inet_ntoa(_pIPheader->ip_dst));
	fprintf(stdout, "------end---------------------------\n");
}

void DisplayArp(struct arphdr *_pArpheader)
{
	#if 0
	struct arphdr  {    
		unsigned short int ar_hrd;		// Format of hardware address.  
		unsigned short int ar_pro;		// Format of protocol address.   
		unsigned char ar_hln;			// Length of hardware address.  
		unsigned char ar_pln;			// Length of protocol address.
		unsigned short int ar_op;		// ARP opcode (command).
	};
	#endif
	static const char *hardwaretype[] = {	"ARPHRD_NETROM", 
											"ARPHRD_ETHER",
											"ARPHRD_EETHER",
											"ARPHRD_AX25",
											"ARPHRD_PRONET",
											"ARPHRD_CHAOS",
											"ARPHRD_IEEE802",
											"ARPHRD_ARCNET",
											"ARPHRD_APPLETLK"
										};
	char *ptStr = NULL;
	unsigned short ht = ntohs(_pArpheader->ar_hrd);
	unsigned short pt = ntohs(_pArpheader->ar_pro);
	switch(pt){
		case 0x0800:
			ptStr = "IP";
			break;
		case 0x0806:
			ptStr = "ARP";
			break;
		case 0x0835:
			ptStr = "REVARP";
			break;
		default:
			ptStr = "Unrecognized";
			break;
	}

	char *opStr = NULL;
	unsigned short op = ntohs(_pArpheader->ar_op);
	switch(op){
		case 1:
			opStr = "ARPOP_REQUEST";
			break;
		case 2:
			opStr = "ARPOP_REPLY";
			break;
		default:
			opStr = "Unrecognized";
			break;
	}
	fprintf(stdout, "------this is a arp header------\n");
	fprintf(stdout, "ar_hrd 	= %#02X(%s)\n", ht, hardwaretype[ht]);
	fprintf(stdout, "ar_pro  	= %#02X(%s)\n", pt, ptStr);
	fprintf(stdout, "ar_hln  	= %u\n", _pArpheader->ar_hln);
	fprintf(stdout, "ar_pln  	= %u\n", _pArpheader->ar_pln);
	fprintf(stdout, "ar_op  	= %#02X(%s)\n", op, opStr);
	fprintf(stdout, "------end---------------------------\n");
}

void DisplayRarp(struct arphdr *_pRarpheader)
{
	static const char *hardwaretype[] = {	"ARPHRD_NETROM", 
											"ARPHRD_ETHER",
											"ARPHRD_EETHER",
											"ARPHRD_AX25",
											"ARPHRD_PRONET",
											"ARPHRD_CHAOS",
											"ARPHRD_IEEE802",
											"ARPHRD_ARCNET",
											"ARPHRD_APPLETLK"
										};
	char *ptStr = NULL;
	unsigned short ht = ntohs(_pRarpheader->ar_hrd);
	unsigned short pt = ntohs(_pRarpheader->ar_pro);
	switch(pt){
		case 0x0800:
			ptStr = "IP";
			break;
		case 0x0806:
			ptStr = "ARP";
			break;
		case 0x0835:
			ptStr = "REVARP";
			break;
		default:
			ptStr = "Unrecognized";
			break;
	}

	char *opStr = NULL;
	unsigned short op = ntohs(_pRarpheader->ar_op);
	switch(op){
		case 3:
			opStr = "ARPOP_RREQUEST";
			break;
		case 4:
			opStr = "ARPOP_RREPLY";
			break;
		default:
			opStr = "Unrecognized";
			break;
	}
	fprintf(stdout, "------this is a rarp header------\n");
	fprintf(stdout, "ar_hrd 	= %#02X(%s)\n", ht, hardwaretype[ht]);
	fprintf(stdout, "ar_pro  	= %#02X(%s)\n", pt, ptStr);
	fprintf(stdout, "ar_hln  	= %u\n", _pRarpheader->ar_hln);
	fprintf(stdout, "ar_pln  	= %u\n", _pRarpheader->ar_pln);
	fprintf(stdout, "ar_op  	= %#02X(%s)\n", op, opStr);
	fprintf(stdout, "------end---------------------------\n");
}

void DisplayTcp(struct tcphdr *_pTcpheader)
{
	#if 0
	struct tcphdr  {    
		u_int16_t source;    
		u_int16_t dest;    
		u_int32_t seq;    
		u_int32_t ack_seq;
		#  if __BYTE_ORDER == __LITTLE_ENDIAN    
		u_int16_t res1:4;    
		u_int16_t doff:4;    
		u_int16_t fin:1;    
		u_int16_t syn:1;    
		u_int16_t rst:1;    
		u_int16_t psh:1;    
		u_int16_t ack:1;    
		u_int16_t urg:1;    
		u_int16_t res2:2;
		#  elif __BYTE_ORDER == __BIG_ENDIAN    
		u_int16_t doff:4;    
		u_int16_t res1:4;    
		u_int16_t res2:2;    
		u_int16_t urg:1;    
		u_int16_t ack:1;    
		u_int16_t psh:1;    
		u_int16_t rst:1;    
		u_int16_t syn:1;    
		u_int16_t fin:1;
		#  else
		#   error "Adjust your <bits/endian.h> defines"
		#  endif    
		u_int16_t window;    
		u_int16_t check;    
		u_int16_t urg_ptr;
	};
	#endif
	fprintf(stdout, "------this is a tcp header------\n");
	fprintf(stdout, "source  	= %u\n", ntohs(_pTcpheader->source));
	fprintf(stdout, "dest  		= %u\n", ntohs(_pTcpheader->dest));
	fprintf(stdout, "seq  		= %u\n", ntohl(_pTcpheader->seq));
	fprintf(stdout, "ack_seq	= %u\n", ntohl(_pTcpheader->ack_seq));
	fprintf(stdout, "res1  		= %u\n", _pTcpheader->res1);
	fprintf(stdout, "doff  		= %u\n", _pTcpheader->doff);
	fprintf(stdout, "fin  		= %u\n", _pTcpheader->fin);
	fprintf(stdout, "syn  		= %u\n", _pTcpheader->syn);
	fprintf(stdout, "rst  		= %u\n", _pTcpheader->rst);
	fprintf(stdout, "psh  		= %u\n", _pTcpheader->psh);
	fprintf(stdout, "ack  		= %u\n", _pTcpheader->ack);
	fprintf(stdout, "urg  		= %u\n", _pTcpheader->urg);
	fprintf(stdout, "res2  		= %u\n", _pTcpheader->res2);
	fprintf(stdout, "window  	= %u\n", ntohs(_pTcpheader->window));
	fprintf(stdout, "check  	= %u\n", ntohs(_pTcpheader->check));
	fprintf(stdout, "urg  		= %u\n", ntohs(_pTcpheader->urg_ptr));
	fprintf(stdout, "------end---------------------------\n");
	
}

void DisplayUdp(struct udphdr *_pUdpheader)
{
	#if 0
	struct udphdr{  
		u_int16_t source;  
		u_int16_t dest;  
		u_int16_t len;  
		u_int16_t check;
	};
	#endif
	
	fprintf(stdout, "------this is a udp header------\n");
	fprintf(stdout, "source  	= %u\n", ntohs(_pUdpheader->source));
	fprintf(stdout, "dest  		= %u\n", ntohs(_pUdpheader->dest));
	fprintf(stdout, "len  		= %u\n", ntohs(_pUdpheader->len));
	fprintf(stdout, "check		= %u\n", ntohs(_pUdpheader->check));
	fprintf(stdout, "------end---------------------------\n");
}

void DisplayIcmp(struct icmphdr *_pIcmpheader)
{
	#if 0
	struct icmphdr{  
		u_int8_t type;		/* message type */  
		u_int8_t code;		/* type sub-code */  
		u_int16_t checksum;  
		union{    
			struct{      
				u_int16_t	id;      
				u_int16_t	sequence;    
			} echo;			/* echo datagram */    
			u_int32_t	gateway;	/* gateway address */    
			struct{      
				u_int16_t	__unused;      
				u_int16_t	mtu;    
			} frag;			/* path mtu discovery */  
		}un;
	};
	#endif
	const char *typeStr = NULL;
	const char *codeStr = NULL;
	static const char *codeDestUnreachStr[] = {	"ICMP_UNREACH_NET",
												"ICMP_UNREACH_HOST",
												"ICMP_UNREACH_PROTOCOL",
												"ICMP_UNREACH_PORT",
												"ICMP_UNREACH_NEEDFRAG",
												"ICMP_UNREACH_SRCFAIL",
												"ICMP_UNREACH_NET_UNKNOWN",
												"ICMP_UNREACH_HOST_UNKNOWN",
												"ICMP_UNREACH_ISOLATED",
												"ICMP_UNREACH_NET_PROHIB",
												"ICMP_UNREACH_HOST_PROHIB",
												"ICMP_UNREACH_TOSNET",
												"ICMP_UNREACH_TOSHOST",
												"ICMP_UNREACH_FILTER_PROHIB",
												"ICMP_UNREACH_HOST_PRECEDENCE",
												"ICMP_UNREACH_PRECEDENCE_CUTOFF"
											};
	static const char *codeRedirectStr[] = 	{	"ICMP_REDIRECT_NET",
												"ICMP_REDIRECT_HOST",
												"ICMP_REDIRECT_TOSNET",
												"ICMP_REDIRECT_TOSHOST"
											};
	static const char *codeTimeExceedStr[] = {
												"ICMP_TIMXCEED_INTRANS",
												"ICMP_TIMXCEED_REASS"
											};
	static const char *codeParaProb[] = {
												"ICMP_PARAMPROB_BADIP",
												"ICMP_PARAMPROB_OPTABSENT"
								}		;
	switch(_pIcmpheader->type){
		case 0:
			typeStr = "ICMP_ECHOREPLY";
			break;
		case 3:
			typeStr = "ICMP_DEST_UNREACH";
			if(_pIcmpheader->code <= 15){
				codeStr = codeDestUnreachStr[_pIcmpheader->code];
			}else{
				codeStr = "Unrecognized";
			}
			break;
		case 4:
			typeStr = "ICMP_SOURCE_QUENCH";
			break;
		case 5:
			typeStr = "ICMP_REDIRECT";
			if(_pIcmpheader->code <= 3){
				codeStr = codeRedirectStr[_pIcmpheader->code];
			}else{
				codeStr = "Unrecognized";
			}
			break;
		case 8:
			typeStr = "ICMP_ECHO";
			break;
		case 11:
			typeStr = "ICMP_TIME_EXCEEDED";
			if(_pIcmpheader->code <= 1){
				codeStr = codeTimeExceedStr[_pIcmpheader->code];
			}else{
				codeStr = "Unrecognized";
			}
			break;
		case 12:
			typeStr = "ICMP_PARAMETERPROB";
			if(_pIcmpheader->code <= 1){
				codeStr = codeParaProb[_pIcmpheader->code];
			}else{
				codeStr = "Unrecognized";
			}
			break;
		case 13:
			typeStr = "ICMP_TIMESTAMP";
			break;
		case 14:
			typeStr = "ICMP_TIMESTAMPREPLY";
			break;
		case 15:
			typeStr = "ICMP_INFO_REQUEST";
			break;
		case 16:
			typeStr = "ICMP_INFO_REPLY";
			break;
		case 17:
			typeStr = "ICMP_ADDRESS";
			break;
		case 18:
			typeStr = "ICMP_ADDRESSREPLY";
			break;
		default:
			typeStr = "Unrecognized";
			break;		
	}
	fprintf(stdout, "------this is a icmp header------\n");
	fprintf(stdout, "type  		= %u(%s)\n", _pIcmpheader->type, typeStr);
	fprintf(stdout, "code  		= %u(%s)\n", _pIcmpheader->code, codeStr);
	fprintf(stdout, "checksum  	= %u\n", ntohs(_pIcmpheader->checksum));
	fprintf(stdout, "------end---------------------------\n");
}

/*设置网卡为混杂模式*/
int SetIfPromisc(int _sockfd, char *_pcIfname)
{
	struct ifreq ifr;

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, _pcIfname, IF_NAMESIZE);
	if(ioctl(_sockfd, SIOCGIFFLAGS, &ifr) < 0){
		fprintf(stdout, "%03d----%02d----%s\n", __LINE__, errno, strerror(errno));
		return EXIT_FIILURE;
	}

	ifr.ifr_flags |= IFF_PROMISC;
	if(ioctl(_sockfd, SIOCSIFFLAGS, &ifr) < 0){
		fprintf(stdout, "%03d----%02d----%s\n", __LINE__, errno, strerror(errno));
		return EXIT_FIILURE;
	}

	return EXIT_SUCCESS;
}

/*取消网卡的混杂模式*/
int UnsetIfPromisc(int _sockfd, char *_pcIfname)
{
	struct ifreq ifr;

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, _pcIfname, IF_NAMESIZE);
	if(ioctl(_sockfd, SIOCGIFFLAGS, &ifr) < 0){
		fprintf(stdout, "%03d----%02d----%s\n", __LINE__, errno, strerror(errno));
		return EXIT_FIILURE;
	}

	ifr.ifr_flags &= ~IFF_PROMISC;
	if(ioctl(_sockfd, SIOCSIFFLAGS, &ifr) < 0){
		fprintf(stdout, "%03d----%02d----%s\n", __LINE__, errno, strerror(errno));
		return EXIT_FIILURE;
	}

	return EXIT_SUCCESS;
}

/*设置过滤器*/
int SetFilter(int _sockfd)
{
	/*
	BPF code generated by tcpdump -dd host 150.150.150.15
	{ 0x28, 0, 0, 0x0000000c },
	{ 0x15, 0, 4, 0x00000800 },
	{ 0x20, 0, 0, 0x0000001a },
	{ 0x15, 8, 0, 0x96969610 },
	{ 0x20, 0, 0, 0x0000001e },
	{ 0x15, 6, 7, 0x96969610 },
	{ 0x15, 1, 0, 0x00000806 },
	{ 0x15, 0, 5, 0x00008035 },
	{ 0x20, 0, 0, 0x0000001c },
	{ 0x15, 2, 0, 0x96969610 },
	{ 0x20, 0, 0, 0x00000026 },
	{ 0x15, 0, 1, 0x96969610 },
	{ 0x6, 0, 0, 0x00000060 },
	{ 0x6, 0, 0, 0x00000000 },
	*/
	struct sock_filter BPF_code[]= {
		{ 0x28, 0, 0, 0x0000000c },
		{ 0x15, 0, 4, 0x00000800 },
		{ 0x20, 0, 0, 0x0000001a },
		{ 0x15, 8, 0, 0x96969610 },
		{ 0x20, 0, 0, 0x0000001e },
		{ 0x15, 6, 7, 0x96969610 },
		{ 0x15, 1, 0, 0x00000806 },
		{ 0x15, 0, 5, 0x00008035 },
		{ 0x20, 0, 0, 0x0000001c },
		{ 0x15, 2, 0, 0x96969610 },
		{ 0x20, 0, 0, 0x00000026 },
		{ 0x15, 0, 1, 0x96969610 },
		{ 0x6, 0, 0, 0x00000060  },
		{ 0x6, 0, 0, 0x00000000  }
	};

	struct sock_fprog Filter;
	Filter.len = 14;
	Filter.filter = BPF_code;
	
	if(setsockopt(_sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter))<0){
		fprintf(stdout, "%03d----%02d----%s\n", __LINE__, errno, strerror(errno));
		return EXIT_FIILURE;
	}
	
	return EXIT_SUCCESS;
}

int main()
{
	int sockfd;
	char buf[2048];
	ssize_t nRet;
	struct ether_header *etherheader;
	unsigned short ethertype;
	struct ip *ipheader;
	struct arphdr *arpheader;
	struct tcphdr *tcpheader;
	struct udphdr *udpheader;
	struct icmphdr *icmpheader;
	
	//sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
	//sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
	sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if(-1 == sockfd){
		fprintf(stdout, "%03d----%02d----%s\n", __LINE__, errno, strerror(errno));
		exit(EXIT_FIILURE);
	}

	if(EXIT_SUCCESS != SetIfPromisc(sockfd, "eth0")){
		exit(EXIT_FIILURE);
	}

	//if(EXIT_SUCCESS != SetFilter(sockfd)){
	//	exit(EXIT_FIILURE);
	//}

	while(1){
		memset(buf, 0, 2048);
		nRet = recvfrom(sockfd, buf, 2048, 0, NULL, NULL);
		if(-1 == nRet){
			fprintf(stdout, "%03d----%02d----%s\n", __LINE__, errno, strerror(errno));
			exit(EXIT_FIILURE);
		}

		if(nRet < sizeof(struct ether_header))	{continue;}

		etherheader = (struct ether_header *)buf;
		
		DisplayEthernet(etherheader);
		
		ethertype = ntohs(etherheader->ether_type);

		if(ethertype == ETHERTYPE_IP){
			if(nRet < sizeof(struct ether_header) + sizeof(struct ip))	{continue;}

			ipheader = (struct ip *)(buf + sizeof(struct ether_header));
			DisplayIp(ipheader);

			if(ipheader->ip_p == IPPROTO_TCP){
				if(nRet < sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct tcphdr))	{continue;}

				tcpheader = (struct tcphdr *)(buf + sizeof(struct ether_header) + sizeof(struct ip));
				DisplayTcp(tcpheader);	
			}else if(ipheader->ip_p == IPPROTO_UDP){
				if(nRet < sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr))	{continue;}

				udpheader = (struct udphdr *)(buf + sizeof(struct ether_header) + sizeof(struct ip));
				DisplayUdp(udpheader);
			}else if(ipheader->ip_p == IPPROTO_ICMP){
				if(nRet < sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct icmphdr))	{continue;}

				icmpheader = (struct icmphdr *)(buf + sizeof(struct ether_header) + sizeof(struct ip));
				DisplayIcmp(icmpheader);
			}
		}else if(ethertype == ETHERTYPE_ARP){
			if(nRet < sizeof(struct ether_header) + sizeof(struct arphdr))	{continue;}

			arpheader = (struct arphdr *)(buf + sizeof(struct ether_header));
			DisplayArp(arpheader);
		}else if(ethertype == ETHERTYPE_REVARP){
			if(nRet < sizeof(struct ether_header) + sizeof(struct arphdr))	{continue;}

			arpheader = (struct arphdr *)(buf + sizeof(struct ether_header));
			DisplayRarp(arpheader);
		}else{
			fprintf(stdout, "ethertype not support:%#02x\n", ethertype);
		}
		sleep(1);
	}

	exit(EXIT_SUCCESS);


你可能感兴趣的:(linux,socket)