UDP socket port allocation failure - c++

I am creating a winsock UDP program. code i am using is shown below.
I am always getting port assignment error.
I am not able to understand why port always allocated is zero. If some can help me with this....
void UDPecho(const char *, const char *);
void errexit(const char *, ...);
#define LINELEN 128
#define WSVERS MAKEWORD(2, 0)
void main(int argc, char *argv[])
{
char *host = "localhost";
char *service = "echo";
WSADATA wsadata;
switch (argc) {
case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: UDPecho [host [port]]\n");
exit(1);
}
if (WSAStartup(WSVERS, &wsadata))
errexit("WSAStartup failed\n");
UDPecho(host, service);
WSACleanup();
exit(0);
}
void UDPecho(const char *host, const char *service)
{
char buf[LINELEN+1];
SOCKET s;
int nchars;
struct hostent *phe;
struct servent *pse;
struct protoent *ppe;
struct sockaddr_in sin, my_sin;
int type, status, client_port, size;
char *transport = "udp";
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
/* Map service name to port number */
if ( pse = getservbyname(service, transport) )
sin.sin_port = pse->s_port;
else if ( (sin.sin_port = htons((u_short)atoi(service)))== 0)
errexit("can't get \"%s\" service entry\n", service);
/* Map host name to IP address, allowing for dotted decimal */
if ( phe = gethostbyname(host) )
memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
errexit("can't get \"%s\" host entry\n", host);
printf("Our target server is at address %s\n", inet_ntoa(sin.sin_addr));
printf("The size of an FD set is %d\n", sizeof(FD_SET));
/* Map protocol name to protocol number */
if ( (ppe = getprotobyname(transport)) == 0)
errexit("can't get \"%s\" protocol entry\n", transport);
/* Use protocol to choose a socket type */
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
/* Allocate a socket */
s = socket(PF_INET, type, ppe->p_proto);
if (s == INVALID_SOCKET)
errexit("can't create socket: %d\n", GetLastError());
size = sizeof(sin);
memset(&my_sin, 0, sizeof(sin));
getsockname (s, (struct sockaddr *) &my_sin, &size);
client_port = ntohs(my_sin.sin_port);
if (client_port != 0)
printf ("We are using port %2d\n", client_port);
else {
printf("No port assigned yet\n");
}
}
void errexit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
WSACleanup();
exit(1);
}

UDP doesn't bind to the listening port until you either issue a sendto() or a bind() on the socket. The latter lets you select the port that you want to listen on. Sendto(), on the other hand, will pick an ephemeral port for you. I would expect that the port will remain zero until you do one of these two things.
Clarification
I looked into this a little more after some of the comment. According to the Single UNIX Specification the result of calling socket() is an unbound socket. A socket is bound explicitly by calling bind() or implicitly sendto().
Think of a socket's name as a tuple containing its (Address Family, Protocol, local IP Address, and local Port Number). The first two are specified in the socket() call and the last two by calling bind(). In the case of connectionless protocols, a call to sendto() on a disconnected socket will result in an implicit bind to an OS chosen port number.
The most surprising thing is that the only reference that I can find to this behavior is in the remarks section of the Microsoft documentation for sendto().
If the socket is unbound, unique values are assigned to the local association by the system and the socket is then marked as bound. An application can use getsockname (Windows Sockets) to determine the local socket name in this case.
The Single UNIX Specification for getsockname() states:
If the socket has not been bound to a local name, the value stored in the object pointed to by address is unspecified.
It seems that a successful return with an unspecified result is the "standard" behavior... hmmm... The implementations that I have tried all return successfully with a socket address of 0.0.0.0:0 which corresponds to INADDR_ANY with an unspecified port. After calling either bind() or sendto(), getsockname() returns a populated socket address though the address portion might still be INADDR_ANY.

Related

UDP broadcast and multicast messages arrive but recvfrom does not receive anything

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.

accept() doesn't block after client close (with Ctrl+C) error 10093

