Esempio n. 1
0
/***************************************************************
  Set socket to non-blocking.
***************************************************************/
void fc_nonblock(int sockfd)
{
#ifdef NONBLOCKING_SOCKETS
#ifdef HAVE_WINSOCK
  unsigned long b = 1;
  ioctlsocket(sockfd, FIONBIO, &b);
#else  /* HAVE_WINSOCK */
#ifdef HAVE_FCNTL
  int f_set;

  if ((f_set = fcntl(sockfd, F_GETFL)) == -1) {
    log_error("fcntl F_GETFL failed: %s", fc_strerror(fc_get_errno()));
  }

  f_set |= O_NONBLOCK;

  if (fcntl(sockfd, F_SETFL, f_set) == -1) {
    log_error("fcntl F_SETFL failed: %s", fc_strerror(fc_get_errno()));
  }
#else  /* HAVE_FCNTL */
#ifdef HAVE_IOCTL
  long value=1;

  if (ioctl(sockfd, FIONBIO, (char*)&value) == -1) {
    log_error("ioctl failed: %s", fc_strerror(fc_get_errno()));
  }
#endif /* HAVE_IOCTL */
#endif /* HAVE_FCNTL */
#endif /* HAVE_WINSOCK */
#else  /* NONBLOCKING_SOCKETS */
  log_debug("NONBLOCKING_SOCKETS not available");
#endif /* NONBLOCKING_SOCKETS */
}
Esempio n. 2
0
/**************************************************************************
  Try to connect to a server (get_server_address() must be called first!):
   - try to create a TCP socket and connect it to `server_addr'
   - if successful:
	  - start monitoring the socket for packets from the server
	  - send a "login request" packet to the server
      and - return 0
   - if unable to create the connection, close the socket, put an error
     message in ERRBUF and return the Unix error code (ie., errno, which
     will be non-zero).
**************************************************************************/
static int try_to_connect(const char *username, char *errbuf, int errbufsize)
{
  close_socket_set_callback(close_socket_callback);

  /* connection in progress? wait. */
  if (client.conn.used) {
    (void) mystrlcpy(errbuf, _("Connection in progress."), errbufsize);
    return -1;
  }
  
  if ((client.conn.sock = socket(server_addr.saddr.sa_family,
                                 SOCK_STREAM, 0)) == -1) {
    (void) mystrlcpy(errbuf, fc_strerror(fc_get_errno()), errbufsize);
    return -1;
  }

  if (fc_connect(client.conn.sock, &server_addr.saddr,
                 sockaddr_size(&server_addr)) == -1) {
    (void) mystrlcpy(errbuf, fc_strerror(fc_get_errno()), errbufsize);
    fc_closesocket(client.conn.sock);
    client.conn.sock = -1;
#ifdef HAVE_WINSOCK
    return -1;
#else
    return errno;
#endif
  }

  make_connection(client.conn.sock, username);

  return 0;
}
Esempio n. 3
0
/***************************************************************
  Set socket to non-blocking.
***************************************************************/
void fc_nonblock(int sockfd)
{
#ifdef NONBLOCKING_SOCKETS
#ifdef HAVE_WINSOCK
  unsigned long b = 1;
  ioctlsocket(sockfd, FIONBIO, &b);
#else
#ifdef HAVE_FCNTL
  int f_set;

  if ((f_set=fcntl(sockfd, F_GETFL)) == -1) {
    freelog(LOG_ERROR, "fcntl F_GETFL failed: %s", fc_strerror(fc_get_errno()));
  }

  f_set |= O_NONBLOCK;

  if (fcntl(sockfd, F_SETFL, f_set) == -1) {
    freelog(LOG_ERROR, "fcntl F_SETFL failed: %s", fc_strerror(fc_get_errno()));
  }
#else
#ifdef HAVE_IOCTL
  long value=1;

  if (ioctl(sockfd, FIONBIO, (char*)&value) == -1) {
    freelog(LOG_ERROR, "ioctl failed: %s", fc_strerror(fc_get_errno()));
  }
#endif
#endif
#endif
#else
  freelog(LOG_DEBUG, "NONBLOCKING_SOCKETS not available");
#endif
}
Esempio n. 4
0
/****************************************************************************
  Send the request string to the metaserver.
****************************************************************************/
static void meta_send_request(struct server_scan *scan)
{
  const char *capstr;
  char str[MAX_LEN_PACKET];
  char machine_string[128];

  my_uname(machine_string, sizeof(machine_string));

  capstr = fc_url_encode(our_capability);

  my_snprintf(str, sizeof(str),
    "POST %s HTTP/1.1\r\n"
    "Host: %s:%d\r\n"
    "User-Agent: Freeciv/%s %s %s\r\n"
    "Connection: close\r\n"
    "Content-Type: application/x-www-form-urlencoded; charset=\"utf-8\"\r\n"
    "Content-Length: %lu\r\n"
    "\r\n"
    "client_cap=%s\r\n",
    scan->meta.urlpath,
    scan->meta.name, scan->meta.port,
    VERSION_STRING, client_string, machine_string,
    (unsigned long) (strlen("client_cap=") + strlen(capstr)),
    capstr);

  if (fc_writesocket(scan->sock, str, strlen(str)) != strlen(str)) {
    /* Even with non-blocking this shouldn't fail. */
    scan->error_func(scan, fc_strerror(fc_get_errno()));
    return;
  }

  scan->meta.state = META_WAITING;
}
Esempio n. 5
0
/**************************************************************************
  Check for data received from the metaserver.
**************************************************************************/
static enum server_scan_status
get_metaserver_list(struct server_scan *scan)
{
  struct timeval tv;
  fd_set sockset;

  if (!scan || scan->sock < 0) {
    return SCAN_STATUS_ERROR;
  }

  tv.tv_sec = 0;
  tv.tv_usec = 0;
  FD_ZERO(&sockset);
  FD_SET(scan->sock, &sockset);

  switch (scan->meta.state) {
  case META_CONNECTING:
    if (fc_select(scan->sock + 1, NULL, &sockset, NULL, &tv) < 0) {
      scan->error_func(scan, fc_strerror(fc_get_errno()));
    } else if (FD_ISSET(scan->sock, &sockset)) {
      meta_send_request(scan);
    }
    /* Keep waiting. */
    return SCAN_STATUS_WAITING;
    break;
  case META_WAITING:
    if (fc_select(scan->sock + 1, &sockset, NULL, NULL, &tv) < 0) {
      scan->error_func(scan, fc_strerror(fc_get_errno()));
    } else if (FD_ISSET(scan->sock, &sockset)) {
      meta_read_response(scan);
      return SCAN_STATUS_PARTIAL;
    }
    /* Keep waiting. */
    return SCAN_STATUS_WAITING;
    break;
  case META_DONE:
    return SCAN_STATUS_DONE;
    break;
  default:
    break;
  }

  assert(0);
  return SCAN_STATUS_ERROR;
}
Esempio n. 6
0
/****************************************************************************
  Begin a metaserver scan for servers.  This just initiates the connection
  to the metaserver; later get_meta_server_list should be called whenever
  the socket has data pending to read and parse it.

  Returns FALSE on error (in which case errbuf will contain an error
  message).
****************************************************************************/
static bool begin_metaserver_scan(struct server_scan *scan)
{
  union fc_sockaddr addr;
  int s;

  scan->meta.urlpath = fc_lookup_httpd(scan->meta.name, &scan->meta.port,
				       metaserver);
  if (!scan->meta.urlpath) {
    scan->error_func(scan,
                     _("Invalid $http_proxy or metaserver value, must "
                       "start with 'http://'"));
    return FALSE;
  }

  if (!net_lookup_service(scan->meta.name, scan->meta.port, &addr, FALSE)) {
    scan->error_func(scan, _("Failed looking up metaserver's host"));
    return FALSE;
  }
  
  if ((s = socket(addr.saddr.sa_family, SOCK_STREAM, 0)) == -1) {
    scan->error_func(scan, fc_strerror(fc_get_errno()));
    return FALSE;
  }

  fc_nonblock(s);
  
  if (fc_connect(s, &addr.saddr, sockaddr_size(&addr)) == -1) {
    if (errno == EINPROGRESS) {
      /* With non-blocking sockets this is the expected result. */
      scan->meta.state = META_CONNECTING;
      scan->sock = s;
    } else {
      fc_closesocket(s);
      scan->error_func(scan, fc_strerror(fc_get_errno()));
      return FALSE;
    }
  } else {
    /* Instant connection?  Whoa. */
    scan->sock = s;
    scan->meta.state = META_CONNECTING;
    meta_send_request(scan);
  }

  return TRUE;
}
Esempio n. 7
0
/***************************************************************
  Initialize network stuff.
***************************************************************/
void fc_init_network(void)
{
#ifdef HAVE_WINSOCK
  WSADATA wsa;

  if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0) {
    log_error("no usable WINSOCK.DLL: %s", fc_strerror(fc_get_errno()));
  }
