Linux daemon localhost works but not the actual IP

c, daemon, linux, service, sockets

Solution

I'm not a C expert but I know one thing: your daemon is probably binding to just one address/interface, and is accepting connections only from it.

Use `netstat -nap` to check to which address the daemon is binding to.

If it is listening to `0.0.0.0` then it is accepting connections from every interface, including localhost.

If it is listening to `127.0.0.1` it will only accept from localhost.

I bet it's bound only to `127.0.0.1`, that's why you can't connect with any other address.

Problem

I am learning APUE.2e (Advanced Programming in the UNIX® Environment, Second Edition) these days and have got to the chapter 16 Network IPC: sockets. When I run the program `ruptime` (for the client end) and `ruptimed` (for the server end), something weird happened. The client program shows a client command that communicates with a server to obtain the output from a system's `uptime` command, and the server program `ruptimed` (note the 'd') receives the command, exec's the `uptime` command and return the output of `uptime` to the client. The two programs compiled OK, and I add the `ruptime 4000/tcp` to the file `/etc/service` which means the service `ruptime` binds to the port `4000/tcp`. I ran the two programs on the same machine (Ubuntu 11.04) and the `ruptimed` first (of course): ``` $ ./ruptimed $ ./ruptime 127.0.0.1 21:35:48 up 13:06, 3 users, load average: 0.56, 0.85, 0.94 ``` The output is what I have expected. However, when I ran: ``` $ ./ruptimed $ ./ruptime 192.168.1.221 # the terminal blocks here ``` The IP address 192.168.1.221 is the machine's actual IP in the intranet. What's wrong here? I searched the Internet, and found out it seems to need a shell script in the `/etc/init.d/`. Then I add a file named `ruptime` in the `/etc/init.d/` directory. Here it is: ``` #! /bin/sh ### BEGIN INIT INFO # Provides: ruptimed # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: # Short-Description: ruptime ### END INIT INFO start() { echo "start ruptime" /home/tlh1987/apue-practice/tlh-practice/ruptimed exit 0; } stop() { killall ruptimed echo "stop ruptime" } case "$1" in start) start ;; stop) stop ;; restart) stop start ;; *) echo "usage: $0 start|stop|restart" exit 0; esac ``` However, it didn't work and I don't know why. I also set the ufw enabled and allowed ruptime like this: ``` sudo ufw allow ruptime ``` It didn't work either. The source code for `ruptime` and `ruptimed` are as follows: connect_retry() ``` // the functions used in the two program // the connect_retry() function #include "apue.h" #include <sys/socket.h> #define MAXSLEEP 128 int connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen) { int nsec; for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) { if (connect(sockfd, addr, alen) == 0) { return (0); } if (nsec <= MAXSLEEP/2) sleep(nsec); } return (-1); } ``` init_server() ``` // the initserver function #include "apue.h" #include <errno.h> #include <sys/socket.h> int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen) { int fd; int err = 0; if ((fd = socket(addr->sa_family, type, 0)) < 0) return (-1); if (bind(fd, addr, alen) < 0) { err = errno; goto errout; } if (type == SOCK_STREAM || type == SOCK_SEQPACKET) { if (listen(fd, qlen) < 0) { err = errno; goto errout; } } return(fd); errout: close(fd); errno = err; return (-1); } ``` The client end ``` // the client end i.e. ruptime.c #include "apue.h" #include <netdb.h> #include <errno.h> #include <sys/socket.h> #define MAXADDRLEN 256 #define BUFLEN 128 extern int connect_retry(int, const struct sockaddr *, socklen_t); void print_uptime(int sockfd) { int n; char buf[BUFLEN]; while ((n = recv(sockfd, buf, BUFLEN, 0)) > 0) write(STDOUT_FILENO, buf, n); if (n < 0) err_sys("recv error"); } int main(int argc, char *argv[]) { struct addrinfo *ailist, *aip; struct addrinfo hint; int sockfd, err; if (argc !=2 ) err_quit("usage: ruptime hostname"); hint.ai_flags = 0; hint.ai_family = 0; hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_canonname = NULL; hint.ai_addr = NULL; hint.ai_next = NULL; if ((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0) err_quit("getaddrinfo error: %s", gai_strerror(err)); for (aip = ailist; aip != NULL; aip = aip->ai_next) { if ((sockfd = socket(aip->ai_family, SOCK_STREAM, 0)) < 0) err = errno; if (connect_retry(sockfd, aip->ai_addr, aip->ai_addrlen) < 0) { err = errno; } else { print_uptime(sockfd); exit(0); } } fprintf(stderr, "can't connect to %s: %s\n", argv[1], strerror(err)); exit(1); } ``` The server end ``` //the server end i.e. ruptimed is as follows: #include "apue.h" #include <netdb.h> #include <errno.h> #include <syslog.h> #include <sys/socket.h> #define BUFLEN 128 #define QLEN 10 #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 #endif extern int initserver(int, struct sockaddr *, socklen_t, int); void serve(int sockfd) { int clfd; FILE *fp; char buf[BUFLEN]; for ( ; ; ) { clfd = accept(sockfd, NULL, NULL); if (clfd < 0) { syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno)); exit(1); } if ((fp = popen("/usr/bin/uptime", "r")) == NULL) { sprintf(buf, "error: %s\n", strerror(errno)); send(clfd, buf, strlen(buf), 0); } else { while (fgets(buf, BUFLEN, fp) != NULL) send(clfd, buf, strlen(buf), 0); pclose(fp); } close(clfd); } } int main(int argc, char *argv[]) { struct addrinfo *ailist, *aip; struct addrinfo hint; int sockfd, err, n; char *host; if (argc != 1) err_quit("usage: ruptimed"); #ifdef _SC_HOST_NAME_MAX n = sysconf(_SC_HOST_NAME_MAX); if (n < 0) #endif n = HOST_NAME_MAX; host = malloc(n); if (host == NULL) err_sys("malloc error"); if (gethostname(host, n) < 0) err_sys("gethostname error"); daemonize("ruptimed"); hint.ai_flags = AI_CANONNAME; hint.ai_family = 0; hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_canonname = NULL; hint.ai_addr = NULL; hint.ai_next = NULL; if ((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) { syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err)); exit(1); } for (aip = ailist; aip != NULL; aip = aip->ai_next) { if ((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0) serve(sockfd); exit(0); } exit(1); } ``` Is there anyone can help? The header lines of the command netstat -nap: ``` Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:4000 0.0.0.0:* LISTEN 2252/ruptimed tcp 0 13033 192.168.1.221:47380 91.189.89.114:443 LAST_ACK - tcp 74 0 192.168.1.221:43914 91.189.89.76:443 ESTABLISHED 1846/python tcp 0 0 127.0.0.1:4000 127.0.0.1:56344 TIME_WAIT - tcp 0 1 192.168.1.221:35442 192.168.1.89:24800 SYN_SENT 1478/synergyc tcp 0 0 192.168.1.221:34957 74.125.71.125:5222 ESTABLISHED 1618/chrome tcp 0 0 192.168.1.221:57795 203.208.46.163:443 ESTABLISHED 1618/chrome tcp6 0 0 :::22 :::* LISTEN - ``` The 3th record is my service ruptime. Is it something wrong? I don't know much about its meanig.

Original source