#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"limits.h"
#include"errors.h"
#include"rr.h"
#include"rrparser.h"

// parametry: -q <query_str> && preklada na raw query
// pokud neni tak cte ze stdin raw query && preklada na query_str

int
rrparse_a(char *l, rr_t *rr) {
  struct in_addr a_in;
  
  if (!l || !*l) return XELEN;
  if (!inet_aton(l, &a_in)) return XEINVAL;
  
  rr->rdlength=4;
  rr->rdata=malloc(4);
  memcpy(rr->rdata, &a_in.s_addr, 4);
  
  rr->rdlength=htons(rr->rdlength);
  return XENONE;
}

int
rrparse_ns(char *l, rr_t *rr) {
  if (!l || !*l) return XELEN;
  
  rr->rdlength=strlen(l)+2;
  rr->rdata=malloc(rr->rdlength);
  *rr->rdata=(u_int8_t)rr->rdlength-2;
  strncpy(rr->rdata+1, l, rr->rdlength-2);
  domain2labels(rr->rdata);

  rr->rdlength=htons(rr->rdlength);
  return XENONE;
}

int
rrparse_md(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_mf(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_cname(char *l, rr_t *rr) {
  if (!l || !*l) return XELEN;
  
  rr->rdlength=strlen(l)+2;
  rr->rdata=malloc(rr->rdlength);
  *rr->rdata=(u_int8_t)rr->rdlength-2;
  strncpy(rr->rdata+1, l, rr->rdlength-2);
  domain2labels(rr->rdata);

  rr->rdlength=htons(rr->rdlength);
  return XENONE;
}

int
rrparse_soa(char *l, rr_t *rr) {
  char **p=malloc(7*sizeof(char *)), *o; int n, lp0, lp1;
  
  if (!l || !*l) return XELEN;

  for (n=0; n<6; n++) {
    char *t=strchr(l, ' ');
    if (!t) return XELEN;
    *t=0; p[n]=malloc(t-l+1); strcpy(p[n], l);
    *t=' '; l=t+1;
  }
  p[n]=malloc(strlen(l)+1); strcpy(p[n], l);
  
  rr->rdlength=(lp0=strlen(p[0]))+(lp1=strlen(p[1]))+24;
  o=rr->rdata=malloc(rr->rdlength);
  *o=(u_int8_t)lp0; strncpy(o+1, p[0], lp0); rr->e.soa.mname=o; domain2labels(rr->e.soa.mname); o+=lp0+2;
  *o=(u_int8_t)lp1; strncpy(o+1, p[1], lp1); rr->e.soa.rname=o; domain2labels(rr->e.soa.rname); o+=lp1+2;
  *(u_int32_t *)o=htonl(atoi(p[2])); rr->e.soa.serial=(u_int32_t *)o; o+=4;
  *(int32_t *)o=htonl(atoi(p[3])); rr->e.soa.refresh=(int32_t *)o; o+=4;
  *(int32_t *)o=htonl(atoi(p[4])); rr->e.soa.retry=(int32_t *)o; o+=4;
  *(int32_t *)o=htonl(atoi(p[5])); rr->e.soa.expire=(int32_t *)o; o+=4;
  *(u_int32_t *)o=htonl(atoi(p[6])); rr->e.soa.minimum=(u_int32_t *)o;

  rr->rdlength=htons(rr->rdlength);
  return XENONE;
}

int
rrparse_mb(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_mg(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_mr(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_null(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_wks(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_ptr(char *l, rr_t *rr) {
  if (!l || !*l) return XELEN;
  
  rr->rdlength=strlen(l)+2;
  rr->rdata=malloc(rr->rdlength);
  *rr->rdata=(u_int8_t)rr->rdlength-2;
  strncpy(rr->rdata+1, l, rr->rdlength-2);
  domain2labels(rr->rdata);

  rr->rdlength=htons(rr->rdlength);
  return XENONE;
}

int
rrparse_hinfo(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_minfo(char *l, rr_t *rr) {
  return XEINVAL;
}

int
rrparse_mx(char *l, rr_t *rr) {
  char *p;
  
  if (!l || !*l) return XELEN;
  
  p=l;
  l=strchr(l, ' '); if (!l) return XELEN;
  *l=(u_int8_t)0; l++;
  
  rr->rdlength=strlen(l)+4;
  rr->rdata=malloc(rr->rdlength);
  
  *(int16_t *)rr->rdata=htons(atoi(p));
    
  *(rr->rdata+2)=(u_int8_t)rr->rdlength-4;
  strncpy(rr->rdata+3, l, rr->rdlength-4);

  rr->e.mx.preference=(int16_t *)rr->rdata;
  rr->e.mx.exchange=rr->rdata+2;
  domain2labels(rr->e.mx.exchange);

  rr->rdlength=htons(rr->rdlength);
  return XENONE;
}

int
rrparse_txt(char *l, rr_t *rr) {
  if (!l || !*l) return XELEN;
  
  rr->rdlength=strlen(l)+1;
  rr->rdata=malloc(rr->rdlength);
  *rr->rdata=(u_int8_t)rr->rdlength-1;
  strncpy(rr->rdata+1, l, *rr->rdata-1);

  rr->rdlength=htons(rr->rdlength);
  return XENONE;
}

typedef int (* rrparser_t)(char *, rr_t *); // return 0 if success, errorcode (>0) if not

rrparser_t rrparser[]={rrparse_null, rrparse_a, rrparse_ns, rrparse_md, rrparse_mf, rrparse_cname, rrparse_soa, rrparse_mb,
                          rrparse_mg, rrparse_mr, rrparse_null, rrparse_wks, rrparse_ptr, rrparse_hinfo, rrparse_minfo, rrparse_mx,
                          rrparse_txt};

char *
rrmake_a(rr_t *rr) {
  char *l, *i;
  struct in_addr a_in;
  
  memcpy(&a_in.s_addr, rr->rdata, 4);
  i=inet_ntoa(a_in);
  l=malloc(strlen(i)+1);
  strcpy(l, i);

  return l;
}

char *
rrmake_ns(rr_t *rr) {
  char *l;
  
  l=malloc(*rr->rdata+1);
  labels2domain(rr->rdata);
  strncpy(l, rr->rdata+1, *rr->rdata);
  l[(int)(*rr->rdata)]=0;

  return l;
}

char *
rrmake_md(rr_t *rr) {
  return NULL;
}

char *
rrmake_mf(rr_t *rr) {
  return NULL;
}

char *
rrmake_cname(rr_t *rr) {
  char *l;
  
  l=malloc(*rr->rdata+1);
  labels2domain(rr->rdata);
  strncpy(l, rr->rdata+1, *rr->rdata);
  l[(int)(*rr->rdata)]=0;

  return l;
}

char *
rrmake_soa(rr_t *rr) {
  char *l, *p;
  
  p=l=malloc(*rr->e.soa.mname+*rr->e.soa.rname+72);
  
  labels2domain(rr->e.soa.mname);
  strncpy(p, rr->e.soa.mname+1, *rr->e.soa.mname);
  p[(int)(*rr->e.soa.mname)]=' '; p+=*rr->e.soa.mname+1;
  
  labels2domain(rr->e.soa.rname);
  strncpy(p, rr->e.soa.rname+1, *rr->e.soa.rname);
  /*p[*rr->e.soa.rname]=' ';*/ p+=*rr->e.soa.rname;
  *p=0;
  
  p=l;
  sprintf(p, "%s %d %d %d %d %d", l,
      ntohl(*rr->e.soa.serial),
      ntohl(*rr->e.soa.refresh),
      ntohl(*rr->e.soa.retry),
      ntohl(*rr->e.soa.expire),
      ntohl(*rr->e.soa.minimum));
 
  return p;
}

char *
rrmake_mb(rr_t *rr) {
  return NULL;
}

char *
rrmake_mg(rr_t *rr) {
  return NULL;
}

char *
rrmake_mr(rr_t *rr) {
  return NULL;
}

char *
rrmake_null(rr_t *rr) {
  return NULL;
}

char *
rrmake_wks(rr_t *rr) {
  return NULL;
}

char *
rrmake_ptr(rr_t *rr) {
  char *l;
  
  l=malloc(*rr->rdata+1);
  labels2domain(rr->rdata);
  strncpy(l, rr->rdata+1, *rr->rdata);
  l[(int)(*rr->rdata)]=0;

  return l;
}

char *
rrmake_hinfo(rr_t *rr) {
  return NULL;
}

char *
rrmake_minfo(rr_t *rr) {
  return NULL;
}

char *
rrmake_mx(rr_t *rr) {
  char *l, *p;
  
  l=malloc(*rr->e.mx.exchange+8);
  p=malloc(*rr->e.mx.exchange+1);
  labels2domain(rr->e.mx.exchange);
  strncpy(p, rr->e.mx.exchange+1, *rr->e.mx.exchange);
  p[(int)(*rr->e.mx.exchange)]=0;
  sprintf(l, "%d %s", ntohs(*rr->e.mx.preference),p);

  return l;
}

char *
rrmake_txt(rr_t *rr) {
  char *l;
  
  l=malloc(*rr->rdata+1);
  strncpy(l, rr->rdata+1, *rr->rdata);
  l[(int)(*rr->rdata)]=0;

  return l;
}
			  
typedef char *(* rrmaker_t)(rr_t *); // returns ptr to malloc()ed str w/ txt ver of rr

rrmaker_t rrmaker[T_S+1]={rrmake_null, rrmake_a, rrmake_ns, rrmake_md, rrmake_mf, rrmake_cname, rrmake_soa, rrmake_mb,
                        rrmake_mg, rrmake_mr, rrmake_null, rrmake_wks, rrmake_ptr, rrmake_hinfo, rrmake_minfo, rrmake_mx,
                        rrmake_txt};

char *classes[C_S+1]={"","IN","CS","CH","HS"};
char *types[T_S+1]={"","A","NS","MD","MF","CNAME","SOA","MB","MG","MR","NULL","WKS","PTR","HINFO","MINFO","MX","TXT"};

int
read_str_rr(char *l, rr_t *rr) {
  char *p; int n;
#define seek_next p=l; l=strchr(l, ' '); if (!l) return XELEN; *l=0;
  
  seek_next
  rr->name=malloc((n=strlen(p))+1); *rr->name=(u_int8_t)n; strncpy(rr->name+1, p, n);
  domain2labels(rr->name);
  
  l++; seek_next
  rr->ttl=htonl(atol(p));
  
  l++; seek_next
  if (strlen(p)!=2) return XELEN;
  for (n=0; n<C_S; n++) if (!strcmp(classes[n], p)) { rr->rrclass=htons(n); break; }
  if (n==C_S) return XEINVAL;

  l++; seek_next
  for (n=0; n<T_S; n++) if (!strcmp(types[n], p)) { rr->rrtype=htons(n); break; }
  if (n==T_S) return XEINVAL;
  if (!rrparser[n]) return XEINVAL;
#undef seek_next
  
  l++; p=l;
  return rrparser[n](p, rr);
}

int
dump_str_rr(char **s, rr_t *rr) {
  char *l,*p; int ll=*rr->name+1, n;
  
  rr->rrclass=ntohs(rr->rrclass);
  rr->rrtype=ntohs(rr->rrtype);
  
  if (!rr->rrclass || rr->rrclass>C_S || !rr->rrtype || rr->rrtype>T_S)
    return XEINVAL;
  
  l=malloc(ll); p=malloc(256);
  labels2domain(rr->name);
  strncpy(l, rr->name+1, *rr->name); l[(int)(*rr->name)]=0;
  sprintf(p, " %d %s %s ", ntohl(rr->ttl), classes[rr->rrclass], types[n=rr->rrtype]);
  l=realloc(l, ll+strlen(p)); strcat(l, p);

  free(p); p=rrmaker[n](rr);
  l=realloc(l, ll+strlen(p)); strcat(l, p);
  free(p);
  
  *s=malloc(strlen(l)+1);
  strcpy(*s, l);

  return XENONE;
}

