Esempio n. 1
0
int
tcp_connect(const char *hostname, int port, const char *bindaddr,
            char *errbuf, size_t errbufsize, int timeout)
{
  int fd, r, res, err;
  struct addrinfo *ai, hints;
  char portstr[6];
  socklen_t errlen = sizeof(err);

  snprintf(portstr, 6, "%u", port);
  memset(&hints, 0, sizeof(struct addrinfo));
  hints.ai_family = AF_UNSPEC;
  res = getaddrinfo(hostname, portstr, &hints, &ai);
  
  if (res != 0) {
    snprintf(errbuf, errbufsize, "%s", gai_strerror(res));
    return -1;
  }

  fd = tvh_socket(ai->ai_family, SOCK_STREAM, 0);
  if(fd == -1) {
    snprintf(errbuf, errbufsize, "Unable to create socket: %s",
	     strerror(errno));
    freeaddrinfo(ai);
    return -1;
  }

  /**
   * Switch to nonblocking
   */
  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

  if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
    if (bindaddr && bindaddr[0] != '\0') {
      struct sockaddr_storage ip;
      memset(&ip, 0, sizeof(ip));
      ip.ss_family = ai->ai_family;
      if (inet_pton(AF_INET, bindaddr, IP_IN_ADDR(ip)) <= 0 ||
          bind(fd, (struct sockaddr *)&ip, IP_IN_ADDRLEN(ip)) < 0) {
        snprintf(errbuf, errbufsize, "Cannot bind to IPv%s addr '%s'",
                                     ai->ai_family == AF_INET6 ? "6" : "4",
                                     bindaddr);
        freeaddrinfo(ai);
        return -1;
      }
    }
  } else {
    snprintf(errbuf, errbufsize, "Invalid protocol family");
    freeaddrinfo(ai);
    return -1;
  }

  r = connect(fd, ai->ai_addr, ai->ai_addrlen);
  freeaddrinfo(ai);

  if(r == -1) {
    /* timeout < 0 - do not wait at all */
    if(errno == EINPROGRESS && timeout < 0) {
      err = 0;
    } else if(errno == EINPROGRESS) {
      tvhpoll_event_t ev;
      tvhpoll_t *efd;

      efd = tvhpoll_create(1);
      memset(&ev, 0, sizeof(ev));
      ev.events   = TVHPOLL_OUT;
      ev.fd       = fd;
      ev.data.ptr = &fd;
      tvhpoll_add(efd, &ev, 1);

      /* minimal timeout is one second */
      if (timeout < 1)
        timeout = 0;

      while (1) {
        if (!tvheadend_running) {
          errbuf[0] = '\0';
          tvhpoll_destroy(efd);
          close(fd);
          return -1;
        }

        r = tvhpoll_wait(efd, &ev, 1, timeout * 1000);
        if (r > 0)
          break;
        
        if (r == 0) { /* Timeout */
          snprintf(errbuf, errbufsize, "Connection attempt timed out");
          tvhpoll_destroy(efd);
          close(fd);
          return -1;
        }
      
        if (!ERRNO_AGAIN(errno)) {
          snprintf(errbuf, errbufsize, "poll() error: %s", strerror(errno));
          tvhpoll_destroy(efd);
          close(fd);
          return -1;
        }
      }
      
      tvhpoll_destroy(efd);
      getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen);
    } else {
      err = errno;
    }
  } else {
    err = 0;
  }

  if(err != 0) {
    snprintf(errbuf, errbufsize, "%s", strerror(err));
    close(fd);
    return -1;
  }
  
  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);


  /* Set the keep-alive active */
  err = 1;
  setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&err, errlen);

  return fd;
}
Esempio n. 2
0
/*
 *  Discovery thread
 */
static void *
upnp_thread( void *aux )
{
  char *bindaddr = aux;
  tvhpoll_t *poll = tvhpoll_create(2);
  tvhpoll_event_t ev[2];
  upnp_data_t *data;
  udp_connection_t *multicast = NULL, *unicast = NULL;
  udp_connection_t *conn;
  unsigned char buf[16384];
  upnp_service_t *us;
  struct sockaddr_storage ip;
  socklen_t iplen;
  size_t size;
  int r;

  multicast = udp_bind("upnp", "upnp_thread_multicast",
                       "239.255.255.250", 1900,
                       NULL, 32*1024);
  if (multicast == NULL || multicast == UDP_FATAL_ERROR)
    goto error;
  unicast = udp_bind("upnp", "upnp_thread_unicast", bindaddr, 0,
                     NULL, 32*1024);
  if (unicast == NULL || unicast == UDP_FATAL_ERROR)
    goto error;

  memset(&ev, 0, sizeof(ev));
  ev[0].fd       = multicast->fd;
  ev[0].events   = TVHPOLL_IN;
  ev[0].data.ptr = multicast;
  ev[1].fd       = unicast->fd;
  ev[1].events   = TVHPOLL_IN;
  ev[1].data.ptr = unicast;
  tvhpoll_add(poll, ev, 2);

  while (upnp_running && multicast->fd >= 0) {
    r = tvhpoll_wait(poll, ev, 2, 1000);

    while (r-- > 0) {
      if ((ev[r].events & TVHPOLL_IN) != 0) {
        conn = ev[r].data.ptr;
        iplen = sizeof(ip);
        size = recvfrom(conn->fd, buf, sizeof(buf), 0,
                                           (struct sockaddr *)&ip, &iplen);
#if ENABLE_TRACE
        if (size > 0) {
          char tbuf[256];
          inet_ntop(ip.ss_family, IP_IN_ADDR(ip), tbuf, sizeof(tbuf));
          tvhtrace("upnp", "%s - received data from %s:%hu [size=%zi]",
                   conn == multicast ? "multicast" : "unicast",
                   tbuf, (unsigned short) IP_PORT(ip), size);
          tvhlog_hexdump("upnp", buf, size);
        }
#endif
        /* TODO: a filter */
        TAILQ_FOREACH(us, &upnp_services, us_link)
          us->us_received(buf, size, conn, &ip);
      }
    }

    while (1) {
      pthread_mutex_lock(&upnp_lock);
      data = TAILQ_FIRST(&upnp_data_write);
      if (data)
        TAILQ_REMOVE(&upnp_data_write, data, data_link);
      pthread_mutex_unlock(&upnp_lock);
      if (data == NULL)
        break;
      udp_write_queue(unicast, &data->queue, &data->storage);
      htsbuf_queue_flush(&data->queue);
      free(data);
    }
  }

error:
  upnp_running = 0;
  tvhpoll_destroy(poll);
  udp_close(unicast);
  udp_close(multicast);
  return NULL;
}