I'm running a client and server code on Linux.
The server uses TCP/IP with AF_INET
and SOCK_STREAM
protocol.
The code is the following:
void hostname_to_ip(char *hostname, int *connection)
{
int check_sfd;
struct addrinfo hints, *p, *servinfo;
//struct sockaddr_in* ret_value;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
int res = getaddrinfo(hostname, NULL, &hints, &servinfo)
if (res != 0) {
fprintf(stderr, "Error: error in getaddrinfo on hostname: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
// getaddrinfo returned a linked list of relevant addresses
// loop through the addresses and return the first one available
for (p = servinfo; p != NULL; p = p->ai_next) {
check_sfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (check_sfd == -1)
continue;
if (connect(check_sfd, p->ai_addr, p->ai_addrlen) == 0) { // connection successful
*connection = check_sfd;
break;
}
close(check_sfd);
}
if (p == NULL) {
// print error
exit(EXIT_FAILURE);
}
freeaddrinfo(&servinfo);
}
However, now I can't seem to connect to the server, as connect
always returns -1 (p
starts as non-NULL
, so the connection to the socket just fails). The error is connection refused
.
When I change the ai_socktype
to SOCK_DGRAM
the connection is successful but the client fails later when sending the data (makes sense since the server is using SOCK_STREAM
), which is pretty weird.
Any ideas? The code of the client and server is pretty long so I didn't attached all of it, but I tried to add all the relevant information. If anything is missing, comment and I'll add it.
This is the strace
info on the client:
execve("./pcc_client", ["./pcc_client", "localhost", "2001", "1000"], [/* 71 vars */]) = 0
brk(NULL) = 0x2446000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=88205, ...}) = 0
mmap(NULL, 88205, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0a0f4fa000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0a0f4f9000
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0a0ef21000
mprotect(0x7f0a0f0e1000, 2097152, PROT_NONE) = 0
mmap(0x7f0a0f2e1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f0a0f2e1000
mmap(0x7f0a0f2e7000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0a0f2e7000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0a0f4f8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0a0f4f7000
arch_prctl(ARCH_SET_FS, 0x7f0a0f4f8700) = 0
mprotect(0x7f0a0f2e1000, 16384, PROT_READ) = 0
mprotect(0x601000, 4096, PROT_READ) = 0
mprotect(0x7f0a0f510000, 4096, PROT_READ) = 0
munmap(0x7f0a0f4fa000, 88205) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
getsockname(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("0.0.0.0")}, [16]) = 0
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4) = 0
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4) = 0
brk(NULL) = 0x2446000
brk(0x2467000) = 0x2467000
open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=529, ...}) = 0
read(4, "# /etc/nsswitch.conf\n#\n# Example"..., 4096) = 529
read(4, "", 4096) = 0
close(4) = 0
open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
read(4, "# The \"order\" line is only used "..., 4096) = 92
read(4, "", 4096) = 0
close(4) = 0
getpid() = 12014
open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=184, ...}) = 0
read(4, "# Dynamic resolv.conf(5) file fo"..., 4096) = 184
read(4, "", 4096) = 0
close(4) = 0
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=88205, ...}) = 0
mmap(NULL, 88205, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7f0a0f4fa000
close(4) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 4
read(4, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260!\0\0\0\0\0\0"..., 832) = 832
fstat(4, {st_mode=S_IFREG|0644, st_size=47600, ...}) = 0
mmap(NULL, 2168600, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 4, 0) = 0x7f0a0ed0f000
mprotect(0x7f0a0ed1a000, 2093056, PROT_NONE) = 0
mmap(0x7f0a0ef19000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 4, 0xa000) = 0x7f0a0ef19000
mmap(0x7f0a0ef1b000, 22296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0a0ef1b000
close(4) = 0
mprotect(0x7f0a0ef19000, 4096, PROT_READ) = 0
munmap(0x7f0a0f4fa000, 88205) = 0
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=233, ...}) = 0
read(4, "127.0.0.1\tlocalhost\n127.0.1.1\tmi"..., 4096) = 233
read(4, "", 4096) = 0
close(4) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
write(1, "x\n", 2x
) = 2
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 4
write(1, "4\n", 24
) = 2
write(1, "\n", 1
) = 1
connect(4, {sa_family=AF_INET, sin_port=htons(53511), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ECONNREFUSED (Connection refused)
close(4) = 0
write(1, "Error: couldn't connect to hostn"..., 56Error: couldn't connect to hostname. Connection refused
) = 56
exit_group(1) = ?
+++ exited with 1 +++
Thanks!
Your fix for the problem will work as long as the compiler cooperates and you don't need to support IPv6. But a better fix is to pass the port number to getaddrinfo
in the first place, as the "service" argument. You have to convert it to a string to do that, but you can go back to using AF_UNSPEC
and not having to cast or manipulate the sockaddrs in getaddrinfo
's return list at all:
void hostname_to_ip(char *hostname, int *connection_socket,
unsigned short port_number)
{
int check_sfd;
struct addrinfo hints, *p, *servinfo;
char port_number_s[sizeof("65535")];
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV;
hints.ai_protocol = 0;
snprintf(port_number_s, sizeof port_number_s, "%u", port_number);
int res = getaddrinfo(hostname, port_number_s, &hints, &servinfo);
if (res == EAI_SYSTEM) {
fprintf(stderr, "Error looking up %s: %s\n",
hostname, strerror(errno));
exit(1);
} else if (res != 0) {
fprintf(stderr, "Error looking up %s: %s\n",
hostname, gai_strerror(res));
exit(1);
} else if (servinfo == NULL) {
fprintf(stderr, "Error looking up %s: No addresses found\n",
hostname);
exit(1);
}
// getaddrinfo returned a linked list of relevant addresses
// loop through the addresses and return the first one we can connect to
for (p = servinfo; p != NULL; p = p->ai_next) {
check_sfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (check_sfd == -1)
continue;
if (connect(check_sfd, p->ai_addr, p->ai_addrlen)) {
*connection_socket = check_sfd;
freeaddrinfo(servinfo);
return;
}
close(check_sfd);
}
// If we get here, we couldn't connect to any of the addresses.
fprintf(stderr, "Couldn't connect to %s: %s\n", hostname, strerror(errno));
exit(1);
}
I also fixed some other subtle bugs:
port_number
should be unsigned short
because that's what it is in TCP.getaddrinfo
returns EAI_SYSTEM
, you need to print strerror(errno)
instead of gai_strerror(res)
.getaddrinfo
can claim to have succeeded but return zero addresses.connect
succeeds, you don't need to check again for success after the loop. It's not necessary to free memory when you're about to print an error message and exit.Follow-up exercises for you, in ascending order of difficulty:
getnameinfo
to print "Connecting to 128.52.0.2..." messages like telnet
does.select
or poll
(your choice) to get the first one that works and close all the others.