Winsock2 - how to open a TCP socket that allows recv() with MSG_WAITALL? - winsock

In this code:
// error checking is omitted
// init Winsock2
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
// connect to server
struct addrinfo *res = NULL, *ptr = NULL, hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo(server_ip, "9999", &hints, &res);
SOCKET client_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
connect(client_socket, res->ai_addr, (int)res->ai_addrlen);
freeaddrinfo(res);
res = NULL;
// read the data
unsinged int size1;
if (recv(client_socket, (char*)&size1, sizeof(int), MSG_WAITALL) == SOCKET_ERROR)
{
return WSAGetLastError();
}
(note the MSG_WAITALL flag in recv()) everything works fine, expect for recv(). WSAGetLastError() returns WSAEOPNOTSUPP.
MSDN states that
Note that if the underlying transport does not support MSG_WAITALL, or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then this call will fail with WSAEOPNOTSUPP. This flag is not supported on datagram sockets or message-oriented sockets.
But it doesn't look like I'm doing something from this list. Why my recv() call doesn't work?

it doesn't look like I'm doing something from this list.
Yes, you are - the very first item on the list:
the underlying transport does not support MSG_WAITALL
Microsoft's default TCP transport provider does not support MSG_WAITALL. recv(), and Winsock in general, is not limited to just Microsoft's TCP provider. It supports 3rd party providers, and any transport protocols that the provider supports - TCP, UDP, IPX, ICMP, RAW, etc.
When using Microsoft's TCP, if you want recv() to wait until all of the requested TCP data has been received, you have to set the socket to blocking mode (its default mode) and then set the flags parameter of recv() to 0. But even that is not guaranteed, recv() can return with fewer bytes than requested, so you should be prepared to call recv() in a loop until all intended bytes have actually been received.

Related

Is a non-blocking connect guaranteed to fail with EINPROGRESS?

If I set up a socket for non-blocking operation, as follows:
int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
int rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
...is connect guaranteed to fail with EINPROGRESS, or do I need to handle the case where it succeeds immediately?
Not necessarily. Connecting to 127.0.0.1 may connect or fail immediately.
You need to handle the case where it succeeds immediately. That's why it returns 0 or -1. The documentation doesn't make any exception about that for non-blocking mode.

Winsock TCP connection, send fine but recv firewall blocked

I have an application that sends a GET request using winsock on port 80 using a TCP socket. A few users have reported an issue where no response is received, looking at network logs and seeing the network device is getting the data just the app isn't it was clear that the firewall was blocking it.
Having disabled the firewall it then worked fine but what I don't understand is why it was getting blocked. The connection is created from the users computer, it connects fine and sends (which I assumes automatically opens a port) so how can data be lost on the same connection when received? Should I be providing additional winsock settings? Or is there simply no way around stopping the firewall blocking an already active connection?
Here is a stripped down version of the winsock code
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
return -1;
struct sockaddr_in client;
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(80);
client.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0]));
if (connect(sock, (struct sockaddr *)&client, sizeof(client)) < 0){
closesocket(sock);
return -1;
}
if (send(sock, buffer, buflength, 0) != buflength){
closesocket(sock);
return -1;
}
//get response
response = "";
int resp_leng = BUFFERSIZE;
while (resp_leng == BUFFERSIZE)
{
resp_leng = recv(sock, (char*)&buffer, BUFFERSIZE, 0);
if (resp_leng > 0)
response += std::string(buffer).substr(0, resp_leng);
else
return -1;
}
closesocket(sock);
Your while loop exits if a recv() returns less than BUFFERSIZE. This is wrong -- you must always assume that recv() can return any amount of data from 1 byte up to and including the supplied buffer size.

VxWorks 6.8: setsockopt with IP_ADD_MEMBERSHIP returning EADDRNOTAVAIL

