Example #1
0
/*
 * dhtc_args: parses command line args.
 *
 * Returns 0 on success or 1 on failure.  On successful return, *sname points to the server's FQDN,
 * and "port" points to the port to connect at server, in network byte order.  Both "*sname", and
 * "port" must be allocated by caller.  The variable "*imagename" points to the name of the image
 * to search for.
 *
 * Nothing else is modified.
 */
int
dhtc_args(int argc, char *argv[], char **sname, u_short *port, char **imagename)
{
  char c, *p;
  extern char *optarg;

  if (argc < 5) {
    return (1);
  }
  
  while ((c = getopt(argc, argv, "s:q:")) != EOF) {
    switch (c) {
    case 's':
      for (p = optarg+strlen(optarg)-1;      // point to last character of addr:port arg
           p != optarg && *p != NETIMG_PORTSEP;  // search for ':' separating addr from port
           p--);
      net_assert((p == optarg), "dhtc_args: server address malformed");
      *p++ = '\0';
      *port = htons((u_short) atoi(p)); // always stored in network byte order

      net_assert((p-optarg > NETIMG_MAXFNAME), "dhtc_args: FQDN too long");
      *sname = optarg;
      break;
    case 'q':
      net_assert((strlen(optarg) >= NETIMG_MAXFNAME), "dhtc_args: image name too long");
      *imagename = optarg;
      break;
    default:
      return(1);
      break;
    }
  }

  return (0);
}
Example #2
0
/*
 * dhtc_recvimsg: receive an imsg_t packet from server and store it 
 * in the global variable imsg.  The type imsg_t is defined in netimg.h.
 * Check that received message is of the right version number.
 * If message is of a wrong version number and for any other
 * error in receiving packet, terminate process.
 * Convert the integer fields of imsg back to host byte order.
 * If the received imsg has im_depth field = 0, it indicates
 * that no image is sent back, most likely due to image not found.
 * In which case, return 0, otherwise return 1.
 */
int
dhtc_recvimsg()
{
  int bytes;
  double img_dsize;

  bytes = recv(sd, (char *) &imsg, sizeof(imsg_t), 0);   // imsg global
  if (bytes <= 0) {
    // the other could have closed connection since
    // only one active search is allowed at any time
    close(sd);
    return(-1);
  }

  net_assert((bytes != sizeof(imsg_t)), "dhtc_recvimsg: malformed header");
  net_assert((imsg.im_vers != NETIMG_VERS), "dhtc_recvimg: wrong imsg version");

  if (imsg.im_depth) {
    imsg.im_height = ntohs(imsg.im_height);
    imsg.im_width = ntohs(imsg.im_width);
    imsg.im_format = ntohs(imsg.im_format);
    
    img_dsize = (double) (imsg.im_height*imsg.im_width*(u_short)imsg.im_depth);
    net_assert((img_dsize > (double) LONG_MAX), "dhtc_recvimsg: image too large");
    img_size = (long) img_dsize;                 // global
    image = (char *)malloc(img_size*sizeof(char));
    return (1);
  }

  return (0);
}
Example #3
0
/*
 * peer_args: parses command line args.
 *
 * Returns 0 on success or 1 on failure.  On successful return, the
 * provided peer FQDN, if any, is copied to memory pointed to by
 * "pname". Then "port" points to the port to connect at peer, in
 * network byte order.  Both "pname" and "port" must be allocated 

 * by caller.  The buffer pname points to must be of size PR_MAXFQDN+1.
 * Nothing else is modified.
 */
