Beispiel #1
0
static enum proxy_status socks5_connect(struct host *h,
 struct addrinfo *ai_data)
{
  /* This handshake is more complicated than SOCKS4 or 4a...
   * and we're also only supporting none or user/password auth.
   */

  char handshake[10];
  int rBuf;

  _raw_host_connect(h, conf->socks_host, conf->socks_port);

  // Version[0x05]|Number of auth methods|auth methods
  if (!__send(h, "\5\1\0", 3))
    return PROXY_SEND_ERROR;

  rBuf = __recv(h, handshake, 2);
  if (rBuf == -1)
    return PROXY_CONNECTION_FAILED;
  if (handshake[0] != 0x5)
    return PROXY_HANDSHAKE_FAILED;

#ifdef CONFIG_IPV6
  if (ai_data->ai_family == AF_INET6)
    return PROXY_ADDRESS_TYPE_UNSUPPORTED;
#endif

  // Version[0x05]|Command|0x00|address type|destination|port
  if (!__send(h, "\5\1\0\1", 4))
    return PROXY_SEND_ERROR;
  if (!__send(h, ai_data->ai_addr->sa_data+2, 4))
    return PROXY_SEND_ERROR;
  if (!__send(h, ai_data->ai_addr->sa_data, 2))
    return PROXY_SEND_ERROR;

  rBuf = __recv(h, handshake, 10);
  if (rBuf == -1)
    return PROXY_CONNECTION_FAILED;
  switch (handshake[1])
  {
    case 0x0:
      return PROXY_SUCCESS;
    case 0x1:
    case 0x7:
      return PROXY_UNKNOWN_ERROR;
    case 0x2:
      return PROXY_ACCESS_DENIED;
    case 0x3:
    case 0x4:
    case 0x6:
      return PROXY_REFLECTION_FAILED;
    case 0x5:
      return PROXY_TARGET_REFUSED;
    case 0x8:
      return PROXY_ADDRESS_TYPE_UNSUPPORTED;
    default:
      return PROXY_UNKNOWN_ERROR;
  }
}
Beispiel #2
0
static int http_recv_line(struct host *h, char *buffer, int len)
{
  int pos;

  assert(len > 0);

  // Grab data bytewise until we find CRLF characters
  for(pos = 0; pos < len; pos++)
  {
    // If recv() times out or fails, abort
    if(!__recv(h, &buffer[pos], 1))
    {
      pos = -HOST_RECV_FAILED;
      goto err_out;
    }

    // Erase terminating CRLF and fix up count
    if(buffer[pos] == '\n')
    {
      buffer[pos--] = 0;
      buffer[pos] = 0;
      break;
    }
  }

  // We didn't find CRLF; this is bad
  if(pos == len)
    pos = -HOST_HTTP_EXCEEDED_BUFFER;

err_out:
  return pos;
}
Beispiel #3
0
static enum proxy_status socks4a_connect(struct host *h,
 const char *target_host, int target_port)
{
  char handshake[8];
  int rBuf;
  target_port = platform_htons(target_port);

  _raw_host_connect(h, conf->socks_host, conf->socks_port);

  if (!__send(h, "\4\1", 2))
    return PROXY_SEND_ERROR;
  if (!__send(h, &target_port, 2))
    return PROXY_SEND_ERROR;
  if (!__send(h, "\0\0\0\1anonymous", 14))
    return PROXY_SEND_ERROR;
  if (!__send(h, target_host, strlen(target_host)))
    return PROXY_SEND_ERROR;
  if (!__send(h, "\0", 1))
    return PROXY_SEND_ERROR;

  rBuf = __recv(h, handshake, 8);
  if (rBuf == -1)
    return PROXY_CONNECTION_FAILED;
  if (handshake[1] != 90)
    return PROXY_HANDSHAKE_FAILED;
  return PROXY_SUCCESS;
}
Beispiel #4
0
ssize_t
__recv_chk (int fd, void *buf, size_t n, size_t buflen, int flags)
{
  if (n > buflen)
    __chk_fail ();

  return __recv (fd, buf, n, flags);
}
Beispiel #5
0
int __get_product(product_name name, Product * productp){
	char toSend[sizeof(product_name_msg)];
	msg_type resp_type;
	char resp_body[sizeof(product_resp)];
	
	__connect();
	msg_serialize_product_name_msg(__get_id(), GET_PRODUCT, name, toSend);
	__send(toSend, sizeof(product_name_msg));
	__recv(&resp_type, sizeof(msg_type));

	if (resp_type == OK_RESP) {
		__recv(resp_body, sizeof(Product));
		msg_deserialize_product(resp_body, productp);
		__disconnect();
		return OK;
	}
	return __handle_not_ok_resp(resp_type);
}
Beispiel #6
0
int __write_product(product_name name, int quantity){
	char toSend[sizeof(product_msg)];
	msg_type resp_type;
	char resp_body[sizeof(error_resp)];
	int code;

	__connect();	
	msg_serialize_product_msg(__get_id(), WRITE_PRODUCT, product_new(name, quantity), toSend);
	__send(toSend, sizeof(product_msg));
	__recv(&resp_type, sizeof(msg_type));
	if (resp_type == OK_RESP) {
		__recv(resp_body, sizeof(int));
		msg_deserialize_code(resp_body, &code);
		__disconnect();
		return OK;
	}
	return __handle_not_ok_resp(resp_type);
}
Beispiel #7
0
int __handle_not_ok_resp(msg_type resp_type) {
	char resp_body[sizeof(int)];
	int code;
	__recv(resp_body, sizeof(int));
	msg_deserialize_code(resp_body, &code);
	__disconnect();
	switch(resp_type) {
	case ERR_RESP:
		//TODO: check!
		return code;
	default: //Should never happen
		printf("Clt: critical error. Cannot recover.\n");
		exit(1);
		return false;
	}
}
Beispiel #8
0
static enum proxy_status socks4_connect(struct host *h,
 struct addrinfo *ai_data)
{
  char handshake[8];
  int rBuf;

