views:

104

answers:

4

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;

}

+5  A: 

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.

Charles Bailey
If you know the protocol, such as IPv4, `sockaddr_in` or similar would be a better choice. If you don't know the protocol, `sockaddr_storage` would be a better choice.
Thanatos
@Thanatos: Possibly, yes. It's work checking your platform. On mine, `sockaddr` is big enough for a `sockaddr_in` and which ever one you choose you have to do some casting of pointers where you need the other one. The important think is to specify the buffer length correctly.
Charles Bailey
+1  A: 

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);
...
Pavel Minaev
Ummm, you're casting a `struct sockaddr **` to a `struct sockaddr*`. Did you mean that?
Charles Bailey
Pavel Minaev
Sorry, I was looking at your new getsockname call and the original declarations. I hadn't spot that you'd fixed the types. I'll delete my comments in a bit.
Charles Bailey
+2  A: 

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

George
Sorry -- meant to say if I comment out that line and the line(s) below it.
BSchlinker
+1  A: 

just to add, valgrind is pretty useful in detecting uninitialized pointers or for that matter any pointer related errors.

Pigol
Thanks -- I was unaware of this tool set. memcheck looks particularly useful.
BSchlinker