int
peer_args(int argc, char *argv[], char *pname, u_short *port)
{
  char c, *p;
  extern char *optarg;

  net_assert(!pname, "peer_args: pname not allocated");
  net_assert(!port, "peer_args: port not allocated");

  while ((c = getopt(argc, argv, "p:")) != EOF) {
    switch (c) {
    case 'p':
      for (p = optarg+strlen(optarg)-1;     // point to last character of addr:port arg
           p != optarg && *p != PR_PORTSEP; // search for ':' separating addr from port
           p--);
      net_assert((p == optarg), "peer_args: peer addressed malformed");
      *p++ = '\0';
      *port = htons((u_short) atoi(p)); // always stored in network byte order

      net_assert((p-optarg > PR_MAXFQDN), "peer_args: FQDN too long");
      strcpy(pname, optarg);
      break;
    default:
      return(1);
      break;
    }
  }

  return (0);
}
Example #4
0
/*
 * dhtc_sockinit: creates a new socket to connect to the provided server.
 * The server's FQDN and port number are provided.  The port number
 * provided is assumed to already be in netowrk byte order.
 *
 * On success, the global socket descriptor sd is initialized.
 * On error, terminates process.
 */
void
dhtc_sockinit(char *sname, u_short port)
{
  int err;
  struct sockaddr_in server;
  struct hostent *sp;

#ifdef _WIN32
  WSADATA wsa;
  
  err = WSAStartup(MAKEWORD(2,2), &wsa);  // winsock 2.2
  net_assert(err, "dhtc_sockinit: WSAStartup");
#endif

  /* 
   * create a new TCP socket, store the socket in the global variable sd
  */
  sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);   // sd global
  net_assert((sd < 0), "dhtc_sockinit: socket");

  /* obtain the server's IPv4 address from sname and initialize the
     socket address with server's address and port number . */
  memset((char *) &server, 0, sizeof(struct sockaddr_in));
  server.sin_family = AF_INET;
  server.sin_port = port;
  sp = gethostbyname(sname);
  net_assert((sp == 0), "dhtc_sockinit: gethostbyname");
  memcpy(&server.sin_addr, sp->h_addr, sp->h_length);

  /* connect to server */
  err = connect(sd, (struct sockaddr *) &server, sizeof(struct sockaddr_in));
  net_assert(err, "dhtc_sockinit: connect");

  return;
}
Example #5
0
/*
 * netimg_recvimsg: receive an imsg_t packet from server and store it
 * in the global variable imsg.  The type imsg_t is defined in
 * netimg.h.  Check that received message is of the right version
 * number and of type NETIMG_DIM.  If message is of the wrong version
 * number or the wrong type, terminate process. For error in receiving
 * packet, return -1.  If packet successfully
 * received, convert the integer fields of imsg back to host byte
 * order.  If the received imsg has im_found field == 0, it indicates
 * that no image is sent back, most likely due to image not found.  In
 * which case, return 0, otherwise return 1.
 */