  _raw_host_connect(h, conf->socks_host, conf->socks_port);

  if (!__send(h, "\4\1", 2))
    return PROXY_SEND_ERROR;
  if (!__send(h, ai_data->ai_addr->sa_data, 6))
    return PROXY_SEND_ERROR;
  if (!__send(h, "anonymous\0", 10))
    return PROXY_SEND_ERROR;

  rBuf = __recv(h, handshake, 8);
  if (rBuf == -1)
    return PROXY_CONNECTION_FAILED;
  if (handshake[1] != 90)
    return PROXY_HANDSHAKE_FAILED;
  return PROXY_SUCCESS;
}
Beispiel #9
0
enum host_status host_recv_file(struct host *h, struct http_info *req,
 FILE *file)
{
  boolean mid_inflate = false, mid_chunk = false, deflated = false;
  unsigned int content_length = 0;
  const char *host_name = h->name;
  unsigned long len = 0, pos = 0;
  char line[LINE_BUF_LEN];
  z_stream stream;
  ssize_t line_len;

  enum {
    NONE,
    NORMAL,
    CHUNKED,
  } transfer_type = NONE;

  // Tell the server that we support pipelining
  snprintf(line, LINE_BUF_LEN, "GET %s HTTP/1.1", req->url);
  line[LINE_BUF_LEN - 1] = 0;
  if(http_send_line(h, line) < 0)
    return -HOST_SEND_FAILED;

  // For vhost resolution
  if (h->proxied)
    host_name = h->endpoint;

  snprintf(line, LINE_BUF_LEN, "Host: %s", host_name);

  line[LINE_BUF_LEN - 1] = 0;
  if(http_send_line(h, line) < 0)
    return -HOST_SEND_FAILED;

  // We support DEFLATE/GZIP payloads
  if(http_send_line(h, "Accept-Encoding: gzip") < 0)
    return -HOST_SEND_FAILED;

  // Black line tells server we are done
  if(http_send_line(h, "") < 0)
    return -HOST_SEND_FAILED;

  // Read in the HTTP status line
  line_len = http_recv_line(h, line, LINE_BUF_LEN);
  if(line_len < 0)
  {
    warn("No response for url '%s': %d!\n", req->url, (int)line_len);
    return line_len;
  }

  if(!http_read_status(req, line, line_len))
  {
    warn("Invalid status: %s\nFailed for url '%s'\n", line, req->url);
    return -HOST_HTTP_INVALID_STATUS;
  }

  // Unhandled status categories
  switch(req->status_type)
  {
    case 1:
      return -HOST_HTTP_INFO;

    case 3:
      return -HOST_HTTP_REDIRECT;

    case 4:
      return -HOST_HTTP_CLIENT_ERROR;

    case 5:
      return -HOST_HTTP_SERVER_ERROR;
  }

  // Now parse the HTTP headers, extracting only the pertinent fields

  while(true)
  {
    int len = http_recv_line(h, line, LINE_BUF_LEN);
    char *key, *value, *buf = line;

    if(len < 0)
      return -HOST_HTTP_INVALID_HEADER;
    else if(len == 0)
      break;

    key = strsep(&buf, ":");
    value = strsep(&buf, ":");

    if(!key || !value)
      return -HOST_HTTP_INVALID_HEADER;

    // Skip common prefix space if present
    if(value[0] == ' ')
      value++;

    /* Parse pertinent headers. These are:
     *
     *   Content-Length     Necessary to determine payload length
     *   Transfer-Encoding  Instead of Content-Length, can only be "chunked"
     *   Content-Type       Text or binary; also used for sanity checks
     *   Content-Encoding   Present and set to 'gzip' if deflated
     */

    if(strcmp(key, "Content-Length") == 0)
    {
      char *endptr;

      content_length = (unsigned int)strtoul(value, &endptr, 10);
      if(endptr[0])
        return -HOST_HTTP_INVALID_CONTENT_LENGTH;

      transfer_type = NORMAL;
    }

    else if(strcmp(key, "Transfer-Encoding") == 0)
    {
      if(strcmp(value, "chunked") != 0)
        return -HOST_HTTP_INVALID_TRANSFER_ENCODING;

      transfer_type = CHUNKED;
    }

    else if(strcmp(key, "Content-Type") == 0)
    {
      strncpy(req->content_type, value, 63);

      if(strcmp(value, req->expected_type) != 0)
        return -HOST_HTTP_INVALID_CONTENT_TYPE;
    }

    else if(strcmp(key, "Content-Encoding") == 0)
    {
      if(strcmp(value, "gzip") != 0)
        return -HOST_HTTP_INVALID_CONTENT_ENCODING;

      deflated = true;
    }
  }

  if(transfer_type != NORMAL && transfer_type != CHUNKED)
    return -HOST_HTTP_INVALID_TRANSFER_ENCODING;

  while(true)
  {
    unsigned long block_size;
    char block[BLOCK_SIZE];

    /* Both transfer mechanisms need preambles. For NORMAL, this will
     * happen only once, because we have a predetermined length for
     * transfer. However, for CHUNKED we don't know the total payload
     * size, so this will be invoked each time we exhaust a chunk.
     *
     * The CHUNKED handling basically involves chopping away the
     * headers and determining the next chunk size.
     */
    if(!mid_chunk)
    {
      if(transfer_type == NORMAL)
        len = content_length;

      else if(transfer_type == CHUNKED)
      {
        char *endptr, *length, *buf = line;

        // Get a chunk_length;parameters formatted line (CRLF terminated)
        if(http_recv_line(h, line, LINE_BUF_LEN) <= 0)
          return -HOST_HTTP_INVALID_CHUNK_LENGTH;

        // HTTP 1.1 says we can ignore trailing parameters
        length = strsep(&buf, ";");
        if(!length)
          return -HOST_HTTP_INVALID_CHUNK_LENGTH;

        // Convert hex length to unsigned long; check for conversion errors
        len = strtoul(length, &endptr, 16);
        if(endptr[0])
          return -HOST_HTTP_INVALID_CHUNK_LENGTH;
      }

      mid_chunk = true;
      pos = 0;
    }

    /* For NORMAL transfers, this indicates that there was a zero byte
     * payload. This is unusual but we can handle it safely by aborting.
     *
     * For CHUNKED transfers, zero indicates that there are no more chunks
     * to process, and that final footer handling should occur. We then
     * abort as with NORMAL.
     */
    if(len == 0)
    {
      if(transfer_type == CHUNKED)
        if(!http_skip_headers(h))
          return -HOST_HTTP_INVALID_HEADER;
      break;
    }

    /* For a NORMAL transfer, the block_size computation should yield
     * BLOCK_SIZE until the final block, which will be len % BLOCK_SIZE.
     *
     * For CHUNKED, this block_size can be more volatile. In most cases it
     * will be BLOCK_SIZE if chunk size > BLOCK_SIZE, until the final block.
     *
     * However for very small chunks (which are unlikely) this will always
     * be shorter than BLOCK_SIZE.
     */
    block_size = MIN(BLOCK_SIZE, len - pos);

    /* In either case, all headers and block computation has now been done,
     * and the buffer can be streamed to disk.
     */
    if(!__recv(h, block, block_size))
      return -HOST_RECV_FAILED;

    if(deflated)
    {
      /* This is the first block requiring inflation. In this case, we must
       * parse the GZIP header in order to compute an offset to the DEFLATE
       * formatted data.
       */
      if(!mid_inflate)
      {
        ssize_t deflate_offset = 0;

        /* Compute the offset within this block to begin the inflation
         * process. For all but the first block, deflate_offset will be
         * zero.
         */
        deflate_offset = zlib_skip_gzip_header(block, block_size);
        if(deflate_offset < 0)
          return deflate_offset;

        /* Now we can initialize the decompressor. Pass along the block
         * without the GZIP header (and for a GZIP, this is also without
         * the DEFLATE header too, which is what the -MAX_WBITS trick is for).
         */
        stream.avail_in = block_size - (unsigned long)deflate_offset;
        stream.next_in = (Bytef *)&block[deflate_offset];
        stream.zalloc = Z_NULL;
        stream.zfree = Z_NULL;
        stream.opaque = Z_NULL;

        if(inflateInit2(&stream, -MAX_WBITS) != Z_OK)
          return -HOST_ZLIB_INFLATE_FAILED;

        mid_inflate = true;
      }
      else
      {
        stream.avail_in = block_size;
        stream.next_in = (Bytef *)block;
      }

      while(true)
      {
        char outbuf[BLOCK_SIZE];
        int ret;

        // Each pass, only decompress a maximum of BLOCK_SIZE
        stream.avail_out = BLOCK_SIZE;
        stream.next_out = (Bytef *)outbuf;

        /* Perform the inflation (this will modify avail_in and
         * next_in automatically.
         */
        ret = inflate(&stream, Z_NO_FLUSH);
        if(ret != Z_OK && ret != Z_STREAM_END)
          return -HOST_ZLIB_INFLATE_FAILED;

        // Push the block to disk
        if(fwrite(outbuf, BLOCK_SIZE - stream.avail_out, 1, file) != 1)
          return -HOST_FWRITE_FAILED;

        // If the stream has terminated, flag it and break out
        if(ret == Z_STREAM_END)
        {
          mid_inflate = false;
          break;
        }

        /* The stream hasn't terminated but we've exhausted input
         * data for this pass.
         */
        if(stream.avail_in == 0)
          break;
      }

      // The stream terminated, so we should free associated data-structures
      if(!mid_inflate)
        inflateEnd(&stream);
    }
    else
    {
      /* If the transfer is not deflated, we can simply write out
       * block_size bytes to the file now.
       */
      if(fwrite(block, block_size, 1, file) != 1)
        return -HOST_FWRITE_FAILED;
    }

    pos += block_size;

    if(h->recv_cb)
      h->recv_cb(ftell(file));

    /* For NORMAL transfers we can now abort since we have reached the end
     * of our payload.
     *
     * For CHUNKED transfers, we remove the trailing newline and flag that
     * a new set of chunk headers should be read.
     */
    if(len == pos)
    {
      if(transfer_type == NORMAL)
        break;

      else if(transfer_type == CHUNKED)
      {
        if(http_recv_line(h, line, LINE_BUF_LEN) != 0)
          return -HOST_HTTP_INVALID_HEADER;
        mid_chunk = false;
      }
    }
  }

  return HOST_SUCCESS;
}
Beispiel #10
0
boolean host_recv_raw(struct host *h, char *buffer, unsigned int len)
{
  return __recv(h, buffer, len);
}
Beispiel #11
0
int
res_send(struct SocketBase *	libPtr,
	 const char *		buf,
	 int			buflen,
	 char *			answer,
	 int 			anslen)
{
	register int n;
	int try, v_circuit, resplen, nscount;
	int gotsomewhere = 0, connected = 0;
	int connreset = 0;
	u_short id, len;
	char *cp;
	fd_set dsmask;
	struct timeval timeout;
	struct in_addr *ns;
	struct sockaddr_in host;
	HEADER *hp = (HEADER *) buf;
	HEADER *anhp = (HEADER *) answer;
	u_char terrno = ETIMEDOUT;
#define JUNK_SIZE 512
	char junk[JUNK_SIZE]; /* buffer for trash data */

#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send()\n"));
D(bug("[AROSTCP](res_send.c) res_send: using socket %d\n", res_sock));
#endif

#ifdef RES_DEBUG
		printf("res_send()\n");
		__p_query(buf, libPtr);
#endif /* RES_DEBUG */

	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
	id = hp->id;
	/*
	 * Send request, RETRY times, or until successful
	 */
	for (try = 0; try < _res.retry; try++) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Attempt %d\n", try));
#endif
	  nscount = 0;
	  DRES(Printf("Retry #%ld\n",try);)
	  for (ns = _res.nsaddr_list; ns->s_addr; ns++) {
	    nscount++;
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Querying server #%ld address = %s\n", nscount,
			      __inet_ntoa(ns->s_addr, libPtr)));
