#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <errno.h>
#include <ctype.h>

void make_address(sockaddr_in & s)
{ bzero(&s, sizeof(sockaddr_in));
  s.sin_family=AF_INET; }

void set_ip(sockaddr_in & s, char * name)
{ struct hostent * remote_info=gethostbyname(name);
  if (remote_info==NULL)
  { fprintf(stderr, "(1) host \"%s\" not known\n", name); 
    s.sin_family=0;
    return; }
  struct in_addr * remote_addr=(struct in_addr *)remote_info->h_addr;
  if (remote_addr->s_addr==NULL)
  { fprintf(stderr,"(2) host %s not known\n", inet_ntoa(*remote_addr)); 
    s.sin_family=0;
    return; }
  s.sin_addr.s_addr=remote_addr->s_addr; }

void set_port(sockaddr_in & s, char * port)
{ int port_number;
  int n=sscanf(port, "%d", &port_number);
  if (n!=1)
  { fprintf(stderr, "\"%s\" is not a valid port number\n", port);
    s.sin_family=0;
    return; }
  s.sin_port=htons(port_number); }

void process_input(char * line, sockaddr_in & dest, char * message)
{ make_address(dest);
  char * s=strtok(line, ": \n");
  if (s!=NULL)
    set_ip(dest, s);
  s=strtok(NULL, ": \n");
  if (s!=NULL)
    set_port(dest, s);
  s=strtok(NULL, "");
  message[0]=0;
  if (s!=NULL)
    strcpy(message, s);  }

void send_message(int sock, sockaddr_in & dest, char * msg)
{ int num=sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)&dest, sizeof(dest));
  if (num<=0)
  { printf("[num=%d]\n", num);
    perror("sendto");
    return; }
  printf("transmitted (%d)\n", num); }

int socket_connected_to_port(int portnum)
{ struct sockaddr_in connection_info;
  int sock=socket(AF_INET, SOCK_DGRAM, 0);
  if (sock<0) { perror("socket creation"); exit(1); }
  bzero(&connection_info, sizeof(connection_info));
  connection_info.sin_family=AF_INET;
  connection_info.sin_addr.s_addr=htonl(INADDR_ANY);
  connection_info.sin_port=htons(portnum);
  int r=bind(sock, (sockaddr *)&connection_info, sizeof(connection_info));
  if (r<0)
  { perror("bind");
    exit(1); }
  return sock; }

int socket_connected_to_random_port(int & portnum)
{ struct sockaddr_in connection_info;
  int sock=socket(AF_INET, SOCK_DGRAM, 0);
  if (sock<0) { perror("socket creation"); exit(1); }
  while (1)
  { portnum=1024+(random() & 0x1FFF);
    bzero(&connection_info, sizeof(connection_info));
    connection_info.sin_family=AF_INET;
    connection_info.sin_addr.s_addr=htonl(INADDR_ANY);
    connection_info.sin_port=htons(portnum);
    int r=bind(sock, (sockaddr *)&connection_info, sizeof(connection_info));
    if (r>=0) break; }
  return sock; }

void main(int argc, char * argv[])
{ int portnum, sock;
  if (argc>1)
  { portnum=atol(argv[1]);
    sock=socket_connected_to_port(portnum); }
  else
  { srandomdev();
    sock=socket_connected_to_random_port(portnum); }
  printf("[receiver established on port %d]\n", portnum);
  printf(" > ");
  fflush(stdout);
  while (1)
  { fd_set files;
    FD_ZERO(&files);
    FD_SET(0, &files);
    FD_SET(sock, &files);
    timeval timeout = { 1, 0 };
    int n=select(sock+1, &files, NULL, NULL, &timeout);
    if (n>0 && FD_ISSET(0, &files))
    { char buff[1030], line[1000];
      sockaddr_in dest;
      char *s=fgets(line, 999, stdin);
      if (s==NULL) break;
      process_input(line, dest, buff);
      if (dest.sin_family!=0)
        send_message(sock, dest, buff);
      printf(" > ");
      fflush(stdout); }
    else if (n>0 && FD_ISSET(sock, &files))
    { unsigned char buff[1030];
      struct sockaddr_in sender_info;
      unsigned int si_size=sizeof(sender_info);
      int num=recvfrom(sock, buff, sizeof(buff), 0, (struct sockaddr *)&sender_info, &si_size);
      if (num<=0)
      { printf("[num=%d]\n", num);
        sleep(1);
        continue; }
      printf("\nreceived (%d): '", num);
      for (int i=0; i<num; i+=1)
      { if (buff[i]>=32 && buff[i]<=126)
          printf("%c", buff[i]);
        else
          printf("\\%03o", buff[i]); }
      printf("'\n > ");
      fflush(stdout); }
    else 
      sleep(1); }
  close(sock); }