Adam Tkac 788dab
/*
Adam Tkac 788dab
 * Copyright (C) 2004, 2005 Stig Venaas <venaas@uninett.no>
Adam Tkac c768d8
 * $Id: ldap2zone.c,v 1.1 2007/07/24 15:18:00 atkac Exp $
Adam Tkac 788dab
 *
Adam Tkac 788dab
 * Permission to use, copy, modify, and distribute this software for any
Adam Tkac 788dab
 * purpose with or without fee is hereby granted, provided that the above
Adam Tkac 788dab
 * copyright notice and this permission notice appear in all copies.
Adam Tkac 788dab
 */
Adam Tkac 788dab
Adam Tkac c768d8
#define LDAP_DEPRECATED 1
Adam Tkac c768d8
Adam Tkac 788dab
#include <sys/types.h>
Adam Tkac 788dab
#include <stdio.h>
Adam Tkac 788dab
#include <stdlib.h>
Adam Tkac 788dab
#include <ctype.h>
Adam Tkac 788dab
Adam Tkac 788dab
#include <ldap.h>
Adam Tkac 788dab
Adam Tkac 788dab
struct string {
Adam Tkac 788dab
    void *data;
Adam Tkac 788dab
    size_t len;
Adam Tkac 788dab
};
Adam Tkac 788dab
Adam Tkac 788dab
struct assstack_entry {
Adam Tkac 788dab
    struct string key;
Adam Tkac 788dab
    struct string val;
Adam Tkac 788dab
    struct assstack_entry *next;
Adam Tkac 788dab
};
Adam Tkac 788dab
Adam Tkac 788dab
struct assstack_entry *assstack_find(struct assstack_entry *stack, struct string *key);
Adam Tkac 788dab
void assstack_push(struct assstack_entry **stack, struct assstack_entry *item);
Adam Tkac 788dab
void assstack_insertbottom(struct assstack_entry **stack, struct assstack_entry *item);
Adam Tkac 788dab
void printsoa(struct string *soa);
Adam Tkac 788dab
void printrrs(char *defaultttl, struct assstack_entry *item);
Adam Tkac 788dab
void print_zone(char *defaultttl, struct assstack_entry *stack);
Adam Tkac 788dab
void usage(char *name);
Adam Tkac 788dab
void err(char *name, const char *msg);
Adam Tkac 788dab
int putrr(struct assstack_entry **stack, struct berval *name, char *type, char *ttl, struct berval *val);
Adam Tkac 788dab
Adam Tkac 788dab
struct assstack_entry *assstack_find(struct assstack_entry *stack, struct string *key) {
Adam Tkac 788dab
    for (; stack; stack = stack->next)
Adam Tkac 788dab
	if (stack->key.len == key->len && !memcmp(stack->key.data, key->data, key->len))
Adam Tkac 788dab
	    return stack;
Adam Tkac 788dab
    return NULL;
Adam Tkac 788dab
}
Adam Tkac 788dab
Adam Tkac 788dab
void assstack_push(struct assstack_entry **stack, struct assstack_entry *item) {
Adam Tkac 788dab
    item->next = *stack;
Adam Tkac 788dab
    *stack = item;
Adam Tkac 788dab
}
Adam Tkac 788dab
Adam Tkac 788dab
void assstack_insertbottom(struct assstack_entry **stack, struct assstack_entry *item) {
Adam Tkac 788dab
    struct assstack_entry *p;
Adam Tkac 788dab
    
Adam Tkac 788dab
    item->next = NULL;
Adam Tkac 788dab
    if (!*stack) {
Adam Tkac 788dab
	*stack = item;
Adam Tkac 788dab
	return;
Adam Tkac 788dab
    }
Adam Tkac 788dab
    /* find end, should keep track of end somewhere */
Adam Tkac 788dab
    /* really a queue, not a stack */
Adam Tkac 788dab
    p = *stack;
Adam Tkac 788dab
    while (p->next)
Adam Tkac 788dab
	p = p->next;
Adam Tkac 788dab
    p->next = item;
Adam Tkac 788dab
}
Adam Tkac 788dab
Adam Tkac 788dab
void printsoa(struct string *soa) {
Adam Tkac 788dab
    char *s;
Adam Tkac 788dab
    size_t i;
Adam Tkac 788dab
    
Adam Tkac 788dab
    s = (char *)soa->data;
Adam Tkac 788dab
    i = 0;
Adam Tkac 788dab
    while (i < soa->len) {
Adam Tkac 788dab
	putchar(s[i]);
Adam Tkac 788dab
	if (s[i++] == ' ')
Adam Tkac 788dab
	    break;
Adam Tkac 788dab
    }
Adam Tkac 788dab
    while (i < soa->len) {
Adam Tkac 788dab
	putchar(s[i]);
Adam Tkac 788dab
	if (s[i++] == ' ')
Adam Tkac 788dab
	    break;
Adam Tkac 788dab
    } 
Adam Tkac 788dab
    printf("(\n\t\t\t\t");
Adam Tkac 788dab
    while (i < soa->len) {
Adam Tkac 788dab
	putchar(s[i]);
Adam Tkac 788dab
	if (s[i++] == ' ')
Adam Tkac 788dab
	    break;
Adam Tkac 788dab
    }
Adam Tkac 788dab
    printf("; Serialnumber\n\t\t\t\t");
Adam Tkac 788dab
    while (i < soa->len) {
Adam Tkac 788dab
	if (s[i] == ' ')
Adam Tkac 788dab
	    break;
Adam Tkac 788dab
	putchar(s[i++]);
Adam Tkac 788dab
    }
Adam Tkac 788dab
    i++;
Adam Tkac 788dab
    printf("\t; Refresh\n\t\t\t\t");
Adam Tkac 788dab
    while (i < soa->len) {
Adam Tkac 788dab
	if (s[i] == ' ')
Adam Tkac 788dab
	    break;
Adam Tkac 788dab
	putchar(s[i++]);
Adam Tkac 788dab
    }
Adam Tkac 788dab
    i++;
Adam Tkac 788dab
    printf("\t; Retry\n\t\t\t\t");
Adam Tkac 788dab
    while (i < soa->len) {
Adam Tkac 788dab
	if (s[i] == ' ')
Adam Tkac 788dab
	    break;
Adam Tkac 788dab
	putchar(s[i++]);
Adam Tkac 788dab
    }
Adam Tkac 788dab
    i++;
Adam Tkac 788dab
    printf("\t; Expire\n\t\t\t\t");
Adam Tkac 788dab
    while (i < soa->len) {
Adam Tkac 788dab
	putchar(s[i++]);
Adam Tkac 788dab
    }
Adam Tkac 788dab
    printf(" )\t; Minimum TTL\n");
Adam Tkac 788dab
}
Adam Tkac 788dab
Adam Tkac 788dab
void printrrs(char *defaultttl, struct assstack_entry *item) {
Adam Tkac 788dab
    struct assstack_entry *stack;
Adam Tkac 788dab
    char *s;
Adam Tkac 788dab
    int first;
Adam Tkac 788dab
    size_t i;
Adam Tkac 788dab
    char *ttl, *type;
Adam Tkac 788dab
    int top;
Adam Tkac 788dab
    
Adam Tkac 788dab
    s = (char *)item->key.data;
Adam Tkac 788dab
Adam Tkac 788dab
    if (item->key.len == 1 && *s == '@') {
Adam Tkac 788dab
	top = 1;
Adam Tkac 788dab
	printf("@\t");
Adam Tkac 788dab
    } else {
Adam Tkac 788dab
	top = 0;
Adam Tkac 788dab
	for (i = 0; i < item->key.len; i++)
Adam Tkac 788dab
	    putchar(s[i]);
Adam Tkac 788dab
	if (item->key.len < 8)
Adam Tkac 788dab
	    putchar('\t');
Adam Tkac 788dab
	putchar('\t');
Adam Tkac 788dab
    }
Adam Tkac 788dab
    
Adam Tkac 788dab
    first = 1;
Adam Tkac 788dab
    for (stack = (struct assstack_entry *) item->val.data; stack; stack = stack->next) {
Adam Tkac 788dab
	ttl = (char *)stack->key.data;
Adam Tkac 788dab
	s = strchr(ttl, ' ');
Adam Tkac 788dab
	*s++ = '\0';
Adam Tkac 788dab
	type = s;
Adam Tkac 788dab
	
Adam Tkac 788dab
	if (first)
Adam Tkac 788dab
	    first = 0;
Adam Tkac 788dab
        else
Adam Tkac 788dab
	    printf("\t\t");
Adam Tkac 788dab
	    
Adam Tkac 788dab
	if (strcmp(defaultttl, ttl))
Adam Tkac 788dab
	    printf("%s", ttl);
Adam Tkac 788dab
	putchar('\t');
Adam Tkac 788dab
	
Adam Tkac 788dab
	if (top) {
Adam Tkac 788dab
	    top = 0;
Adam Tkac 788dab
	    printf("IN\t%s\t", type);
Adam Tkac 788dab
	    /* Should always be SOA here */
Adam Tkac 788dab
	    if (!strcmp(type, "SOA")) {
Adam Tkac 788dab
		printsoa(&stack->val);
Adam Tkac 788dab
		continue;
Adam Tkac 788dab
	    }
Adam Tkac 788dab
	} else
Adam Tkac 788dab
	    printf("%s\t", type);
Adam Tkac 788dab
Adam Tkac 788dab
	s = (char *)stack->val.data;
Adam Tkac 788dab
	for (i = 0; i < stack->val.len; i++)
Adam Tkac 788dab
	    putchar(s[i]);
Adam Tkac 788dab
	putchar('\n');
Adam Tkac 788dab
    }
Adam Tkac 788dab
}
Adam Tkac 788dab
Adam Tkac 788dab
void print_zone(char *defaultttl, struct assstack_entry *stack) {
Adam Tkac 788dab
    printf("$TTL %s\n", defaultttl);
Adam Tkac 788dab
    for (; stack; stack = stack->next)
Adam Tkac 788dab
	printrrs(defaultttl, stack);
Adam Tkac 788dab
};
Adam Tkac 788dab
Adam Tkac 788dab
void usage(char *name) {
Adam Tkac 788dab
    fprintf(stderr, "Usage:%s zone-name LDAP-URL default-ttl [serial]\n", name);
Adam Tkac 788dab
    exit(1);
Adam Tkac 788dab
};
Adam Tkac 788dab
Adam Tkac 788dab
void err(char *name, const char *msg) {
Adam Tkac 788dab
    fprintf(stderr, "%s: %s\n", name, msg);
Adam Tkac 788dab
    exit(1);
Adam Tkac 788dab
};
Adam Tkac 788dab
Adam Tkac 788dab
int putrr(struct assstack_entry **stack, struct berval *name, char *type, char *ttl, struct berval *val) {
Adam Tkac 788dab
    struct string key;
Adam Tkac 788dab
    struct assstack_entry *rr, *rrdata;
Adam Tkac 788dab
    
Adam Tkac 788dab
    /* Do nothing if name or value have 0 length */
Adam Tkac 788dab
    if (!name->bv_len || !val->bv_len)
Adam Tkac 788dab
	return 0;
Adam Tkac 788dab
Adam Tkac 788dab
    /* see if already have an entry for this name */
Adam Tkac 788dab
    key.len = name->bv_len;
Adam Tkac 788dab
    key.data = name->bv_val;
Adam Tkac 788dab
Adam Tkac 788dab
    rr = assstack_find(*stack, &key);
Adam Tkac 788dab
    if (!rr) {
Adam Tkac 788dab
	/* Not found, create and push new entry */
Adam Tkac 788dab
	rr = (struct assstack_entry *) malloc(sizeof(struct assstack_entry));
Adam Tkac 788dab
	if (!rr)
Adam Tkac 788dab
	    return -1;
Adam Tkac 788dab
	rr->key.len = name->bv_len;
Adam Tkac 788dab
	rr->key.data = (void *) malloc(rr->key.len);
Adam Tkac 788dab
	if (!rr->key.data) {
Adam Tkac 788dab
	    free(rr);
Adam Tkac 788dab
	    return -1;
Adam Tkac 788dab
	}
Adam Tkac 788dab
	memcpy(rr->key.data, name->bv_val, name->bv_len);
Adam Tkac 788dab
	rr->val.len = sizeof(void *);
Adam Tkac 788dab
	rr->val.data = NULL;
Adam Tkac 788dab
	if (name->bv_len == 1 && *(char *)name->bv_val == '@')
Adam Tkac 788dab
	    assstack_push(stack, rr);
Adam Tkac 788dab
	else
Adam Tkac 788dab
	    assstack_insertbottom(stack, rr);
Adam Tkac 788dab
    }
Adam Tkac 788dab
Adam Tkac 788dab
    rrdata = (struct assstack_entry *) malloc(sizeof(struct assstack_entry));
Adam Tkac 788dab
    if (!rrdata) {
Adam Tkac 788dab
	free(rr->key.data);
Adam Tkac 788dab
	free(rr);
Adam Tkac 788dab
	return -1;
Adam Tkac 788dab
    }
Adam Tkac 788dab
    rrdata->key.len = strlen(type) + strlen(ttl) + 1;
Adam Tkac 788dab
    rrdata->key.data = (void *) malloc(rrdata->key.len);
Adam Tkac 788dab
    if (!rrdata->key.data) {
Adam Tkac 788dab
	free(rrdata);
Adam Tkac 788dab
	free(rr->key.data);
Adam Tkac 788dab
	free(rr);
Adam Tkac 788dab
	return -1;
Adam Tkac 788dab
    }
Adam Tkac 788dab
    sprintf((char *)rrdata->key.data, "%s %s", ttl, type);
Adam Tkac 788dab
	
Adam Tkac 788dab
    rrdata->val.len = val->bv_len;
Adam Tkac 788dab
    rrdata->val.data = (void *) malloc(val->bv_len);
Adam Tkac 788dab
    if (!rrdata->val.data) {
Adam Tkac 788dab
	free(rrdata->key.data);
Adam Tkac 788dab
	free(rrdata);
Adam Tkac 788dab
	free(rr->key.data);
Adam Tkac 788dab
	free(rr);
Adam Tkac 788dab
	return -1;
Adam Tkac 788dab
    }
Adam Tkac 788dab
    memcpy(rrdata->val.data, val->bv_val, val->bv_len);
Adam Tkac 788dab
Adam Tkac 788dab
    if (!strcmp(type, "SOA"))
Adam Tkac 788dab
	assstack_push((struct assstack_entry **) &(rr->val.data), rrdata);
Adam Tkac 788dab
    else
Adam Tkac 788dab
	assstack_insertbottom((struct assstack_entry **) &(rr->val.data), rrdata);
Adam Tkac 788dab
    return 0;
Adam Tkac 788dab
}
Adam Tkac 788dab
Adam Tkac 788dab
int main(int argc, char **argv) {
Adam Tkac 788dab
    char *s, *hostporturl, *base = NULL;
Adam Tkac 788dab
    char *ttl, *defaultttl;
Adam Tkac 788dab
    LDAP *ld;
Adam Tkac 788dab
    char *fltr = NULL;
Adam Tkac 788dab
    LDAPMessage *res, *e;
Adam Tkac 788dab
    char *a, **ttlvals, **soavals, *serial;
Adam Tkac 788dab
    struct berval **vals, **names;
Adam Tkac 788dab
    char type[64];
Adam Tkac 788dab
    BerElement *ptr;
Adam Tkac 788dab
    int i, j, rc, msgid;
Adam Tkac 788dab
    struct assstack_entry *zone = NULL;
Adam Tkac 788dab
    
Adam Tkac 788dab
    if (argc < 4 || argc > 5)
Adam Tkac 788dab
        usage(argv[0]);
Adam Tkac 788dab
Adam Tkac 788dab
    hostporturl = argv[2];
Adam Tkac 788dab
Adam Tkac 788dab
    if (hostporturl != strstr( hostporturl, "ldap"))
Adam Tkac 788dab
	err(argv[0], "Not an LDAP URL");
Adam Tkac 788dab
Adam Tkac 788dab
    s = strchr(hostporturl, ':');
Adam Tkac 788dab
Adam Tkac 788dab
    if (!s || strlen(s) < 3 || s[1] != '/' || s[2] != '/')
Adam Tkac 788dab
	err(argv[0], "Not an LDAP URL");
Adam Tkac 788dab
Adam Tkac 788dab
    s = strchr(s+3, '/');
Adam Tkac 788dab
    if (s) {
Adam Tkac 788dab
	*s++ = '\0';
Adam Tkac 788dab
	base = s;
Adam Tkac 788dab
	s = strchr(base, '?');
Adam Tkac 788dab
	if (s)
Adam Tkac 788dab
	    err(argv[0], "LDAP URL can only contain host, port and base");
Adam Tkac 788dab
    }
Adam Tkac 788dab
Adam Tkac 788dab
    defaultttl = argv[3];
Adam Tkac 788dab
    
Adam Tkac 788dab
    rc = ldap_initialize(&ld, hostporturl);
Adam Tkac 788dab
    if (rc != LDAP_SUCCESS)
Adam Tkac 788dab
	err(argv[0], "ldap_initialize() failed");
Adam Tkac 788dab
Adam Tkac 788dab
    if (argc == 5) {
Adam Tkac 788dab
	/* serial number specified, check if different from one in SOA */
Adam Tkac 788dab
	fltr = (char *)malloc(strlen(argv[1]) + strlen("(&(relativeDomainName=@)(zoneName=))") + 1);
Adam Tkac 788dab
	sprintf(fltr, "(&(relativeDomainName=@)(zoneName=%s))", argv[1]);
Adam Tkac 788dab
	msgid = ldap_search(ld, base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
Adam Tkac 788dab
	if (msgid == -1)
Adam Tkac 788dab
	    err(argv[0], "ldap_search() failed");
Adam Tkac 788dab
Adam Tkac 788dab
	while ((rc = ldap_result(ld, msgid, 0, NULL, &res)) != LDAP_RES_SEARCH_RESULT ) {
Adam Tkac 788dab
	    /* not supporting continuation references at present */
Adam Tkac 788dab
	    if (rc != LDAP_RES_SEARCH_ENTRY)
Adam Tkac 788dab
		err(argv[0], "ldap_result() returned cont.ref? Exiting");
Adam Tkac 788dab
Adam Tkac 788dab
	    /* only one entry per result message */
Adam Tkac 788dab
	    e = ldap_first_entry(ld, res);
Adam Tkac 788dab
	    if (e == NULL) {
Adam Tkac 788dab
		ldap_msgfree(res);
Adam Tkac 788dab
		err(argv[0], "ldap_first_entry() failed");
Adam Tkac 788dab
	    }
Adam Tkac 788dab
	
Adam Tkac 788dab
	    soavals = ldap_get_values(ld, e, "SOARecord");
Adam Tkac 788dab
	    if (soavals)
Adam Tkac 788dab
		break;
Adam Tkac 788dab
	}
Adam Tkac 788dab
Adam Tkac 788dab
	ldap_msgfree(res);
Adam Tkac 788dab
	if (!soavals) {
Adam Tkac 788dab
		err(argv[0], "No SOA Record found");
Adam Tkac 788dab
	}
Adam Tkac 788dab
	
Adam Tkac 788dab
	/* We have a SOA, compare serial numbers */
Adam Tkac 788dab
	/* Only checkinf first value, should be only one */
Adam Tkac 788dab
	s = strchr(soavals[0], ' ');
Adam Tkac 788dab
	s++;
Adam Tkac 788dab
	s = strchr(s, ' ');
Adam Tkac 788dab
	s++;
Adam Tkac 788dab
	serial = s;
Adam Tkac 788dab
	s = strchr(s, ' ');
Adam Tkac 788dab
	*s = '\0';
Adam Tkac 788dab
	if (!strcmp(serial, argv[4])) {
Adam Tkac 788dab
	    ldap_value_free(soavals);
Adam Tkac 788dab
	    err(argv[0], "serial numbers match");
Adam Tkac 788dab
	}
Adam Tkac 788dab
	ldap_value_free(soavals);
Adam Tkac 788dab
    }
Adam Tkac 788dab
Adam Tkac 788dab
    if (!fltr)
Adam Tkac 788dab
	fltr = (char *)malloc(strlen(argv[1]) + strlen("(zoneName=)") + 1);
Adam Tkac 788dab
    if (!fltr)
Adam Tkac 788dab
	err(argv[0], "Malloc failed");
Adam Tkac 788dab
    sprintf(fltr, "(zoneName=%s)", argv[1]);
Adam Tkac 788dab
Adam Tkac 788dab
    msgid = ldap_search(ld, base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
Adam Tkac 788dab
    if (msgid == -1)
Adam Tkac 788dab
	err(argv[0], "ldap_search() failed");
Adam Tkac 788dab
Adam Tkac 788dab
    while ((rc = ldap_result(ld, msgid, 0, NULL, &res)) != LDAP_RES_SEARCH_RESULT ) {
Adam Tkac 788dab
	/* not supporting continuation references at present */
Adam Tkac 788dab
	if (rc != LDAP_RES_SEARCH_ENTRY)
Adam Tkac 788dab
	    err(argv[0], "ldap_result() returned cont.ref? Exiting");
Adam Tkac 788dab
Adam Tkac 788dab
	/* only one entry per result message */
Adam Tkac 788dab
	e = ldap_first_entry(ld, res);
Adam Tkac 788dab
	if (e == NULL) {
Adam Tkac 788dab
	    ldap_msgfree(res);
Adam Tkac 788dab
	    err(argv[0], "ldap_first_entry() failed");
Adam Tkac 788dab
	}
Adam Tkac 788dab
	
Adam Tkac 788dab
	names = ldap_get_values_len(ld, e, "relativeDomainName");
Adam Tkac 788dab
	if (!names)
Adam Tkac 788dab
	    continue;
Adam Tkac 788dab
	
Adam Tkac 788dab
	ttlvals = ldap_get_values(ld, e, "dNSTTL");
Adam Tkac 788dab
	ttl = ttlvals ? ttlvals[0] : defaultttl;
Adam Tkac 788dab
Adam Tkac 788dab
	for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; a = ldap_next_attribute(ld, e, ptr)) {
Adam Tkac 788dab
	    char *s;
Adam Tkac 788dab
Adam Tkac 788dab
	    for (s = a; *s; s++)
Adam Tkac 788dab
		*s = toupper(*s);
Adam Tkac 788dab
	    s = strstr(a, "RECORD");
Adam Tkac 788dab
	    if ((s == NULL) || (s == a) || (s - a >= (signed int)sizeof(type))) {
Adam Tkac 788dab
		ldap_memfree(a);
Adam Tkac 788dab
		continue;
Adam Tkac 788dab
	    }
Adam Tkac 788dab
			
Adam Tkac 788dab
	    strncpy(type, a, s - a);
Adam Tkac 788dab
	    type[s - a] = '\0';
Adam Tkac 788dab
	    vals = ldap_get_values_len(ld, e, a);
Adam Tkac 788dab
	    if (vals) {
Adam Tkac 788dab
		for (i = 0; vals[i]; i++)
Adam Tkac 788dab
		    for (j = 0; names[j]; j++)
Adam Tkac 788dab
			if (putrr(&zone, names[j], type, ttl, vals[i]))
Adam Tkac 788dab
			    err(argv[0], "malloc failed");
Adam Tkac 788dab
		ldap_value_free_len(vals);
Adam Tkac 788dab
	    }
Adam Tkac 788dab
	    ldap_memfree(a);
Adam Tkac 788dab
	}
Adam Tkac 788dab
Adam Tkac 788dab
	if (ptr)
Adam Tkac 788dab
	    ber_free(ptr, 0);
Adam Tkac 788dab
	if (ttlvals)
Adam Tkac 788dab
	    ldap_value_free(ttlvals);
Adam Tkac 788dab
	ldap_value_free_len(names);
Adam Tkac 788dab
	/* free this result */
Adam Tkac 788dab
	ldap_msgfree(res);
Adam Tkac 788dab
    }
Adam Tkac 788dab
Adam Tkac 788dab
    /* free final result */
Adam Tkac 788dab
    ldap_msgfree(res);
Adam Tkac 788dab
Adam Tkac 788dab
    print_zone(defaultttl, zone);
Adam Tkac 788dab
    return 0;
Adam Tkac 788dab
}