Logo Search packages:      
Sourcecode: maradns version File versions

zoneserver.c

/* Placed in the public domain 2001 by Sam Trenholme */

/* This is the core DNS server */

/* Language specific labels */
#include "zoneserver_en.h"

/* Include stuff needed to be a TCP server */

#include "../libs/MaraHash.h"
#include "../MaraDns.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#ifdef __FreeBSD__
#include <sys/time.h>
#endif
#include <sys/types.h>
#ifndef DARWIN
#include <sys/resource.h>
#endif
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
/* Function prototypes */
#include "../dns/functions_dns.h"
#include "../parse/functions_parse.h"
extern int decomp_init();
extern int show_timestamp();

/* Our one global variable: The number of children processes running */
int num_children = 0;

/* Signal handler for handling the exit of a child */
void handle_childs() {
    if(waitpid(0,NULL,WNOHANG) > 0)
        num_children--;
    }

/* Print out log messages
   Input: Null-terminated string with the message to log
   Output: JS_SUCCESS on success, JS_ERROR on error
*/

int mlog(char *logmessage) {

    if(logmessage == 0)
        return JS_ERROR;
    printf("%s%s%s",L_LOG,logmessage,LF); /* "Log: ", logmessage, LF */

    return JS_SUCCESS;
    }

/* Handler to handle fatal errors.
   Input: Pointer to null-terminalted string with fatal error
   Output: MaraDNS exits
*/

void harderror(char *why) {
    printf("%s%s%s",L_FATAL,why,LF); /* "Fatal error: ", why, "\n" */
    exit(3);
    }

/* Bind to TCP port 53.
   Input: pointer to socket to bind on, js_string with the dotted-decimal
          ip address to bind to
   Output: JS_ERROR on error, JS_SUCCESS on success
*/

int tcpbind(int *sock, js_string *ip_ddip) {
    int len_inet; /* Length */
    struct sockaddr_in dns_tcp;
    char ip[64];

    /* Sanity checks */
    if(sock == 0)
        return JS_ERROR;
    if(js_has_sanity(ip_ddip) == JS_ERROR)
        return JS_ERROR;
    if(ip_ddip->unit_size != 1)
        return JS_ERROR;

    /* Create string to hold IP */
    if(js_js2str(ip_ddip,ip,60) == JS_ERROR)
        return JS_ERROR;

    /* Create a raw TCP socket */
    if((*sock = socket(PF_INET,SOCK_STREAM,0)) == -1) {
        return JS_ERROR;
      }

    /* Choose an IP and port to bind to */
    memset(&dns_tcp,0,sizeof(dns_tcp));
    dns_tcp.sin_family = AF_INET;
    dns_tcp.sin_port = htons(53);
    if((dns_tcp.sin_addr.s_addr = inet_addr(ip)) == INADDR_NONE)
        return JS_ERROR;

    len_inet = sizeof(dns_tcp);

    /* Bind to the socket.  Note that we usually have to be root to do this */
    if(bind(*sock,(struct sockaddr *)&dns_tcp,len_inet) == -1)
        return JS_ERROR;

    /* Set up an active listen on the socket */
    if(listen(*sock,250) == -1)
        return JS_ERROR;

    /* We are now on TCP port 53.  Leave */
    return JS_SUCCESS;
    }

/* Start a TCP connection on socket sock.
   Input: pointer to socket, pointer to ACL list, max elements allowed in 
          acl list
   Output: Integer value of TCP connection on success, JS_ERROR on error
           (or permission denied)
*/

int gettcp(int *sock, ipv4pair *acl, int max) {
    int ret, counter;
    struct sockaddr_in adr_clnt;
    int len_inet;
    uint32 ip;

    len_inet = sizeof(adr_clnt);
    ret = accept(*sock, (struct sockaddr *)&adr_clnt,&len_inet);
    if(ret == -1)
        return JS_ERROR;

    /* Make sure the client is authorized to connect to the zone server */
    ip = htonl(adr_clnt.sin_addr.s_addr);
    counter = 0;
    while(counter < max && (acl[counter]).ip != 0xffffffff) {
        if((ip & (acl[counter]).mask) == 
           ((acl[counter]).ip & (acl[counter]).mask))
            return ret;
        counter++;
      }
   
    /* OK, they were not on the ACL list.  Clise the connection and
         return an error */
    close(ret);
    return JS_ERROR;
    }