I am trying to set up a socket to receive multicast UDP packets on VxWorks 6.8.
sin.sin_len = (u_char)sizeof (sin);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
/* UDP port number to match for the received packets */
sin.sin_port = htons (mcastPort);
/* bind a port number to the socket */
if (bind(sockDesc, (struct sockaddr *)&sin, sizeof(sin)) != 0)
{
perror("bind");
status = errno;
goto cleanUp;
}
/* fill in the argument structure to join the multicast group */
/* initialize the multicast address to join */
ipMreq.imr_multiaddr.s_addr = inet_addr (mcastAddr);
/* unicast interface addr from which to receive the multicast packets */
ipMreq.imr_interface.s_addr = inet_addr (ifAddr);
printf ("Interface address on which to receive multicast packets: %s\n", ifAddr);
/* set the socket option to join the MULTICAST group */
int code = setsockopt (sockDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&ipMreq,
sizeof (ipMreq));
The setsockopt() call is returning -1 and errno is being set to 49 or EADDRNOTAVAIL. On wireshark, when we perform setsockopt I can see a properly formed group unsubscribe packet being sent out from the right port/interface. All different combinations of interfaces, ports, and multicast groups give the same result.
I am unable to debug very far into setsockopt as there doesnt seem to be anything wrong before the task calls ipcom_pipe_send and ipnet_usr_sock_pipe_recv, and after the recv call errno is set. I dont know how to debug the relevant tNetTask code that may be generating the error.
It could be that there's an issue with the interface index you supplied. Define ipMreq to be a struct ip_mreq, which does not have the imr_ifindex, instead of a struct ip_mreqn and remove the ipMreq.imr_ifindex = 2; line.

SIP Registration Issue

