I don't normally go to stackoverflow for sigsegv errors, but I have done all I can with my debugger at the moment.
The segmentation fault error is thrown following the completion of the function. Any ideas what I'm overlooking? I suspect that it is due to the casting of the sockaddr to the sockaddr_in, but I am unable to find any mistakes there. (Removing that line gets rid of the seg fault -- but I know that may not be the root cause here).
// basic setup
int sockfd;
char str[INET_ADDRSTRLEN];
sockaddr* sa;
socklen_t* sl;
struct addrinfo hints, *servinfo, *p;
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
// return string
string foundIP;
// setup the struct for a connection with selected IP
if ((rv = getaddrinfo("4.2.2.1", NULL, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return "1";
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return "2";
}
// connect the UDP socket to something
connect(sockfd, p->ai_addr, p->ai_addrlen); // we need to connect to get the systems local IP
// get information on the local IP from the socket we created
getsockname(sockfd, sa, sl);
// convert the sockaddr to a sockaddr_in via casting
struct sockaddr_in *sa_ipv4 = (struct sockaddr_in *)sa;
// get the IP from the sockaddr_in and print it
inet_ntop(AF_INET, &(sa_ipv4->sin_addr), str, INET_ADDRSTRLEN);
printf("%s\n", str);
// return the IP
return foundIP;
}
It doesn't look like you ever initialize the pointer sa to point at a valid sockaddr (or sockaddr_in) object.
If you replace
sockaddr* sa;
with
sockaddr addr;
and replace all uses of sa with &addr you should be in better shape.
The same is also true of sl. At least according to the documentation for my getsockname the socklen_t* parameter needs to point at a valid socklen_t object initialized to the size in bytes of the address buffer.
E.g.
socklen_t slen = sizeof addr;
and use &slen instead of sl.
It looks to me like you don't ever set up the sa pointer to actually point at anything. Commenting out the line "struct sockaddr_in *sa_ipv4 = (struct sockaddr_in *)sa;" should cause a compile error, so I guess I can see how not being able to compile your program would also cause it to not segfault - hard for a non-existent binary to crash :)
just to add, valgrind is pretty useful in detecting uninitialized pointers or for that matter any pointer related errors.
You did not allocate sa before passing it to getsockname, so you effectively passed in some garbage pointer value there. It needs to be:
struct sockaddr_in sa;
socklen_t sl;
...
getsockname(sockfd, (struct sockaddr*)&sa, &sl);
...
Related
I'm trying to build a block that acts as server and client to send and receive data (2 duplicate versions in 2 different computers) through a TCP connection.
This is what I did and I'm trying to test using the windows commandd line "netstat -ab" to try to find the tcp connection but I can't find it.
Apart from the given error, what am i doing wrong?
bool IPTunnel::runBlock(void) {
int ready =
inputSignals[0]->ready(); // int ready2 = inputTCPConnetion[0]->ready();
// server
SOCKET sockfd, newsockfd;
int portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
SOCKET n;
// create a socket(int domain, int type, int protocol)
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) printf("\n ERROR opening socket");
// bzero((char *)&serv_addr, sizeof(serv_addr));
portno = 5500;
serv_addr.sin_family = AF_INET;
char ipad[10] = "127.0.0.1";
serv_addr.sin_addr.s_addr = *ipad; // INADDR_ANY;
serv_addr.sin_port = htons(portno);
// if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
auto sd = bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
// if (sd < 0)
// printf("\n ERROR on binding");
listen(sockfd, 5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen);
if(newsockfd < 0) printf("ERROR on accept");
printf("server: got connection from %s port %d\n",
inet_ntop(serv_addr.sin_family, &ipad, buffer, clilen),
ntohs(cli_addr.sin_port));
send(newsockfd, "Hello, world!\n", 13, 0);
// bzero(buffer, 256);
n = _read(newsockfd, buffer, 255);
if(n < 0) printf("ERROR reading from socket");
printf("Here is the message: %s\n", buffer);
while(true) {
}
// close(newsockfd);
// close(sockfd);
return 0;
}
This is the error that gives: Unhandled exception at 0x00007FFE5031B7EC (ucrtbased.dll) in ip_tunnel.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.
in the _read function...
Well, this line is definitely wrong:
serv_addr.sin_addr.s_addr = *ipad; //INADDR_ANY;
If you want to receive incoming TCP connections on the loopback device, you should do something more like this:
serv_addr.sin_addr.s_addr = inet_aton("127.0.0.1");
(or if you want them to be received from any connected network device, specify INADDR_ANY instead)
Also, make sure you called WSAStartup() at the beginning of your program, Windows sockets won't work correctly if you haven't done that.
One last nitpick:
while(true){}
is not a good way to pause execution of your program. For one thing, it will typically spin a CPU at 100% usage, which is very inefficient, and for another, it invokes undefined behavior according to the C++ standard.
A better way to do get that behavior would be something like:
while(true) {Sleep(1000);}
Also this part is wrong/weird:
SOCKET n;
[...]
n = _read(newsockfd, buffer, 255);
... in that _read doesn't return a SOCKET, it returns an int. I think you meant to declare int n; instead.
One last potential problem: if your call to bzero(buffer, 256); is commented out, then it's quite possible for buffer to contain no zero-bytes after the _read() call returns, in which case your printf("Here is the message: %s\n", buffer); call afterwards could read right past the end of the buffer array and out into the wild blue yonder of other memory, potentially causing a crash (or at least causing a lot of garbage bytes to be printed). The fix is to make sure the buffer array contains a 0/NUL byte at the end of the valid bytes that were placed there by the _read() call.
I'm writing a UDP socket program which is provided an address from the command line.
To make sending and writing easier, I'm using getaddrinfo to convert the address to a sockaddr struct: either sockaddr_in or sockaddr_in6. Now I understand that I should use a union of sockaddrs:
typedef union address
{
struct sockaddr s;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
struct sockaddr_storage ss;
} address_t;
As I understand they can't be pointers to avoid hiding strict aliasing problems. I'm having trouble seamlessly putting the information from getaddrinfo's addrinfo into this address_t:
struct addrinfo hint, *serv = NULL;
address_t addr;
hint.ai_family = AF_UNSPEC;
hint.ai_flags = 0;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &serv);
//address_sr and s_port are strings with the address and port
switch (serv->ai_addr) {
case AF_INET: {
addr->s4 = * (sockaddr_in*) serv->ai_addr;
//Here I want to fill the address_t struct with information
//This line causes a segfault
}
break;
case AF_INET6: {
addr->s6 = * (sockaddr_in6*) serv->ai_addr;
//Conversion here
}
break;
Also, copying the memory:
memcpy(&addr, serv->ai_addr, serv->ai_addrlen);
Causes a segfault too.
How exactly should I do this? I tried a dozen different ways and I just can't figure it out. How do I put an address from addrinfo to this union? Do I use sockaddr_storage or the sockaddr_ins?
EDIT: Editing for clarity and additional code information.
You need to de-reference the pointer.
addr->s4 = *(sockaddr_in*) serv->ai_addr;
I think you're not getting getaddrinfo right.
About the third argument, :
const struct addrinfo *hints
The hints argument points to an addrinfo structure that specifies
criteria for selecting the socket address structures returned in the
list pointed to by res. If hints is not NULL it points to an addrinfo
structure whose ai_family, ai_socktype, and ai_protocol specify
criteria that limit the set of socket addresses returned by
getaddrinfo()[...]
For example, you can ask for IPv4 address family only, and/or for datagram sockets only (which could be fine given your attempt to use UDP).
Basically, you provide an addrinfo instance, set the fields of interest, then pass a pointer to it to the function, as its third argument:
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &serv);
In this example, the function could return not just one, but a whole list of address structures:
The getaddrinfo() function allocates and initializes a linked list of
addrinfo structures, one for each network address that matches node
and service, subject to any restrictions imposed by hints, and returns
a pointer to the start of the list in res. The items in the linked
list are linked by the ai_next field.
So you have to loop through the function result this way:
for (rp = serv; rp != NULL; rp = rp->ai_next)
I strongly suggest to carefully read the documentation at the link I provided. There is also a long and detailed example which could alone solve your issues.
sockaddr_storage is large enough to hold any sockaddr_... type, thus making address_t just as large. So, I would just memcpy() the entire serv->ai_addr in one operation and get rid of the switch, eg:
struct addrinfo hints = {};
struct addrinfo *addrs, *serv;
address_t addr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
...
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &addrs);
if (ret == 0)
{
for (serv = addrs; serv != NULL; serv = serv->ai_next)
{
memcpy(&addr, serv->ai_addr, serv->ai_addrlen);
...
}
freeaddrinfo(addrs);
}
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.
In my C++ application I use getnameinfo and it returns EAI_AGAIN - what causes this error?
struct sockaddr_storage ss;
socklen_t salen = sizeof(ss);
struct sockaddr *sa;
struct addrinfo hints, *paddr, *paddrp;
sa = (struct sockaddr *)&ss;
if (getpeername(sock, sa, &salen) != 0) {
error = errno;
return -1;
}
char hbuf[NI_MAXHOST];
char pbuf[NI_MAXSERV];
if (0 != (error = getnameinfo(sa, salen,
hbuf, sizeof(hbuf),
pbuf, sizeof(pbuf),
0))) {
return -1;//here it returns, the error is -3=EAI_AGAIN
}
According to getnameinfo:
[EAI_AGAIN] The name could not be resolved at this time. Future attempts may succeed.
The most likely reason would be some kind of timeout during the DNS lookup.
I've been using getaddrinfo for looking up socket addresses for basic socket commands. Recently, though, the addresses it returns to me are for bogus IP addresses, which I have found using inet_ntop. I've tried my code, as well as that provided in Beej's Guide, and they both produce the same results. Here's the code:
struct addrinfo hints, *info;
int status;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if(status = getaddrinfo(address, port, &hints, &info)) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
}
char ip4[INET_ADDRSTRLEN];
inet_ntop(AF_INET, info->ai_addr, ip4, INET_ADDRSTRLEN);
std::cout<<ip4<<std::endl;
No matter what address I use, it always gives me an IP of the form
16.2.x.y
where 256*x + y is equal to the port number. Has anyone ever seen this happen, or can anyone guess why it's giving me this?
Shouldn't you be passing
((sockaddr_in const *)info->ai_addr)->sin_addr
to inet_ntop?