I'm developing a simple client-server application using winsock2, in which I send an integer value from the client and the server receives it.When I send one (or more) integer and the client closes the socket correctly, the server understands that the client has closed the connection and goes to the accept() function waiting for another connection. However, when I stop the client for example with the combination Ctrl+C, the accept() doesn't stops and continues the main loop of the server returning error 10093 every time it loops (which is involved with WSAStartup()).I think that in some way I have to manage the signal that is sent to the server, like SIPIPE in Linux, or something similar, but I don't know how. What is the best way to manage this problem? Here my accept implementation:
bool Network::Accept() {
caddrlen = sizeof(clientAddr);
int ret;
if ((ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen) ) == INVALID_SOCKET) {
myFormatMessage(WSAGetLastError());
closesocket(listeningSocket);
WSACleanup();
return false;
}
else {
//save client ip address in a string
getpeername(listeningSocket, (SOCKADDR *)&clientAddr, (int *)sizeof(clientAddr));
char ip[20];
inet_ntop(AF_INET, (sockaddr*)&clientAddr.sin_addr, ip, 20);
clientIPaddr.assign(ip);
connectedSocket = ret;
return true;
}
}
Winsock error 10093 is WSANOTINITIALISED:
Successful WSAStartup not yet performed.
Either the application has not called WSAStartup or WSAStartup failed. The application may be accessing a socket that the current active task does not own (that is, trying to share a socket between tasks), or WSACleanup has been called too many times.
Your Network::Accept() method is calling WSACleanup() when accept() fails for any reason. Network::Accept() should not be doing that at all. It should not also be closing the listening socket, either. Remove those two lines from Network::Accept(), and then make your main loop stop calling Network::Accept() if it returns false, and then clean up your listening socket as needed.
There are other problems with your Network::Accept() code as well:
accept() returns a SOCKET, not an int.
when accept() succeeds, you are calling getpeername() with bad parameter values. You are passing in the listening socket instead of the accepted client socket, and you are passing in an invalid pointer for its namelen parameter (you need to pass a pointer to your caddrlen value, not type-cast the return value of sizeof()). For that matter, calling getpeername() is redundant anyway since accept() has already given you the same address that getpeername() would give you.
when calling inet_ntop(), you are type-casting the client address's sin_addr field to a sockaddr*, which is wrong. But in this case, the compiler accepts it since the pAddr parameter is a void*. You don't need the type-cast at all.
if listeningSocket is an AF_INET (IPv4) socket, then hard-coding AF_INET when calling inet_ntop() is fine, since the accepted client will always be using an IPv4 address (sockaddr_in). However, if you want/need to support IPv6 (and you should), then you should be checking the client's actual address family to know whether the address is using a sockaddr_in or sockaddr_in6 and then pass parameters to inet_ntop() accordingly.
With that said, try something more like this:
If you are supporting IPv4 only:
bool Network::Accept() {
// declare clientAddr as sockaddr_in...
caddrlen = sizeof(clientAddr);
SOCKET ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen);
if (ret == INVALID_SOCKET) {
myFormatMessage(WSAGetLastError());
return false;
}
//save client ip address in a string
char ip[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, &(clientAddr.sin_addr), ip, INET_ADDRSTRLEN);
clientIPaddr.assign(ip);
// declare connectedSocket as SOCKET...
connectedSocket = ret;
return true;
}
If you are supporting IPv6 only:
bool Network::Accept() {
// declare clientAddr as sockaddr_in6...
caddrlen = sizeof(clientAddr);
SOCKET ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen);
if (ret == INVALID_SOCKET) {
myFormatMessage(WSAGetLastError());
return false;
}
//save client ip address in a string
char ip[INET6_ADDRSTRLEN] = {0};
inet_ntop(AF_INET6, &(clientAddr.sin6_addr), ip, INET6_ADDRSTRLEN);
clientIPaddr.assign(ip);
// declare connectedSocket as SOCKET...
connectedSocket = ret;
return true;
}
If you are supporting both IPv4 and IPv6:
bool Network::Accept() {
// declare clientAddr as SOCKADDR_STORAGE...
caddrlen = sizeof(clientAddr);
SOCKET ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen);
if (ret == INVALID_SOCKET) {
myFormatMessage(WSAGetLastError());
return false;
}
//save client ip address in a string
char ip[INET6_ADDRSTRLEN] = {0};
switch (clientAddr.ss_family)
{
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)&clientAddr)->sin_addr), ip, INET_ADDRSTRLEN);
break;
case AF_INET6:
inet_ntop(AF_INET6, &((struct sockaddr_in6*)&clientAddr)->sin6_addr), ip, INET6_ADDRSTRLEN);
break;
}
clientIPaddr.assign(ip);
// declare connectedSocket as SOCKET...
connectedSocket = ret;
return true;
}

Calling accept() causes WSAEFAULT 10014 Bad address

