/* 
 * udpserver.c - A simple UDP echo server 
 * usage: udpserver <port>
 *
 * Based on: https://www.cs.cmu.edu/afs/cs/academic/class/15213-f99/www/class26/udpserver.c
 *   (which was probably based on W.R. Stevens' "Unix Network Programming" original).
 *
 * Connect to this on localhost with, e.g., "nc -i 127.0.0.1 8888" and type some messages;
 *   each message should generate a packet to the server, and its response packet in return.
 *
 * What happens if you pipe in a string longer than the single UDP packet's payload length?
 *  E.g.: $ perl -e 'print "A"x2500;' | nc -u tahoe.cs.dartmouth.edu 8888  OR
 *        $ perl -e 'print "B"x1000 . "A"x1500;' | nc -u tahoe.cs.dartmouth.edu 8888
 *    (assuming your server runs on tahoe.cs.dartmouth.edu port 8888)
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE 1024

void lookup_and_log_peer(struct sockaddr_in *cliaddr);

/*
 * error - wrapper for perror
 */
void error(char *msg) {
     perror(msg);
     exit(1);
}

int main(int argc, char **argv) {
     int sockfd;                    /* socket */
     int portno;                    /* port to listen on */
     int clientlen;                 /* byte size of client's address */
     struct sockaddr_in serveraddr; /* server's addr */
     struct sockaddr_in clientaddr; /* client addr */
     char buf[BUFSIZE];             /* message buf */
     int optval;                    /* flag value for setsockopt */
     int n;                         /* message byte size */

     /* 
      * check command line arguments 
      */
     if (argc != 2) {
          fprintf(stderr, "usage: %s <port>\n", argv[0]);
          exit(1);
     }
     portno = atoi(argv[1]);
     
     /* 
      * socket: create the parent socket 
      */
     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
     if (sockfd < 0) 
          error("ERROR opening socket");
     
     /* setsockopt: Handy debugging trick that lets 
      * us rerun the server immediately after we kill it; 
      * otherwise we have to wait about 20 secs. 
      * Eliminates "ERROR on binding: Address already in use" error. 
      */
     optval = 1;
     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 
                (const void *)&optval , sizeof(int));
     
     /*
      * build the server's Internet address
      */
     bzero((char *) &serveraddr, sizeof(serveraddr));
     serveraddr.sin_family = AF_INET;
     serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);  // Listen on any interface/address this machine has.
     serveraddr.sin_port = htons((unsigned short)portno);
     
     /* 
      * bind: associate the parent socket with a port 
      *  (Note that creating the socket had nothing about any port or IP.
      *    Creation in sockets is separate from configuration).
      */
     if (bind(sockfd, (struct sockaddr *) &serveraddr, 
              sizeof(serveraddr)) < 0) 
          error("ERROR on binding");
     
     /* 
      * main loop: wait for a datagram, then echo it
      */
     clientlen = sizeof(clientaddr);
     while (1) {
          
          /* 
           *  NOTE: No listen(). UDP does not queue requests, no has any special 
           *    state in which it acknowledges packets without sending any 
           *    data of its own (unlike SYN+ACK for a TCP listening socket).
           *    UDP sockets are "born" ready to write/send, and, once bound, are
           *    ready to read/receive. Simple.
           *
           *  But there is a price from this simplicity: the reading/receiving 
           *    call such as revcfrom() must take a pointer to an address struct
           *    to fill in with the address of the connecting peer. Otherwise
           *    we would not know who connected to us, i.e., who to send back to.
           *    Note also that we pass a pointer to a length of that struct,
           *    so that we may know how many bytes were actually filled in
           *    (recall that sockaddr structs were designed to accommodate all
           *     kinds of address schemes, not just the Internet's AF_INET!).
           *
           *  But wait, there's more! Since there's no queue, what happens if
           *    messages come too fast, say, while thise code is busy sending
           *    a response back? See top of file for some testing ideas.
           */   

          /*
           * recvfrom: receive a UDP datagram from a client
           */
          bzero(buf, BUFSIZE);
          n = recvfrom(sockfd, buf, BUFSIZE, 0,
                       (struct sockaddr *) &clientaddr, (socklen_t *)&clientlen);
          if (n < 0)
               error("ERROR in recvfrom");

          lookup_and_log_peer( &clientaddr );
          printf("server received %lu/%d bytes: %s\n", strlen(buf), n, buf);
          
          /* 
           * sendto: echo the input back to the client 
           */
          n = sendto(sockfd, buf, strlen(buf), 0, 
                     (struct sockaddr *) &clientaddr, clientlen);
          if (n < 0) 
               error("ERROR in sendto");
     }
}

void lookup_and_log_peer(struct sockaddr_in *sinaddr)
{   
     struct hostent *hostp;  /* client host info */
     char *hostaddrp;        /* dotted decimal host addr string */

     /* 
      * Note: gethostbyaddr allocates or reuses its with hostent buffer! 
      *       inet_ntoa also allocates or reuses its own string buffer!
      */

     /* 
      * gethostbyaddr: determine who sent the datagram
      */
     hostp = gethostbyaddr((const char *)&(sinaddr->sin_addr.s_addr), 
                           sizeof(sinaddr->sin_addr.s_addr), AF_INET);
     if (hostp == NULL)
          error("ERROR on gethostbyaddr");
     hostaddrp = inet_ntoa(sinaddr->sin_addr); 
     if (hostaddrp == NULL)
          error("ERROR on inet_ntoa\n");
     printf("server received datagram from %s (%s)\n", 
            hostp->h_name, hostaddrp);
}