I am currently working on SIP sample application.
I am trying to do the registration using C based Socket programming on Unix. I have been successfully being able to get register with PJSIP, but when the same parameters I am sending with normal socket programming, then I am not being able to receive any response from server.
Here is the source code:
char *server = (char *)serverAddress; // First arg: server address/name
char *echoString = "Request msg REGISTER/cseq=46476 (tdta0x8857200)\r\nREGISTER sip:DOMAIN_NAME SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.1.120:51648;rport;branch=z9hG4bKPjwEt4VvIVdjIJKRmEJbkidYDAu-zQbIqv\r\nMax-Forwards: 70\r\nFrom: <sip:USER_NAME#DOMAIN_NAME>;tag=epCBN7JXsQE1nnI5d5SOZe9a5ujRyI67\r\nTo: <sip:USER_NAME#DOMAIN_NAME>\r\nCall-ID: .5yYCqh2jEYdy5T4kxhzxwDYEkCO1XlD\r\nCSeq: 46476 REGISTER\r\nContact: <sip:USER_NAME#192.168.1.120:51648;ob>\r\nExpires: 300\r\nAllow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS\r\nAuthorization: Digest username=\"USER_NAME\", realm=\"asterisk\", nonce=\"3b63254c\", uri=\"sip:DOMAIN_NAME\", response=\"9e8fc78829d143a58fba5a79f6ad44fd\", algorithm=MD5\r\nContent-Length: 0";
size_t echoStringLen = strlen(echoString);
// Third arg (optional): server port/service
char *servPort = (char *)service;
// Tell the system what kind(s) of address info we want
struct addrinfo addrCriteria; // Criteria for address match
memset(&addrCriteria, 0, sizeof(addrCriteria)); // Zero out structure
addrCriteria.ai_family = AF_UNSPEC; // For the following fields, a zero value means
// Any address family "don't care"
addrCriteria.ai_socktype = SOCK_DGRAM; // Only datagram sockets
addrCriteria.ai_protocol = IPPROTO_UDP; // Only UDP protocol
// Get address(es)
struct addrinfo *servAddr; // List of server addresses
int rtnVal = getaddrinfo(server, servPort, &addrCriteria, &servAddr);
if (rtnVal != 0)
DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal));
// Create a datagram/UDP socket
int sock = socket(servAddr->ai_family, servAddr->ai_socktype,
servAddr->ai_protocol); // Socket descriptor for client
if (sock < 0)
DieWithSystemMessage("socket() failed");
// Send the string to the server
ssize_t numBytes = sendto(sock, echoString, echoStringLen, 0,
servAddr->ai_addr, servAddr->ai_addrlen);
if (numBytes < 0)
DieWithSystemMessage("sendto() failed");
else if (numBytes != echoStringLen)
DieWithUserMessage("sendto() error", "sent unexpected number of bytes");
// Receive a response
struct sockaddr_storage fromAddr; // Source address of server
// Set length of from address structure (in-out parameter)
socklen_t fromAddrLen = sizeof(fromAddr);
char buffer[100 + 1]; // I/O buffer
numBytes = recvfrom(sock, buffer, 100, 0,
(struct sockaddr *) &fromAddr, &fromAddrLen);
if (numBytes < 0)
DieWithSystemMessage("recvfrom() failed");
else if (numBytes != 100)
DieWithUserMessage("recvfrom() error", "received unexpected number of bytes");
// Verify reception from expected source
int value = SockAddrsEqual(servAddr->ai_addr, (struct sockaddr *) &fromAddr);
if (value == 0)
DieWithUserMessage("recvfrom()", "received a packet from unknown source");
freeaddrinfo(servAddr);
buffer[echoStringLen] = '\0'; // Null-terminate received data
printf("Received: %s\n", buffer); // Print the echoed string
close(sock);
exit(0);
When I am trying to debug the code, then the breakpoint gets disappeared from recvfrom method call as follows.
numBytes = recvfrom(sock, buffer, 100, 0, (struct sockaddr *) &fromAddr, &fromAddrLen);
Thanks in advance for your co-operation.
If you are not getting a response from the Registrar server it's most likely because it couldn't understand the REGISTER request you sent it or rejected it as a duplicate. More that likely you will need to get a bit more sophisticated about how you are constructing your REGISTER request rather than simply sending a hard coded string.
Apart from that one obvious thing you could try is to add the required \r\n\r\n onto the end of your echoString. All SIP requests are required to end the header portion with a double line break and that is missing from your string.
recvfrom() blocks until a response is received, unless you set the socket to non-blocking. A better approach would be to use select() with a timeout (like a second or two). Better still would be to then put the entire thing into a loop which retries some number of times (like three or four).
Remember that UDP makes no guarantees that your datagram will be delivered: it is entirely up to you to decide when you've waited long enough and need to try again.
Also, check that the values being set in servAddr by getaddrinfo() make sense. If the address or port number is wrong, your packet may silently disappear.
If you confirm that this is socket issue then good to trace.
But if you are not sure then for this kind of issue use network analyze application (wireshark) & see the packet you send & receive. Then look into socket issue.

Retrieving the protocol of a socket in winsock

I am working in networking reliability simulation, I need to simulate packet dropping based on a quality of service percentage. Currently I have a DLL that hooks into send, sendto, recv and recvfrom. My hooks then 'drop' packets based on the quality of service.
I just need to apply the hook to UDP packets, and not disturb TCP (TCP is used for remote debugging).
Is there a way that I can query WinSock for the protocol that a socket is bound to?
int WSAAPI HookedSend(SOCKET s, const char FAR * buf, int len, int flags)
{
//if(s is UDP)
//Drop according to QOS
else
//Send TCP packets undisturbed
return send(s, buf, len, flags);
}
I think you could get the socket type by using getsockopt:
int optVal;
int optLen = sizeof(int);
getsockopt(socket,
SOL_SOCKET,
SO_TYPE,
(char*)&optVal,
&optLen);
if(optVal = SOCK_STREAM)
printf("This is a TCP socket.\n");
else if(optVal = SOCK_DGRAM)
printf("This is a UTP socket.\n");
else
printf("Error");