/* Given a socket TCP connection, serve a zone over the connection in
   question.
   Input: socket that TCP connection is on
   Ouput: JS_SUCCESS or JS_ERROR
*/

int serve_zone(int connect) {
    int length, rr_type;
    q_header header, soa_reply_header;
    js_file desc;
    unsigned char get[2];
    js_string *name, *data, *zone, *query, *response, *soa, *filename,
              *binzone;
    int is_soa = 1, soa_q = 0;
    uint32 ttl;

    /* Create the js_string objects */
    if((name = js_create((MAX_RECORD_LENGTH * 2) + 3,1)) == 0)
        return JS_ERROR;
    if(js_set_encode(name,MARA_LOCALE) == JS_ERROR)
        return JS_ERROR;
    if((data = js_create((MAX_RECORD_LENGTH * 2) + 3,1)) == 0) {
        js_destroy(name);
        return JS_ERROR;
      }
    if(js_set_encode(data,MARA_LOCALE) == JS_ERROR) 
        goto clean_nd;
    
    /* Get the Zone query length header */
    if(connect == 1) {
        if(read(0,get,2) != 2)
            goto clean_nd;
      }
    else {
        if(recv(connect,get,2,MSG_WAITALL) != 2) 
            goto clean_nd;
        }

    /* Determine how long the actual query will be */
    length = (get[0] & 0xff) << 8 | (get[1] & 0xff);
    if(length > 380) 
        goto clean_nd;
    
    /* Get the actual query */
    if(connect == 1) {
        if(read(0,name->string,length) != length)
          goto clean_nd;
        }
    else {
        if(recv(connect,name->string,length,MSG_WAITALL) != length) 
            goto clean_nd;
        }

    name->unit_count = length;
    /* Decompress (Should not be needed, but still) */
    if(decompress_data(name,data) == JS_ERROR) 
        goto clean_nd;

    /* Convert the header */
    if(read_hdr(data,&header) == JS_ERROR) 
        goto clean_nd;

    /* We only answer questions (Thanks to Roy Arends for pointing out this
       security flaw) */
    if(header.qr != 0) {
        goto clean_nd;
        }

    /* Determine the length of the zone they want an AXFR for */
    length = dlabel_length(data,12);
    if(length == JS_ERROR) 
        goto clean_nd;

    /* Get the name of the Zone they want */
    if(js_substr(data,name,12,length) == JS_ERROR) 
        goto clean_nd;

    /* Make sure this is a AXFR or IXFR request */
    if(data->unit_count < 14 + length) /* 12 bytes header, 2 bytes type */
        goto clean_nd;
    /* The query must be query 0-255 */
    if(*(data->string + 12 + length) != 0)
        goto clean_nd;
    /* The query must be SOA, AXFR, or IXFR (hack: IXFR treated like AXFR) */
    switch(*(data->string + 13 + length)) {
        case RR_SOA: /* SOA */
          soa_q = 1;
        case 251: /* IXFR */
      case 252: /* AXFR */
          break;
        default:
          goto clean_nd;
      } 

    /* Create the binzone string */
    if((binzone = js_create(260,1)) == 0)
        goto clean_nd;
    /* Give "filename" the name of the file with the zone file */
    if(js_copy(name,binzone) == JS_ERROR) {
      goto clean_ndb;
      }
    /* Convert the name in to a dotted decimal format that the mararc
       file uses */
    if(hname_translate(name,RR_A) == JS_ERROR) 
        goto clean_ndb;
    if(js_substr(name,data,1,name->unit_count - 1) == JS_ERROR) 
        goto clean_ndb;
      /* (data is now something like "example.com.") */
    /* See if we have a zone file for the zone they are requesting */
    if(js_qstr2js(name,"csv1") == JS_ERROR) 
        goto clean_ndb;
    if(read_dvar(name,data,name) == JS_ERROR) { /* XXX Covers "no such zone" */
        mlog(L_NO_ZONE_HERE); /* "Zone we do not have asked for, disconnecting" */
        goto clean_ndb;
        }

    /* name now has the file with the zone in question */
    /* Create the filename */
    if((filename = js_create(390,1)) == 0)
        goto clean_ndb;
    /* Give "filename" the name of the file with the zone file */
    if(js_copy(name,filename) == JS_ERROR) {
        js_destroy(filename);
      goto clean_ndb;
      }

    /* Copy the name of the zone over to zone */
    if((zone = js_create(390,1)) == 0) 
        goto clean_ndb;
    if(js_copy(data,zone) == JS_ERROR) {
        js_destroy(filename); js_destroy(zone);
        goto clean_ndb;
      }
    /* Create the query string */
    if((query = js_create(390,1)) == 0) {
        js_destroy(zone); js_destroy(filename);
        goto clean_ndb;
      }
    if(js_set_encode(query,MARA_LOCALE) == JS_ERROR) {
        js_destroy(zone); js_destroy(query); js_destroy(filename);
      goto clean_ndb;
      }
    /* Create the response string */
    if((response = js_create(512,1)) == 0) {
        js_destroy(zone); js_destroy(query); js_destroy(filename);
        goto clean_ndb;
      }
    if(js_set_encode(response,MARA_LOCALE) == JS_ERROR) {
        js_destroy(zone); js_destroy(query); js_destroy(response);
      js_destroy(filename);
      goto clean_ndb;
      }
    /* Create the soa string */
    if((soa = js_create(390,1)) == 0) {
        js_destroy(zone); js_destroy(query); js_destroy(response);
      js_destroy(filename); 
      goto clean_ndb;
      }
    if(js_set_encode(soa,MARA_LOCALE) == JS_ERROR) 
        goto clean_ndzqrs;

    /* Open up the zone file */
    if(js_open_read(filename,&desc) == JS_ERROR) {
        show_timestamp();
        printf("Unable to open zone file ");
        show_esc_stdout(filename);
      printf("\n");
        goto clean_ndzqrs;
      }

    /* Build up the header, which is the same for all the replies */ 
    header.qr = header.aa = 1;
    header.ancount = 1;
    header.opcode = header.tc = header.ra = header.z = header.rcode =
    header.qdcount = header.nscount = header.arcount = 0;

    /* If they asked for a SOA record, generate a SOA record.
       Since BIND insists that the SOA record have an authority
       section, we have to read the entire zone file to synthesize one */
    if(soa_q == 1) {
        soa_reply_header = header;
      /* Make a 12-byte header so we can start appending answers to 
         the header */
        if(make_hdr(&soa_reply_header,response) == JS_ERROR)
            goto clean_ndzqrs; 
        /* Add the question to the reply */
      if(js_append(binzone,response) == JS_ERROR)
          goto clean_ndzqrs;
        if(js_adduint16(response,6) == JS_ERROR) /* SOA Query */
          goto clean_ndzqrs;
        if(js_adduint16(response,1) == JS_ERROR) /* IN Class */
          goto clean_ndzqrs;
        soa_reply_header.qdcount = 1;
        /* Start reading the zone file */
      while(!js_buf_eof(&desc)) {
            /* Get the line */
            if(js_buf_getline(&desc,data) <= JS_ERROR)
                goto clean_ndzqrs;
            /* Process the % character and any \ escape sequences */
            if(bs_process(data,name,zone) == JS_ERROR) {
                show_timestamp();
                printf("Fatal syntax error in file ");
                show_esc_stdout(filename);
              printf("\n");
                goto clean_ndzqrs;
            }
            /* Get the data from the line in the zone file */
            rr_type = parse_csv1_line(name,query,data,&ttl);
            if(rr_type == JS_ERROR) {
                show_timestamp();
                printf("Fatal syntax error in file ");
                show_esc_stdout(filename);
              printf("\n");
                goto clean_ndzqrs;
            }
            if(rr_type == -2) /* Non-fatal Syntax error */ {
                continue;
            }
            if(rr_type > 0 && rr_type < 65536) { /* Line with RR */
              /* If we have added all of the NS records, add no more */
              if(soa_q == 2 && rr_type != RR_NS)
                soa_q = 0;
                /* If this is the first SOA record or an authoritative NS
               record */
                if(soa_q > 0) {
                /* The "query" also has the Qtype in it */ 
                if(js_append(query,response) == JS_ERROR)
                    goto clean_ndzqrs;
                    if(js_adduint16(response,1) == JS_ERROR)  /* Class is 1 */
                    goto clean_ndzqrs;
                    if(js_adduint32(response,ttl) == JS_ERROR)
                    goto clean_ndzqrs;
                    if(js_adduint16(response,data->unit_count) == JS_ERROR) 
                    goto clean_ndzqrs;
                    if(js_append(data,response) == JS_ERROR)
                    goto clean_ndzqrs;
                    /* We only add the SOA once */
                if(soa_q == 1)
                    soa_q = 2; /* Adding name servers */
                    else if(soa_q == 2)
                    soa_reply_header.nscount++;
                }
              } 
          }

        /* Give the response to the client */
      /* Hack to change the header without truncating the string */
        soa_q = response->unit_count;
        if(make_hdr(&soa_reply_header,response) == JS_ERROR)
            goto clean_ndzqrs; 
        response->unit_count = soa_q;
      soa_q = 0;
      /* Determine the length of the response to send */
      get[0] = (response->unit_count & 0xff00) >> 8;
      get[1] = response->unit_count & 0xff;
      if(write(connect,get,2) == -1)
          goto clean_ndzqrs;
        if(write(connect,response->string,response->unit_count) == -1)
          goto clean_ndzqrs;

        /* Pretend to get the next query */

        /* Get two bytes for the length */
      if(connect == 1) {
          if(read(0,get,2) != 2)
              goto clean_ndzqrs; 
          }
        else {
          if(recv(connect,get,2,MSG_WAITALL) != 2)
              goto clean_ndzqrs; 
            }

        /* Determine how long the second query will be */
        length = (get[0] & 0xff) << 8 | (get[1] & 0xff);
      /* Pretend to get the actual query */
        while(length > 0) {
          if(connect == 1) {
              if(read(0,get,1) != 1)
                goto clean_ndzqrs;
                }
            else {
              if(recv(connect,get,1,MSG_WAITALL) != 1)
                  goto clean_ndzqrs;
                }
            length--;
          }

        /* Close and reopen the zone file so we can read it for the AXFR
         request */
        if(js_close(&desc) == JS_ERROR)
            goto clean_ndzqrs;
        if(js_open_read(filename,&desc) == JS_ERROR) 
            goto clean_ndzqrs;

      }

    /* Read all of the lines from the zone file one by one */
    while(!js_buf_eof(&desc)) {
        /* Get the line */
      if(js_buf_getline(&desc,data) <= JS_ERROR) 
          goto clean_ndzqrs;
        /* Process the % character and any \ escape sequences */
        if(bs_process(data,name,zone) == JS_ERROR) 
          goto clean_ndzqrs;
        /* Get the data from the line in the zone file */
      rr_type = parse_csv1_line(name,query,data,&ttl);
        if(rr_type == JS_ERROR) 
          goto clean_ndzqrs;
        if(rr_type == -2) /* Syntax error */
          continue;
        if(rr_type > 0 && rr_type < 65536) { /* Non-blank line */ 
          /* Make the header */
          if(make_hdr(&header,response) == JS_ERROR) 
              goto clean_ndzqrs;
            /* Add this response to the message, in the answer section */
            /* The "query" also has the Qtype in it */
          if(js_append(query,response) == JS_ERROR) 
              goto clean_ndzqrs;
            if(js_adduint16(response,1) == JS_ERROR)  /* Class, always 1 */
              goto clean_ndzqrs;
            if(js_adduint32(response,ttl) == JS_ERROR) 
              goto clean_ndzqrs;
            if(js_adduint16(response,data->unit_count) == JS_ERROR) {
              goto clean_ndzqrs;
            }
            if(js_append(data,response) == JS_ERROR) 
              goto clean_ndzqrs;
            /* Spit out the data over the TCP pipe */
          get[0] = (response->unit_count & 0xff00) >> 8;
          get[1] = response->unit_count & 0xff;
          if(write(connect,get,2) == -1) 
              goto clean_ndzqrs;
            if(write(connect,response->string,response->unit_count) == -1) 
              goto clean_ndzqrs;
            /* If this is the first SOA record, copy it so we can send it
               at the end */
            if(is_soa == 1 && rr_type == RR_SOA) {
                is_soa = 0;
                if(js_copy(response,soa) == JS_ERROR) 
                goto clean_ndzqrs;
                }
          } 
        }
    /* Show them the SOA record again */
    if(is_soa == 0) { /* If we showed them the SOA before */
        get[0] = (soa->unit_count & 0xff00) >> 8;
      get[1] = soa->unit_count & 0xff;
      if(write(connect,get,2) == -1) 
          goto clean_ndzqrs;
        if(write(connect,soa->string,soa->unit_count) == -1) 
          goto clean_ndzqrs;
        }
    close(connect); /* Close connection after AXFR is done */
   
    /* Destroy all allocated strings */ 
    js_destroy(soa);
    js_destroy(response);
    js_destroy(query);
    js_destroy(zone);
    js_destroy(filename);
    js_destroy(binzone);
    js_destroy(name);
    js_destroy(data);
    js_close(&desc); /* Remove memory this sucks up */
    return JS_SUCCESS; /* Return success */

    /* We are using gotos because C does not have decent error handling */
    clean_ndzqrs:
        js_destroy(soa);
      js_destroy(response);
      js_destroy(query);
      js_destroy(zone);
      js_destroy(filename);
    clean_ndb:
        js_destroy(binzone);
    clean_nd:
      js_destroy(name);
      js_destroy(data);
        close(connect); /* Close connection on error */
        js_close(&desc); /* Remove memory this sucks up */
      return JS_ERROR;
    }

