diff --git a/gsock.cpp b/gsock.cpp index 51a918c..828ebdd 100644 --- a/gsock.cpp +++ b/gsock.cpp @@ -7,10 +7,12 @@ #include "gsock.h" +#define GSOCK_DEBUG + #ifdef GSOCK_DEBUG #pragma message("GSock Debug mode compiled in") #include -#define myliblog(fmt,...) printf("GSock: " fmt,__VA_ARGS__) +#define myliblog(fmt,...) printf(" " fmt,__func__,__VA_ARGS__) #else #define myliblog(fmt,...) #endif @@ -46,6 +48,7 @@ class _init_winsock2_2_class public: _init_winsock2_2_class() { + myliblog("sockaddr %d sockaddr_in %d sockaddr_in6 %d\n", sizeof(sockaddr), sizeof(sockaddr_in), sizeof(sockaddr_in6)); /// Windows Platform need WinSock2.DLL initialization. #ifdef _WIN32 WORD wd; @@ -67,7 +70,7 @@ public: /// Windows Platform need WinSock2.DLL clean up. #ifdef _WIN32 WSACleanup(); - myliblog("WSACleanup() called."); + myliblog("WSACleanup() called.\n"); #endif } } _init_winsock2_2_obj; @@ -114,6 +117,64 @@ vsock::~vsock() } } +struct sock::_impl +{ + static int connect_ipv4(vsock::_impl* _vp,const std::string& IPStr, int Port); + static int connect_ipv6(vsock::_impl* _vp,const std::string& IPStr, int Port); +}; + +int sock::_impl::connect_ipv4(vsock::_impl* _vp, const std::string& IPStr, int Port) +{ + struct sockaddr_in saddr; + + memset(&saddr, 0, sizeof(saddr)); + if (inet_pton(AF_INET, IPStr.c_str(), &(saddr.sin_addr.s_addr)) != 1) + { + return -4; + } + saddr.sin_port = htons(Port); + saddr.sin_family = AF_INET; + + // Create socket + _vp->sfd = socket(AF_INET, SOCK_STREAM, 0); + if (_vp->sfd<0) + { + myliblog("socket() returns %d. WSAGetLastError: %d\n", _vp->sfd, WSAGetLastError()); + return -3; + } + + myliblog("Socket created: [%d] with _vp %p\n", _vp->sfd, _vp); + _vp->created = true; + + return ::connect(_vp->sfd, (sockaddr*)&saddr, sizeof(saddr)); +} + +int sock::_impl::connect_ipv6(vsock::_impl* _vp, const std::string& IPStr, int Port) +{ + struct sockaddr_in6 saddr; + + memset(&saddr, 0, sizeof(saddr)); + if (inet_pton(AF_INET6, IPStr.c_str(), &(saddr.sin6_addr)) != 1) + { + return -4; + } + saddr.sin6_port = htons(Port); + saddr.sin6_family = AF_INET6; + + // Create socket + _vp->sfd = socket(AF_INET6, SOCK_STREAM, 0); + if (_vp->sfd<0) + { + myliblog("socket() returns %d. WSAGetLastError: %d\n", _vp->sfd, WSAGetLastError()); + return -3; + } + + myliblog("Socket created: [%d] with _vp %p\n", _vp->sfd, _vp); + _vp->created = true; + + return ::connect(_vp->sfd, (sockaddr*)&saddr, sizeof(saddr)); +} + int sock::connect(const std::string& IPStr,int Port) { myliblog("sock::connect() %p\n",this); @@ -122,23 +183,17 @@ int sock::connect(const std::string& IPStr,int Port) { return -2; } - _vp->sfd=socket(AF_INET,SOCK_STREAM,0); - if(_vp->sfd<0) - { - myliblog("socket() returns %d. WSAGetLastError: %d\n",_vp->sfd,WSAGetLastError()); - return -3; - } - myliblog("Socket created: [%d] in %p\n",_pp->sfd,this); - _vp->created=true; - - struct sockaddr_in saddr; - memset(&saddr,0,sizeof(saddr)); - saddr.sin_addr.s_addr=inet_addr(IPStr.c_str()); - saddr.sin_port=htons(Port); - saddr.sin_family=AF_INET; - - return ::connect(_vp->sfd,(sockaddr*)&saddr,sizeof(saddr)); + if (IPStr.find(":") != std::string::npos) + { + // Maybe IPv6 + return sock::_impl::connect_ipv6(_vp, IPStr, Port); + } + else + { + // Maybe IPv4 + return sock::_impl::connect_ipv4(_vp, IPStr, Port); + } } int sock::send(const void* Buffer,int Length) @@ -222,14 +277,50 @@ using _sock_getname_callback_t = decltype(getsockname); static int _sock_getname_call(int sfd,std::string& ip,int& port,_sock_getname_callback_t fn) { - struct sockaddr_in saddr; + struct sockaddr saddr; socklen_t saddrlen=sizeof(saddr); memset(&saddr,0,saddrlen); - int ret=fn(sfd,(sockaddr*)&saddr,&saddrlen); + int ret=fn(sfd,&saddr,&saddrlen); if(ret<0) return ret; //don't bother errno. stop here. - ip=inet_ntoa(saddr.sin_addr); - port=ntohs(saddr.sin_port); - return ret; + if (saddr.sa_family == AF_INET) + { + struct sockaddr_in* paddr = (sockaddr_in*)&saddr; + char ip_buff[64] = { 0 }; + const char* pret = inet_ntop(AF_INET, paddr, ip_buff, 64); + if (pret) + { + ip = std::string(ip_buff); + port = ntohs(paddr->sin_port); + return 0; + } + else + { + // inet_ntop call failed. + return -3; + } + } + else if (saddr.sa_family == AF_INET6) + { + struct sockaddr_in6* paddr = (sockaddr_in6*)&saddr; + char ip_buff[128] = { 0 }; + const char* pret = inet_ntop(AF_INET6, paddr, ip_buff, 128); + if (pret) + { + ip = std::string(ip_buff); + port = ntohs(paddr->sin6_port); + return 0; + } + else + { + // inet_ntop call failed. + return -3; + } + } + else + { + // protocol not supported. + return -4; + } } int sock::getlocal(std::string& IPStr,int& Port) @@ -265,7 +356,7 @@ public: myliblog("socket() returns %d. WSAGetLastError: %d\n", _vp->sfd, WSAGetLastError()); return -3; } - myliblog("Socket created: [%d] in %p\n", _vp->sfd, this); + myliblog("Socket created: [%d] with _vp %p\n", _vp->sfd, _vp); _vp->created = true; return 0; } @@ -332,7 +423,7 @@ int serversock::accept(sock& _out_s) s._vp->sfd=ret; s._vp->created=true; - myliblog("Socket opened: [%d] in %p by serversock %p\n",s._vp->sfd,&s,this); + myliblog("Socket opened: [%d] in %p by serversock _vp: %p\n",s._vp->sfd,&s,_vp); /// Move resource. _out_s=std::move(s); @@ -340,94 +431,376 @@ int serversock::accept(sock& _out_s) } } -udpsock::udpsock() +struct udpsock::_impl { - _vp->sfd = socket(AF_INET, SOCK_DGRAM, 0); - _vp->created = true; + int protocol; + bool is_protocol_decided; + + int make_decided(vsock::_impl* _vp) + { + if (_vp->created) + { + return -2; + } + else + { + _vp->sfd = socket(protocol, SOCK_DGRAM, 0); + _vp->created = true; + return 0; + } + } +}; + +static inline const char* get_family_name(int family) +{ + switch (family) + { + case AF_INET: + return "AF_INET"; + case AF_INET6: + return "AF_INET6"; + default: + return "Unknown"; + } +} + +udpsock::udpsock(int use_family) : _pp(new _impl) +{ + + if (use_family == 1) + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + } + else if (use_family == 2) + { + _pp->protocol = AF_INET6; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + } + else + { + _pp->is_protocol_decided = false; + } +} + +// Convert from IPStr to sockaddr +// Parameters: +// flag_ipv46: +// -1: Undecided +// 0: IPv4 +// 1: IPv6 +// Return: +// -1: inet_pton() call failed. +// 0: Success, IPv4 +// 1: Success, IPv6 +static int convert_ipv46(const std::string& IPStr, int Port, + struct sockaddr*& _out_psockaddr, int& _out_szsockaddr, + struct sockaddr_in* paddr, struct sockaddr_in6* paddr6, int flag_ipv46) +{ + if ( (flag_ipv46==1) || + ( (flag_ipv46==-1) && (IPStr.find(":") != std::string::npos) ) + ) + { + // Maybe IPv6 + memset(paddr6, 0, sizeof(sockaddr_in6)); + if (inet_pton(AF_INET6, IPStr.c_str(), &(paddr6->sin6_addr)) != 1) + { + return -1; + } + paddr6->sin6_port = htons(Port); + paddr6->sin6_family = AF_INET6; + + _out_psockaddr = (sockaddr*)&paddr6; + _out_szsockaddr = sizeof(sockaddr_in6); + return 1; + } + else // flag_ipv46==-1 && IPStr.find(":")==string::npos, flag_ipv46==0 + { + // Maybe IPv4 + memset(paddr, 0, sizeof(sockaddr_in)); + if (inet_pton(AF_INET, IPStr.c_str(), &(paddr->sin_addr)) != 1) + { + return -1; + } + paddr->sin_port = htons(Port); + paddr->sin_family = AF_INET; + + _out_psockaddr = (sockaddr*)&paddr; + _out_szsockaddr = sizeof(sockaddr_in); + return 0; + } +} + +// Convert from sockaddr to IPStr +// Return: +// -1: inet_ntop() call failed. +// -2: Unsupported protocol +// 0: Success, IPv4 +// 1: Success, IPv6 +static int convertback_ipv46(const sockaddr* paddr, std::string& _out_IPStr) +{ + char buff[128] = { 0 }; + if (paddr->sa_family == AF_INET) + { + if (inet_ntop(AF_INET, paddr, buff, 128)!=NULL) + { + _out_IPStr = std::move(std::string(buff)); + return 0; + } + else return -1; + } + else if (paddr->sa_family == AF_INET6) + { + if (inet_ntop(AF_INET6, paddr, buff, 128) != NULL) + { + _out_IPStr = std::move(std::string(buff)); + return 1; + } + else return -1; + } + else return -2; } int udpsock::connect(const std::string& IPStr,int Port) { sockaddr_in saddr; - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(Port); - saddr.sin_addr.s_addr = inet_addr(IPStr.c_str()); + sockaddr_in6 saddr6; + sockaddr* paddr; + int addrsz; + + int ret = convert_ipv46(IPStr, Port, paddr, addrsz, &saddr, &saddr6, + (_pp->is_protocol_decided) ? ((_pp->protocol == AF_INET) ? 0 : 1) : -1); + + if (ret < 0) + { + return -4; + } + else + { + _pp->protocol = (ret == 0) ? (AF_INET) : (AF_INET6); + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + } - return ::connect(_vp->sfd,(const sockaddr*)&saddr,sizeof(saddr)); + return ::connect(_vp->sfd, (const sockaddr*)paddr, addrsz); } int udpsock::broadcast_at(int Port) { - sockaddr_in saddr; - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(Port); - saddr.sin_addr.s_addr = INADDR_BROADCAST; - - return ::connect(_vp->sfd,(const sockaddr*)&saddr,sizeof(saddr)); + if (_pp->is_protocol_decided) + { + if (_pp->protocol == AF_INET) + { + sockaddr_in saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(Port); + saddr.sin_addr.s_addr = INADDR_BROADCAST; + + return ::connect(_vp->sfd, (const sockaddr*)&saddr, sizeof(saddr)); + } + else + { + myliblog("IPv6 does not support broadcast!\n"); + return -1; + } + } + else + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + return broadcast_at(Port); + } } int udpsock::set_broadcast() { - socklen_t opt=1; - return ::setsockopt(_vp->sfd,SOL_SOCKET,SO_BROADCAST,(const char*)&opt,sizeof(opt)); + if (_pp->is_protocol_decided) + { + socklen_t opt = 1; + return ::setsockopt(_vp->sfd, SOL_SOCKET, SO_BROADCAST, (const char*)&opt, sizeof(opt)); + } + else + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + return set_broadcast(); + } } int udpsock::bind(int Port) { - sockaddr_in saddr; - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(Port); - saddr.sin_addr.s_addr = INADDR_ANY; - return ::bind(_vp->sfd, (const sockaddr*)&saddr, sizeof(saddr)); + if (_pp->is_protocol_decided) + { + if (_pp->protocol == AF_INET) + { + sockaddr_in saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(Port); + saddr.sin_addr.s_addr = INADDR_ANY; + + return ::bind(_vp->sfd, (const sockaddr*)&saddr, sizeof(saddr)); + } + else + { + sockaddr_in6 saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin6_family = AF_INET6; + saddr.sin6_port = htons(Port); + saddr.sin6_addr = in6addr_any; + + return ::bind(_vp->sfd, (const sockaddr*)&saddr, sizeof(saddr)); + } + } + else + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + return bind(Port); + } } int udpsock::sendto(const std::string& IPStr, int Port, const void* buffer, int length) { sockaddr_in saddr; - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(Port); - saddr.sin_addr.s_addr = inet_addr(IPStr.c_str()); - return ::sendto(_vp->sfd, (const char*)buffer, length, 0, (const sockaddr*)&saddr, sizeof(saddr)); + sockaddr_in6 saddr6; + sockaddr* paddr; + int addrsz; + + int ret = convert_ipv46(IPStr, Port, paddr, addrsz, &saddr, &saddr6, + (_pp->is_protocol_decided) ? ((_pp->protocol == AF_INET) ? 0 : 1) : -1); + if (ret < 0) + { + return -4; + } + else + { + _pp->protocol = (ret == 0) ? (AF_INET) : (AF_INET6); + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + } + + return ::sendto(_vp->sfd, (const char*)buffer, length, 0, (const sockaddr*)paddr, addrsz); } int udpsock::broadcast(int Port,const void* buffer,int length) { - sockaddr_in saddr; - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(Port); - saddr.sin_addr.s_addr = INADDR_BROADCAST; - return ::sendto(_vp->sfd, (const char*)buffer, length, 0, (const sockaddr*)&saddr, sizeof(saddr)); + if (_pp->is_protocol_decided) + { + if (_pp->protocol == AF_INET) + { + sockaddr_in saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(Port); + saddr.sin_addr.s_addr = INADDR_BROADCAST; + return ::sendto(_vp->sfd, (const char*)buffer, length, 0, (const sockaddr*)&saddr, sizeof(saddr)); + } + else + { + myliblog("IPv6 does not support broadcast!\n"); + return -1; + } + } + else + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + + return broadcast(Port, buffer, length); + } } int udpsock::recvfrom(std::string& fromIP, int& fromPort, void* buffer, int bufferLength) { - sockaddr_in saddr; - socklen_t saddrlen = sizeof(saddr); - int ret = ::recvfrom(_vp->sfd, (char*)buffer, bufferLength, 0, (sockaddr*)&saddr, &saddrlen); - - if (ret < 0) + if (_pp->is_protocol_decided) { - return ret; /// don't bother errno. - } + if (_pp->protocol == AF_INET) + { + sockaddr_in saddr; + socklen_t saddrlen = sizeof(saddr); + int ret = ::recvfrom(_vp->sfd, (char*)buffer, bufferLength, 0, (sockaddr*)&saddr, &saddrlen); - fromIP = inet_ntoa(saddr.sin_addr); - fromPort = ntohs(saddr.sin_port); - return ret; + if (ret < 0) + { + return ret; /// don't bother errno. + } + + convertback_ipv46((const sockaddr*)&saddr, fromIP); + fromPort = ntohs(saddr.sin_port); + return ret; + } + else + { + sockaddr_in6 saddr; + socklen_t saddrlen = sizeof(saddr); + int ret = ::recvfrom(_vp->sfd, (char*)buffer, bufferLength, 0, (sockaddr*)&saddr, &saddrlen); + + if (ret < 0) + { + return ret; /// don't bother errno. + } + + convertback_ipv46((const sockaddr*)&saddr, fromIP); + fromPort = ntohs(saddr.sin6_port); + return ret; + } + } + else + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + return recvfrom(fromIP, fromPort, buffer, bufferLength); + } } int udpsock::send(const void* buffer,int length) { - return ::send(_vp->sfd,(const char*)buffer,length,0); + if (_pp->is_protocol_decided) + { + return ::send(_vp->sfd, (const char*)buffer, length, 0); + } + else + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + return send(buffer, length); + } } int udpsock::recv(void* buffer,int bufferLength) { - return ::recv(_vp->sfd,(char*)buffer,bufferLength,0); + if (_pp->is_protocol_decided) + { + return ::recv(_vp->sfd, (char*)buffer, bufferLength, 0); + } + else + { + _pp->protocol = AF_INET; + _pp->is_protocol_decided = true; + myliblog("Protocol decided to %s in udpsock %p\n", get_family_name(_pp->protocol), this); + _pp->make_decided(_vp); + return recv(buffer, bufferLength); + } } // Select @@ -535,8 +908,10 @@ bool selector::is_error(const vsock& v) return FD_ISSET(v._vp->sfd, &_pp->errorset); } -int DNSResolve(const std::string& HostName, std::string& _out_IPStr) +int DNSResolve(const std::string& HostName, std::vector& _out_IPStrVec) { + std::vector vec; + /// Use getaddrinfo instead struct addrinfo hints; memset(&hints, 0, sizeof(hints)); @@ -551,21 +926,61 @@ int DNSResolve(const std::string& HostName, std::string& _out_IPStr) { return -1;/// API Call Failed. } + + int cnt = 0; for (struct addrinfo* ptr = result; ptr != nullptr; ptr = ptr->ai_next) { + cnt++; switch (ptr->ai_family) { - case AF_INET: - sockaddr_in * addr = (struct sockaddr_in*) (ptr->ai_addr); - _out_IPStr = inet_ntoa(addr->sin_addr); - freeaddrinfo(result); - return 0; - break; - } + case AF_INET: + { + sockaddr_in * paddr = (struct sockaddr_in*) (ptr->ai_addr); + char ip_buff[64] = { 0 }; + const char* ptr = inet_ntop(AF_INET, &(paddr->sin_addr), ip_buff, 64); + if (ptr != NULL) + { + vec.push_back(ptr); + } + break; + } + case AF_INET6: + { + sockaddr_in6* paddr = (struct sockaddr_in6*) (ptr->ai_addr); + char ip_buff[128] = { 0 }; + const char* ptr = inet_ntop(AF_INET6, &(paddr->sin6_addr), ip_buff, 128); + if (ptr != NULL) + { + vec.push_back(ptr); + } + break; + } + }// End of switch } - /// Unknown error. + freeaddrinfo(result); - return -2; + + _out_IPStrVec = std::move(vec); + + // if(cnt!=(int)_out_IPStrVec.size()), + // then (cnt-(int)_out_IPStrVec.size()) errors happend while calling inet_ntop(). + return cnt; +} + +int DNSResolve(const std::string& HostName, std::string& _out_IPStr) +{ + std::vector vec; + int ret = DNSResolve(HostName, vec); + if (ret < 0) + { + return -1; + } + if (vec.empty()) + { + return -2; + } + _out_IPStr = vec[0]; + return 0; } diff --git a/gsock.h b/gsock.h index 7f6d671..508a56c 100644 --- a/gsock.h +++ b/gsock.h @@ -8,6 +8,7 @@ #include #include +#include class vsock { @@ -35,6 +36,7 @@ public: /// -1: connect() call error. See errno. /// -2: This socket has been connected before. /// -3: socket() call error. Failed to create socket. See errno. + /// -4: IP Address invalid. int connect(const std::string& IPStr,int Port); template @@ -62,6 +64,8 @@ public: int getpeer(std::string& IPStr,int& Port); friend class serversock; +private: + struct _impl; }; class serversock : public vsock @@ -92,10 +96,17 @@ private: class udpsock : public vsock { public: - udpsock(); + // use_family: + // 0: Auto (Undecided now) (default) + // 1: IPv4 (If family cannot be automatically decided, then IPv4 will be the default option) + // 2: IPv6 + udpsock(int use_family=0); /// Use udp socket as tcp socket. (but of course it is not). /// connect call just copy the target socket data to kernel. See connect() for more info. + /// Return: + /// -1: connect() error. + /// -4: IP Address Invalid. int connect(const std::string& IPStr,int Port); int broadcast_at(int Port); @@ -113,6 +124,9 @@ public: /// send() and recv() should only be called after connect(). Or it will fail. int send(const void* buffer,int length); int recv(void* buffer,int bufferLength); +private: + struct _impl; + _impl* _pp; }; /// Select @@ -141,6 +155,16 @@ private: }; /// Net Tools +// Return: +// -1: getaddrinfo() call failed. +// Other: Number of fetched results from getaddrinfo() call. +int DNSResolve(const std::string& HostName, std::vector& _out_IPStrVec); + +// A wrapper of DNSResolve(...,std::vector&) +// Return: +// -1: getaddrinfo() call failed. +// -2: Failed to resolve. (No results in vector) +// 0: Success. int DNSResolve(const std::string& HostName,std::string& _out_IPStr); #endif // _gsock_h \ No newline at end of file