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; } }
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; }
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; }
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); }
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); }
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); }
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; } }
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; }
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; }
boolean host_recv_raw(struct host *h, char *buffer, unsigned int len) { return __recv(h, buffer, len); }
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); }