int
netimg_recvimsg()
{
  int i;
  int bytes;
  double img_dsize;
  int img_size, format;

  /* receive imsg packet and check its version and type */
  bytes = recv(sd, (char *) &imsg, sizeof(imsg_t), 0);   // imsg global
  if (bytes <= 0) {
    return(-1);
  }
  net_assert((bytes != sizeof(imsg_t)), "netimg_recvimsg: malformed header");
  net_assert((imsg.im_vers != NETIMG_VERS), "netimg_recvimg: wrong imsg version");
  net_assert((imsg.im_type != NETIMG_DIM), "netimg_recvimg: wrong imsg type");

  if (imsg.im_found) {
    imsg.im_height = ntohs(imsg.im_height);
    imsg.im_width = ntohs(imsg.im_width);
    imsg.im_format = ntohs(imsg.im_format);
    
    /* compute image size */
    img_dsize = (double) (imsg.im_height*imsg.im_width*(u_short)imsg.im_depth);
    net_assert((img_dsize > (double) NETIMG_MAXSEQ), "netimg_recvimsg: image too large");
    img_size = (int) img_dsize;                 // global

    /* allocate space for image */
    image = (unsigned char *)calloc(img_size, sizeof(unsigned char));

    /* determine pixel format */
    switch(imsg.im_format) {
    case GL_RGBA:
      format = 4;
      break;
    case GL_RGB:
      format = 3;
      break;
    case GL_LUMINANCE_ALPHA:
      format = 2;
      break;
    default:
      format = 1;
      break;
    }

    /* paint the image texture background red if color, white otherwise
     to better visualize lost segments */
    for (i = 0; i < img_size; i += format) {
      image[i] = (unsigned char) 0xff;
    }

    return (1);
  }

  return (0);
}
Example #6
0
void static socket_init()
{
  WORD version_requested = MAKEWORD (2, 2);
  WSADATA wsa_data;
  int rc = WSAStartup (version_requested, &wsa_data);
  wsa_assert (rc == 0);
  net_assert (LOBYTE (wsa_data.wVersion) == 2 &&HIBYTE (wsa_data.wVersion) == 2);
}
Example #7
0
//  Closes the underlying socket.
int nsock_close (fd_t fd_)
{
    int rc;
    net_assert (fd_ != retired_fd);
    rc = closesocket (fd_);
    wsa_assert (rc != SOCKET_ERROR);
    return 0;
}
Example #8
0
//  Closes the underlying socket.
int nsock_close (fd_t fd_)
{
    int rc ;
    net_assert (fd_ != retired_fd);
    rc = close (fd_);
    if (rc != 0)
        return -1;
    return 0;
}
Example #9
0
/*
 * netimg_sockinit: creates a new socket to connect to the provided server.
 * The server's FQDN and port number are provided.  The port number
 * provided is assumed to already be in network byte order.
 *
 * On success, the global socket descriptor sd is initialized.
 * On error, terminates process.
 */
void
netimg_sockinit(char *sname, u_short port)
{
  int err;
  struct sockaddr_in server;
  struct hostent *sp;
  int bufsize=0;
  socklen_t optlen;

#ifdef _WIN32
  int sndbuf;
  socklen_t optlen;
  WSADATA wsa;
  
  err = WSAStartup(MAKEWORD(2,2), &wsa);  // winsock 2.2
  net_assert(err, "netimg_sockinit: WSAStartup");
#endif

  /* 
   * create a new UDP socket, store the socket in the global variable sd
  */
  sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);   // sd global
  net_assert((sd < 0), "netimg_sockinit: socket");

  /* obtain the server's IPv4 address from sname and initialize the
     socket address with server's address and port number . */
  memset((char *) &server, 0, sizeof(struct sockaddr_in));
  server.sin_family = AF_INET;
  server.sin_port = port;
  sp = gethostbyname(sname);
  net_assert((sp == 0), "netimg_sockinit: gethostbyname");
  memcpy(&server.sin_addr, sp->h_addr, sp->h_length);

  /* 
   * set socket receive buffer size to be at least mss*rwnd bytes.
  */
  bufsize = mss*(rwnd+1);  // rwnd and mss are global
  err = setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(int));
  net_assert((err < 0), "netimg_sockinit: setsockopt RCVBUF");
  optlen = sizeof(int);
  err = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &bufsize, &optlen);
  net_assert((err < 0), "netimg_sockinit: getsockopt RCVBUF");

  fprintf(stderr, "netimg_sockinit: socket receive buffer set to %d bytes\n", bufsize);
  
  /* since this is a UDP socket, connect simply "remembers"
     server's address+port# */
  err = connect(sd, (struct sockaddr *) &server, sizeof(struct sockaddr_in));
  net_assert(err, "netimg_sockinit: connect");

  return;
}
Example #10
0
/*
 * dhtc_recvimage: called by GLUT when idle
 * On each call, receive as much of the image is available on the network and
 * store it in global variable "image" at offset "img_offset" from the
 * start of the buffer.  The global variable "img_offset" must be updated
 * to reflect the amount of data received so far.  Another global variable "img_size"
 * stores the expected size of the image transmitted from the server.
 * The variable "img_size" must NOT be modified.
 * Terminate process on receive error.
 */