/* The core of the DNS Zone server */

int main(int argc, char **argv) {
   
    js_string *mararc_loc, *errors, *chrootn, *kvar_str, *maxpstr,
              *kvar_query, *bind_address, *incoming, *uncomp, *verbstr;
    unsigned char chroot_zt[255];
    int errorn, uid, sock, maxprocs, verbose, counter, connection,
        inetd = 0;
#ifndef DARWIN
    struct rlimit rlim;
#endif
    pid_t pid;
    ipv4pair acl[512];

    /* Initialize the strings (allocate memory for them, etc.) */
    if((mararc_loc = js_create(256,1)) == 0)
        harderror(L_MLC); /* "Could not create mararc_loc string" */
    if(js_set_encode(mararc_loc,MARA_LOCALE) == JS_ERROR)
        harderror("Could not set locale for mararc_loc string");
    if((errors = js_create(256,1)) == 0)
        harderror(L_EC); /* "Could not create errors string" */
    if(js_set_encode(errors,MARA_LOCALE) == JS_ERROR)
        harderror("Could not set locale for errors string");
    if((kvar_str = js_create(256,1)) == 0)
        harderror(L_KSC); /* "Could not create kvar_str string" */
    if(js_set_encode(kvar_str,MARA_LOCALE) == JS_ERROR)
        harderror(L_KSL); /* "Could not set locale for kvar_str string" */
    if((verbstr = js_create(256,1)) == 0)
        harderror(L_VC); /* "Could not create verbstr string" */
    if(js_set_encode(verbstr,MARA_LOCALE) == JS_ERROR)
        harderror(L_VL); /* "Could not set locale for verbstr string" */
    if((maxpstr = js_create(256,1)) == 0)
        harderror(L_MC); /* "Could not create maxpstr string" */
    if(js_set_encode(maxpstr,MARA_LOCALE) == JS_ERROR)
        harderror(L_ML); /* "Could not set locale for maxpstr string" */
    if((chrootn = js_create(256,1)) == 0)
        harderror(L_CC); /* "Could not create chrootn string" */
    if(js_set_encode(chrootn,MARA_LOCALE) == JS_ERROR)
        harderror(L_CL); /* "Could not set locale for chrootn string" */
    if((kvar_query = js_create(256,1)) == 0)
        harderror(L_KQC); /* "Could not create kvar_query string" */
    if(js_set_encode(kvar_query,MARA_LOCALE) == JS_ERROR)
        harderror(L_KQL); /* "Could not set locale for kvar_query string" */
    if((bind_address = js_create(64,1)) == 0)
        harderror(L_BAC); /* "Could not create bins_address string" */
    if(js_set_encode(bind_address,MARA_LOCALE) == JS_ERROR)
        harderror(L_BAL); /* "Could not set locale for bind_address string" */
    if((incoming = js_create(768,1)) == 0)
        harderror(L_IC); /* "Could not create incoming string" */
    if(js_set_encode(incoming,MARA_LOCALE) == JS_ERROR)
        harderror(L_IL); /* "Could not set locale for incoming string" */
    if((uncomp = js_create(768,1)) == 0)
        harderror(L_UC); /* "Could not create uncomp string" */
    if(js_set_encode(uncomp,MARA_LOCALE) == JS_ERROR)
        harderror(L_UL); /* "Could not set locale for uncomp string" */

    /* First, find the mararc file */
    if(argc == 1) { /* No arguments */
        if(find_mararc(mararc_loc) == JS_ERROR)
            harderror(L_MW); /* "Error locating mararc file" */
        }
    else if(argc==2) { /* maradns -v or maradns --version */
        printf("%s %s\n%s\n",L_THISIS,VERSION,L_RTFM); /* "This is MaraDNS versi
on %s\nFor usage information, type in 'man maradns'" */
        exit(0);
        }
    else if(argc==3) { /* maradns -f /wherever/mararc */
        if(js_qstr2js(mararc_loc,argv[2]) == JS_ERROR)
            harderror(L_GET_MARARC); /* "Could not get mararc from command line" */
        }
    else
        harderror(L_USAGE); /* "Usage: mararc [-f mararc_location]" */

    /* Then parse that file */
    if(read_mararc(mararc_loc,errors,&errorn) == JS_ERROR)
        harderror(L_PARSE_MARARC); /* "Error parsing contents of mararc file" */
    if(errorn != 0) {
        if(errorn != -1)
          /* "Error parsing contents of mararc file on line " */
          printf("%s%d%s",L_PARSE_MARARC_LINE,errorn,LF); /* errorn, "\n" */
      printf("%s",L_ERROR_CODE); /* "Error code: " */
      js_show_stdout(errors);
      printf("%s",LF);
      exit(2);
      }

    /* Determine if we are standalone, or if we should run from inetd */
    if(js_qstr2js(kvar_query,"tuzona_inetd") == JS_ERROR)
        harderror(L_MAKE_KQ);
    if(read_kvar(kvar_query,kvar_str) == JS_SUCCESS) {
        inetd = js_atoi(kvar_str,0);
        }

    /* Get in to a state of least privledge ASAP */

    /* Limit the maximum number of processes */
    if(js_qstr2js(kvar_query,"maxprocs") == JS_ERROR)
        harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,maxpstr) == JS_ERROR)
        harderror(L_MAXPROCS); /* "Problem getting maxprocs value.\nmaxprocs must be set before starting the MaraDNS server" */
    if((maxprocs = js_atoi(maxpstr,0)) == 0)
        harderror(L_MAXPROCS_NUM); /* "Problem converting maxprocs to a number\nThis must be a non-zero number" */

    /* If we have both maxprocs and max_tcp_procs defined, the zone server
       will use max_tcp_procs. */
    if(js_qstr2js(kvar_query,"max_tcp_procs") == JS_ERROR)
        harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
    /* See if max_tcp_procs is set */
    if(read_kvar(kvar_query,maxpstr) != 0) {
        maxprocs = js_atoi(maxpstr,0);
      }

