I am create socket class but I want to make my Connect function dynamic and can connect to address(ipv4 or ipv6) use switch to make IPv test and connect to supported IPv just wan to ask if I am right or is there an easy way to make it to make IPv4 or IPv6?
bool Connect(short port,std::string addr,bool vlisten,HWND WindowHandle,WSADATA& wsaData,bool async)
{
if(!hSocket);
{
this->port = port;
this->addr =addr;
this->vlisten = vlisten;
this->WindowHandle = WindowHandle;
this->wsaData =wsaData;
this->init = true;
// Provide big enough buffer, ipv6 should be the biggest
char ipstr[INET6_ADDRSTRLEN];
char ipstr2[INET6_ADDRSTRLEN];
struct sockaddr_in* sockaddr_ipv4;
struct sockaddr_in6* sockaddr_ipv6;
//struct sockaddr_in6* sockaddr_ipv6;
if(WSAStartup(MAKEWORD(2,2),&wsaData) !=0)
{
throw runtime_error("Error WSAStartup:" + WSAGetLastError());
}
if((this->hSocket = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))== INVALID_SOCKET)
{
Close();
throw runtime_error("Error init sockect:" + WSAGetLastError());
}
if(addr != "INADDR_ANY")
{
struct addrinfo *result = nullptr;
getaddrinfo(addr.c_str(), nullptr, nullptr, &result);
struct addrinfo *it;
for (it = result; it != nullptr; it = it->ai_next)
{
//sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
//addr = inet_ntoa(sockaddr_ipv4->sin_addr);
//if (addr != "0.0.0.0") break;
switch (it->ai_family)
{
case AF_UNSPEC:
cout<<"Unspecified\n"<<endl;
break;
case AF_INET:
cout<<"AF_INET (IPv4)\n"<<endl;
sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
//printf("\tIPv4 address %s\n",
addr = inet_ntoa(sockaddr_ipv4->sin_addr);
/*if (addr != "0.0.0.0") break;*/
break;
case AF_INET6:
cout<<"AF_INET (IPv6)\n"<<endl;
sockaddr_ipv6 = reinterpret_cast<sockaddr_in6*>(it->ai_addr);
addr = inet_ntop(it->ai_family,sockaddr_ipv6,(PSTR)ipstr,sizeof(ipstr));
break;
case AF_NETBIOS:
cout<<"AF_NETBIOS (NetBIOS)\n"<<endl;
break;
default:
printf("Other %ld\n", it->ai_family);
break;
}
}
freeaddrinfo(result);
}
}
SOCKADDR_IN sockAddrIn;
memset(&sockAddrIn,0,sizeof(sockAddrIn));
sockAddrIn.sin_port = htons(port);
sockAddrIn.sin_family = AF_INET;
sockAddrIn.sin_addr.s_addr = (addr == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(addr.c_str()));
if(vlisten && (bind(hSocket,reinterpret_cast<SOCKADDR*>(&sockAddrIn),sizeof(sockAddrIn))== SOCKET_ERROR))
{
Close();
throw runtime_error("Error vlisten & bind: " + WSAGetLastError());
}
if(async && WindowHandle)
{
if(WSAAsyncSelect(hSocket,WindowHandle,WM_SOCKET,FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE|FD_ACCEPT) !=0)
{
Close();
throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
}
}
if(vlisten && (listen(hSocket,SOMAXCONN)== SOCKET_ERROR))
{
Close();
throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
}
if(!vlisten && (connect(hSocket, reinterpret_cast<SOCKADDR*>(&sockAddrIn), sizeof(sockAddrIn)) == SOCKET_ERROR))
{
if(async && WindowHandle && (WSAGetLastError() != WSAEWOULDBLOCK))
{
Close();
throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
}
}
}
Your code has multiple issues:
First, you correctly called getaddrinfo(), but then you completely threw away the results without using them.
You called listen() but you appear to intend to make an outgoing connection; listen() is meant to listen for incoming connections.
Instead of using the information from getaddrinfo() you ignore it and assume IPv4 when filling out your sockaddr_in structure. This part of the code should just be scrapped.
There's no need to explicitly check the returned address family. You won't get any address families that the computer can't handle.
You appear to be writing a single method which does more than one thing, i.e. both make outgoing connections and accept incoming connections. Methods should only do one thing.
Let's go back to the beginning and get a minimal outgoing connection up. I'm omitting anything here not directly related to creating the connection (e.g. the call to WSAAsyncSelect() and other stuff which belongs in separate methods anyway):
// Connect to a remote host, given its address and port number.
bool Connect(short port, std::string addr)
{
struct addrinfo *result, *rp;
// TODO: You passed us an integer port number. We need a C string.
// Clean up this mess.
char portstr[255];
portstr = sprintf("%d", port);
if (getaddrinfo(addr.c_str(), portstr, nullptr, &result)) {
throw runtime_error("getaddrinfo: " + WSAGetLastError());
}
// A host can have multiple addresses. Try each of them in turn until
// one succeeds. Typically this will try the IPv6 address first, if
// one exists, then the IPv4 address. The OS controls this ordering
// and you should not attempt to alter it. (RFC 6724)
for (rp = result; rp != nullptr; rp = rp->ai_next) {
this->hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
// Check socket creation failed; maybe harmless (e.g. computer
// doesn't have IPv6 connectivity). Real errors will get thrown below.
if (this->hSocket == -1)
continue;
if (connect(this->hSocket, rp->ai_addr, rp->ai_addrlen) != -1)
break; // Success
close(this->hSocket);
}
if (rp == NULL) { // No address succeeded
throw runtime_error("connect: " + WSAGetLastError());
}
freeaddrinfo(result);
// Now this->hSocket has an open socket connection. Enjoy!
}
The major thing to note is that getaddrinfo() handles all the heavy lifting for you. The structure it returns has all the information needed to create the connection; you only have to use it.
If you want the connection information, such as address and family, you can copy those out of rp and store them somewhere before it goes out of scope.
Writing the separate method to handle incoming connections is left as an exercise for the reader. Your example code appears to be partly based on the sample on the MSDN page for getaddrinfo; the Linux getaddrinfo manual page has much better examples (the sample code actually works with minimal change on Windows).
Related
I am new to stackoverflow and also pretty much a beginner at programming and hope to find a solution here.
My code is written in C++ and should run on a computer on module with linux operating system. The program should receive messages from other linux or windows systems and then depending on the content of the messages execute further subroutines and send back a response. The windows program is also written in C++. The linux system and the windows system are connected via a switch and the switch is connected to the home network via a powerline adapter. The multicast function is enabled and supported in the switch settings, as well as in the linux system.
The linux code to test the functionality looks like this:
int createIPv4MulticastSocket(uint16_t socket_port, int allowReuseAddress)
{
int Socket;
int broadcast = 1;
sockaddr_in localSock = {};
// Bind to the proper port number with the IP address specified as INADDR_ANY
memset(&localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(socket_port);
localSock.sin_addr.s_addr = INADDR_ANY;
// Creating the Socket
printf("Creating a socket...");
if ((Socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Creating a socket failed:");
printf("\n");
}
else
{
printf("Socket created. \n");
}
// set the reuse address options
if (setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (char*)&allowReuseAddress, sizeof(allowReuseAddress)) < 0)
{
perror("Error setting the reuse address option");
printf("\n");
}
else
printf("Setting the reuse address option...OK. \n");
// bind the socket to the defined address
printf("Try to bind the created Socket to my address. \n");
if (bind(Socket, (struct sockaddr*)&localSock, sizeof(localSock)) == -1) {
perror("Binding socket failed:");
printf("\n");
}
else
{
printf("Bind was succesful. \n");
}
// sets the socket options so you can send Broadcast messages
printf("Setting the socket options to allow Broadcast. \n");
if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &broadcast,
sizeof(broadcast)) == -1) {
perror("Setting the socket options for allowing broadcast failed:");
printf("\n");
}
else
{
printf("Setting the broadcast options...OK. \n");
}
return Socket;
}
void joinMulticastGroup(const char* IPMulticastGroup, const char* IPLocalInterfaceAddr, int SocketDescriptor)
{
struct ip_mreq group;
int LocalIP;
int conv_ip;
if (IPLocalInterfaceAddr[0] == '\0')
{
conv_ip = inet_pton(AF_INET, IPMulticastGroup, &group.imr_multiaddr.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
group.imr_interface.s_addr = htonl(INADDR_ANY);
}
else
{
conv_ip = inet_pton(AF_INET, IPMulticastGroup, &group.imr_multiaddr.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
conv_ip = inet_pton(AF_INET, IPLocalInterfaceAddr, &group.imr_interface.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
}
if (setsockopt(SocketDescriptor, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&group, sizeof(group)) < 0)
{
perror("Adding multicast group error");
printf("\n");
}
else
printf("Adding multicast group...OK. \n");
}
void sendUDP(int sending_socket, const char* destination_ipaddress,
uint16_t destination_port, unsigned char sending_message[], int size_of_sending_message)
{
struct sockaddr_in destination_address;
long int numbytes_send;
int conv_ip;
// define destination address
printf("Convert the destination address to sockaddr_in. \n");
destination_address.sin_family = AF_INET; // IPv4 address
destination_address.sin_port = htons(destination_port); // destination port
conv_ip = inet_pton(AF_INET, destination_ipaddress, &destination_address.sin_addr.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
memset(destination_address.sin_zero, '\0', sizeof(destination_address.sin_zero)); // fill up sin_zero with "0"
printf("Correct destination address sockaddr. \n");
printf("Sending a message...");
if ((numbytes_send = sendto(sending_socket, sending_message, size_of_sending_message, 0,
(struct sockaddr*)&destination_address, sizeof(destination_address))) == -1) {
perror("sendto() failed:");
printf("\n");
}
else
{
printf(" %i Bytes of data have been sent. \n", numbytes_send);
}
}
void receiveUDP(int receiving_socket, struct sockaddr* received_from,
unsigned char receiving_message[], int size_of_receiving_message)
{
long int numbytes_received;
unsigned int len_received_from = sizeof(*received_from);
socklen_t len_recv_from = len_received_from;
printf("Trying to receive a message...");
if ((numbytes_received = recvfrom(receiving_socket, receiving_message, size_of_receiving_message, 0,
received_from, &len_recv_from)) == -1) {
perror("Receiving message failed:");
printf("\n");
}
else
{
printf("%i Bytes an Daten erhalten\n", numbytes_received);
}
}
int main()
{
struct sockaddr received_from;
int socketfd;
unsigned char sending_message[1472], receiving_message[1472];
const char* destination_ipaddress = "192.168.178.35"; //Laptop
const char* multicast_ipaddress = "224.0.1.14"; //Multicast
const char* broadcast_ipaddress = "192.168.178.255"; //Broadcast
uint16_t destination_port = 3300;
uint16_t port = 3300;
uint16_t messageid = 0;
double altitude = 0;
double longitude = 0;
double lattitude = 0;
// Clean message buffer
memset(sending_message, '\0', sizeof(sending_message));
memset(receiving_message, '\0', sizeof(receiving_message));
// Create a Socket
socketfd = createIPv4MulticastSocket(port, 1);
// join the multicast group
joinMulticastGroup(multicast_ipaddress, "", socketfd);
// Send UDP message.
sendUDP(socketfd, broadcast_ipaddress, destination_port, sending_message, sizeof(sending_message));
/// Receive messages and read Data
while (1)
{
receiveUDP(socketfd, &received_from, receiving_message, sizeof(receiving_message));
messageid = unpackunsignedint16(receiving_message, 1);
altitude = unpackdouble(receiving_message, 3);
lattitude = unpackdouble(receiving_message, 11);
longitude = unpackdouble(receiving_message, 19);
printf("actual altitude is: %lf \n", altitude);
printf("actual lattitude is: %lf \n", lattitude);
printf("actual longitude is: %lf \n", longitude);
}
close(socketfd);
}
If i now send unicast messages to the linux program, everything works as it should and the message is received and the sent values are inserted correct into the printf() functions. Sending unicast messages also works without problems. However, if I want to receive broadcast or multicast messages, the program stops in the recvfrom() line. If I check the connected port with tcpdump port 3300 for incoming messages, they arrive at the linux system. If I try to send broadcast or multicast messages, this does not work and no outgoing messages are displayed in tcpdump.
If I try to receive broadcast or multicast messages and then go back to try again to receive unicast messages, this also does not work anymore. I do not get any error messages during the showed errorchecking.
Thanks in advance for your help.
Edit: Maybe I forgot to mention something because I thought it shouldn't be that big of a deal, but I've also read now that this could be a problem. The application is deployed to the system using a docker container
You're not correctly setting the incoming interface for multicast traffic, and you're not setting the outgoing interface at all.
When you call joinMulticastGroup, you pass an empty string for the second argument which is supposed to contain the IP address of the incoming multicast interface as a string. So if for example the machine's IP is 192.168.178.34, then you pass "192.168.178.34" for that argument.
If you don't set the outgoing multicast interface explicitly, the OS will choose whichever interface is the "default". You should use the IP_MULTICAST_IF socket option, passing the address of a struct in_addr specifying the IP address.
I am currently learning C++ and I'm working on a library that will allow sockets to be created on Windows and Linux, supporting IPv4 and IPv6.
I've got it working fine on IPv4 but I'm having a problem with IPv6.
I've tried doing it so it binds to an IP address or binds to a specific IPV6 address but either fail.
Below is how I create a socket.
this->serverSocket = socket(family, socketType, 0);
if (this->serverSocket < 0)
{
stringstream logstream;
logstream << "Error opening socket. Most likely trying to bind to an ";
logstream << "invalid IP or the port is already in use";
bitsLibrary->writeToLog(logstream.str(), "LinuxSocket", "createSocket");
return false;
}
switch (family)
{
case AF_INET: {
this->serv_addr = new sockaddr();
bzero((sockaddr*)this->serv_addr, sizeof(this->serv_addr));
sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(serv_addr);
sin->sin_family = family;
//sin->sin_addr.s_addr = INADDR_ANY;
//If IP Address is NULL then set to IPADDR_ANY
if (ipAddress.empty())
{
sin->sin_addr.s_addr = INADDR_ANY;
}
else
{
inet_pton(AF_INET, ipAddress.c_str(), &sin->sin_addr);
}
sin->sin_port = htons(port);
break;
}
case AF_INET6: {
this->serv_addr = new sockaddr();
bzero((sockaddr*)this->serv_addr, sizeof(this->serv_addr));
sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(serv_addr);
sin->sin6_family = family;
if (ipAddress.empty())
{
sin->sin6_addr = IN6ADDR_ANY_INIT;
}
else
{
inet_pton(AF_INET6, ipAddress.c_str(), &(sin->sin6_addr));
}
sin->sin6_port = htons(port);
break;
}
default:
this->bitsLibrary->writeToLog("Invalid socket family. Only AF_INET or AF_INET6 is supported");
return false;
}
Below is how I then bind the socket
stringstream logstream;
int result = bind(this->serverSocket, (sockaddr * )serv_addr, sizeof(*serv_addr));
if (result < 0)
{
logstream << "Failed to bind socket. Error: " << strerror(result);
throw SocketException(logstream.str().c_str());
close(this->serverSocket);
return false;
}
result = listen(this->serverSocket, this->socketPort);
if (result < 0)
{
logstream << "Failed to start listening. Socket Error: " << strerror(result);
throw SocketException(logstream.str().c_str());
}
logstream << "Socket " << this->socketPort << " has been successfully bound";
this->bitsLibrary->writeToLog(logstream.str(), "LinuxSocket", "bindAndStartListening");
return true;
I call the create socket function as follows
if (!socketManager.createSocket(AF_INET6, SOCK_STREAM, 500, 50, "fe80::20c:29ff:fea0:7da8"))
The bind returns -1 strerror says an unknown error occurred.
The IP address that I am passing into createSocket method is fe80::20c:29ff:fea0:7da8.
When I run my program through strace I then get the following on the bind
socket(PF_INET6, SOCK_STREAM, IPPROTO_IP) = 4
bind(4, {sa_family=AF_INET6, sin6_port=htons(500), inet_pton(AF_INET6, "fe80::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 16) = -1 EINVAL (Invalid argument)
Notice that the IP has been truncated to fe80::.
If I don't use an IP so it binds to anything the IP is then ::
Update
Below is the code I have changed for the AF_INET6 socket.
case AF_INET6: {
this->serv_addr = new sockaddr_storage();
bzero((sockaddr_in6*)this->serv_addr, sizeof(this->serv_addr));
sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(serv_addr);
sin->sin6_family = family;
if (ipAddress.empty())
{
sin->sin6_addr = IN6ADDR_ANY_INIT;
}
else
{
inet_pton(AF_INET6, ipAddress.c_str(), &sin->sin6_addr);
}
sin->sin6_port = htons(port);
break;
}
I've also change this->servAddr is sockaddr_storage in the header as suggested
At least on Windows, sizeof(sockaddr) < sizeof(sockaddr_in6). When you allocate memory for this->serv_addr, you allocate not enough, then corrupt your heap block, and then you have bugs.
It's suggested that you use sockaddr_storage which is "Large enough to accommodate all supported protocol-specific address structures". Or you can allocate different structures in different cases, which shouldn't be a problem for the rest of your code.
UPDATE 1
There still seems to be a few mistakes in your code. You need to check all references to this->serv_addr and verify types/sizeof's.
-- Example 1: You bzero() a wrong number of bytes here - not only a wrong sizeof of wrong type, but also you do sizeof on a pointer:
bzero((sockaddr_in6*)this->serv_addr, sizeof(this->serv_addr));
Should be fixed like that:
sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(serv_addr);
bzero(sin, sizeof(*sin));
-- Example 2: You pass wrong buffer size here:
int result = bind(this->serverSocket, (sockaddr * )serv_addr, sizeof(*serv_addr));
You should remember the actual size for this->serv_addr at the time you allocate it and pass this size to bind()
I am getting an error when I try to connect to my ipv4 server. Currently the ios app users are required to enter their sever's an IP address, port, and account information.
The ios app then calls Connect on the SocketSender class (included in the header search path) which in turns calls the connect function of Socket.h and then checks the results.
Connect - SocketSender.cpp
bool SocketSender::Connect (const char *host, int port, CApiError &err)
{
errno = 0;
struct hostent *hostinfo;
hostinfo = gethostbyname (host);
if (!hostinfo) {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
#else
/* Linux stores the gethostbyname error in h_errno. */
m_nLastErrorNo = EINVAL; // h_errno value is incompatible with the "normal" error codes
err.SetError(FIX_SN(h_errno, hstrerror(h_errno)), CATEGORY_SYSTEM | ERR_TYPE_ERROR);
#endif
return false;
}
socket_fd = socket (AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
return false;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons (port);
address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
int result;
SetSocketOptions();
result = connect (socket_fd, (struct sockaddr *) &address, sizeof (address));
if (result == -1) {
if (IS_IN_PROGRESS()) {
fd_set f1,f2,f3;
struct timeval tv;
/* configure the sets */
FD_ZERO(&f1);
FD_ZERO(&f2);
FD_ZERO(&f3);
FD_SET(socket_fd, &f2);
FD_SET(socket_fd, &f3);
/* we will have a timeout period */
tv.tv_sec = 5;
tv.tv_usec = 0;
int selrez = select(socket_fd + 1,&f1,&f2,&f3,&tv);
if (selrez == -1) { // socket error
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (FD_ISSET(socket_fd, &f3)) { // failed to connect ..
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (!FD_ISSET(socket_fd, &f2)) { // cannot read, so some (unknown) error occured (probably time-out)
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#ifndef PLATFORM_WIN32 // FIXME: is the same needed for windows ?
// unix always marks socket as "success", however error code has to be double-checked
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
err.SetSystemError();
return false;
}
if(error != 0) {
m_nLastErrorNo = error;
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#endif
} else {
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
}
m_nIP = ntohl(address.sin_addr.s_addr);
m_bServerSocket = false;
return true;
}
That is the original version that worked without any problems. When i changed the above to use AF_INET6 and in_addr6->sin6_addr, i kept getting errors and the application failed to connect. I tried using getaddrinfo but this still did not connect.
struct addrinfo hints, *res, *res0;
int error;
const char *cause = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
error = getaddrinfo(host, "PORT", &hints, &res0);
if (error) {
errx(1, "%s", gai_strerror(error));
/*NOTREACHED*/
}
socket_fd = -1;
printf("IP addresses for %s:\n\n", host);
int result;
void *addr;
char *ipver;
for (res = res0; res!=NULL; res = res->ai_next) {
socket_fd = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (socket_fd < 0) {
cause = "socket";
continue;
}
if ((result = connect(socket_fd, res->ai_addr, res->ai_addrlen)) < 0) {
cause = "connect";
close(socket_fd);
socket_fd = -1;
continue;
}
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (res->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
SetSocketOptions();
break; /* okay we got one */
}
I need to make it backwards compatible with ipv6 and ipv4. Any help would be much appreciated as i have been stuck testing this for the past week. Also if anyone knows how to debug the SocketSender.cpp on XCode that would be alot of help.
So after two weeks of testing out different approaches and familiarizing myself with networking (POSIX) I finally got this to work mainly due to #user102008 suggestion.
This is relevant to Client-Server applications.My application is a client application that connects to a IPv4 server/system at a remote location. We have yet to support IPv6 for our products which include clients(iOS,android,windows,unix) and servers (windows & unix), but will support upon future releases. The reason for this support was solely due to Apple changing their apple review process environment.
Approach, Tips and Issues
Apple has provided a way to test IPv6 compatibility with your app. This is sharing your connection from your Ethernet using NAT64/DNS64. This failed many times for me. After researching and resetting my SMC, I came across this article and realized i may have been messing with the configuration too much. So I reset my SMC, restarted and created the internet sharing host. Always remember to turn off WiFi before making any changes to internet sharing.
Users were required to connect to the server with a IPv4 IP address. The Application ran perfectly on an IPv4 networking but failed in an IPv6 network. This was due to the app not resolving the IP address literal. The networking library my application uses was a cpp library that was included as a preprocess macro. One of the biggest annoyance was trying to debug, because you cant debug compile time code. So what I did was move over my cpp files with their headers to the project (luckily it was only 3 files).
IMPORTANT TO AYONE PASSING PORT NUMBERS. This is tied to #2 and resolving the IPv4 literal. I used apples exact implementation on the networking overview (listing 10-1). Every time i tested, the connect function was returning -1, meaning it did not connect. Thanks to #user102008 providing me with this article, I realized apple implementation of getaddrinfo was broken when trying to pass a string literal for the port. Yes, they ask for a constant char, even when trying c_str() it would still return port number of 0. For this reason, an apple developer who Ive noticed answer and addresses countless networking problems provided a work around. This fixed my issue of the port continuously returning 0, code is posted below as well. What i did, was simply add this into my networking class (SocketSender.cpp) and instead of calling getaddrinfo in Connect, i called get getaddrinfo_compat. This allowed me to connect perfectly in IPv4 and IPv6 networks.
static int getaddrinfo_compat(
const char * hostname,
const char * servname,
const struct addrinfo * hints,
struct addrinfo ** res
) {
int err;
int numericPort;
// If we're given a service name and it's a numeric string, set `numericPort` to that,
// otherwise it ends up as 0.
numericPort = servname != NULL ? atoi(servname) : 0;
// Call `getaddrinfo` with our input parameters.
err = getaddrinfo(hostname, servname, hints, res);
// Post-process the results of `getaddrinfo` to work around <rdar://problem/26365575>.
if ( (err == 0) && (numericPort != 0) ) {
for (const struct addrinfo * addr = *res; addr != NULL; addr = addr->ai_next) {
in_port_t * portPtr;
switch (addr->ai_family) {
case AF_INET: {
portPtr = &((struct sockaddr_in *) addr->ai_addr)->sin_port;
} break;
case AF_INET6: {
portPtr = &((struct sockaddr_in6 *) addr->ai_addr)->sin6_port;
} break;
default: {
portPtr = NULL;
} break;
}
if ( (portPtr != NULL) && (*portPtr == 0) ) {
*portPtr = htons(numericPort);
}
}
}
return err;
}
I actually save IP (address.sin_addr.s_addr) in a long data type that is a private variable, m_nIP. problem was i didnt need the IPv6 as our entire product groups use IPv4. Solved this using the code is below.
const uint8_t *bytes = ((const struct sockaddr_in6 *)addrPtr)->sin6_addr.s6_addr;
bytes += 12;
struct in_addr addr = { *(const in_addr_t *)bytes };
m_nIP = ntohl(addr.s_addr);
RELEVANT GUIDES Beej's Guide to Network Programming, UserLevel IPv6 Intro, Porting Applications to IPv6
I'm just starting out on gaining a better understanding of socket programming, and I'm trying to build a simple program that can send and receive messages. I've run into an issue with binding a socket to an address to use it. Here is what I have-
#include "stdafx.h"
using namespace std;
int main()
{
bool devbuild = true;
WSADATA mainSdata;
SOCKET sock = INVALID_SOCKET;
sockaddr tobind;
tobind.sa_family = AF_INET;
char stringaddr[] = "192.168.1.1";
inet_pton(AF_INET,stringaddr,&tobind);
//initiating Windows Socket API (WSA)
if (WSAStartup(2.2, &mainSdata) == 0)
{
if (devbuild == true)
{
printf("WSA successfully started...\n");
}
}
else
{
printf("WSA failed to set up, press [ENTER] to exit...\n");
pause();
return 1;
}
//instantiating the socket
sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, NULL);
if (sock != INVALID_SOCKET)
{
if (devbuild == true)
{
printf("Socket successfully created...\n");
}
}
else
{
printf("Socket failed to set up, press [ENTER] to exit...\n");
pause();
return 2;
}
//binding the socket
if (bind(sock, &tobind, sizeof(tobind)) == 0)
{
if (devbuild == true)
{
printf("Socket successfully bound...\n");
}
}
else
{
printf("Socket failed to bind, press [ENTER] to exit...\n");
printf("Last WSA error was: %d", WSAGetLastError());
pause();
return 3;
}
pause();
return 0;
}
I'm getting a return of 3, with WSA error code 10047
10047 - WSAEAFNOSUPPORT
Address family not supported by protocol family.
An address incompatible with the requested protocol was used. All sockets are created with an associated address family (that is, AF_INET for Internet Protocols) and a generic protocol type (that is, SOCK_STREAM). This error is returned if an incorrect protocol is explicitly requested in the socket call, or if an address of the wrong family is used for a socket, for example, in sendto.
This doesn't make sense, because I am only using SOCK_STREAM and AF_INET, which support one another.
I believe one problem (possibly not the only problem, but this is what jumps out at me) is in this line:
inet_pton(AF_INET,stringaddr,&tobind);
The problem is that you are passing &tobind as the final argument, and tobind is a sockaddr, but inet_pton() expects its third argument to point to a struct in_addr instead when using AF_INET (the fact that inet_pton() takes a void-pointer rather than a typed pointer for its third argument makes this kind of mistake really easy to make).
So what you should be doing instead is (note added error checking also):
if (inet_pton(AF_INET,stringaddr,&tobind.sin_addr) != 1)
printf("inet_pton() failed!\n");
Also, you need to make tobind be of type struct sockaddr_in rather than just a sockaddr, and also you need to zero out the struct before using it:
struct sockaddr_in tobind;
memset(&tobind, 0, sizeof(tobind)); // make sure the uninitialized fields are all zero
tobind.sa_family = AF_INET;
[...]
I'm working on a legacy VC6 application, that uses winsocket to listen to a UDP port for incoming packets. However I am getting the following errors. If I use WSAGetLastError() I get WSAECONNRESET, which if I read the description, doesn't seem to make sense, because its saying that remote host forcibly closed the socket, But I want use UDP connection-less manor, so it shouldn't matter what the other machine is doing... we should just listen. If I check the errno and use sterror() I get the following message. "No such file or directory". (I think this it's enum is EIO, according to http://pubs.opengroup.org/onlinepubs/009695399/functions/recvfrom.html)
I've had some success in narrowing down the issue, it appears if I take out a sendto() call, that calls back on the same port as the recvfrom(), the code seems to work ok. So something with that sendto() is putting in a bad state.
I'm looking for suggestions on why this socket is going bad, and either how to prevent or recover.
Edit
Here is the other weird part, if do the setup again for that socket (after a recvfrom() fails)... it all seems to work, even additional calls to sendto() don't seem trigger a recvfrom() fail, which in turn would call the setup again..
CODE
static VOID SetupSocketAddress( SOCKADDR_U &saRx, int nRTDPort )
{
memset(&saRx.saIPX, 0, sizeof(SOCKADDR_IPX));
saRx.saIPX.sa_family = AF_IPX; // IPX type address
memset(saRx.saIPX.sa_netnum,0x00,4); // we may have to get this number
memset(saRx.saIPX.sa_nodenum,0xff,6); // broadcast address
saRx.saIPX.sa_socket=(unsigned short)nRTDPort; // socket number
}
void CRealTimeData::SetupSocket( CRealTimeData * lpRTD, BOOL &bDone, SOCKADDR_U &saRx, int nRTDPort, SOCKADDR_U &saFrom, int &cbAddr,
DWORD &dwLocalAddress, int &nMaxIpIpxBuf, char * &pbyIpIpxRxBuf, int nFlags, BOOL bDo)
{
char szErrorCode[32];
int nReturn = 0;
if (lpRTD->m_eSourceType == V7_RTD_IPX)
{
// open IPX socket
// packet type = 4
lpRTD->m_Socket=socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX+4);
if (lpRTD->m_Socket == INVALID_SOCKET)
{
nReturn = AddSocketErrorToEventViewer( lpRTD);
bDone = TRUE;
}
// Socket must be bound prior to calling recvfrom()
// setup address
SetupSocketAddress(saRx, nRTDPort);
#ifdef _DEBUG_IPX
// test changing host number to network number
// we can't actually change though, because other programs use it this way
u_short nNetPort=0;
int nRet=WSAHtons(lpRTD->m_Socket, (unsigned short)nRTDPort, &nNetPort);
TRACE(_T("RTDIpxThread: Host Port=%04x Net Port=%04x RTD Input=%d \n"),nRTDPort, nNetPort, lpRTD->GetInputNumber());
#endif
// setup address for Sending Data on RTD
SetupSocketAddress( lpRTD->m_saRTD, nRTDPort );
// copy address - Why are we copying the address just over right later (in recvfrom() )? -NG
memcpy(&saFrom.saIPX, &lpRTD->m_saRTD.saIPX, sizeof(SOCKADDR_IPX));
cbAddr = sizeof(SOCKADDR_IPX);
}
else
{
// open IP socket
lpRTD->m_Socket=socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); // ??? should this use IPPROTO_UDP???
if (lpRTD->m_Socket == INVALID_SOCKET)
{
nReturn = AddSocketErrorToEventViewer( lpRTD);
bDone = TRUE;
}
// Socket must be bound prior to calling recvfrom()
// setup address
memset(&saRx.saIP, 0, sizeof(SOCKADDR_IN));
saRx.saIP.sin_family = AF_INET; // IP type address
saRx.saIP.sin_port=htons((u_short)nRTDPort); // PORT number
saRx.saIP.sin_addr.s_addr=htonl(INADDR_ANY); // ADDRESS number
// setup for Sending Data on RTD port
memset(&lpRTD->m_saRTD.saIP, 0, sizeof(SOCKADDR_IN));
lpRTD->m_saRTD.saIP.sin_family = AF_INET; // IP type address
lpRTD->m_saRTD.saIP.sin_port=htons((u_short)nRTDPort); // PORT number
lpRTD->m_saRTD.saIP.sin_addr.s_addr=htonl(INADDR_BROADCAST); // ADDRESS number
// copy address - Why are we copying the address just over right later (in recvfrom() )? -NG
memcpy(&saFrom.saIP, &lpRTD->m_saRTD.saIP, sizeof(SOCKADDR_IN));
cbAddr = sizeof(SOCKADDR_IN);
char szHostName[MAX_PATH+1];
if (gethostname(szHostName, MAX_PATH)==0)
{
hostent *phe=gethostbyname(szHostName);
dwLocalAddress = *(DWORD*)&phe->h_addr_list[0];
}
} // end IP socket
if (!bDone)
{
// enable broadcasting
BOOL bOptVal=TRUE;
nReturn=setsockopt(lpRTD->m_Socket, SOL_SOCKET, SO_BROADCAST, (char *)&bOptVal, sizeof(BOOL));
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
}
// enable reuse of address
bOptVal=TRUE;
nReturn=setsockopt(lpRTD->m_Socket, SOL_SOCKET, SO_REUSEADDR, (char *)&bOptVal, sizeof(BOOL));
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
}
// get the socket's max message size
int nOptSize=sizeof(UINT);
UINT nMaxMsgSize=600;
nReturn=getsockopt(lpRTD->m_Socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&nMaxMsgSize, &nOptSize);
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
nMaxMsgSize=600; // default max size
}
nMaxIpIpxBuf=nMaxMsgSize; // always create buffer that is as big as the sockets max message size
pbyIpIpxRxBuf = new char[nMaxIpIpxBuf]; // allocate buffer for receiving data from socket
if (!pbyIpIpxRxBuf)
bDone = TRUE;
else
memset(pbyIpIpxRxBuf,0,nMaxIpIpxBuf*sizeof(char));
// bind to address
nReturn=bind(lpRTD->m_Socket, &saRx.sa, cbAddr);
if (nReturn == SOCKET_ERROR)
{
nReturn = AddSocketErrorToEventViewer(lpRTD);
bDone = TRUE;
}
// send data to indicate startup
if (lpRTD->m_eProtocol == V7_RTD_ENHANCED)
{
int nLen=lpRTD->BuildErrorMsg(V7_ERTD_SERVICE_STARTUP, szErrorCode, sizeof(szErrorCode));
nReturn=sendto(lpRTD->m_Socket,szErrorCode,nLen, nFlags, &lpRTD->m_saRTD.sa, cbAddr);
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
}
}
} // end if not done
}
Main()
nFromLen = cbAddr;
nReturn=recvfrom(lpRTD->m_Socket, pbyIpIpxRxBuf, nMaxIpIpxBuf, nFlags, &saFrom.sa, &nFromLen);
if(nReturn == SOCKET_ERROR)
{
SetupSocket(lpRTD, bDone, saRx, nRTDPort, saFrom, cbAddr, dwLocalAddress, nMaxIpIpxBuf, pbyIpIpxRxBuf,nFlags, FALSE);
nReturn=recvfrom(lpRTD->m_Socket, pbyIpIpxRxBuf, nMaxIpIpxBuf, nFlags, &saFrom.sa, &nFromLen);
}
// if i take this out no error....
nReturn=sendto(lpRTD->m_Socket, szErrorCode, nLen, nFlags, &saFrom.sa, cbAddr);