void
dhtc_recvimage(void)
{
  int bytes;
   
  // img_offset is a global variable that keeps track of how many bytes
  // have been received and stored in the buffer.  Initialy it is 0.
  //
  // img_size is another global variable that stores the size of the image.
  // If all goes well, we should receive img_size bytes of data from the server.
  if (img_offset <  img_size) { 
    /* 
     * Receive as much of the remaining image as available from the network
     * put the data in the buffer pointed to by the global variable 
     * "image" starting at "img_offset".
     *
     * For example, the first time this function is called, img_offset is 0
     * so the received data is stored at the start (offset 0) of the "image" 
     * buffer.  The global variable "image" should not be modified.
     *
     * Update img_offset by the amount of data received, in preparation for the
     * next iteration, the next time this function is called.
     */
    bytes = recv(sd, image+img_offset, img_size-img_offset, 0);
    net_assert((bytes < 0), "dhtc_recvimage: recv error");
    fprintf(stderr, "dhtc_recvimage: offset 0x%x, received %d bytes\n", (unsigned int) img_offset, bytes);
    img_offset += bytes;
    
    /* give the updated image to OpenGL for texturing */
    glTexImage2D(GL_TEXTURE_2D, 0, (GLint) imsg.im_format,
                 (GLsizei) imsg.im_width, (GLsizei) imsg.im_height, 0,
                 (GLenum) imsg.im_format, GL_UNSIGNED_BYTE, image);
    /* redisplay */
    glutPostRedisplay();
  }

  return;
}
Example #11
0
/*
 * netimg_args: parses command line args.
 *
 * Returns 0 on success or 1 on failure.  On successful return, *sname
 * points to the server's FQDN, and "port" points to the port to
 * connect at server, in network byte order.  Both "*sname", and
 * "port" must be allocated by caller.  The variable "*imagename"
 * points to the name of the image to search for.  The global
 * variables mss, rwnd, and srate are initialized.
 *
 * Nothing else is modified.
 */
int
netimg_args(int argc, char *argv[], char **sname, u_short *port, char **imagename)
{
  char c, *p;
  extern char *optarg;
  int arg;

  if (argc < 5) {
    return (1);
  }
  
  rwnd = NETIMG_RCVWIN;
  mss = NETIMG_MSS;
  srate = NETIMG_SRATE;

  while ((c = getopt(argc, argv, "s:q:w:m:r:")) != EOF) {
    switch (c) {
    case 's':
      for (p = optarg+strlen(optarg)-1;      // point to last character of addr:port arg
           p != optarg && *p != NETIMG_PORTSEP;  // search for ':' separating addr from port
           p--);
      net_assert((p == optarg), "netimg_args: server address malformed");
      *p++ = '\0';
      *port = htons((u_short) atoi(p)); // always stored in network byte order

      net_assert((p-optarg > NETIMG_MAXFNAME), "netimg_args: FQDN too long");
      *sname = optarg;
      break;
    case 'q':
      net_assert((strlen(optarg) >= NETIMG_MAXFNAME), "netimg_args: image name too long");
      *imagename = optarg;
      break;
    case 'w':
      arg = atoi(optarg);
      if (arg < 2|| arg > NETIMG_MAXWIN) {
        return(1);
      }
      rwnd = (unsigned char) arg; 
      break;
    case 'm':
      arg = atoi(optarg);
      if (arg < NETIMG_MINSS) {
        return(1);
      }
      mss = (unsigned short) arg;
      break;
    case 'r':
      arg = atoi(optarg);
      if (arg < 10 || arg > NETIMG_MAXSRATE) {
        return(1);
      }
      srate = (unsigned short) arg;
      break;
    default:
      return(1);
      break;
    }
  }

  return (0);
}
Example #12
0
/*
 * netimg_recvimage: called by GLUT when idle
 * On each call, receive a chunk of the image from the network and
 * store it in global variable "image" at offset from the
 * start of the buffer as specified in the header of the packet.
 *
 * Terminate process on receive error.
 */
