#include "config.h" #include "config_file.h" #include "server.h" #include "zones.h" #include "process.h" /* global variables set by signals */ static int work = 1; static int hupped = 0; /* signal handler for server */ void server_handle_signal(int sig) { switch(sig) { case SIGHUP: hupped = 1; work = 0; break; case SIGTERM: case SIGINT: case SIGQUIT: work = 0; break; default: printf("unhandled signal %d\n", sig); break; } } int server_start(const char* config) { struct server_info_t* sinfo = (struct server_info_t*)calloc(1, sizeof(struct server_info_t)); if(!sinfo) { printf("out of memory\n"); return 0; } FD_ZERO(&sinfo->rset); FD_ZERO(&sinfo->wset); FD_ZERO(&sinfo->eset); work = 1; hupped = 0; signal(SIGHUP, server_handle_signal); signal(SIGQUIT, server_handle_signal); signal(SIGTERM, server_handle_signal); signal(SIGINT, server_handle_signal); signal(SIGPIPE, SIG_IGN); sinfo->zones = zones_create(); sinfo->cfg = config_file_create(config); config_file_read(sinfo->cfg, config, sinfo->zones); zones_read(sinfo->zones); server_bind(sinfo, sinfo->cfg->port); if(!sinfo->sock_list) { printf("Could not start server.\n"); server_free(sinfo); return 0; } printf("service for %d zones (%s) on port %d\n", (int)sinfo->zones->ztree->count, config, sinfo->cfg->port); printf("Masterdont started pid %d\n", (int)getpid()); while(work) { server_handle_net(sinfo); } server_free(sinfo); return hupped; } void server_handle_net(struct server_info_t *sinfo) { struct socket_service* p, **prevp; int delete_me; fd_set rset, wset, eset; rset = sinfo->rset; wset = sinfo->wset; eset = sinfo->eset; if(select(sinfo->maxfd+1, &rset, &wset, &eset, NULL) == -1) { if(errno==EINTR) return; printf("select: %s\n", strerror(errno)); return; } p = sinfo->sock_list; prevp = &sinfo->sock_list; while(p) { delete_me = 0; if(FD_ISSET(p->s, &rset)) handle_read(sinfo, p, &delete_me, sinfo->zones); if(!delete_me && FD_ISSET(p->s, &wset)) handle_write(sinfo, p, &delete_me); /* eset ignored */ if(delete_me) { printf("delete me\n"); FD_CLR(p->s, &sinfo->rset); FD_CLR(p->s, &sinfo->wset); FD_CLR(p->s, &sinfo->eset); if(p->is_tcp) sinfo->num_tcp --; /* cannot lower maxfd */ *prevp = p->next; /* snip out of list */ server_service_free(p); p = *prevp; /* go to next */ } else { prevp = &p->next; p = p->next; } } } void server_free(struct server_info_t* sinfo) { struct socket_service* p = sinfo->sock_list, *np=0; config_file_delete(sinfo->cfg); zones_free(sinfo->zones); while(p) { np = p->next; server_service_free(p); p = np; } free(sinfo); } void server_bind(struct server_info_t* sinfo, int port) { struct addrinfo hints; struct addrinfo *res = 0; struct addrinfo *p = 0; int err=0; char service[16]; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE #ifdef AI_NUMERICSERV | AI_NUMERICSERV #endif ; sprintf(service, "%d", port); if((err=getaddrinfo(NULL, service, &hints, &res)) != 0) { printf("getaddrinfo: %s\n", gai_strerror(err)); return; } p = res; while(p) { /* only TCP and UDP */ if(p->ai_protocol == IPPROTO_TCP || p->ai_protocol == IPPROTO_UDP) { struct socket_service* sv; sv = server_service_create(p); if(!sv) { err=errno; } else { sv->next = sinfo->sock_list; sinfo->sock_list = sv; FD_SET(sv->s, &sinfo->rset); if(sv->s > sinfo->maxfd) sinfo->maxfd = sv->s; } } p = p->ai_next; } if(!sinfo->sock_list) { printf("Fatal: No interfaces could be initialized: %s\n", strerror(err)); } if(res) freeaddrinfo(res); } struct socket_service* server_service_create(struct addrinfo *ai) { struct socket_service* svr; int s; const int on = 1; s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if(s == -1) { return 0; } if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { goto error; } #ifdef IPV6_V6ONLY if(ai->ai_protocol == AF_INET6 && setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { goto error; } #endif if(bind(s, ai->ai_addr, ai->ai_addrlen) == -1) { goto error; } if(ai->ai_socktype == SOCK_STREAM && listen(s, TCP_LISTEN_BACKLOG) == -1) { goto error; } /* create service struct */ svr = (struct socket_service*)malloc(sizeof(struct socket_service)); if(!svr) { errno = ENOMEM; goto error; } memset(svr, 0, sizeof(struct socket_service)); svr->tcp_state = svr_tcp_listen; svr->s = s; if(ai->ai_socktype == SOCK_STREAM) svr->is_tcp = 1; svr->buffer = ldns_buffer_new(SERVER_BUFFER_SIZE); if(!svr->buffer) { errno = ENOMEM; free(svr); goto error; } return svr; error: close(s); return 0; } void server_service_free(struct socket_service* svr) { close(svr->s); if(svr->reply) ldns_rr_list_deep_free(svr->reply); if(svr->buffer) ldns_buffer_free(svr->buffer); free(svr); } void handle_listen(struct server_info_t *sinfo, struct socket_service* listen_v) { struct socket_service* sh; int newfd; if(sinfo->num_tcp >= MAX_TCP) { printf("Error: incoming tcp query, but MAX_TCP reached (%d)\n", MAX_TCP); return; } if((newfd=accept(listen_v->s, NULL, NULL)) == -1) { printf("Error tcp accept: %s", strerror(errno)); return; } if(fcntl(newfd, F_SETFL, O_NONBLOCK) == -1) { printf("Error fcntl: %s\n", strerror(errno)); close(newfd); return; } sh = (struct socket_service*)malloc(sizeof(struct socket_service)); if(!sh) { printf("out of memory\n"); close(newfd); return; } memset(sh, 0, sizeof(struct socket_service)); sh->tcp_state = svr_tcp_read; sh->s = newfd; sh->buffer = ldns_buffer_new(SERVER_BUFFER_SIZE); sh->is_tcp = 1; if(!sh->buffer) { printf("out of memory\n"); close(newfd); free(sh); return; } ldns_buffer_clear(sh->buffer); if(sh->s > sinfo->maxfd) sinfo->maxfd = sh->s; FD_SET(sh->s, &sinfo->rset); sinfo->num_tcp++; /* put after the current service */ sh->next = listen_v->next; listen_v->next = sh; } void handle_read(struct server_info_t *sinfo, struct socket_service* sv, int *del, struct zones_t* zones) { printf("handle_read %s %s\n", sv->is_tcp?"tcp":"udp", sv->tcp_state==svr_tcp_listen?"listen":""); /* get the data */ if(sv->is_tcp) { if(sv->tcp_state == svr_tcp_listen) { handle_listen(sinfo, sv); return; } if(sv->tcp_state != svr_tcp_read) { FD_CLR(sv->s, &sinfo->rset); return; } /* read some more from channel */ if(!read_tcp_query(sv, del)) return; /* continue later */ } else { /* udp recv */ if(!read_udp_query(sv)) return; } /* handle request */ if(!process_query(sv, zones)) return; if(!sv->is_tcp) { /* sendto */ send_udp_answer(sv); } else { /* change socket tcp to writing mode */ sv->tcp_state = svr_tcp_write; sv->bytes_done = 0; FD_CLR(sv->s, &sinfo->rset); FD_SET(sv->s, &sinfo->wset); } } void handle_write(struct server_info_t *sinfo, struct socket_service* sv, int *del) { if(sv->is_tcp) { if(sv->tcp_state != svr_tcp_write) { FD_CLR(sv->s, &sinfo->wset); return; } write_tcp_answer(sv, del); } else { FD_CLR(sv->s, &sinfo->wset); } } int read_tcp_query(struct socket_service *sv, int* del) { ssize_t ret; uint16_t len; printf("read tcp query %d\n", (int)sv->bytes_done); if(sv->bytes_done < sizeof(len)) { ret = read(sv->s, ldns_buffer_current(sv->buffer), sizeof(len)-ldns_buffer_position(sv->buffer)); printf("read tcp query got %d\n", (int)ret); if(ret == 0) { *del = 1; return 0; } if(ret == -1) { *del = 1; printf("read: %s\n", strerror(errno)); return 0; } ldns_buffer_skip(sv->buffer, ret); if(ldns_buffer_position(sv->buffer) < sizeof(len)) return 0; /* more later */ len = ldns_buffer_read_u16_at(sv->buffer, 0); printf("so len is %d\n", len); ldns_buffer_clear(sv->buffer); ldns_buffer_set_limit(sv->buffer, len); sv->bytes_done = sizeof(len); } ret = read(sv->s, ldns_buffer_current(sv->buffer), ldns_buffer_remaining(sv->buffer)); printf("read tcp query content got %d\n", (int)ret); if(ret == 0) { *del = 1; return 0; } if(ret == -1) { *del = 1; printf("read: %s\n", strerror(errno)); return 0; } ldns_buffer_skip(sv->buffer, ret); if(ldns_buffer_remaining(sv->buffer) > 0) { return 0; } ldns_buffer_flip(sv->buffer); return 1; } int read_udp_query(struct socket_service *sv) { ssize_t sz; ldns_buffer_clear(sv->buffer); printf("udp read\n"); sv->peerlen = sizeof(sv->peer); sz = recvfrom(sv->s, ldns_buffer_begin(sv->buffer), ldns_buffer_capacity(sv->buffer), 0, (struct sockaddr*)&sv->peer, &sv->peerlen); printf("udp read %d\n", (int)sz); if(sz == -1) { printf("recvfrom: %s\n", strerror(errno)); return 0; } if(sz == 0) return 0; ldns_buffer_skip(sv->buffer, sz); ldns_buffer_flip(sv->buffer); return 1; } static void set_error(struct socket_service* sv, ldns_pkt_rcode rcode) { LDNS_QR_SET(ldns_buffer_begin(sv->buffer)); LDNS_TC_CLR(ldns_buffer_begin(sv->buffer)); LDNS_RCODE_SET(ldns_buffer_begin(sv->buffer), rcode); } int process_query(struct socket_service* sv, struct zones_t* zones) { ldns_pkt* q = 0, *r = 0; ldns_status status; ldns_pkt_rcode rcode = LDNS_RCODE_NOERROR; /* if QR bit is set drop packet */ if(ldns_buffer_limit(sv->buffer) < LDNS_HEADER_SIZE || LDNS_QR_WIRE(ldns_buffer_begin(sv->buffer))) return 0; status = ldns_wire2pkt(&q, ldns_buffer_begin(sv->buffer), ldns_buffer_limit(sv->buffer)); if(status != LDNS_STATUS_OK || !q) { /* form error */ printf("bad packet: %s\n", ldns_get_errorstr_by_id(status)); if(q) ldns_pkt_free(q); set_error(sv, LDNS_RCODE_FORMERR); return 1; } if(1) { printf("Got query:\n"); ldns_pkt_print(stdout, q); } r = ldns_pkt_new(); ldns_pkt_set_id(r, ldns_pkt_id(q)); ldns_pkt_set_qr(r, true); ldns_pkt_set_aa(r, true); ldns_pkt_set_rd(r, ldns_pkt_rd(q)); rcode = process_pkts(sv, q, r, zones); if(rcode != LDNS_RCODE_NOERROR) { printf("answer error %d\n", rcode); ldns_pkt_free(q); ldns_pkt_free(r); set_error(sv, rcode); return 1; } if(sv->reply) fill_r_up_pkt(sv, r); if(1) { printf("Got answer %s:\n", sv->reply?"(head, more to follow)":"pkt"); ldns_pkt_print(stdout, r); } ldns_buffer_clear(sv->buffer); status = ldns_pkt2buffer_wire(sv->buffer, r); if(status != LDNS_STATUS_OK) { printf("could not wireformat pkt: %s\n", ldns_get_errorstr_by_id(status)); ldns_pkt_free(q); ldns_pkt_free(r); return 0; } ldns_buffer_flip(sv->buffer); ldns_pkt_free(q); ldns_pkt_free(r); return 1; } void send_udp_answer(struct socket_service* sv) { ssize_t sz = sendto(sv->s, ldns_buffer_begin(sv->buffer), ldns_buffer_limit(sv->buffer), 0, (struct sockaddr*)&sv->peer, sv->peerlen); if(sz == -1) { printf("sendto: %s\n", strerror(errno)); return; } if(sz < (int)ldns_buffer_limit(sv->buffer)) { printf("sendto: message not completely sent.\n"); return; } } void write_tcp_answer(struct socket_service* sv, int* del) { uint16_t len = htons(ldns_buffer_limit(sv->buffer)); ssize_t ret; if(sv->bytes_done < sizeof(len)) { ret = write(sv->s, ((uint8_t*)&len) + ldns_buffer_position(sv->buffer), sizeof(len)-ldns_buffer_position(sv->buffer)); if(ret == 0) { *del = 1; return; } if(ret == -1) { printf("write: %s\n", strerror(errno)); *del = 1; return; } ldns_buffer_skip(sv->buffer, ret); if(ldns_buffer_position(sv->buffer) < sizeof(len)) return; /* later */ sv->bytes_done = sizeof(len); ldns_buffer_set_position(sv->buffer, 0); } ret = write(sv->s, ldns_buffer_current(sv->buffer), ldns_buffer_remaining(sv->buffer)); if(ret == 0) { *del = 1; return; } if(ret == -1) { printf("write: %s\n", strerror(errno)); *del = 1; return; } ldns_buffer_skip(sv->buffer, ret); if(ldns_buffer_remaining(sv->buffer) > 0) return; /* later */ /* finished TCP packet, more? */ if(sv->reply) { fill_r_up(sv); /* write that next time */ return; } *del = 1; return; } void fill_r_up_pkt(struct socket_service* sv, ldns_pkt* in_here) { size_t max = 512; /* max bytes in packet */ size_t now = 0; size_t i; size_t added = 0; if(sv->is_tcp) max = TCP_PKT_SIZE; else { /* EDNS 0 */ } if(!sv->reply) return; now = LDNS_HEADER_SIZE; for(i=0; ireply); i++) { now += ldns_rr_uncompressed_size(ldns_rr_list_rr( sv->reply, i)); if(now >= max) { /* TCP: always add one, maybe more */ /* UDP: never cross limit. */ if(!sv->is_tcp || added != 0) break; } ldns_pkt_push_rr(in_here, LDNS_SECTION_ANSWER, ldns_rr_list_rr(sv->reply, i)); ldns_rr_list_set_rr(sv->reply, NULL, i); added++; } /* fixup the rrlist */ memmove(sv->reply->_rrs, sv->reply->_rrs+added, sizeof(ldns_rr*)*(sv->reply->_rr_count-added)); sv->reply->_rr_count -= added; if(ldns_rr_list_rr_count(sv->reply) == 0) { ldns_rr_list_free(sv->reply); sv->reply = 0; } if(!sv->is_tcp && sv->reply) { ldns_pkt_set_tc(in_here, true); ldns_rr_list_deep_free(sv->reply); sv->reply = 0; } } void fill_r_up(struct socket_service* sv) { ldns_pkt* p = ldns_pkt_new(); ldns_status status; if(!p) { printf("out of memory\n"); LDNS_RCODE_SET(ldns_buffer_begin(sv->buffer), LDNS_RCODE_SERVFAIL); ldns_buffer_set_limit(sv->buffer, LDNS_HEADER_SIZE); ldns_rr_list_deep_free(sv->reply); sv->reply = 0; return; } ldns_buffer_clear(sv->buffer); ldns_pkt_set_id(p, LDNS_ID_WIRE(ldns_buffer_begin(sv->buffer))); ldns_pkt_set_qr(p, LDNS_QR_WIRE(ldns_buffer_begin(sv->buffer))); ldns_pkt_set_opcode(p, LDNS_OPCODE_WIRE(ldns_buffer_begin(sv->buffer))); ldns_pkt_set_aa(p, LDNS_AA_WIRE(ldns_buffer_begin(sv->buffer))); ldns_pkt_set_rd(p, LDNS_RD_WIRE(ldns_buffer_begin(sv->buffer))); ldns_pkt_set_ra(p, LDNS_RA_WIRE(ldns_buffer_begin(sv->buffer))); ldns_pkt_set_rcode(p, LDNS_RCODE_WIRE(ldns_buffer_begin(sv->buffer))); fill_r_up_pkt(sv, p); status = ldns_pkt2buffer_wire(sv->buffer, p); if(status != LDNS_STATUS_OK) { printf("could not wireformat continuation packet\n"); LDNS_RCODE_SET(ldns_buffer_begin(sv->buffer), LDNS_RCODE_SERVFAIL); ldns_buffer_set_limit(sv->buffer, LDNS_HEADER_SIZE); if(sv->reply) ldns_rr_list_deep_free(sv->reply); sv->reply = 0; } ldns_buffer_flip(sv->buffer); }