I'm writing a custom TCP server for Windows, using MinGW compiler and winsock2 API.
I have this piece of code:
TCPSocket TCPSocket::accept() {
TCPSocket clSocket;
struct sockaddr_in clAddr;
socklen_t clAddrSize;
clAddrSize = sizeof(clAddr);
clSocket.shared->sockFd = ::accept(shared->sockFd, (struct sockaddr *)&clAddr, &clAddrSize);
if (clSocket.shared->sockFd < 0) {
printf("failed to accept incoming connection (code: %d)\n", WSAGetLastError());
throw SocketException(6, "failed to accept incoming connection");
}
clSocket.shared->buffer = new byte [BUFFER_SIZE];
clSocket.shared->curPos = clSocket.shared->endPos = clSocket.shared->buffer;
return clSocket;
}
However after calling accept() i get
failed to accept incoming connection (code: 10014)
which is according to MSDN:
WSAEFAULT
10014
Bad address.
The system detected an invalid pointer address in attempting to use a pointer argument of a call. This error occurs if an application
passes an invalid pointer value, or if the length of the buffer is too
small. For instance, if the length of an argument, which is a sockaddr
structure, is smaller than the sizeof(sockaddr).
I don't see, how these pointers can be bad, they both directly address a local variable. The clAddrSize is initialized and shared->sockFd is initialized in another function
void TCPSocket::listen(uint16_t port, int backlog) {
struct addrinfo * ainfo;
char portStr[8];
int res;
if (shared->sockFd != -1)
logicError(1, "socket already initialized, need to close first");
snprintf(portStr, sizeof(portStr), "%hu", (ushort)port);
if (getaddrinfo("localhost", portStr, NULL, &ainfo) != 0)
systemError(2, "failed to retrieve info about localhost", false);
shared->sockFd = socket(ainfo->ai_family, SOCK_STREAM, IPPROTO_TCP);
if (shared->sockFd < 0)
systemError(3, "failed to create a TCP socket", false);
res = bind(shared->sockFd, ainfo->ai_addr, ainfo->ai_addrlen);
if (res != 0)
systemError(5, "failed to bind socket to local port", true);
res = ::listen(shared->sockFd, backlog);
if (res != 0)
systemError(6, "failed to set socket to listen state", true);
freeaddrinfo(ainfo);
}
Do you see anything that i overlooked?
Ok, so thanks to CristiFati i found the problem.
The function getaddrinfo("localhost", portStr, NULL, &ainfo) used that way was returning an IPv6 address. While accept was getting sockaddr_in, which is a struct for IPv4 address.
It could be probably solved more ways, for example
using sockaddr_in6 for IPv6 communication
telling getaddrinfo to to search only IPv4 results with 3rd argument
picking up next result in the linked list returned by getaddrinfo
But i chose to manualy init the socket for IPv4 protocol this way:
struct sockaddr_in myAddr;
memset(&myAddr, 0, sizeof(myAddr));
myAddr.sin_family = AF_INET;
myAddr.sin_port = htons((ushort)port);
shared->sockFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (shared->sockFd < 0)
systemError(3, "failed to create a TCP socket", false);
res = bind(shared->sockFd, (struct sockaddr *)&myAddr, sizeof(myAddr));
if (res != 0)
systemError(5, "failed to bind socket to local port", true);
Since that, everything works.

Win Socket UDP connection-less recvfrom() Error

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);

how to specify which interface to use for a socket in linux

Is it possible to bind a udp socket to a specific interface so it sends data through that interface? I have an application that uses several Udp Sockets to send data and it is running on a machine with several interfaces. I know it's possible to do this by specifying the interface name by using this code:
int UdpSocket::open(const char *interface)
{
send_fd_ = ::socket(AF_INET, SOCK_DGRAM, 0);
if (send_fd_ < 0)
{
perror("socket");
return -1;
}
int val = 1;
int rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
if (rc < 0)
{
perror("sesockopt");
close();
return -1;
}
unsigned char ttl = 16;
rc = ::setsockopt(send_fd_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
if (rc < 0)
{
perror("sesockopt_ttl");
close();
return -1;
}
if (interface != NULL)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), interface);
rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr));
if (rc < 0)
{
perror("sesockopt");
close();
return -1;
}
}
const int flags = ::fcntl(send_fd_, F_GETFL, 0);
::fcntl(send_fd_, F_SETFL, flags | O_NONBLOCK);
return 0;
}
But this requires that the app is run with root privileges, otherwise it it will throw an error saying the "operation not permitted."
The easiest, and by far the most sane, approach is to add route(s) matching your multicast destinations:
~# route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
since OS network stack selects outbound interface for multicast packets based on the routing table. This also works for listening - you just bind to group address and kernel would pick correct interface for you. You still have to join the group as usual.
From the manpage:
SO_BINDTODEVICE
Bind this socket to a particular device like “eth0”, as specified in
the passed interface name. If the name is an empty string or the
option length is zero, the
socket device binding is removed. The passed option is a variable-length null-terminated interface name string with the
maximum size of IFNAMSIZ. If a socket
is bound to an interface, only packets received from that particular interface are processed by the socket. Note that this
only works for some socket types,
particularly AF_INET sockets. It is not supported for packet sockets (use normal bind(2) there).
This means you have to get the interface from the name yourself, possibly using getifaddrs, and then bind to that address.