#endif

#ifdef RES_DEBUG
			Printf("Querying server #%ld address = %s\n", nscount,
			      __Inet_NtoA(ns->s_addr, libPtr));
#endif /* RES_DEBUG */
	    host.sin_len = sizeof(host);
	    host.sin_family = AF_INET;
	    host.sin_port = htons(NAMESERVER_PORT);
	    host.sin_addr.s_addr = ns->s_addr;
	    aligned_bzero_const(&host.sin_zero, sizeof(host.sin_zero));
	usevc:
		if (v_circuit) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Using v_circuit\n"));
#endif
			int truncated = 0;

			/*
			 * Use virtual circuit;
			 * at most one attempt per server.
			 */
			try = _res.retry;
			if (res_sock < 0) {
				res_sock = __socket(AF_INET, SOCK_STREAM, 0, libPtr);
				if (res_sock < 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Failed to create socket!!\n"));
					terrno = readErrnoValue(libPtr);
#endif
#ifdef RES_DEBUG
					    Perror("socket (vc)");
#endif /* RES_DEBUG */
					continue;
				}
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: created socket %d\n", res_sock));
#endif
				if (__connect(res_sock,
					    (struct sockaddr *)&host,
					    sizeof(struct sockaddr), libPtr) < 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Failed to connect\n"));
#endif
				        terrno = readErrnoValue(libPtr);
#ifdef RES_DEBUG
					    Perror("connect (vc)");
#endif /* RES_DEBUG */
					(void) __CloseSocket(res_sock, libPtr);
					res_sock = -1;
					continue;
				}
			}
			/*
			 * Send length & message
			 */
			len = htons((u_short)buflen);
			if ((__send(res_sock, (char *)&len, sizeof(len), 0, libPtr)
			     != sizeof(len)) ||
			   ((__send(res_sock, (char *)buf, buflen, 0, libPtr)
			     != buflen))) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Failed sending query\n"));