void
netimg_recvimage(void)
{
  int bytes;
  ihdr_t hdr;
  struct msghdr msg;
  struct iovec iov[NETIMG_NUMIOVEC];
   
  /* 
   * The image data packet from the server consists of an ihdr_t header
   * followed by a chunk of data.  We want to put the data directly into
   * the buffer pointed to by the global variable "image" without any
   * additional copying. To determine the correct offset from the start of
   * the buffer to put the data into, we first need to retrieve the
   * sequence number stored in the packet header.  Since we're dealing with
   * UDP packet, however, we can't simply read the header off the network,
   * leaving the rest of the packet to be retrieved by subsequent calls to
   * recv(). Instead, what we need to do is call recv() with flags == MSG_PEEK.
   * This allows us to retrieve a copy of the header without removing the packet
   * from the receive buffer.
   *
   * Since our socket has been set to non-blocking mode, if there's no packet
   * ready to be retrieved from the socket, the call to recv() will return
   * immediately with return value -1 and the system global variable "errno"
   * set to EAGAIN or EWOULDBLOCK (defined in errno.h).  In which case, 
   * this function should simply return to caller.
   * 
   * Once a copy of the header is made, check that it has the version number and
   * that it is of type NETIMG_DAT.  Convert the size and sequence number in the
   * header to host byte order.
   */
  bytes = recv(sd, (char *) &hdr, sizeof(ihdr_t), MSG_PEEK);
  if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
    return;
  }
  net_assert((bytes != sizeof(ihdr_t)), "netimg_recvimage: recv bad header size");
  net_assert((hdr.ih_vers != NETIMG_VERS), "netimg_recvimage: wrong version");
  net_assert((!(hdr.ih_type & NETIMG_DAT)), "netimg_recvimage: wrong type");
  hdr.ih_size = ntohs(hdr.ih_size);
  hdr.ih_seqn = ntohl(hdr.ih_seqn);

  fprintf(stderr, "netimg_recvimage: received offset 0x%x, %d bytes\n",
          hdr.ih_seqn, hdr.ih_size);

  /* 
   * Now that we have the offset/seqno information from the packet
   * header, populate a struct msghdr with a pointer to a struct iovec
   * array.  The iovec array should be of size NETIMG_NUMIOVEC.  The
   * first entry of the iovec should be initialized to point to the
   * header above, which should be re-used for each chunk of data
   * received.
   * 
   * Point the second entry of the iovec to the correct offset from
   * the start of the image buffer pointed to by the global variable
   * "image".  Both the offset/seqno and the size of the data to be
   * received into the image buffer are recorded in the packet header
   * retrieved above. Receive the segment by calling recvmsg().
  */
  memset((char *) &msg, 0, sizeof(struct msghdr));
  msg.msg_iov = iov;
  msg.msg_iovlen = NETIMG_NUMIOVEC;
  iov[0].iov_base = &hdr;
  iov[0].iov_len = sizeof(ihdr_t);
  iov[1].iov_base = image+hdr.ih_seqn;
  iov[1].iov_len = hdr.ih_size;
  
  bytes = recvmsg(sd, &msg, 0);
  net_assert((bytes != (int)(sizeof(ihdr_t)+ntohs(hdr.ih_size))), "netimg_recvimage: recv bad packet size");
  
  /* give the updated image to OpenGL for texturing */
  glTexImage2D(GL_TEXTURE_2D, 0, (GLint) imsg.im_format,
               (GLsizei) imsg.im_width, (GLsizei) imsg.im_height, 0,
               (GLenum) imsg.im_format, GL_UNSIGNED_BYTE, image);
  /* redisplay */
  glutPostRedisplay();

  return;
}
Example #13
0
int main(int argc, char *argv[])
{
    if(argc < 3) {
        printf("usage:\n\tspp <server IP> <port>\nspp will run on <port> and "
                "connect to the server on it, too\n");
        return 0;
    }

    int n;

    pthread_mutex_init(&net_mutex, NULL);

    crypt_active = 0;

    /* set up addr structs */
    memset(&proxyaddr, 0, sizeof(proxyaddr));
    memset(&servaddr, 0, sizeof(servaddr));
    memset(&clientaddr, 0, sizeof(clientaddr));
    clientaddr_size = sizeof(clientaddr);

    /* set up sockets */
    servfd = socket(PF_INET, SOCK_STREAM, 0);
    net_assert(servfd, "socket");

    listenfd = socket(PF_INET, SOCK_STREAM, 0);
    net_assert(listenfd, "socket");

    /* set up listenfd */
    proxyaddr.sin_family = AF_INET;
    proxyaddr.sin_port = htons(PORT);
    proxyaddr.sin_addr.s_addr = INADDR_ANY;

    n = bind(listenfd, (struct sockaddr *) &proxyaddr, sizeof(proxyaddr));
    net_assert(n, "bind");

    n = listen(listenfd, BACKLOG);
    net_assert(n, "listen");

    /* initialise servaddr struct */
    struct hostent *h = gethostbyname(SERVER);
    /* uses h_error so net_assert won't work normally */
    if(!h) {
        errno = h_errno;
        net_assert(-1, "gethostbyname");
    }

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr = (*((struct in_addr *) h->h_addr));

    /* accept new client */
    clientfd = accept(listenfd, (struct sockaddr *) &clientaddr,
            &clientaddr_size);
    net_assert(clientfd, "accept");

    /* connect to server */
    connect(servfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    net_assert(n, "connect");

    printf("%s connected\n", inet_ntoa(clientaddr.sin_addr));

    pthread_t servthread_id;
    pthread_create(&servthread_id, NULL, &servthread, NULL);

    void *recvbuf;

    while(1) {
        recvbuf = net_recv(clientfd, ENCRYPTION_CLIENT_IN);

        pthread_mutex_lock(&net_mutex);        

        /* in event of d/c */
        if(recvbuf == NULL) {
            printf("CLIENT CLOSING CONNECTION\n");
            n = close(servfd);
            net_assert(n, "close");
            close(clientfd);
            net_assert(n, "close");
            printf("SERVER CONNECTION CLOSED\n");

            exit(0);
        }

        printf("CLIENT -> SERVER\n");
        pkt_dump(recvbuf, pkt_sizeof(recvbuf));
        printf("\n\n");

        net_send(servfd, recvbuf, ENCRYPTION_CLIENT_IN);

        free(recvbuf);

        pthread_mutex_unlock(&net_mutex);
    }

    return 0;
}
Example #14
0
int 
main(int argc, char *argv[])
{
  char c;
  fd_set rset;
  int i, err, sd, maxsd;
  struct hostent *phost;                              // the FQDN of this host
  struct sockaddr_in self;                            // the address of this host

  int npeers;
  pte_t pte[PR_MAXPEERS], redirected;                 // a 2-entry peer table
  char pnamebuf[PR_MAXPEERS*(PR_MAXFQDN+1)] = { 0 };  // space to hold 2 FQDNs 
  char *pname[PR_MAXPEERS];                           // pointers to above spaces
  pmsg_t msg;

  // init
  npeers=0;
  memset((char *) &self, 0, sizeof(struct sockaddr_in));
  for (i=0; i < PR_MAXPEERS; i++) {
    pname[i] = &pnamebuf[i*(PR_MAXFQDN+1)];
    pte[i].pte_sd = PR_UNINIT_SD;
  }
  
  // parse args, see the comments for peer_args()
  if (peer_args(argc, argv, pname[0], &pte[0].pte_peer.peer_port)) {
    peer_usage(argv[0]);
  }

#ifdef _WIN32
  WSADATA wsa;
  
  err = WSAStartup(MAKEWORD(2,2), &wsa);  // winsock 2.2
  net_assert(err, "peer: WSAStartup");
#endif
  
#ifndef _WIN32
  signal(SIGPIPE, SIG_IGN);    /* don't die if peer is dead */
#endif

  /* if pname is provided, connect to peer */
  if (*pname[0]) {

    pte[0].pte_pname = pname[0];

    /* Task 2: YOUR CODE HERE
     *
     * Given the peer whose FQDN is stored in pname[0],
     * get peer's address and stores it in the first entry
     * of the peer table (pte[0]).
    */
    struct hostent *sp;
    sp = gethostbyname(pname[0]);
    memcpy(&pte[0].pte_peer.peer_addr, sp->h_addr, sp->h_length); 
    /* connect to peer in pte[0] */
    peer_connect(pte);  // Task 2: fill in the peer_connect() function above
    /* Task 2: YOUR CODE HERE
     *
     * Upon return from peer_connect(), the socket descriptor in
     * pte[0] should have been initialized and connected to the peer.
     * Obtain the ephemeral port assigned by the OS kernel to this
     * socket and store it in the variable "self" (along with the
     * peer's address).
    */
    socklen_t len = sizeof(self);
    if(getsockname(pte[0].pte_sd, (struct sockaddr *)&self, &len) < 0)
        abort();
 
    npeers++;

    /* inform user of connection to peer */
    fprintf(stderr, "Connected to peer %s:%d\n", pname[0],
            ntohs(pte[0].pte_peer.peer_port));
  }

  /* setup and listen on connection */
  sd = peer_setup(self.sin_port);  // Task 1: fill in the peer_setup() function above
  if (!self.sin_port) {
    /* Task 1: YOUR CODE HERE
       If a peer was not provided in the command line using "-p", the
       port number will be 0 and the socket sd would have been
       assigned an ephemeral port when calling bind() in peer_setup().
       Obtain the ephemeral port and store it in the variable "self"
       (along with peer's address) by copying the same chunk of code
       you just wrote at the end of the if statement block above. */
    socklen_t len = sizeof(self);
    if (getsockname(sd, (struct sockaddr *) &self, &len) < 0) 
      abort();   
  }
  
  /* Task 1: YOUR CODE HERE
     Find out the FQDN of the current host (use pname[1] as scratch
     space to put the name). Use gethostname(), it is sufficient most
     of the time. */
  gethostname (pname[1], PR_MAXFQDN+1);
  /* inform user which port this peer is listening on */
  fprintf(stderr, "This peer address is %s:%d\n",
          pname[1], ntohs(self.sin_port));

  do {
    /* determine the largest socket descriptor */
    maxsd = (sd > pte[0].pte_sd ? sd : pte[0].pte_sd);
    if (maxsd < pte[1].pte_sd) { maxsd = pte[1].pte_sd; }

    /* set all the descriptors to select() on */
    FD_ZERO(&rset);
#ifndef _WIN32
    FD_SET(STDIN_FILENO, &rset); // wait for input from std input,
        // Winsock only works with socket and stdin is not a socket
#endif
    FD_SET(sd, &rset);           // or the listening socket,
    for (i = 0; i < PR_MAXPEERS; i++) {
      if (pte[i].pte_sd > 0) {

        FD_SET(pte[i].pte_sd, &rset);  // or the peer connected sockets
      }
    }
    
    /* Task 1: YOUR CODE HERE
       Call select() to wait for any activity on any of the above
       descriptors. */
    select(maxsd+1, &rset, NULL, NULL, NULL);

#ifndef _WIN32
    if (FD_ISSET(STDIN_FILENO, &rset)) {
      // user input: if getchar() returns EOF or if user hits q, quit,
      // else flush input and go back to waiting
      if (((c = getchar()) == EOF) || (c == 'q') || (c == 'Q')) {
        fprintf(stderr, "Bye!\n");
        break;
      }
      fflush(stdin);
    }
#endif

    if (FD_ISSET(sd, &rset)) {

      // a connection is made to this host at the listened to socket
      if (npeers < PR_MAXPEERS) {
        /* Peer table is not full.  Accept the peer, send a welcome
         * message.  if we are connected to another peer, also sends
         * back the peer's address+port#
         */
        // Task 1: fill in the functions peer_accept() and peer_ack() above
        peer_accept(sd, &pte[npeers]);
        err = peer_ack(pte[npeers].pte_sd, PM_WELCOME,
                       (npeers > 0 ? &pte[0].pte_peer : 0));

        err = (err != sizeof(pmsg_t));
        net_assert(err, "peer: peer_ack welcome");
        pte[npeers].pte_pname = pname[npeers];

        /* log connection */
        /* get the host entry info on the connected host. */
        phost = gethostbyaddr((char *) &pte[npeers].pte_peer.peer_addr,
                            sizeof(struct in_addr), AF_INET);
        strcpy(pname[npeers], 
               ((phost && phost->h_name) ? phost->h_name:
                inet_ntoa(pte[npeers].pte_peer.peer_addr)));
        
        /* inform user of new peer */
        fprintf(stderr, "Connected from peer %s:%d\n",
                pname[npeers], ntohs(pte[npeers].pte_peer.peer_port));
        
        npeers++;

      } else {
        // Peer table full.  Accept peer, send a redirect message.
        // Task 1: the functions peer_accept() and peer_ack() you wrote
        //         must work without error in this case also.
        peer_accept(sd, &redirected);
        err = peer_ack(redirected.pte_sd, PM_RDIRECT,
                       (npeers > 0 ? &pte[0].pte_peer : 0));
        err = (err != sizeof(pmsg_t));
        net_assert(err, "peer: peer_ack redirect");

        /* log connection */
        /* get the host entry info on the connected host. */
        phost = gethostbyaddr((char *) &redirected.pte_peer.peer_addr,
                            sizeof(struct in_addr), AF_INET);

        /* inform user of peer redirected */
        fprintf(stderr, "Peer table full: %s:%d redirected\n",
               ((phost && phost->h_name) ? phost->h_name:
                inet_ntoa(redirected.pte_peer.peer_addr)),
                ntohs(redirected.pte_peer.peer_port));

        /* closes connection */
        close(redirected.pte_sd);
      } 
    }

    for (i = 0; i < PR_MAXPEERS; i++) {


      if (pte[i].pte_sd > 0 && FD_ISSET(pte[i].pte_sd, &rset)) {
        // a message arrived from a connected peer, receive it
        err = peer_recv(pte[i].pte_sd, &msg); // Task 2: fill in the functions peer_recv() above
        net_assert((err < 0), "peer: peer_recv");



        if (err == 0) {
          // if connection closed by peer, reset peer table entry
          pte[i].pte_sd = PR_UNINIT_SD;
        } else {
          // if connection closed by peer, reset peer table entry
          fprintf(stderr, "Received ack from %s:%d\n", pte[i].pte_pname,
                  ntohs(pte[i].pte_peer.peer_port));
          
          if (msg.pm_vers != PM_VERS) {
            fprintf(stderr, "unknown message version.\n");
          } else {
            if (msg.pm_npeers) {
              // if message contains a peer address, inform user of
              // the peer two hops away
              phost = gethostbyaddr((char *) &msg.pm_peer.peer_addr,
                                    sizeof(struct in_addr), AF_INET);
              fprintf(stderr, "  which is peered with: %s:%d\n", 
                      ((phost && phost->h_name) ? phost->h_name :
                       inet_ntoa(msg.pm_peer.peer_addr)),
                      ntohs(msg.pm_peer.peer_port));
            }
            
            if (msg.pm_type == PM_RDIRECT) {
              // inform user if message is a redirection
              fprintf(stderr, "Join redirected, try to connect to the peer above.\n");
            } 
          }
        }
      }
    }

  } while (1);

#ifdef _WIN32
  WSACleanup();
#endif
  exit(0);
}