/** filter out unsuitable targets * @param iter_env: iterator environment with ipv6-support flag. * @param env: module environment with infra cache. * @param name: zone name * @param namelen: length of name * @param qtype: query type (host order). * @param now: current time * @param a: address in delegation point we are examining. * @return an integer that signals the target suitability. * as follows: * -1: The address should be omitted from the list. * Because: * o The address is bogus (DNSSEC validation failure). * o Listed as donotquery * o is ipv6 but no ipv6 support (in operating system). * o is ipv4 but no ipv4 support (in operating system). * o is lame * Otherwise, an rtt in milliseconds. * 0 .. USEFUL_SERVER_TOP_TIMEOUT-1 * The roundtrip time timeout estimate. less than 2 minutes. * Note that util/rtt.c has a MIN_TIMEOUT of 50 msec, thus * values 0 .. 49 are not used, unless that is changed. * USEFUL_SERVER_TOP_TIMEOUT * This value exactly is given for unresponsive blacklisted. * USEFUL_SERVER_TOP_TIMEOUT+1 * For non-blacklisted servers: huge timeout, but has traffic. * USEFUL_SERVER_TOP_TIMEOUT*1 .. * parent-side lame servers get this penalty. A dispreferential * server. (lame in delegpt). * USEFUL_SERVER_TOP_TIMEOUT*2 .. * dnsseclame servers get penalty * USEFUL_SERVER_TOP_TIMEOUT*3 .. * recursion lame servers get penalty * UNKNOWN_SERVER_NICENESS * If no information is known about the server, this is * returned. 376 msec or so. * +BLACKLIST_PENALTY (of USEFUL_TOP_TIMEOUT*4) for dnssec failed IPs. * * When a final value is chosen that is dnsseclame ; dnsseclameness checking * is turned off (so we do not discard the reply). * When a final value is chosen that is recursionlame; RD bit is set on query. * Because of the numbers this means recursionlame also have dnssec lameness * checking turned off. */ static int iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env, uint8_t* name, size_t namelen, uint16_t qtype, time_t now, struct delegpt_addr* a) { int rtt, lame, reclame, dnsseclame; if(a->bogus) return -1; /* address of server is bogus */ if(donotq_lookup(iter_env->donotq, &a->addr, a->addrlen)) { log_addr(VERB_ALGO, "skip addr on the donotquery list", &a->addr, a->addrlen); return -1; /* server is on the donotquery list */ } if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) { return -1; /* there is no ip6 available */ } if(!iter_env->supports_ipv4 && !addr_is_ip6(&a->addr, a->addrlen)) { return -1; /* there is no ip4 available */ } /* check lameness - need zone , class info */ if(infra_get_lame_rtt(env->infra_cache, &a->addr, a->addrlen, name, namelen, qtype, &lame, &dnsseclame, &reclame, &rtt, now)) { log_addr(VERB_ALGO, "servselect", &a->addr, a->addrlen); verbose(VERB_ALGO, " rtt=%d%s%s%s%s", rtt, lame?" LAME":"", dnsseclame?" DNSSEC_LAME":"", reclame?" REC_LAME":"", a->lame?" ADDR_LAME":""); if(lame) return -1; /* server is lame */ else if(rtt >= USEFUL_SERVER_TOP_TIMEOUT) /* server is unresponsive, * we used to return TOP_TIMEOUT, but fairly useless, * because if == TOP_TIMEOUT is dropped because * blacklisted later, instead, remove it here, so * other choices (that are not blacklisted) can be * tried */ return -1; /* select remainder from worst to best */ else if(reclame) return rtt+USEFUL_SERVER_TOP_TIMEOUT*3; /* nonpref */ else if(dnsseclame || a->dnsseclame) return rtt+USEFUL_SERVER_TOP_TIMEOUT*2; /* nonpref */ else if(a->lame) return rtt+USEFUL_SERVER_TOP_TIMEOUT+1; /* nonpref */ else return rtt; } /* no server information present */ if(a->dnsseclame) return UNKNOWN_SERVER_NICENESS+USEFUL_SERVER_TOP_TIMEOUT*2; /* nonpref */ else if(a->lame) return USEFUL_SERVER_TOP_TIMEOUT+1+UNKNOWN_SERVER_NICENESS; /* nonpref */ return UNKNOWN_SERVER_NICENESS; }
/** clear proxy list */ static void proxy_list_clear(struct proxy* p) { char from[109]; struct proxy* np; int i=0, port; while(p) { np = p->next; port = (int)ntohs(((struct sockaddr_in*)&p->addr)->sin_port); if(addr_is_ip6(&p->addr, p->addr_len)) { if(inet_ntop(AF_INET6, &((struct sockaddr_in6*)&p->addr)->sin6_addr, from, (socklen_t)sizeof(from)) == 0) (void)strlcpy(from, "err", sizeof(from)); } else { if(inet_ntop(AF_INET, &((struct sockaddr_in*)&p->addr)->sin_addr, from, (socklen_t)sizeof(from)) == 0) (void)strlcpy(from, "err", sizeof(from)); } printf("client[%d]: last %s@%d of %d : %u in, %u out, " "%u returned\n", i++, from, port, (int)p->numreuse+1, (unsigned)p->numwait, (unsigned)p->numsent, (unsigned)p->numreturn); #ifndef USE_WINSOCK close(p->s); #else closesocket(p->s); #endif free(p); p = np; } }
int addr_in_common(struct sockaddr_storage* addr1, int net1, struct sockaddr_storage* addr2, int net2, socklen_t addrlen) { int min = (net1<net2)?net1:net2; int i, to; int match = 0; uint8_t* s1, *s2; if(addr_is_ip6(addr1, addrlen)) { s1 = (uint8_t*)&((struct sockaddr_in6*)addr1)->sin6_addr; s2 = (uint8_t*)&((struct sockaddr_in6*)addr2)->sin6_addr; to = 16; } else { s1 = (uint8_t*)&((struct sockaddr_in*)addr1)->sin_addr; s2 = (uint8_t*)&((struct sockaddr_in*)addr2)->sin_addr; to = 4; } /* match = bits_in_common(s1, s2, to); */ for(i=0; i<to; i++) { if(s1[i] == s2[i]) { match += 8; } else { uint8_t z = s1[i]^s2[i]; log_assert(z); while(!(z&0x80)) { match++; z<<=1; } break; } } if(match > min) match = min; return match; }
/** open TCP socket to svr */ static int open_svr(const char* svr, int udp) { struct sockaddr_storage addr; socklen_t addrlen; int fd = -1; /* svr can be ip@port */ memset(&addr, 0, sizeof(addr)); if(!extstrtoaddr(svr, &addr, &addrlen)) { printf("fatal: bad server specs '%s'\n", svr); exit(1); } fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET, udp?SOCK_DGRAM:SOCK_STREAM, 0); if(fd == -1) { #ifndef USE_WINSOCK perror("socket() error"); #else printf("socket: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); } if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { #ifndef USE_WINSOCK perror("connect() error"); #else printf("connect: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); } return fd; }
/** contact the server with TCP connect */ static int contact_server(const char* svr, struct config_file* cfg, int statuscmd) { struct sockaddr_storage addr; socklen_t addrlen; int fd; /* use svr or the first config entry */ if(!svr) { if(cfg->control_ifs) svr = cfg->control_ifs->str; else svr = "127.0.0.1"; /* config 0 addr (everything), means ask localhost */ if(strcmp(svr, "0.0.0.0") == 0) svr = "127.0.0.1"; else if(strcmp(svr, "::0") == 0 || strcmp(svr, "0::0") == 0 || strcmp(svr, "0::") == 0 || strcmp(svr, "::") == 0) svr = "::1"; } if(strchr(svr, '@')) { if(!extstrtoaddr(svr, &addr, &addrlen)) fatal_exit("could not parse IP@port: %s", svr); } else { if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen)) fatal_exit("could not parse IP: %s", svr); } fd = socket(addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET, SOCK_STREAM, 0); if(fd == -1) { #ifndef USE_WINSOCK fatal_exit("socket: %s", strerror(errno)); #else fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); #endif } if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { log_addr(0, "address", &addr, addrlen); #ifndef USE_WINSOCK log_err("connect: %s", strerror(errno)); if(errno == ECONNREFUSED && statuscmd) { printf("unbound is stopped\n"); exit(3); } #else log_err("connect: %s", wsa_strerror(WSAGetLastError())); if(WSAGetLastError() == WSAECONNREFUSED && statuscmd) { printf("unbound is stopped\n"); exit(3); } #endif exit(1); } return fd; }
/** store port number into sockaddr structure */ void sockaddr_store_port(struct sockaddr_storage* addr, socklen_t addrlen, int port) { if(addr_is_ip6(addr, addrlen)) { struct sockaddr_in6* sa = (struct sockaddr_in6*)addr; sa->sin6_port = (in_port_t)htons((uint16_t)port); } else { struct sockaddr_in* sa = (struct sockaddr_in*)addr; sa->sin_port = (in_port_t)htons((uint16_t)port); } }
void addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, char* buf, size_t len) { int af = (int)((struct sockaddr_in*)addr)->sin_family; void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr; if(addr_is_ip6(addr, addrlen)) sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr; if(inet_ntop(af, sinaddr, buf, (socklen_t)len) == 0) { snprintf(buf, len, "(inet_ntop_error)"); } }
int addr_is_ip4mapped(struct sockaddr_storage* addr, socklen_t addrlen) { /* prefix for ipv4 into ipv6 mapping is ::ffff:x.x.x.x */ const uint8_t map_prefix[16] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 0,0,0,0}; uint8_t* s; if(!addr_is_ip6(addr, addrlen)) return 0; /* s is 16 octet ipv6 address string */ s = (uint8_t*)&((struct sockaddr_in6*)addr)->sin6_addr; return (memcmp(s, map_prefix, 12) == 0); }
/** setup perf test environment */ static void perfsetup(struct perfinfo* info) { size_t i; if(gettimeofday(&info->start, NULL) < 0) fatal_exit("gettimeofday: %s", strerror(errno)); sig_info = info; if( signal(SIGINT, perf_sigh) == SIG_ERR || #ifdef SIGQUIT signal(SIGQUIT, perf_sigh) == SIG_ERR || #endif #ifdef SIGHUP signal(SIGHUP, perf_sigh) == SIG_ERR || #endif #ifdef SIGBREAK signal(SIGBREAK, perf_sigh) == SIG_ERR || #endif signal(SIGTERM, perf_sigh) == SIG_ERR) fatal_exit("could not bind to signal"); info->io = (struct perfio*)calloc(sizeof(struct perfio), info->io_num); if(!info->io) fatal_exit("out of memory"); #ifndef S_SPLINT_S FD_ZERO(&info->rset); #endif info->since = info->start; for(i=0; i<info->io_num; i++) { info->io[i].id = i; info->io[i].info = info; info->io[i].fd = socket( addr_is_ip6(&info->dest, info->destlen)? AF_INET6:AF_INET, SOCK_DGRAM, 0); if(info->io[i].fd == -1) { #ifndef USE_WINSOCK fatal_exit("socket: %s", strerror(errno)); #else fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); #endif } if(info->io[i].fd > info->maxfd) info->maxfd = info->io[i].fd; #ifndef S_SPLINT_S FD_SET(FD_SET_T info->io[i].fd, &info->rset); info->io[i].timeout.tv_usec = ((START_IO_INTERVAL*i)%1000) *1000; info->io[i].timeout.tv_sec = (START_IO_INTERVAL*i)/1000; perf_tv_add(&info->io[i].timeout, &info->since); #endif } }
/** calculate the hash value for a host key */ static hashvalue_t hash_addr(struct sockaddr_storage* addr, socklen_t addrlen) { hashvalue_t h = 0xab; /* select the pieces to hash, some OS have changing data inside */ if(addr_is_ip6(addr, addrlen)) { struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr; h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h); h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h); h = hashlittle(&in6->sin6_addr, INET6_SIZE, h); } else { struct sockaddr_in* in = (struct sockaddr_in*)addr; h = hashlittle(&in->sin_family, sizeof(in->sin_family), h); h = hashlittle(&in->sin_port, sizeof(in->sin_port), h); h = hashlittle(&in->sin_addr, INET_SIZE, h); } return h; }
int delegpt_add_target_mlc(struct delegpt* dp, uint8_t* name, size_t namelen, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t bogus, uint8_t lame) { struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen); log_assert(dp->dp_type_mlc); if(!ns) { /* ignore it */ return 1; } if(!lame) { if(addr_is_ip6(addr, addrlen)) ns->got6 = 1; else ns->got4 = 1; if(ns->got4 && ns->got6) ns->resolved = 1; } return delegpt_add_addr_mlc(dp, addr, addrlen, bogus, lame); }
int delegpt_add_target(struct delegpt* dp, struct regional* region, uint8_t* name, size_t namelen, struct sockaddr_storage* addr, socklen_t addrlen, int bogus, int lame, int nodup) { struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen); if(!ns) { /* ignore it */ return 1; } if(!lame) { if(addr_is_ip6(addr, addrlen)) ns->got6 = 1; else ns->got4 = 1; if(ns->got4 && ns->got6) ns->resolved = 1; } return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, nodup); }
/** emit warnings for IP in hosts */ static void warn_hosts(const char* typ, struct config_stub* list) { struct sockaddr_storage a; socklen_t alen; struct config_stub* s; struct config_strlist* h; for(s=list; s; s=s->next) { for(h=s->hosts; h; h=h->next) { if(extstrtoaddr(h->str, &a, &alen)) { fprintf(stderr, "unbound-checkconf: warning:" " %s %s: \"%s\" is an IP%s address, " "and when looked up as a host name " "during use may not resolve.\n", s->name, typ, h->str, addr_is_ip6(&a, alen)?"6":"4"); } } } }
void addr_mask(struct sockaddr_storage* addr, socklen_t len, int net) { uint8_t mask[8] = {0x0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe}; int i, max; uint8_t* s; if(addr_is_ip6(addr, len)) { s = (uint8_t*)&((struct sockaddr_in6*)addr)->sin6_addr; max = 128; } else { s = (uint8_t*)&((struct sockaddr_in*)addr)->sin_addr; max = 32; } if(net >= max) return; for(i=net/8+1; i<max/8; i++) { s[i] = 0; } s[net/8] &= mask[net&0x7]; }
/** recv new waiting packets */ static void service_recv(int s, struct ringbuf* ring, sldns_buffer* pkt, fd_set* rorig, int* max, struct proxy** proxies, struct sockaddr_storage* srv_addr, socklen_t srv_len, struct timeval* now, struct timeval* delay, struct timeval* reuse) { int i; struct sockaddr_storage from; socklen_t from_len; ssize_t len; struct proxy* p; for(i=0; i<TRIES_PER_SELECT; i++) { from_len = (socklen_t)sizeof(from); len = recvfrom(s, (void*)sldns_buffer_begin(pkt), sldns_buffer_capacity(pkt), 0, (struct sockaddr*)&from, &from_len); if(len < 0) { #ifndef USE_WINSOCK if(errno == EAGAIN || errno == EINTR) return; fatal_exit("recvfrom: %s", strerror(errno)); #else if(WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS) return; fatal_exit("recvfrom: %s", wsa_strerror(WSAGetLastError())); #endif } sldns_buffer_set_limit(pkt, (size_t)len); /* find its proxy element */ p = find_create_proxy(&from, from_len, rorig, max, proxies, addr_is_ip6(srv_addr, srv_len), now, reuse); if(!p) fatal_exit("error: cannot find or create proxy"); p->lastuse = *now; ring_add(ring, pkt, now, delay, p); p->numwait++; log_addr(1, "recv from client", &p->addr, p->addr_len); } }
void server_stats_insquery(struct server_stats* stats, struct comm_point* c, uint16_t qtype, uint16_t qclass, struct edns_data* edns, struct comm_reply* repinfo) { uint16_t flags = ldns_buffer_read_u16_at(c->buffer, 2); if(qtype < STATS_QTYPE_NUM) stats->qtype[qtype]++; else stats->qtype_big++; if(qclass < STATS_QCLASS_NUM) stats->qclass[qclass]++; else stats->qclass_big++; stats->qopcode[ LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) ]++; if(c->type != comm_udp) stats->qtcp++; if(repinfo && addr_is_ip6(&repinfo->addr, repinfo->addrlen)) stats->qipv6++; if( (flags&BIT_QR) ) stats->qbit_QR++; if( (flags&BIT_AA) ) stats->qbit_AA++; if( (flags&BIT_TC) ) stats->qbit_TC++; if( (flags&BIT_RD) ) stats->qbit_RD++; if( (flags&BIT_RA) ) stats->qbit_RA++; if( (flags&BIT_Z) ) stats->qbit_Z++; if( (flags&BIT_AD) ) stats->qbit_AD++; if( (flags&BIT_CD) ) stats->qbit_CD++; if(edns->edns_present) { stats->qEDNS++; if( (edns->bits & EDNS_DO) ) stats->qEDNS_DO++; } }
/** contact the server with TCP connect */ static int contact_server(const char* svr, struct config_file* cfg, int statuscmd) { struct sockaddr_storage addr; socklen_t addrlen; int addrfamily = 0; int fd; /* use svr or the first config entry */ if(!svr) { if(cfg->control_ifs) svr = cfg->control_ifs->str; else svr = "127.0.0.1"; /* config 0 addr (everything), means ask localhost */ if(strcmp(svr, "0.0.0.0") == 0) svr = "127.0.0.1"; else if(strcmp(svr, "::0") == 0 || strcmp(svr, "0::0") == 0 || strcmp(svr, "0::") == 0 || strcmp(svr, "::") == 0) svr = "::1"; } if(strchr(svr, '@')) { if(!extstrtoaddr(svr, &addr, &addrlen)) fatal_exit("could not parse IP@port: %s", svr); #ifdef HAVE_SYS_UN_H } else if(svr[0] == '/') { struct sockaddr_un* usock = (struct sockaddr_un *) &addr; usock->sun_family = AF_LOCAL; #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN usock->sun_len = (socklen_t)sizeof(usock); #endif (void)strlcpy(usock->sun_path, svr, sizeof(usock->sun_path)); addrlen = (socklen_t)sizeof(struct sockaddr_un); addrfamily = AF_LOCAL; #endif } else { if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen)) fatal_exit("could not parse IP: %s", svr); } if(addrfamily == 0) addrfamily = addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET; fd = socket(addrfamily, SOCK_STREAM, 0); if(fd == -1) { #ifndef USE_WINSOCK fatal_exit("socket: %s", strerror(errno)); #else fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); #endif } if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { #ifndef USE_WINSOCK log_err_addr("connect", strerror(errno), &addr, addrlen); if(errno == ECONNREFUSED && statuscmd) { printf("unbound is stopped\n"); exit(3); } #else log_err_addr("connect", wsa_strerror(WSAGetLastError()), &addr, addrlen); if(WSAGetLastError() == WSAECONNREFUSED && statuscmd) { printf("unbound is stopped\n"); exit(3); } #endif exit(1); } return fd; }
/** accept new TCP connections, and set them up */ static void service_tcp_listen(int s, fd_set* rorig, int* max, struct tcp_proxy** proxies, struct sockaddr_storage* srv_addr, socklen_t srv_len, struct timeval* now, struct timeval* tcp_timeout) { int newfd; struct sockaddr_storage addr; struct tcp_proxy* p; socklen_t addr_len; newfd = accept(s, (struct sockaddr*)&addr, &addr_len); if(newfd == -1) { #ifndef USE_WINSOCK if(errno == EAGAIN || errno == EINTR) return; fatal_exit("accept: %s", strerror(errno)); #else if(WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAECONNRESET) return; fatal_exit("accept: %s", wsa_strerror(WSAGetLastError())); #endif } p = (struct tcp_proxy*)calloc(1, sizeof(*p)); if(!p) fatal_exit("out of memory"); memmove(&p->addr, &addr, addr_len); p->addr_len = addr_len; log_addr(1, "new tcp proxy", &p->addr, p->addr_len); p->client_s = newfd; p->server_s = socket(addr_is_ip6(srv_addr, srv_len)?AF_INET6:AF_INET, SOCK_STREAM, 0); if(p->server_s == -1) { #ifndef USE_WINSOCK fatal_exit("tcp socket: %s", strerror(errno)); #else fatal_exit("tcp socket: %s", wsa_strerror(WSAGetLastError())); #endif } fd_set_nonblock(p->client_s); fd_set_nonblock(p->server_s); if(connect(p->server_s, (struct sockaddr*)srv_addr, srv_len) == -1) { #ifndef USE_WINSOCK if(errno != EINPROGRESS) { log_err("tcp connect: %s", strerror(errno)); close(p->server_s); close(p->client_s); #else if(WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEINPROGRESS) { log_err("tcp connect: %s", wsa_strerror(WSAGetLastError())); closesocket(p->server_s); closesocket(p->client_s); #endif free(p); return; } } p->timeout = *now; dl_tv_add(&p->timeout, tcp_timeout); /* listen to client and server */ FD_SET(FD_SET_T p->client_s, rorig); FD_SET(FD_SET_T p->server_s, rorig); if(p->client_s+1 > *max) *max = p->client_s+1; if(p->server_s+1 > *max) *max = p->server_s+1; /* add into proxy list */ p->next = *proxies; *proxies = p; } /** relay TCP, read a part */ static int tcp_relay_read(int s, struct tcp_send_list** first, struct tcp_send_list** last, struct timeval* now, struct timeval* delay, sldns_buffer* pkt) { struct tcp_send_list* item; ssize_t r = recv(s, (void*)sldns_buffer_begin(pkt), sldns_buffer_capacity(pkt), 0); if(r == -1) { #ifndef USE_WINSOCK if(errno == EINTR || errno == EAGAIN) return 1; log_err("tcp read: %s", strerror(errno)); #else if(WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK) return 1; log_err("tcp read: %s", wsa_strerror(WSAGetLastError())); #endif return 0; } else if(r == 0) { /* connection closed */ return 0; } item = (struct tcp_send_list*)malloc(sizeof(*item)); if(!item) { log_err("out of memory"); return 0; } verbose(1, "read item len %d", (int)r); item->len = (size_t)r; item->item = memdup(sldns_buffer_begin(pkt), item->len); if(!item->item) { free(item); log_err("out of memory"); return 0; } item->done = 0; item->wait = *now; dl_tv_add(&item->wait, delay); item->next = NULL; /* link in */ if(*first) { (*last)->next = item; } else { *first = item; } *last = item; return 1; } /** relay TCP, write a part */ static int tcp_relay_write(int s, struct tcp_send_list** first, struct tcp_send_list** last, struct timeval* now) { ssize_t r; struct tcp_send_list* p; while(*first) { p = *first; /* is the item ready? */ if(!dl_tv_smaller(&p->wait, now)) return 1; /* write it */ r = send(s, (void*)(p->item + p->done), p->len - p->done, 0); if(r == -1) { #ifndef USE_WINSOCK if(errno == EAGAIN || errno == EINTR) return 1; log_err("tcp write: %s", strerror(errno)); #else if(WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS) return 1; log_err("tcp write: %s", wsa_strerror(WSAGetLastError())); #endif return 0; } else if(r == 0) { /* closed */ return 0; } /* account it */ p->done += (size_t)r; verbose(1, "write item %d of %d", (int)p->done, (int)p->len); if(p->done >= p->len) { free(p->item); *first = p->next; if(!*first) *last = NULL; free(p); } else { /* partial write */ return 1; } } return 1; }