#endif
				terrno = readErrnoValue(libPtr);
#ifdef RES_DEBUG
					Perror("write(vc)");
#endif /* RES_DEBUG */
				(void) __CloseSocket(res_sock, libPtr);
				res_sock = -1;
				continue;
			}
			/*
			 * Receive length & response
			 */
			cp = answer;
			len = sizeof(short);
			while (len != 0 &&
			    (n = __recv(res_sock,
				      (char *)cp, (int)len, 0, libPtr)) > 0) {
				cp += n;
				len -= n;
			}
			if (n <= 0) {
				terrno = readErrnoValue(libPtr);
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Failed recieving response\n"));
#endif
#ifdef RES_DEBUG
					Perror("read (vc)");
#endif /* RES_DEBUG */
				(void) __CloseSocket(res_sock, libPtr);
				res_sock = -1;
				/*
				 * A long running process might get its TCP
				 * connection reset if the remote server was
				 * restarted.  Requery the server instead of
				 * trying a new one.  When there is only one
				 * server, this means that a query might work
				 * instead of failing.  We only allow one reset
				 * per query to prevent looping.
				 */
				if (terrno == ECONNRESET && !connreset) {
					connreset = 1;
					ns--;
				}
				continue;
			}
			cp = answer;
			if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Truncated response\n"));