#endif /* HAVE_WINSOCK */

  /* broken pipes are ignored. */
#ifdef HAVE_SIGPIPE
  (void) signal(SIGPIPE, SIG_IGN);
#endif
}
Esempio n. 8
0
/**************************************************************************
  Try to connect to a server (get_server_address() must be called first!):
   - try to create a TCP socket and connect it to `names'
   - if successful:
	  - start monitoring the socket for packets from the server
	  - send a "login request" packet to the server
      and - return 0
   - if unable to create the connection, close the socket, put an error
     message in ERRBUF and return the Unix error code (ie., errno, which
     will be non-zero).
**************************************************************************/
static int try_to_connect(const char *username, char *errbuf, int errbufsize)
{
  int i;
  int sock = -1;

  connections_set_close_callback(client_conn_close_callback);

  /* connection in progress? wait. */
  if (client.conn.used) {
    (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize);
    return -1;
  }

  /* Try all (IPv4, IPv6, ...) addresses until we have a connection. */
  sock = -1;
  for (i = 0; i < name_count; i++) {
    if ((sock = socket(names[i].saddr.sa_family, SOCK_STREAM, 0)) == -1) {
      /* Probably EAFNOSUPPORT or EPROTONOSUPPORT. */
      continue;
    }

    if (fc_connect(sock, &names[i].saddr,
                   sockaddr_size(&names[i])) == -1) {
      fc_closesocket(sock);
      sock = -1;
      continue;
    } else {
      /* We have a connection! */
      break;
    }
  }

  client.conn.sock = sock;
  if (client.conn.sock == -1) {
    fc_errno err = fc_get_errno(); /* Save errno value before calling anything */

    (void) fc_strlcpy(errbuf, fc_strerror(err), errbufsize);
#ifdef HAVE_WINSOCK
    return -1;
#else
    return err;
#endif /* HAVE_WINSOCK */
  }

  make_connection(client.conn.sock, username);

  return 0;
}
Esempio n. 9
0
/**************************************************************************
  Convert string from display encoding (16 bit unicode) to
  local encoding (8 bit char) and resut put in 'pToString'.
  if 'pToString' == NULL then resulting string will be allocate automaticaly.
  'length' give real sizeof 'pToString' array.

  Function return (char *) pointer to (new) pToString.
**************************************************************************/
char *convertcopy_to_chars(char *pToString, size_t length,
			    const Uint16 * pFromUniString)
{
  /* Start Parametrs */
  const char *pFromcode = get_display_encoding();
  const char *pTocode = get_internal_encoding();
  const char *pStart = (char *) pFromUniString;
  size_t ulength = (unistrlen(pFromUniString) + 1) * 2;

  /* ===== */

  char *pResult;
  iconv_t cd;

  /* ===== */

  if (!pStart) {
    return pToString;
  }

  cd = iconv_open(pTocode, pFromcode);
  if (cd == (iconv_t) (-1)) {
    if (errno != EINVAL) {
      return pToString;
    }
  }

  if (pToString) {
    pResult = pToString;
  } else {
    length = ulength * 2; /* UTF-8: up to 4 bytes per char */
    pResult = fc_calloc(1, length);
  }
  
  iconv(cd, NULL, NULL, NULL, NULL);	/* return to the initial state */

  /* Do the conversion for real. */
  {
    const char *pInptr = pStart;
    size_t Insize = ulength;
    char *pOutptr = pResult;
    size_t Outsize = length;

    while (Insize > 0 && Outsize > 0) {
      size_t Res =
	  iconv(cd, (ICONV_CONST char **) &pInptr, &Insize, &pOutptr, &Outsize);
      if (Res == (size_t) (-1)) {
        log_error("iconv() error: %s", fc_strerror(fc_get_errno()));
	if (errno == EINVAL) {
	  break;
	} else {
	  int saved_errno = errno;
	  iconv_close(cd);
	  errno = saved_errno;
	  if(!pToString) {
	    FC_FREE(pResult);
	  }
	  return pToString;
	}
      }
    }

    {
      size_t Res = iconv(cd, NULL, NULL, &pOutptr, &Outsize);
      if (Res == (size_t) (-1)) {
	int saved_errno = errno;
	iconv_close(cd);
	errno = saved_errno;
	if(!pToString) {
	  FC_FREE(pResult);
	}
	return pToString;
      }
    }

  }

  iconv_close(cd);

  return pResult;
}
Esempio n. 10
0
/****************************************************************************
  A wrapper around read_socket_data() which also handles the case the
  socket becomes writeable and there is still data which should be sent
  to the server.

Returns:
    -1  :  an error occurred - you should close the socket
    -2  :  the connection was closed
    >0  :  number of bytes read
    =0  :  no data read, would block
****************************************************************************/
static int read_from_connection(struct connection *pc, bool block)
{
  for (;;) {
    fd_set readfs, writefs, exceptfs;
    int socket_fd = pc->sock;
    bool have_data_for_server = (pc->used && pc->send_buffer
                                 && 0 < pc->send_buffer->ndata);
    int n;
    struct timeval tv;

    tv.tv_sec = 0;
    tv.tv_usec = 0;

    FC_FD_ZERO(&readfs);
    FD_SET(socket_fd, &readfs);

    FC_FD_ZERO(&exceptfs);
    FD_SET(socket_fd, &exceptfs);

    if (have_data_for_server) {
      FC_FD_ZERO(&writefs);
      FD_SET(socket_fd, &writefs);
      n = fc_select(socket_fd + 1, &readfs, &writefs, &exceptfs,
                    block ? NULL : &tv);
    } else {
      n = fc_select(socket_fd + 1, &readfs, NULL, &exceptfs,
                    block ? NULL : &tv);
    }

    /* the socket is neither readable, writeable nor got an
     * exception */
    if (n == 0) {
      return 0;
    }

    if (n == -1) {
      if (errno == EINTR) {
        /* EINTR can happen sometimes, especially when compiling with -pg.
         * Generally we just want to run select again. */
        log_debug("select() returned EINTR");
        continue;
      }

      log_error("select() return=%d errno=%d (%s)",
                n, errno, fc_strerror(fc_get_errno()));
      return -1;
    }

    if (FD_ISSET(socket_fd, &exceptfs)) {
      return -1;
    }

    if (have_data_for_server && FD_ISSET(socket_fd, &writefs)) {
      flush_connection_send_buffer_all(pc);
    }

    if (FD_ISSET(socket_fd, &readfs)) {
      return read_socket_data(socket_fd, pc->buffer);
    }
  }
}
Esempio n. 11
0
/***************************************************************************
  Convert the text.  Both 'from' and 'to' must be 8-bit charsets.  The
  result will be put into the buf buffer unless it is NULL, in which case it
  will be allocated on demand.

  Don't use this function if you can avoid it.  Use one of the
  xxx_to_yyy_string functions.
***************************************************************************/
char *convert_string(const char *text,
		     const char *from,
		     const char *to,
		     char *buf, size_t bufsz)
{
#ifdef HAVE_ICONV
  iconv_t cd = iconv_open(to, from);
  size_t from_len = strlen(text) + 1, to_len;
  bool alloc = (buf == NULL);

  fc_assert_ret_val(is_init && NULL != from && NULL != to, NULL);
  fc_assert_ret_val(NULL != text, NULL);

  if (cd == (iconv_t) (-1)) {
    /* TRANS: "Could not convert text from <encoding a> to <encoding b>:" 
     *        <externally translated error string>."*/
    log_error(_("Could not convert text from %s to %s: %s"),
              from, to, fc_strerror(fc_get_errno()));
    /* The best we can do? */
    if (alloc) {
      return fc_strdup(text);
    } else {
      fc_snprintf(buf, bufsz, "%s", text);
      return buf;
    }
  }

  if (alloc) {
    to_len = from_len;
  } else {
    to_len = bufsz;
  }

  do {
    size_t flen = from_len, tlen = to_len, res;
    const char *mytext = text;
    char *myresult;

    if (alloc) {
      buf = fc_malloc(to_len);
    }

    myresult = buf;

    /* Since we may do multiple translations, we may need to reset iconv
     * in between. */
    iconv(cd, NULL, NULL, NULL, NULL);

    res = iconv(cd, (ICONV_CONST char **)&mytext, &flen, &myresult, &tlen);
    if (res == (size_t) (-1)) {
      if (errno != E2BIG) {
        /* Invalid input. */
        log_error("Invalid string conversion from %s to %s.", from, to);
        iconv_close(cd);
        if (alloc) {
          free(buf);
          return fc_strdup(text); /* The best we can do? */
        } else {
          fc_snprintf(buf, bufsz, "%s", text);
          return buf;
        }
      }
    } else {
      /* Success. */
      iconv_close(cd);

      /* There may be wasted space here, but there's nothing we can do
       * about it. */
      return buf;
    }

    if (alloc) {
      /* Not enough space; try again. */
      buf[to_len - 1] = 0;
      log_verbose("   Result was '%s'.", buf);

      free(buf);
      to_len *= 2;
    }
  } while (alloc);

  return buf;
#else /* HAVE_ICONV */
  if (buf) {
    strncpy(buf, text, bufsz);
    buf[bufsz - 1] = '\0';
    return buf;
  } else {
    return fc_strdup(text);
  }
#endif /* HAVE_ICONV */
}
Esempio n. 12
0
/****************************************************************************
  Read the request string (or part of it) from the metaserver.
****************************************************************************/
static void meta_read_response(struct server_scan *scan)
{
  char buf[4096];
  int result;

  if (!scan->meta.fp) {
#ifdef WIN32_NATIVE
    char filename[MAX_PATH];

    GetTempPath(sizeof(filename), filename);
    cat_snprintf(filename, sizeof(filename), "fctmp%d", myrand(1000));

    scan->meta.fp = fc_fopen(filename, "w+b");
#else
    scan->meta.fp = tmpfile();
#endif

    if (!scan->meta.fp) {
      scan->error_func(scan, _("Could not open temp file."));
    }
  }

  while (1) {
    result = fc_readsocket(scan->sock, buf, sizeof(buf));

    if (result < 0) {
      if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) {
	/* Keep waiting. */
	return;
      }
      scan->error_func(scan, fc_strerror(fc_get_errno()));
      return;
    } else if (result == 0) {
      fz_FILE *f;
      char str[4096];

      /* We're done! */
      rewind(scan->meta.fp);

      f = fz_from_stream(scan->meta.fp);
      assert(f != NULL);

      /* skip HTTP headers */
      /* XXX: TODO check for magic Content-Type: text/x-ini -vasc */
      while (fz_fgets(str, sizeof(str), f) && strcmp(str, "\r\n") != 0) {
	/* nothing */
      }

      /* XXX: TODO check for magic Content-Type: text/x-ini -vasc */

      /* parse HTTP message body */
      scan->servers = parse_metaserver_data(f);
      scan->meta.state = META_DONE;

      /* 'f' (hence 'meta.fp') was closed in parse_metaserver_data(). */
      scan->meta.fp = NULL;

      if (NULL == scan->servers) {
        my_snprintf(str, sizeof(str),
                    _("Failed to parse the metaserver data from http://%s."),
                    scan->meta.name);
        scan->error_func(scan, str);
      }

      return;
    } else {
      if (fwrite(buf, 1, result, scan->meta.fp) != result) {
	scan->error_func(scan, fc_strerror(fc_get_errno()));
      }
    }
  }
}
Esempio n. 13
0
/************************************************************************** 
  Finds the next (lowest) free port.
**************************************************************************/ 
int find_next_free_port(int starting_port, int highest_port,
                        enum fc_addr_family family,
                        char *net_interface, bool not_avail_ok)
{
  int port;
  int s;
  int gafamily;
  bool found = FALSE;

#ifndef IPV6_SUPPORT
  fc_assert(family == FC_ADDR_IPV4 || family == FC_ADDR_ANY);
#endif

  switch (family) {
   case FC_ADDR_IPV4:
     gafamily = AF_INET;
     break;
#ifdef IPV6_SUPPORT
   case FC_ADDR_IPV6:
     gafamily = AF_INET6;
     break;
#endif /* IPV6_SUPPORT */
   case FC_ADDR_ANY:
     gafamily = AF_UNSPEC;
     break;
   default:
     fc_assert(FALSE);
     log_error("Port from unsupported address family requested!");

     return -1;
  }

  for (port = starting_port; !found && highest_port > port; port++) {
    /* HAVE_GETADDRINFO implies IPv6 support */
#ifdef HAVE_GETADDRINFO
    struct addrinfo hints;
    int err;
    char servname[8];
    struct addrinfo *res;

    fc_snprintf(servname, sizeof(servname), "%d", port);

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = gafamily;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE | FC_AI_NUMERICSERV;

    err = getaddrinfo(net_interface, servname, &hints, &res);
    if (!err) {
      struct addrinfo *current = res;
      bool unusable = FALSE;

      while (current != NULL && !unusable) {
        s = socket(current->ai_family, SOCK_STREAM, 0);

        if (s == -1) {
          log_error("socket(): %s", fc_strerror(fc_get_errno()));
        } else {
          if (bind(s, current->ai_addr, current->ai_addrlen) != 0) {
            if (!not_avail_ok || fc_get_errno() != EADDRNOTAVAIL) {
              unusable = TRUE;
            }
          }
        }
        current = current->ai_next;
        fc_closesocket(s);
      }

      freeaddrinfo(res);

      if (!unusable && res != NULL) {
        found = TRUE;
      }
    }
#else /* HAVE_GETADDRINFO */
    union fc_sockaddr tmp;
    struct sockaddr_in *sock4;

    s = socket(gafamily, SOCK_STREAM, 0);

    sock4 = &tmp.saddr_in4;
    memset(&tmp, 0, sizeof(tmp));
    sock4->sin_family = AF_INET;
    sock4->sin_port = htons(port);
    if (net_interface != NULL) {
#if defined(HAVE_INET_ATON)
      if (inet_aton(net_interface, &sock4->sin_addr) == 0) {
#else /* HAVE_INET_ATON */
      sock4->sin_addr.s_addr = inet_addr(net_interface);
      if (sock4->sin_addr.s_addr == INADDR_NONE) {
#endif /* HAVE_INET_ATON */
        struct hostent *hp;

        hp = gethostbyname(net_interface);
        if (hp == NULL) {
          log_error("No hostent for %s!", net_interface);

          return -1;
        }
        if (hp->h_addrtype != AF_INET) {
          log_error("Requested IPv4 address for %s, got something else! (%d)",
                    net_interface, hp->h_addrtype);

          return -1;
        }

        memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length);
      }
    } else {
      sock4->sin_addr.s_addr = htonl(INADDR_ANY);
    }

    if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) {
      found = TRUE;
    }

    fc_closesocket(s);
#endif /* HAVE_GETADDRINFO */
  }

  if (!found) {
    log_error("None of the ports %d - %d is available.",
              starting_port, highest_port);

    return -1;
  }

  /* Rollback the last increment from the loop, back to the port
   * number found to be free. */
  port--;

  return port;
}