int netblockstrtoaddr(const char* str, int port, struct sockaddr_storage* addr, socklen_t* addrlen, int* net) { char buf[64]; char* s; *net = (str_is_ip6(str)?128:32); if((s=strchr(str, '/'))) { if(atoi(s+1) > *net) { log_err("netblock too large: %s", str); return 0; } *net = atoi(s+1); if(*net == 0 && strcmp(s+1, "0") != 0) { log_err("cannot parse netblock: '%s'", str); return 0; } strlcpy(buf, str, sizeof(buf)); s = strchr(buf, '/'); if(s) *s = 0; s = buf; } if(!ipstrtoaddr(s?s:str, port, addr, addrlen)) { log_err("cannot parse ip address: '%s'", str); return 0; } if(s) { addr_mask(addr, *addrlen, *net); } return 1; }
int netblockstrtoaddr(const char* str, int port, struct sockaddr_storage* addr, socklen_t* addrlen, int* net) { char* s = NULL; *net = (str_is_ip6(str)?128:32); if((s=strchr(str, '/'))) { if(atoi(s+1) > *net) { log_err("netblock too large: %s", str); return 0; } *net = atoi(s+1); if(*net == 0 && strcmp(s+1, "0") != 0) { log_err("cannot parse netblock: '%s'", str); return 0; } if(!(s = strdup(str))) { log_err("out of memory"); return 0; } *strchr(s, '/') = '\0'; } if(!ipstrtoaddr(s?s:str, port, addr, addrlen)) { free(s); log_err("cannot parse ip address: '%s'", str); return 0; } if(s) { free(s); addr_mask(addr, *addrlen, *net); } return 1; }
int ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr, socklen_t* addrlen) { uint16_t p; if(!ip) return 0; p = (uint16_t) port; if(str_is_ip6(ip)) { char buf[MAX_ADDR_STRLEN]; char* s; struct sockaddr_in6* sa = (struct sockaddr_in6*)addr; *addrlen = (socklen_t)sizeof(struct sockaddr_in6); memset(sa, 0, *addrlen); sa->sin6_family = AF_INET6; sa->sin6_port = (in_port_t)htons(p); if((s=strchr(ip, '%'))) { /* ip6%interface, rfc 4007 */ if(s-ip >= MAX_ADDR_STRLEN) return 0; (void)strlcpy(buf, ip, sizeof(buf)); buf[s-ip]=0; sa->sin6_scope_id = (uint32_t)atoi(s+1); ip = buf; } if(inet_pton((int)sa->sin6_family, ip, &sa->sin6_addr) <= 0) { return 0; } } else { /* ip4 */ struct sockaddr_in* sa = (struct sockaddr_in*)addr; *addrlen = (socklen_t)sizeof(struct sockaddr_in); memset(sa, 0, *addrlen); sa->sin_family = AF_INET; sa->sin_port = (in_port_t)htons(p); if(inet_pton((int)sa->sin_family, ip, &sa->sin_addr) <= 0) { return 0; } } return 1; }
int ub_ctx_hosts(struct ub_ctx* ctx, char* fname) { FILE* in; char buf[1024], ldata[1024]; char* parse, *addr, *name, *ins; lock_basic_lock(&ctx->cfglock); if(ctx->finalized) { lock_basic_unlock(&ctx->cfglock); errno=EINVAL; return UB_AFTERFINAL; } lock_basic_unlock(&ctx->cfglock); if(fname == NULL) { #if defined(UB_ON_WINDOWS) && defined(HAVE_WINDOWS_H) /* * If this is Windows NT/XP/2K it's in * %WINDIR%\system32\drivers\etc\hosts. * If this is Windows 95/98/Me it's in %WINDIR%\hosts. */ name = getenv("WINDIR"); if (name != NULL) { int retval=0; snprintf(buf, sizeof(buf), "%s%s", name, "\\system32\\drivers\\etc\\hosts"); if((retval=ub_ctx_hosts(ctx, buf)) !=0 ) { snprintf(buf, sizeof(buf), "%s%s", name, "\\hosts"); retval=ub_ctx_hosts(ctx, buf); } free(name); return retval; } return UB_READFILE; #else fname = "/etc/hosts"; #endif /* WIN32 */ } in = fopen(fname, "r"); if(!in) { /* error in errno! perror(fname) */ return UB_READFILE; } while(fgets(buf, (int)sizeof(buf), in)) { buf[sizeof(buf)-1] = 0; parse=buf; while(*parse == ' ' || *parse == '\t') parse++; if(*parse == '#') continue; /* skip comment */ /* format: <addr> spaces <name> spaces <name> ... */ addr = parse; /* skip addr */ while(isxdigit(*parse) || *parse == '.' || *parse == ':') parse++; if(*parse == '\n' || *parse == 0) continue; if(*parse == '%') continue; /* ignore macOSX fe80::1%lo0 localhost */ if(*parse != ' ' && *parse != '\t') { /* must have whitespace after address */ fclose(in); errno=EINVAL; return UB_SYNTAX; } *parse++ = 0; /* end delimiter for addr ... */ /* go to names and add them */ while(*parse) { while(*parse == ' ' || *parse == '\t' || *parse=='\n') parse++; if(*parse == 0 || *parse == '#') break; /* skip name, allows (too) many printable characters */ name = parse; while('!' <= *parse && *parse <= '~') parse++; if(*parse) *parse++ = 0; /* end delimiter for name */ snprintf(ldata, sizeof(ldata), "%s %s %s", name, str_is_ip6(addr)?"AAAA":"A", addr); ins = strdup(ldata); if(!ins) { /* out of memory */ fclose(in); errno=ENOMEM; return UB_NOMEM; } lock_basic_lock(&ctx->cfglock); if(!cfg_strlist_insert(&ctx->env->cfg->local_data, ins)) { lock_basic_unlock(&ctx->cfglock); fclose(in); free(ins); errno=ENOMEM; return UB_NOMEM; } lock_basic_unlock(&ctx->cfglock); } } fclose(in); return UB_NOERROR; }
struct listen_port* listening_ports_open(struct config_file* cfg) { struct listen_port* list = NULL; struct addrinfo hints; int i, do_ip4, do_ip6; int do_tcp, do_auto; char portbuf[32]; snprintf(portbuf, sizeof(portbuf), "%d", cfg->port); do_ip4 = cfg->do_ip4; do_ip6 = cfg->do_ip6; do_tcp = cfg->do_tcp; do_auto = cfg->if_automatic && cfg->do_udp; if(cfg->incoming_num_tcp == 0) do_tcp = 0; /* getaddrinfo */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; /* no name lookups on our listening ports */ if(cfg->num_ifs > 0) hints.ai_flags |= AI_NUMERICHOST; hints.ai_family = AF_UNSPEC; #ifndef INET6 do_ip6 = 0; #endif if(!do_ip4 && !do_ip6) { return NULL; } /* create ip4 and ip6 ports so that return addresses are nice. */ if(do_auto || cfg->num_ifs == 0) { if(do_ip6) { hints.ai_family = AF_INET6; if(!ports_create_if(do_auto?"::0":"::1", do_auto, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port)) { listening_ports_free(list); return NULL; } } if(do_ip4) { hints.ai_family = AF_INET; if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1", do_auto, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port)) { listening_ports_free(list); return NULL; } } } else for(i = 0; i<cfg->num_ifs; i++) { if(str_is_ip6(cfg->ifs[i])) { if(!do_ip6) continue; hints.ai_family = AF_INET6; if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port)) { listening_ports_free(list); return NULL; } } else { if(!do_ip4) continue; hints.ai_family = AF_INET; if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port)) { listening_ports_free(list); return NULL; } } } return list; }
/** delayer main service routine */ static void service(const char* bind_str, int bindport, const char* serv_str, size_t memsize, int delay_msec) { struct sockaddr_storage bind_addr, srv_addr; socklen_t bind_len, srv_len; struct ringbuf* ring = ring_create(memsize); struct timeval delay, reuse; sldns_buffer* pkt; int i, s, listen_s; #ifndef S_SPLINT_S delay.tv_sec = delay_msec / 1000; delay.tv_usec = (delay_msec % 1000)*1000; #endif reuse = delay; /* reuse is max(4*delay, 1 second) */ dl_tv_add(&reuse, &delay); dl_tv_add(&reuse, &delay); dl_tv_add(&reuse, &delay); if(reuse.tv_sec == 0) reuse.tv_sec = 1; if(!extstrtoaddr(serv_str, &srv_addr, &srv_len)) { printf("cannot parse forward address: %s\n", serv_str); exit(1); } pkt = sldns_buffer_new(65535); if(!pkt) fatal_exit("out of memory"); if( signal(SIGINT, delayer_sigh) == SIG_ERR || #ifdef SIGHUP signal(SIGHUP, delayer_sigh) == SIG_ERR || #endif #ifdef SIGQUIT signal(SIGQUIT, delayer_sigh) == SIG_ERR || #endif #ifdef SIGBREAK signal(SIGBREAK, delayer_sigh) == SIG_ERR || #endif #ifdef SIGALRM signal(SIGALRM, delayer_sigh) == SIG_ERR || #endif signal(SIGTERM, delayer_sigh) == SIG_ERR) fatal_exit("could not bind to signal"); /* bind UDP port */ if((s = socket(str_is_ip6(bind_str)?AF_INET6:AF_INET, SOCK_DGRAM, 0)) == -1) { #ifndef USE_WINSOCK fatal_exit("socket: %s", strerror(errno)); #else fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); #endif } i=0; if(bindport == 0) { bindport = 1024 + random()%64000; i = 100; } while(1) { if(!ipstrtoaddr(bind_str, bindport, &bind_addr, &bind_len)) { printf("cannot parse listen address: %s\n", bind_str); exit(1); } if(bind(s, (struct sockaddr*)&bind_addr, bind_len) == -1) { #ifndef USE_WINSOCK log_err("bind: %s", strerror(errno)); #else log_err("bind: %s", wsa_strerror(WSAGetLastError())); #endif if(i--==0) fatal_exit("cannot bind any port"); bindport = 1024 + random()%64000; } else break; } fd_set_nonblock(s); /* and TCP port */ if((listen_s = socket(str_is_ip6(bind_str)?AF_INET6:AF_INET, SOCK_STREAM, 0)) == -1) { #ifndef USE_WINSOCK fatal_exit("tcp socket: %s", strerror(errno)); #else fatal_exit("tcp socket: %s", wsa_strerror(WSAGetLastError())); #endif } #ifdef SO_REUSEADDR if(1) { int on = 1; if(setsockopt(listen_s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, (socklen_t)sizeof(on)) < 0) #ifndef USE_WINSOCK fatal_exit("setsockopt(.. SO_REUSEADDR ..) failed: %s", strerror(errno)); #else fatal_exit("setsockopt(.. SO_REUSEADDR ..) failed: %s", wsa_strerror(WSAGetLastError())); #endif } #endif if(bind(listen_s, (struct sockaddr*)&bind_addr, bind_len) == -1) { #ifndef USE_WINSOCK fatal_exit("tcp bind: %s", strerror(errno)); #else fatal_exit("tcp bind: %s", wsa_strerror(WSAGetLastError())); #endif } if(listen(listen_s, 5) == -1) { #ifndef USE_WINSOCK fatal_exit("tcp listen: %s", strerror(errno)); #else fatal_exit("tcp listen: %s", wsa_strerror(WSAGetLastError())); #endif } fd_set_nonblock(listen_s); printf("listening on port: %d\n", bindport); /* process loop */ do_quit = 0; service_loop(s, listen_s, ring, &delay, &reuse, &srv_addr, srv_len, pkt); /* cleanup */ verbose(1, "cleanup"); #ifndef USE_WINSOCK close(s); close(listen_s); #else closesocket(s); closesocket(listen_s); #endif sldns_buffer_free(pkt); ring_delete(ring); }