//**************************************************************** //** ** //** PHilip RUshik. 2010,2011,2012,2013 ** //** Distributed under the terms of the Beerware License ** //** Philip Rushik wrote this code. As long as you retain ** //** this notice, you can do whatever you want with this ** //** stuff. If we meet someday, and you think this stuff ** //** is worth it, you can buy me a beer in return. ** //** --Philip Rushik ** //** ** //** DNS Server ** //** ** //** Lie to clients about domain names ** //** ** //**************************************************************** #include #include #include #include #include #include struct dns_header { unsigned short int ID; unsigned char QR, OPCODE, AA, TC, RD, RA, Z, RCODE; unsigned short int QDCOUNT; unsigned short int ANCOUNT; unsigned short int NSCOUNT; unsigned short int ARCOUNT; }; struct dns_question { unsigned char QCLABEL; unsigned char * QNAME; unsigned short int QTYPE; unsigned short int QCLASS; }; struct dns_answer { unsigned char * RNAME; unsigned short int RTYPE; unsigned short int RCLASS; unsigned int RTTL; unsigned short int RDLENGTH; unsigned char * RDATA; }; void error(unsigned char *msg) { perror(msg); exit(1); } //Compares s1 (case-insensitive) to s2 short unsigned int lscomp(char* s1,char* s2) { short unsigned int i; for (i=0; iID=packet[0]*0x100+packet[1]; header->QR=(packet[2]>>7)&0x1; header->OPCODE=(packet[2]>>3)&0xF; header->AA=(packet[2]>>2)&0x1; header->TC=(packet[2]>>1)&0x1; header->RD=(packet[2]>>0)&0x1; header->RA=(packet[3]>>7)&0x1; header->Z=(packet[3]>>4)&0x7; header->RCODE=(packet[3]>>0)&0xF; header->QDCOUNT=packet[4]*0x100+packet[5]; header->ANCOUNT=packet[6]*0x100+packet[7]; header->NSCOUNT=packet[8]*0x100+packet[9]; header->ARCOUNT=packet[10]*0x100+packet[11]; return header; } struct dns_question * parse_dns_question(unsigned char *packet) { //QUESTION FORMAT // Octet // 1..n QNAME // n+1,n+2 QTYPE // n+3,n+4 QCLASS struct dns_question *question; question = malloc(sizeof(struct dns_question)); int i=0,j; question->QCLABEL=packet[12]; for(;packet[i+12]!='\0';i++); question->QNAME=malloc(i+1); for(;i!=0;i--) question->QNAME[i]=(packet[i+12]);//<0x10 && packet[i+12]!=0) ? '.' : packet[i+12]; i=strlen(&question->QNAME[1])+14; question->QTYPE=packet[i]*0x100+packet[i+1]; question->QCLASS=packet[i+2]*0x100+packet[i+3]; return question; } unsigned char * assemble_dns_packet(struct dns_header * header, struct dns_question * question, struct dns_answer * answer) { int i=0, j=0; unsigned char * packet; packet = malloc(512); memset(packet, 0,512); //This should be everything needed for the header //Warning: This section uses lots of bit-wise operations, and may be confusing. packet[i++]=header->ID>>8; packet[i++]=header->ID&0xFF; packet[i++]=(header->QR<<7)+(header->OPCODE<<3)+(header->AA<<2)+(header->TC<<1)+(header->RD<<0); packet[i++]=(header->RA<<7)+(header->Z<<4)+(header->RCODE<<0); packet[i++]=header->QDCOUNT>>8; packet[i++]=header->QDCOUNT&0xFF; packet[i++]=header->ANCOUNT>>8; packet[i++]=header->ANCOUNT&0xFF; packet[i++]=header->NSCOUNT>>8; packet[i++]=header->NSCOUNT&0xFF; packet[i++]=header->ARCOUNT>>8; packet[i++]=header->ARCOUNT&0xFF; //This adds the question section //It should be identical to the question section from the question packet[i++]=question->QCLABEL; //Label Type for (j=1;jQNAME[1])+2;j++) packet[i++]=question->QNAME[j]; packet[i++]=question->QTYPE>>8; packet[i++]=question->QTYPE&0xFF; packet[i++]=question->QCLASS>>8; packet[i++]=question->QCLASS&0xFF; //This sets up the answer section packet[i++]=answer->RNAME[0]; packet[i++]=answer->RNAME[1]; packet[i++]=answer->RTYPE>>8; packet[i++]=answer->RTYPE&0xFF; packet[i++]=answer->RCLASS>>8; packet[i++]=answer->RCLASS&0xFF; packet[i++]=(answer->RTTL>>24)&0xFF; packet[i++]=(answer->RTTL>>16)&0xFF; packet[i++]=(answer->RTTL>>8)&0xFF; packet[i++]=answer->RTTL&0xFF; packet[i++]=answer->RDLENGTH>>8; packet[i++]=answer->RDLENGTH&0xFF; for (j=0;jRDLENGTH;j++) packet[i++]=answer->RDATA[j]; return packet; } struct dns_answer * searchRecord(const char *fname, struct dns_question * question) { if (fname==NULL) { printf("No configuration file specified\n"); return; } unsigned int field,i; unsigned char buffer[256], *tmp; FILE * fd; fd=fopen(fname,"r"); if (fd==NULL) { printf("Configuration file could not be opened\n"); return; } while (!feof(fd)) { memset(buffer,0,256); fgets(buffer, 256, fd); if (buffer[0]=='#') printf("Comment : %s",buffer); else if (buffer[0]!='\n') { tmp=strtok(buffer,"|"); if (tmp==NULL) continue; sscanf(tmp,"%X",&field); //Read QTYPE if (field!=question->QTYPE) //Match QTYPE continue; tmp=strtok(NULL,"|"); if (!lscomp(&question->QNAME[1],tmp)) //Match QNAME continue; tmp=strtok(NULL,"\n"); //Warning: This section contains many hacks and hard-coded magic numbers struct dns_answer *answer; answer=malloc(sizeof(struct dns_answer)); answer->RNAME=malloc(2); //Allocate enough memory to store the value "C00C" answer->RNAME[0]=0xc0; answer->RNAME[1]=0x0c; //Reference to QNAME which is always located at 0x0C answer->RTYPE=question->QTYPE; //Answer RTYPE is the same as QTYPE answer->RCLASS=0x1; //Answer class is IN (0x01) answer->RTTL=3600; //Hard-coded 3600 seconds for now. This should be configurable in the future. if (answer->RTYPE==0x1) answer->RDLENGTH=4; //4 bytes, the size of an IP address else answer->RDLENGTH=strlen(tmp)+8; answer->RDATA=malloc(answer->RDLENGTH); //Allocate enough memory to store the answer if (answer->RTYPE==0x1) sscanf(tmp,"%d.%d.%d.%d",&answer->RDATA[0],&answer->RDATA[1],&answer->RDATA[2],&answer->RDATA[3]); else { answer->RDATA[0]=0; answer->RDATA[1]=1; answer->RDATA[2]=0; answer->RDATA[3]=100; answer->RDATA[4]=0x13; answer->RDATA[5]=0xC4; answer->RDATA[6]=strlen(tmp); for (i=0;iRDATA[i+7]=tmp[i]; } fclose(fd); return answer; } } fclose(fd); return NULL; } int main(int argc, unsigned char *argv[]) { printf("DNS Server v0.9\nWritten by PHilip RUshik\n\n"); int sockfd, clilen; unsigned char buffer[512], *data; struct sockaddr_in serv_addr, cli_addr; int n; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) error("Error opening socket"); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(53); //DNS uses port 53 if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error("Error binding"); clilen = sizeof(cli_addr); while (1) { memset(&buffer, 0, 512); n = recvfrom(sockfd,buffer,255,0,(struct sockaddr *) &cli_addr,&clilen); if (n <= 0) error("Error receiving"); struct dns_header * header; header = parse_dns_header(buffer); struct dns_question * question; question = parse_dns_question(buffer); if (question->QTYPE==0x01 || question->QTYPE==0x21 || question->QTYPE==0x1C) { //Set up the header for the response packet struct dns_header rheader; memset(&rheader,0,sizeof(rheader)); //Default everything to 0 rheader.ID=header->ID; //Match the ID of the question rheader.RD=header->RD; //Match the RD of the question rheader.AA=0; //DNS Server is not an authority by default rheader.QR=1; //Answer Code rheader.Z=2; //Authenticated Answer rheader.QDCOUNT=1; //One Question rheader.ANCOUNT=1; //One Answer //Question will be repeated in answer. No new data structure is required. //Build the answer section struct dns_answer *answer; answer=searchRecord("./config",question); if (answer!=NULL) { printf("-%s-\n",&question->QNAME[1]); data=assemble_dns_packet(&rheader,question,answer); n = sendto(sockfd,data,(58-26)+strlen(&question->QNAME[1])+2+answer->RDLENGTH-4,0,(struct sockaddr *) &cli_addr,clilen); if (n <= 0) error("Error sending"); //Release memory free(answer->RNAME); free(answer->RDATA); free(answer); free(data); } else { printf("Domain Not Found\n"); } free(header); free(question->QNAME); free(question); } else { printf("Unsupported Protocol\n"); } } return 0; }