#endif
#ifdef RES_DEBUG
				       Printf("response truncated\n");
#endif /* RES_DEBUG */
				len = anslen;
				truncated = 1;
			} else
				len = resplen;
			while (len != 0 &&
			   (n = __recv(res_sock,
				     (char *)cp, (int)len, 0, libPtr)) > 0) {
				cp += n;
				len -= n;
			}
			if (n <= 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Error recieving response\n"));
#endif
				terrno = readErrnoValue(libPtr);
#ifdef RES_DEBUG
					Perror("read (vc)");
#endif /* RES_DEBUG */
				(void) __CloseSocket(res_sock, libPtr);
				res_sock = -1;
				continue;
			}
			if (truncated) {
				/*
				 * Flush rest of answer
				 * so connection stays in synch.
				 */
				anhp->tc = 1;
				len = resplen - anslen;
				while (len != 0) {
					n = (len > JUNK_SIZE ? JUNK_SIZE : len);
					if ((n = __recv(res_sock,
						      junk, n, 0, libPtr)) > 0)
						len -= n;
					else
						break;
				}
			}
		} else {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Using datagrams\n"));
#endif
			/*
			 * Use datagrams.
			 */
			if (res_sock < 0) {
				res_sock = __socket(AF_INET, SOCK_DGRAM, 0, libPtr);
				if (res_sock < 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Failed to create socket\n"));
#endif
					terrno = readErrnoValue(libPtr);
#ifdef RES_DEBUG
					    Perror("socket (dg)");
#endif /* RES_DEBUG */
					continue;
				}
			}
			/*
			 * I'm tired of answering this question, so:
			 * On a 4.3BSD+ machine (client and server,
			 * actually), sending to a nameserver datagram
			 * port with no nameserver will cause an
			 * ICMP port unreachable message to be returned.
			 * If our datagram socket is "connected" to the
			 * server, we get an ECONNREFUSED error on the next
			 * socket operation, and select returns if the
			 * error message is received.  We can thus detect
			 * the absence of a nameserver without timing out.
			 * If we have sent queries to at least two servers,
			 * however, we don't want to remain connected,
			 * as we wish to receive answers from the first
			 * server to respond.
			 */
#warning "TODO*: see comment here .."
			/* This piece of code still behaves slightly wrong in
			   case of ECONNREFUSED error. On next retry socket will
			   be in disconnected state and instead of getting
			   ECONNREFUSED again we'll timeout in WaitSelect() and
			   get ETIMEDOUT. However, this is not critical and is
			   queued for future - Pavel Fedin*/
			if (try == 0 && nscount == 1) {
				/*
				 * Don't use connect if we might
				 * still receive a response
				 * from another server.
				 */
				if (connected == 0) {
				  if (__connect(res_sock,
					      (struct sockaddr *)&host,
					      sizeof(struct sockaddr),
					      libPtr) < 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Error connecting\n"));
#endif
#ifdef RES_DEBUG
							Perror("connect (dg)");
#endif /* RES_DEBUG */
						continue;
					}
					connected = 1;
				}
				if (__send(res_sock,
					 buf, buflen, 0, libPtr) != buflen) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Error sending\n"));
#endif
#ifdef RES_DEBUG
						Perror("send (dg)");
#endif /* RES_DEBUG */
					continue;
				}
			} else {
				/*
				 * Disconnect if we want to listen
				 * for responses from more than one server.
				 */
				if (connected) {
					(void) __connect(res_sock, &no_addr,
					    sizeof(no_addr), libPtr);
					connected = 0;
				}
				if (__sendto(res_sock, buf, buflen, 0,
				    (struct sockaddr *)&host,
				    sizeof(struct sockaddr), libPtr) != buflen) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: [__sendto] Error\n"));
#endif
#ifdef RES_DEBUG
						Perror("sendto (dg)");
#endif /* RES_DEBUG */
					continue;
				}
			}

			/*
			 * Wait for reply
			 */
			timeout.tv_sec = (_res.retrans << try);
			if (try > 0)
				timeout.tv_sec /= nscount;
			if (timeout.tv_sec <= 0)
				timeout.tv_sec = 1;
			timeout.tv_usec = 0;
