Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux daemon localhost works but not the actual IP

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.

like image 688
toolchainX Avatar asked Feb 21 '23 21:02

toolchainX


1 Answers

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.

like image 151
KurzedMetal Avatar answered Mar 04 '23 12:03

KurzedMetal