IPv6 Daytime Server

home Author: Eva M. Castro
eva arroba gsyc.es

The TCP and UDP versions of the daytime server program use a common function to create the server socket, the listen_server function. This function generates a server socket from a hostname, the service, socket family (IPv4 or IPv6) and socket type (TCP or UDP) parameters.

File "listen_server.h"

#ifndef listen__server__h__
#define listen__server__h__

/*
  listen_server
    creates a server server socket listening at a hostname:service
    using the family and socket type specified in the function
    arguments.
*/

int
listen_server(const char *hostname,
              const char *service,
              int         family,
              int         socktype);

#endif
File "listen_server.cpp"
#include <types.h>
#include <socket.h>
#include <netdb.h>
#include <stdio.h>

#include "listen_server.h"

const int LISTEN_QUEUE=128;

int
listen_server(const char *hostname,
              const char *service,
              int         family,
              int         socktype)
{
    struct addrinfo hints, *res, *ressave;
    int error, sockfd;

    memset(&hints, 0, sizeof(struct addrinfo));

    /*
       AI_PASSIVE flag: the resulting address is used to bind
       to a socket for accepting incoming connections.
       So, when the hostname==NULL, getaddrinfo function will
       return one entry per allowed protocol family containing
       the unspecified address for that family.
    */

    hints.ai_flags    = AI_PASSIVE;
    hints.ai_family   = family;
    hints.ai_socktype = socktype;

    error = getaddrinfo(hostname, service, &hints, &res);

    if (error != 0) {
        fprintf(stderr,
                "getaddrinfo error:: [%s]\n",
                gai_strerror(error));
        return -1;
    }

    ressave=res;

    /*
       Try open socket with each address getaddrinfo returned,
       until getting a valid listening socket.
    */
    sockfd=-1;
    while (res) {
        sockfd = socket(res->ai_family,
                        res->ai_socktype,
                        res->ai_protocol);

        if (sockfd >= 0) {
            if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
                break;

            close(sockfd);
            sockfd=-1;
        }
        res = res->ai_next;
    }

    freeaddrinfo(ressave);

    if (sockfd < 0) {
        fprintf(stderr,
                "socket error:: could not open socket\n");
        return -1;
    }


    listen(sockfd, LISTEN_QUEUE);

    return sockfd;
}
The TCP daytime server uses listen_server with SOCK_STREAM and PF_UNSPEC parameters to obtain a server socket, which will accept connections to all of the interfaces. When clients connect to the server, it will answer with the daytime information and close the client connection.

File "tcp_daytime_server.cpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>

#include "listen_server.h"

const char *DAYTIME_PORT="13";

int
main(int argc, char *argv[])
{
    int listenfd, connfd, error;
    socklen_t addrlen;
    char timeStr[256];
    struct sockaddr_storage clientaddr;
    time_t now;
    char clienthost[NI_MAXHOST];
    char clientservice[NI_MAXSERV];

    /* local server socket listening at daytime port=13 */
    listenfd = listen_server( NULL, DAYTIME_PORT,
                              AF_UNSPEC, SOCK_STREAM);

    if (listenfd < 0) {
         fprintf(stderr,
                 "listen_socket error:: could not create listening "
                 "socket\n");
         return -1;
    }

    for ( ; ;) {
        addrlen = sizeof(clientaddr);

        /* accept daytime client connections */
        connfd = accept(listenfd,
                        (struct sockaddr *)&clientaddr,
                        &addrlen);

        if (connfd < 0)
            continue;

        memset(clienthost, 0, sizeof(clienthost));
        memset(clientservice, 0, sizeof(clientservice));

        error = getnameinfo((struct sockaddr *)&clientaddr, addrlen,
                            clienthost, sizeof(clienthost),
                            clientservice, sizeof(clientservice),
                            NI_NUMERICHOST);
        if (error != 0) {
	     fprintf(stderr, 
	             "getnameinfo error:: can not print the received"
		     "request \n");
	} else {
            printf("Received request from host=[%s] port=[%s]\n",
                   clienthost, clientservice);
        }

        /* process daytime request from a client */
        memset(timeStr, 0, sizeof(timeStr));
        time(&now);
        sprintf(timeStr, "%s", ctime(&now));
        write(connfd, timeStr, strlen(timeStr));
        close(connfd);
    }

    return 0;
}
The UDP daytime server uses listen_server with SOCK_DGRAM and PF_UNSPEC parameters to obtain a server socket, which will receive connections to all of the interfaces. When clients connect to the server, it will answer with the daytime information and close the client connection.

File "udp_daytime_server.cpp"

#include <types.h>
#include <socket.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>

#include "listen_server.h"

const char *DAYTIME_PORT="13";

int
main(int argc, char *argv[])
{
    int listenfd, error;
    socklen_t addrlen;
    char *myhost;
    char timeStr[256];
    struct sockaddr_storage clientaddr;
    time_t now;
    char b[256];
    char clienthost[NI_MAXHOST];
    char clientservice[NI_MAXSERV];

    myhost=NULL;
    if (argc > 1)
        myhost=argv[1];

    listenfd= listen_server(myhost, DAYTIME_PORT, AF_UNSPEC, SOCK_DGRAM);

    if (listenfd < 0) {
         fprintf(stderr,
                 "listen_server error:: could not create listening "
                 "socket\n");
         return -1;
    }

    addrlen = sizeof(clientaddr);
    for ( ; ;) {
        error = recvfrom(listenfd,
                     b,
                     sizeof(b),
                     0,
                     (struct sockaddr *)&clientaddr,
                     &addrlen);

        if (error < 0)
            continue;

        memset(clienthost, 0, sizeof(clienthost));
        memset(clientservice, 0, sizeof(clientservice));

        error = getnameinfo((struct sockaddr *)&clientaddr, addrlen,
                            clienthost, sizeof(clienthost),
                            clientservice, sizeof(clientservice),
                            NI_NUMERICHOST);

        if (error != 0) {
	     fprintf(stderr, 
	             "getnameinfo error:: can not print the received"
		     "request \n");
	} else {
            printf("Received request from host=[%s] port=[%s]\n",
                   clienthost, clientservice);
        }

        memset(timeStr, 0, sizeof(timeStr));
        time(&now);
        sprintf(timeStr, "%s", ctime(&now));

        error = sendto(listenfd, timeStr, sizeof(timeStr), 0,
                       (struct sockaddr *)&clientaddr,
                       addrlen);
        if (error < 0) {
	      ;
	}
    }

    return 0;
}
Migration process is simple because all main changes are grouped inside listen_server function. If application is not correctly structured porting effort increases. Sometimes, it is much better to review the program structure that only to change functions calls to make the adaptation.

This is the reason why scripts to change code automatically are not recommended. Automatic scripts look for concrete functions and change them by new function version however, program structure is not analyzed and many times the result is very poor.


home home Author: Eva M. Castro
eva arroba gsyc.es