#ifndef DARWIN
    rlim.rlim_cur = rlim.rlim_max = maxprocs;

#ifdef RLIMIT_NPROC
    if(setrlimit(RLIMIT_NPROC,&rlim) != 0)
        harderror(L_SETMAX); /* "Unable to set maximum number of processes" */
#endif /* SOLARIS */
#endif /* DARWIN */

    /* Determine the level of error reporting */
    if(js_qstr2js(kvar_query,"verbose_level") == JS_ERROR)
        harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,verbstr) == JS_ERROR)
        verbose = 0;
    else
        verbose = js_atoi(verbstr,0);
        
    /* Determine if we are root */
    if(geteuid() == 0) {
        /* Change the root directory */
        if(js_qstr2js(kvar_query,"chroot_dir") == JS_ERROR)
          harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,chrootn) == JS_ERROR)
          harderror(L_CHROOT); /* "Problem getting chroot kvar.\nYou must have chroot_dir set if you start this as root" */
        if(js_js2str(chrootn,chroot_zt,200) == JS_ERROR)
          harderror(L_CHROOT_NT); /* "Problem making chroot nt string.\nMake sure the chroot directory is 200 chars or less" */
        if(chdir(chroot_zt) != 0)
          harderror(L_NO_CHROOT); /* "Problem changing to chroot dir.\nMake sure chroot_dir points to a valid directory" */
        if(chroot(chroot_zt) != 0)
          harderror(L_CHROOT_ERROR);  /* "Problem changing the root directory." */
 
        if(inetd != 1) /* We have no logs if run from inetd */
            mlog(L_CHROOT_SUCCESS); /* "Root directory changed" */
     
      /* Bind to port 53 
           To Do: use capset to give us privledged bind abilities without 
                  needing to be root.
        */
      if(inetd != 1) { /* If we are a standalone server */
            if(js_qstr2js(kvar_query,"bind_address") == JS_ERROR)
              harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
            if(read_kvar(kvar_query,bind_address) == JS_ERROR)
              harderror(L_BIND_GET); /* "Problem getting chroot kvar.\nYou must have bind_address set to the IP maradns will listen on" */
            if(tcpbind(&sock,bind_address) == JS_ERROR)
                harderror(L_BIND); /* "Problem binding to port 53.\nMost likely, another process is already listening on port 53" */

            mlog(L_SOCKET_SUCCESS);  /* "Socket opened on TCP port 53" */
            }

        /* Drop the elevated privledges */
      if(js_qstr2js(kvar_query,"maradns_uid") == JS_ERROR)
          harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,kvar_str) == JS_ERROR)
          harderror(L_NO_UID); /* "Problem getting maradns_uid kvar.\nYou must have maradns_uid set if you start this as root" */
        if((uid = js_atoi(kvar_str,0)) < 10)
          harderror(L_UID_INVALID); /* "maradns_uid is less than 10 or not a number.\nThis uid must have a value of 10 or more" */
        if(setuid(uid) != 0)
          harderror(L_NODROP); /* "Could not drop root uid" */
        if(setuid(0) == 0)
          harderror(L_STILL_ROOT);  /* "We seem to still be root" */
        
        if(inetd != 1) /* If we are not called from inetd */
            mlog(L_DROP_SUCCESS); /* "Root privledges dropped" */

        }
    else if(inetd != 1) {
      /* Bind to port 53 as a non-root user */
        if(js_qstr2js(kvar_query,"bind_address") == JS_ERROR)
          harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
        if(read_kvar(kvar_query,bind_address) == JS_ERROR)
          harderror(L_BIND_GET); /* "Problem getting chroot kvar.\nYou must have bind_address set to the IP maradns will listen on" */
        if(tcpbind(&sock,bind_address) == JS_ERROR)
            harderror(L_BE_ROOT); /* "Problem binding to port 53.\nYou should run this as root" */
        mlog(L_SOCKET_SUCCESS);  /* "Socket opened on TCP port 53" */
        }

    /* Make a database of IPs permitted to connect to port 53 */
    /* Initialize the ACL list */
    for(counter = 0; counter < 512; counter++) 
        acl[counter].ip = 0xffffffff; 
    if(js_qstr2js(kvar_query,"zone_transfer_acl") == JS_ERROR)
        harderror(L_MAKE_KQ); /* "Could not create kvar_query" */
    if(read_kvar(kvar_query,kvar_str) == JS_ERROR)
        harderror(L_NO_ACL); /* "Could not read zone_transfer_acl data" */
    if(inetd == 1) {
        if(kvar_str->unit_count != 0)
            harderror(L_ACL_INETD); /* "zone_transfer_acl must not be set when running in inetd mode" */
        }
    else {
        if(make_ip_acl(kvar_str,acl,500,0) == JS_ERROR)
            harderror(L_ACL_LIST); /* "Could not make ip ACL list" */
        }

    /* Initialize decompression */
    decomp_init(0);

    /* If we are being called from an external helper, such as inetd
       or tcpserver */
    if(inetd == 1) {
        serve_zone(1);
        exit(0);
        }

    /* Set up a signal handler so we can decrement the number of children
       whenever a child exits */
    signal(SIGCHLD,handle_childs);

    /* Otherwise, listen for data on the TCP socket */
    for(;;) { 
        if(verbose >= 2)
            mlog(L_WAITING); /* "Awaiting data on port 53" */
        connection = gettcp(&sock,acl,500);
        if(connection == JS_ERROR)
          continue; 
        if(verbose >= 2)
            mlog(L_GOT); /* "Message received, processing" */

        /* Make sure we don't have more children than we can handle */
      while(num_children > maxprocs)
          sleep(1);

        /* Fork and have the child handle the data */
        while((pid = fork()) == -1) /* Resource starvation handling */
            sleep(1); 
        
        if(!pid) { /* Child */
            serve_zone(connection);
          exit(0); /* End child */
          } 

        /* Parent */
      num_children++;

        /* Hackish way to clean up child processes without needlessly slowing 
           the program */
        while(waitpid(0,NULL,WNOHANG) > 0) num_children--; 
      close(connection); /* Otherwise we can only serve MAXFD zones
                              before it blows up */
        }
    
    }
    

Generated by  Doxygen 1.6.0   Back to index