wait:
			FD_ZERO(&dsmask);
			FD_SET(res_sock, &dsmask);
			n = __WaitSelect(res_sock+1, &dsmask, NULL,
				NULL, &timeout, NULL, libPtr);
			if (n < 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: [__WaitSelect] Error\n"));
#endif
#ifdef RES_DEBUG
					Perror("select");
#endif /* RES_DEBUG */

            terrno = readErrnoValue(libPtr);
				if (terrno == EINTR) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: closing socket\n"));
#endif
					__CloseSocket(res_sock, libPtr);
					res_sock = -1;
					return (-1);
				}
				continue;
			}
			if (n == 0) {
				/*
				 * timeout
				 */
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Timeout!\n"));
#endif
#ifdef RES_DEBUG
					Printf("timeout\n");
#endif /* RES_DEBUG */
#if 1 || BSD >= 43
				gotsomewhere = 1;
#endif
				continue;
			}
			if ((resplen = __recv(res_sock,
					    answer, anslen, 0, libPtr)) <= 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Error recieving\n"));
#endif
#ifdef RES_DEBUG
					Perror("recv (dg)");
#endif /* RES_DEBUG */
				continue;
			}
			gotsomewhere = 1;
			if (id != anhp->id) {
				/*
				 * response from old query, ignore it
				 */
#ifdef RES_DEBUG
					Printf("old answer:\n");
					__p_query(answer, libPtr);
#endif /* RES_DEBUG */
				goto wait;
			}
			if (!(_res.options & RES_IGNTC) && anhp->tc) {
				/*
				 * get rest of answer;
				 * use TCP with same server.
				 */
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Response is truncated\n"));
#endif
#ifdef RES_DEBUG
					Printf("truncated answer\n");
#endif /* RES_DEBUG */
				(void)__CloseSocket(res_sock, libPtr);
				res_sock = -1;
				v_circuit = 1;
				goto usevc;
			}
		}

#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Recieved answer\n"));
#endif

#ifdef RES_DEBUG
			Printf("got answer:\n");
			__p_query(answer, libPtr);
#endif /* RES_DEBUG */
		/*
		 * If using virtual circuits, we assume that the first server
		 * is preferred * over the rest (i.e. it is on the local
		 * machine) and only keep that one open.
		 * If we have temporarily opened a virtual circuit,
		 * or if we haven't been asked to keep a socket open,
		 * close the socket.
		 */
		if ((v_circuit &&
		    ((_res.options & RES_USEVC) == 0 || ns->s_addr != 0)) ||
		    (_res.options & RES_STAYOPEN) == 0) {
#if defined(__AROS__)
D(bug("[AROSTCP](res_send.c) res_send: Closing socket\n"));
#endif
			(void) __CloseSocket(res_sock, libPtr);
			res_sock = -1;
		}
		return (resplen);
	   }