newvector should be a bool
[freeradius.git] / src / modules / rlm_eap / radeapclient.c
index 30f1b25..3bbd00f 100644 (file)
@@ -22,7 +22,6 @@
  * Copyright 2000  Alan DeKok <aland@ox.org>
  */
 
-#include <freeradius-devel/ident.h>
 RCSID("$Id$")
 
 #include <freeradius-devel/libradius.h>
@@ -44,99 +43,137 @@ extern int sha1_data_problems;
 
 static int retries = 10;
 static float timeout = 3;
-static const char *secret = NULL;
+static char const *secret = NULL;
 static int do_output = 1;
 static int do_summary = 0;
-static int filedone = 0;
+static bool filedone = false;
 static int totalapp = 0;
 static int totaldeny = 0;
 static char filesecret[256];
-const char *radius_dir = RADDBDIR;
-const char *progname = "radeapclient";
+char const *radius_dir = NULL;
+char const *dict_dir = NULL;
+char const *progname = "radeapclient";
 /* fr_randctx randctx; */
 
+struct main_config_t main_config;
+char const *radiusd_version = "";
 
-radlog_dest_t radlog_dest = RADLOG_STDERR;
-const char *radlog_dir = NULL;
-int debug_flag = 0;
+#ifdef WITH_TLS
+#include <freeradius-devel/tls.h>
+#endif
+
+log_debug_t debug_flag = 0;
 char password[256];
 
 struct eapsim_keys eapsim_mk;
 
-static void map_eap_types(RADIUS_PACKET *req);
-static void unmap_eap_types(RADIUS_PACKET *rep);
+static void map_eap_methods(RADIUS_PACKET *req);
+static void unmap_eap_methods(RADIUS_PACKET *rep);
 static int map_eapsim_types(RADIUS_PACKET *r);
 static int unmap_eapsim_types(RADIUS_PACKET *r);
 
 static void NEVER_RETURNS usage(void)
 {
-       fprintf(stderr, "Usage: radeapclient [options] server[:port] <command> [<secret>]\n");
-
-       fprintf(stderr, "  <command>    One of auth, acct, status, or disconnect.\n");
-       fprintf(stderr, "  -c count    Send each packet 'count' times.\n");
-       fprintf(stderr, "  -d raddb    Set dictionary directory.\n");
-       fprintf(stderr, "  -f file     Read packets from file, not stdin.\n");
-       fprintf(stderr, "  -r retries  If timeout, retry sending the packet 'retries' times.\n");
-       fprintf(stderr, "  -t timeout  Wait 'timeout' seconds before retrying (may be a floating point number).\n");
-       fprintf(stderr, "  -i id       Set request id to 'id'.  Values may be 0..255\n");
-       fprintf(stderr, "  -S file     read secret from file, not command line.\n");
-       fprintf(stderr, "  -q          Do not print anything out.\n");
-       fprintf(stderr, "  -s          Print out summary information of auth results.\n");
-       fprintf(stderr, "  -v          Show program version information.\n");
-       fprintf(stderr, "  -x          Debugging mode.\n");
+       fprintf(stdout, "Usage: radeapclient [options] server[:port] <command> [<secret>]");
+
+       fprintf(stdout, " <command>    One of auth, acct, status, or disconnect.");
+       fprintf(stdout, " -d raddb     Set dictionary directory.");
+       fprintf(stdout, " -f file      Read packets from file, not stdin.");
+       fprintf(stdout, " -r retries   If timeout, retry sending the packet 'retries' times.");
+       fprintf(stdout, " -t timeout   Wait 'timeout' seconds before retrying (may be a floating point number).");
+       fprintf(stdout, " -h           Print usage help information.");
+       fprintf(stdout, " -i id        Set request id to 'id'.  Values may be 0..255");
+       fprintf(stdout, " -S file      read secret from file, not command line.");
+       fprintf(stdout, " -q           Do not print anything out.");
+       fprintf(stdout, " -s           Print out summary information of auth results.");
+       fprintf(stdout, " -v           Show program version information.");
+       fprintf(stdout, " -x           Debugging mode.");
+       fprintf(stdout, " -4           Use IPv4 address of server");
+       fprintf(stdout, " -6           Use IPv6 address of server.");
 
        exit(1);
 }
 
-int radlog(int lvl, const char *msg, ...)
+static uint16_t getport(char const *name)
 {
-       va_list ap;
-       int r;
-
-       r = lvl; /* shut up compiler */
+       struct  servent         *svp;
 
-       va_start(ap, msg);
-       r = vfprintf(stderr, msg, ap);
-       va_end(ap);
-       fputc('\n', stderr);
+       svp = getservbyname (name, "udp");
+       if (!svp) {
+               return 0;
+       }
 
-       return r;
+       return ntohs(svp->s_port);
 }
 
-int log_debug(const char *msg, ...)
+#define R_RECV (0)
+#define R_SENT (1)
+static void debug_packet(RADIUS_PACKET *packet, int direction)
 {
-       va_list ap;
-       int r;
+       VALUE_PAIR *vp;
+       char buffer[1024];
+       char const *received, *from;
+       fr_ipaddr_t const *ip;
+       uint16_t port;
 
-       va_start(ap, msg);
-       r = vfprintf(stderr, msg, ap);
-       va_end(ap);
-       fputc('\n', stderr);
+       if (!packet) return;
 
-       return r;
-}
+       if (direction == 0) {
+               received = "Received";
+               from = "from";  /* what else? */
+               ip = &packet->src_ipaddr;
+               port = packet->src_port;
 
-static int getport(const char *name)
-{
-       struct  servent         *svp;
+       } else {
+               received = "Sending";
+               from = "to";    /* hah! */
+               ip = &packet->dst_ipaddr;
+               port = packet->dst_port;
+       }
 
-       svp = getservbyname (name, "udp");
-       if (!svp) {
-               return 0;
+       /*
+        *      Client-specific debugging re-prints the input
+        *      packet into the client log.
+        *
+        *      This really belongs in a utility library
+        */
+       if (is_radius_code(packet->code)) {
+               DEBUG("%s %s packet %s host %s port %d, id=%d, length=%d",
+                      received, fr_packet_codes[packet->code], from,
+                      inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
+                      port, packet->id, (int) packet->data_len);
+       } else {
+               DEBUG("%s packet %s host %s port %d code=%d, id=%d, length=%d",
+                      received, from,
+                      inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
+                      port,
+                      packet->code, packet->id, (int) packet->data_len);
        }
 
-       return ntohs(svp->s_port);
+       for (vp = packet->vps; vp != NULL; vp = vp->next) {
+               vp_prints(buffer, sizeof(buffer), vp);
+               DEBUG("\t%s", buffer);
+       }
+       fflush(stdout);
 }
 
+
 static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
 {
        int i;
        struct timeval  tv;
 
+       if (!req || !rep || !*rep) return -1;
+
        for (i = 0; i < retries; i++) {
                fd_set          rdfdesc;
 
-               rad_send(req, NULL, secret);
+               debug_packet(req, R_SENT);
+
+               if (rad_send(req, NULL, secret) < 0) {
+                       ERROR("%s", fr_strerror());
+                       exit(1);
+               }
 
                /* And wait for reply, timing out as necessary */
                FD_ZERO(&rdfdesc);
@@ -150,7 +187,7 @@ static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
                        continue;
                }
 
-               *rep = rad_recv(req->sockfd);
+               *rep = rad_recv(req->sockfd, 0);
                if (*rep != NULL) {
                        /*
                         *      If we get a response from a machine
@@ -168,21 +205,21 @@ static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
 
                                ip_ntoh(&(*rep)->src_ipaddr, src, sizeof(src));
                                ip_ntoh(&req->dst_ipaddr, dst, sizeof(dst));
-                               fprintf(stderr, "radclient: ERROR: Sent request to host %s port %d, got response from host %s port %d\n!",
+                               ERROR("ERROR: Sent request to host %s port %d, got response from host %s port %d!",
                                        dst, req->dst_port,
                                        src, (*rep)->src_port);
                                exit(1);
                        }
                        break;
                } else {        /* NULL: couldn't receive the packet */
-                       librad_perror("radclient:");
+                       ERROR("%s", fr_strerror());
                        exit(1);
                }
        }
 
        /* No response or no data read (?) */
        if (i == retries) {
-               fprintf(stderr, "radclient: no response from server\n");
+               ERROR("rad_client: no response from server");
                exit(1);
        }
 
@@ -191,26 +228,24 @@ static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
         *
         *      Hmm... we should really be using eapol_test, which does
         *      a lot more than radeapclient.
-                */
+        */
        if (rad_verify(*rep, req, secret) != 0) {
-               librad_perror("rad_verify");
+               ERROR("rad_verify: %s", fr_strerror());
                exit(1);
        }
 
        if (rad_decode(*rep, req, secret) != 0) {
-               librad_perror("rad_decode");
+               ERROR("rad_decode: %s", fr_strerror());
                exit(1);
        }
 
        /* libradius debug already prints out the value pairs for us */
-       if (!librad_debug && do_output) {
-               printf("Received response ID %d, code %d, length = %d\n",
-                               (*rep)->id, (*rep)->code, (*rep)->data_len);
-               vp_printlist(stdout, (*rep)->vps);
+       if (!fr_debug_flag && do_output) {
+               debug_packet(*rep, R_RECV);
        }
-       if((*rep)->code == PW_AUTHENTICATION_ACK) {
+       if((*rep)->code == PW_CODE_ACCESS_ACCEPT) {
                totalapp++;
-       } else {
+       } else if ((*rep)->code == PW_CODE_ACCESS_REJECT) {
                totaldeny++;
        }
 
@@ -226,21 +261,21 @@ static void cleanresp(RADIUS_PACKET *resp)
         * maybe should just copy things we care about, or keep
         * a copy of the original input and start from there again?
         */
-       pairdelete(&resp->vps, PW_EAP_MESSAGE);
-       pairdelete(&resp->vps, ATTRIBUTE_EAP_BASE+PW_EAP_IDENTITY);
+       pairdelete(&resp->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+       pairdelete(&resp->vps, PW_EAP_TYPE_BASE+PW_EAP_IDENTITY, 0, TAG_ANY);
 
        last = &resp->vps;
        for(vp = *last; vp != NULL; vp = vpnext)
        {
                vpnext = vp->next;
 
-               if((vp->attribute > ATTRIBUTE_EAP_BASE &&
-                   vp->attribute <= ATTRIBUTE_EAP_BASE+256) ||
-                  (vp->attribute > ATTRIBUTE_EAP_SIM_BASE &&
-                   vp->attribute <= ATTRIBUTE_EAP_SIM_BASE+256))
+               if((vp->da->attr > PW_EAP_TYPE_BASE &&
+                   vp->da->attr <= PW_EAP_TYPE_BASE+256) ||
+                  (vp->da->attr > PW_EAP_SIM_BASE &&
+                   vp->da->attr <= PW_EAP_SIM_BASE+256))
                {
                        *last = vpnext;
-                       pairbasicfree(vp);
+                       talloc_free(vp);
                } else {
                        last = &vp->next;
                }
@@ -257,23 +292,24 @@ static int process_eap_start(RADIUS_PACKET *req,
 {
        VALUE_PAIR *vp, *newvp;
        VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
-       uint16_t *versions, selectedversion;
+       uint16_t const *versions;
+       uint16_t selectedversion;
        unsigned int i,versioncount;
 
        /* form new response clear of any EAP stuff */
        cleanresp(rep);
 
-       if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST)) == NULL) {
-               fprintf(stderr, "illegal start message has no VERSION_LIST\n");
+       if((vp = pairfind(req->vps, PW_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
+               ERROR("illegal start message has no VERSION_LIST");
                return 0;
        }
 
-       versions = (uint16_t *)vp->vp_strvalue;
+       versions = (uint16_t const *) vp->vp_strvalue;
 
        /* verify that the attribute length is big enough for a length field */
        if(vp->length < 4)
        {
-               fprintf(stderr, "start message has illegal VERSION_LIST. Too short: %d\n", vp->length);
+               ERROR("start message has illegal VERSION_LIST. Too short: %u", (unsigned int) vp->length);
                return 0;
        }
 
@@ -283,7 +319,7 @@ static int process_eap_start(RADIUS_PACKET *req,
         */
        if((unsigned)vp->length <= (versioncount*2 + 2))
        {
-               fprintf(stderr, "start message is too short. Claimed %d versions does not fit in %d bytes\n", versioncount, vp->length);
+               ERROR("start message is too short. Claimed %d versions does not fit in %u bytes", versioncount, (unsigned int) vp->length);
                return 0;
        }
 
@@ -291,7 +327,7 @@ static int process_eap_start(RADIUS_PACKET *req,
         * record the versionlist for the MK calculation.
         */
        eapsim_mk.versionlistlen = versioncount*2;
-       memcpy(eapsim_mk.versionlist, (unsigned char *)(versions+1),
+       memcpy(eapsim_mk.versionlist, (unsigned char const *)(versions+1),
               eapsim_mk.versionlistlen);
 
        /* walk the version list, and pick the one we support, which
@@ -308,10 +344,10 @@ static int process_eap_start(RADIUS_PACKET *req,
        }
        if(selectedversion == 0)
        {
-               fprintf(stderr, "eap-sim start message. No compatible version found. We need %d\n", EAP_SIM_VERSION);
+               ERROR("eap-sim start message. No compatible version found. We need %d", EAP_SIM_VERSION);
                for(i=0; i < versioncount; i++)
                {
-                       fprintf(stderr, "\tfound version %d\n",
+                       ERROR("\tfound version %d",
                                ntohs(versions[i+1]));
                }
        }
@@ -322,56 +358,60 @@ static int process_eap_start(RADIUS_PACKET *req,
         * anyway we like, but it is illegal to have more than one
         * present.
         */
-       anyidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ);
-       fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ);
-       permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ);
+       anyidreq_vp = pairfind(req->vps, PW_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
+       fullauthidreq_vp = pairfind(req->vps, PW_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
+       permanentidreq_vp = pairfind(req->vps, PW_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);
 
-       if(fullauthidreq_vp == NULL ||
+       if(!fullauthidreq_vp ||
           anyidreq_vp != NULL ||
           permanentidreq_vp != NULL) {
-               fprintf(stderr, "start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.\n",
-                       (anyidreq_vp != NULL ? "a " : "no "),
-                       (fullauthidreq_vp != NULL ? "a " : "no "),
-                       (permanentidreq_vp != NULL ? "a " : "no "));
+               ERROR("start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.",
+                       (anyidreq_vp != NULL ? "a ": "no "),
+                       (fullauthidreq_vp != NULL ? "a ": "no "),
+                       (permanentidreq_vp != NULL ? "a ": "no "));
                return 0;
        }
 
        /* okay, we have just any_id_req there, so fill in response */
 
        /* mark the subtype as being EAP-SIM/Response/Start */
-       newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
+       newvp = paircreate(rep, PW_EAP_SIM_SUBTYPE, 0);
        newvp->vp_integer = eapsim_start;
        pairreplace(&(rep->vps), newvp);
 
        /* insert selected version into response. */
-       newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION,
-                          PW_TYPE_OCTETS);
-       versions = (uint16_t *)newvp->vp_strvalue;
-       versions[0] = htons(selectedversion);
-       newvp->length = 2;
-       pairreplace(&(rep->vps), newvp);
+       {
+               uint16_t no_versions;
 
-       /* record the selected version */
-       memcpy(eapsim_mk.versionselect, (unsigned char *)versions, 2);
+               no_versions = htons(selectedversion);
+
+               newvp = paircreate(rep, PW_EAP_SIM_BASE + PW_EAP_SIM_SELECTED_VERSION, 0);
+               pairmemcpy(newvp, (uint8_t *) &no_versions, 2);
+               pairreplace(&(rep->vps), newvp);
+
+               /* record the selected version */
+               memcpy(eapsim_mk.versionselect, &no_versions, 2);
+       }
 
        vp = newvp = NULL;
 
        {
                uint32_t nonce[4];
+               uint8_t *p;
                /*
                 * insert a nonce_mt that we make up.
                 */
-               newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT,
-                                  PW_TYPE_OCTETS);
-               newvp->vp_octets[0]=0;
-               newvp->vp_octets[1]=0;
-               newvp->length = 18;  /* 16 bytes of nonce + padding */
-
                nonce[0]=fr_rand();
                nonce[1]=fr_rand();
                nonce[2]=fr_rand();
                nonce[3]=fr_rand();
-               memcpy(&newvp->vp_octets[2], nonce, 16);
+
+               newvp = paircreate(rep, PW_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT, 0);
+
+               p = talloc_zero_array(newvp, uint8_t, 18); /* 18 = 16 bytes of nonce + padding */
+               memcpy(&p[2], nonce, 16);
+               pairmemsteal(newvp, p);
+
                pairreplace(&(rep->vps), newvp);
 
                /* also keep a copy of the nonce! */
@@ -379,25 +419,28 @@ static int process_eap_start(RADIUS_PACKET *req,
        }
 
        {
-               uint16_t *pidlen, idlen;
+               uint16_t idlen;
+               uint8_t *p;
+               uint16_t no_idlen;
 
                /*
                 * insert the identity here.
                 */
-               vp = pairfind(rep->vps, PW_USER_NAME);
-               if(vp == NULL)
+               vp = pairfind(rep->vps, PW_USER_NAME, 0, TAG_ANY);
+               if(!vp)
                {
-                       fprintf(stderr, "eap-sim: We need to have a User-Name attribute!\n");
+                       ERROR("eap-sim: We need to have a User-Name attribute!");
                        return 0;
                }
-               newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY,
-                                  PW_TYPE_OCTETS);
+               newvp = paircreate(rep, PW_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY, 0);
+
                idlen = strlen(vp->vp_strvalue);
-               pidlen = (uint16_t *)newvp->vp_strvalue;
-               *pidlen = htons(idlen);
-               newvp->length = idlen + 2;
+               p = talloc_zero_array(newvp, uint8_t, idlen + 2);
+               no_idlen = htons(idlen);
+               memcpy(p, &no_idlen, 2);
+               memcpy(p + 2, vp->vp_strvalue, idlen);
+               pairmemsteal(newvp, p);
 
-               memcpy(&newvp->vp_strvalue[2], vp->vp_strvalue, idlen);
                pairreplace(&(rep->vps), newvp);
 
                /* record it */
@@ -418,8 +461,7 @@ static int process_eap_start(RADIUS_PACKET *req,
  * values.
  *
  */
-static int process_eap_challenge(RADIUS_PACKET *req,
-                                RADIUS_PACKET *rep)
+static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
 {
        VALUE_PAIR *newvp;
        VALUE_PAIR *mac, *randvp;
@@ -428,10 +470,10 @@ static int process_eap_challenge(RADIUS_PACKET *req,
        uint8_t calcmac[20];
 
        /* look for the AT_MAC and the challenge data */
-       mac   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC);
-       randvp= pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND);
-       if(mac == NULL || randvp == NULL) {
-               fprintf(stderr, "radeapclient: challenge message needs to contain RAND and MAC\n");
+       mac   = pairfind(req->vps, PW_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY);
+       randvp= pairfind(req->vps, PW_EAP_SIM_BASE+PW_EAP_SIM_RAND, 0, TAG_ANY);
+       if(!mac || !randvp) {
+               ERROR("challenge message needs to contain RAND and MAC");
                return 0;
        }
 
@@ -441,20 +483,20 @@ static int process_eap_challenge(RADIUS_PACKET *req,
         */
        {
          VALUE_PAIR *randcfgvp[3];
-         uint8_t *randcfg[3];
+         uint8_t const *randcfg[3];
 
          randcfg[0] = &randvp->vp_octets[2];
          randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
          randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2];
 
-         randcfgvp[0] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND1);
-         randcfgvp[1] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND2);
-         randcfgvp[2] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND3);
+         randcfgvp[0] = pairfind(rep->vps, PW_EAP_SIM_RAND1, 0, TAG_ANY);
+         randcfgvp[1] = pairfind(rep->vps, PW_EAP_SIM_RAND2, 0, TAG_ANY);
+         randcfgvp[2] = pairfind(rep->vps, PW_EAP_SIM_RAND3, 0, TAG_ANY);
 
-         if(randcfgvp[0] == NULL ||
-            randcfgvp[1] == NULL ||
-            randcfgvp[2] == NULL) {
-           fprintf(stderr, "radeapclient: needs to have rand1, 2 and 3 set.\n");
+         if(!randcfgvp[0] ||
+            !randcfgvp[1] ||
+            !randcfgvp[2]) {
+           ERROR("needs to have rand1, 2 and 3 set.");
            return 0;
          }
 
@@ -463,31 +505,30 @@ static int process_eap_challenge(RADIUS_PACKET *req,
             memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) {
            int rnum,i,j;
 
-           fprintf(stderr, "radeapclient: one of rand 1,2,3 didn't match\n");
+           ERROR("one of rand 1,2,3 didn't match");
            for(rnum = 0; rnum < 3; rnum++) {
-             fprintf(stderr, "received   rand %d: ", rnum);
+             ERROR("received   rand %d: ", rnum);
              j=0;
              for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
                if(j==4) {
-                 printf("_");
+                 DEBUG("_");
                  j=0;
                }
                j++;
 
-               fprintf(stderr, "%02x", randcfg[rnum][i]);
+               ERROR("%02x", randcfg[rnum][i]);
              }
-             fprintf(stderr, "\nconfigured rand %d: ", rnum);
+             ERROR("configured rand %d: ", rnum);
              j=0;
              for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
                if(j==4) {
-                 printf("_");
+                 DEBUG("_");
                  j=0;
                }
                j++;
 
-               fprintf(stderr, "%02x", randcfgvp[rnum]->vp_octets[i]);
+               ERROR("%02x", randcfgvp[rnum]->vp_octets[i]);
              }
-             fprintf(stderr, "\n");
            }
            return 0;
          }
@@ -500,28 +541,28 @@ static int process_eap_challenge(RADIUS_PACKET *req,
         * Really, they should be calculated from the RAND!
         *
         */
-       sres1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES1);
-       sres2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES2);
-       sres3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES3);
-
-       if(sres1 == NULL ||
-          sres2 == NULL ||
-          sres3 == NULL) {
-               fprintf(stderr, "radeapclient: needs to have sres1, 2 and 3 set.\n");
+       sres1 = pairfind(rep->vps, PW_EAP_SIM_SRES1, 0, TAG_ANY);
+       sres2 = pairfind(rep->vps, PW_EAP_SIM_SRES2, 0, TAG_ANY);
+       sres3 = pairfind(rep->vps, PW_EAP_SIM_SRES3, 0, TAG_ANY);
+
+       if(!sres1 ||
+          !sres2 ||
+          !sres3) {
+               ERROR("needs to have sres1, 2 and 3 set.");
                return 0;
        }
        memcpy(eapsim_mk.sres[0], sres1->vp_strvalue, sizeof(eapsim_mk.sres[0]));
        memcpy(eapsim_mk.sres[1], sres2->vp_strvalue, sizeof(eapsim_mk.sres[1]));
        memcpy(eapsim_mk.sres[2], sres3->vp_strvalue, sizeof(eapsim_mk.sres[2]));
 
-       Kc1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC1);
-       Kc2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC2);
-       Kc3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC3);
+       Kc1 = pairfind(rep->vps, PW_EAP_SIM_KC1, 0, TAG_ANY);
+       Kc2 = pairfind(rep->vps, PW_EAP_SIM_KC2, 0, TAG_ANY);
+       Kc3 = pairfind(rep->vps, PW_EAP_SIM_KC3, 0, TAG_ANY);
 
-       if(Kc1 == NULL ||
-          Kc2 == NULL ||
-          Kc3 == NULL) {
-               fprintf(stderr, "radeapclient: needs to have Kc1, 2 and 3 set.\n");
+       if(!Kc1 ||
+          !Kc2 ||
+          !Kc3) {
+               ERROR("needs to have Kc1, 2 and 3 set.");
                return 0;
        }
        memcpy(eapsim_mk.Kc[0], Kc1->vp_strvalue, sizeof(eapsim_mk.Kc[0]));
@@ -536,14 +577,14 @@ static int process_eap_challenge(RADIUS_PACKET *req,
        }
 
        /* verify the MAC, now that we have all the keys. */
-       if(eapsim_checkmac(req->vps, eapsim_mk.K_aut,
+       if(eapsim_checkmac(NULL, req->vps, eapsim_mk.K_aut,
                           eapsim_mk.nonce_mt, sizeof(eapsim_mk.nonce_mt),
                           calcmac)) {
-               printf("MAC check succeed\n");
+               DEBUG("MAC check succeed");
        } else {
                int i, j;
                j=0;
-               printf("calculated MAC (");
+               DEBUG("calculated MAC (");
                for (i = 0; i < 20; i++) {
                        if(j==4) {
                                printf("_");
@@ -551,9 +592,9 @@ static int process_eap_challenge(RADIUS_PACKET *req,
                        }
                        j++;
 
-                       printf("%02x", calcmac[i]);
+                       DEBUG("%02x", calcmac[i]);
                }
-               printf(" did not match\n");
+               DEBUG("did not match");
                return 0;
        }
 
@@ -561,25 +602,30 @@ static int process_eap_challenge(RADIUS_PACKET *req,
        cleanresp(rep);
 
        /* mark the subtype as being EAP-SIM/Response/Start */
-       newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
+       newvp = paircreate(rep, PW_EAP_SIM_SUBTYPE, 0);
        newvp->vp_integer = eapsim_challenge;
        pairreplace(&(rep->vps), newvp);
 
-       /*
-        * fill the SIM_MAC with a field that will in fact get appended
-        * to the packet before the MAC is calculated
-        */
-       newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC,
-                          PW_TYPE_OCTETS);
-       memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
-       memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
-       memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
-       newvp->length = EAPSIM_SRES_SIZE*3;
-       pairreplace(&(rep->vps), newvp);
+       {
+               uint8_t *p;
+               /*
+                * fill the SIM_MAC with a field that will in fact get appended
+                * to the packet before the MAC is calculated
+                */
+               newvp = paircreate(rep, PW_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0);
+
+               p = talloc_zero_array(newvp, uint8_t, EAPSIM_SRES_SIZE*3);
+               memcpy(p+EAPSIM_SRES_SIZE * 0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
+               memcpy(p+EAPSIM_SRES_SIZE * 1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
+               memcpy(p+EAPSIM_SRES_SIZE * 2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
+               pairmemsteal(newvp, p);
+
+               pairreplace(&(rep->vps), newvp);
+       }
+
+       newvp = paircreate(rep, PW_EAP_SIM_KEY, 0);
+       pairmemcpy(newvp, eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);
 
-       newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, PW_TYPE_OCTETS);
-       memcpy(newvp->vp_strvalue,    eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);
-       newvp->length = EAPSIM_AUTH_SIZE;
        pairreplace(&(rep->vps), newvp);
 
        return 1;
@@ -591,20 +637,19 @@ static int process_eap_challenge(RADIUS_PACKET *req,
  * the *reponse* is to the server.
  *
  */
-static int respond_eap_sim(RADIUS_PACKET *req,
-                          RADIUS_PACKET *resp)
+static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp)
 {
        enum eapsim_clientstates state, newstate;
        enum eapsim_subtype subtype;
        VALUE_PAIR *vp, *statevp, *radstate, *eapid;
        char statenamebuf[32], subtypenamebuf[32];
 
-       if ((radstate = paircopy2(req->vps, PW_STATE)) == NULL)
+       if ((radstate = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
        {
                return 0;
        }
 
-       if ((eapid = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL)
+       if ((eapid = paircopy2(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
        {
                return 0;
        }
@@ -613,10 +658,10 @@ static int respond_eap_sim(RADIUS_PACKET *req,
         * outselves to be in EAP-SIM-Start state if there is none.
         */
 
-       if((statevp = pairfind(resp->vps, ATTRIBUTE_EAP_SIM_STATE)) == NULL)
+       if((statevp = pairfind(resp->vps, PW_EAP_SIM_STATE, 0, TAG_ANY)) == NULL)
        {
                /* must be initial request */
-               statevp = paircreate(ATTRIBUTE_EAP_SIM_STATE, PW_TYPE_INTEGER);
+               statevp = paircreate(resp, PW_EAP_SIM_STATE, 0);
                statevp->vp_integer = eapsim_client_init;
                pairreplace(&(resp->vps), statevp);
        }
@@ -627,10 +672,7 @@ static int respond_eap_sim(RADIUS_PACKET *req,
         */
        unmap_eapsim_types(req);
 
-       printf("<+++ EAP-sim decoded packet:\n");
-       vp_printlist(stdout, req->vps);
-
-       if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_SUBTYPE)) == NULL)
+       if((vp = pairfind(req->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL)
        {
                return 0;
        }
@@ -650,7 +692,7 @@ static int respond_eap_sim(RADIUS_PACKET *req,
                case eapsim_notification:
                case eapsim_reauth:
                default:
-                       fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
+                       ERROR("sim in state %s message %s is illegal. Reply dropped.",
                                sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
                                sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
                        /* invalid state, drop message */
@@ -670,7 +712,7 @@ static int respond_eap_sim(RADIUS_PACKET *req,
                        break;
 
                default:
-                       fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
+                       ERROR("sim in state %s message %s is illegal. Reply dropped.",
                                sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
                                sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
                        /* invalid state, drop message */
@@ -680,7 +722,7 @@ static int respond_eap_sim(RADIUS_PACKET *req,
 
 
        default:
-               fprintf(stderr, "radeapclient: sim in illegal state %s\n",
+               ERROR("sim in illegal state %s",
                        sim_state2name(state, statenamebuf, sizeof(statenamebuf)));
                return 0;
        }
@@ -702,45 +744,42 @@ static int respond_eap_md5(RADIUS_PACKET *req,
                           RADIUS_PACKET *rep)
 {
        VALUE_PAIR *vp, *id, *state;
-       size_t valuesize, namesize;
+       size_t valuesize;
        uint8_t identifier;
-       uint8_t *value;
-       uint8_t *name;
+       uint8_t const *value;
        FR_MD5_CTX      context;
        uint8_t    response[16];
 
        cleanresp(rep);
 
-       if ((state = paircopy2(req->vps, PW_STATE)) == NULL)
+       if ((state = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
        {
-               fprintf(stderr, "radeapclient: no state attribute found\n");
+               ERROR("no state attribute found");
                return 0;
        }
 
-       if ((id = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL)
+       if ((id = paircopy2(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
        {
-               fprintf(stderr, "radeapclient: no EAP-ID attribute found\n");
+               ERROR("no EAP-ID attribute found");
                return 0;
        }
        identifier = id->vp_integer;
 
-       if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5)) == NULL)
+       if ((vp = pairfind(req->vps, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL)
        {
-               fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n");
+               ERROR("no EAP-MD5 attribute found");
                return 0;
        }
 
        /* got the details of the MD5 challenge */
        valuesize = vp->vp_octets[0];
        value = &vp->vp_octets[1];
-       name  = &vp->vp_octets[valuesize+1];
-       namesize = vp->length - (valuesize + 1);
 
        /* sanitize items */
        if(valuesize > vp->length)
        {
-               fprintf(stderr, "radeapclient: md5 valuesize if too big (%d > %d)\n",
-                       valuesize, vp->length);
+               ERROR("md5 valuesize if too big (%u > %u)",
+                       (unsigned int) valuesize, (unsigned int) vp->length);
                return 0;
        }
 
@@ -748,17 +787,25 @@ static int respond_eap_md5(RADIUS_PACKET *req,
         * buffer. We could also call rad_chap_encode, but it wants
         * a CHAP-Challenge, which we don't want to bother with.
         */
-       fr_MD5Init(&context);
-       fr_MD5Update(&context, &identifier, 1);
-       fr_MD5Update(&context, (uint8_t *) password, strlen(password));
-       fr_MD5Update(&context, value, valuesize);
-       fr_MD5Final(response, &context);
+       fr_md5_init(&context);
+       fr_md5_update(&context, &identifier, 1);
+       fr_md5_update(&context, (uint8_t *) password, strlen(password));
+       fr_md5_update(&context, value, valuesize);
+       fr_md5_final(response, &context);
 
-       vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, PW_TYPE_OCTETS);
-       vp->vp_octets[0]=16;
-       memcpy(&vp->vp_strvalue[1], response, 16);
-       vp->length = 17;
+       {
+               uint8_t *p;
+               uint8_t lg_response;
+
+               vp = paircreate(rep, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0);
+               vp->length = 17;
 
+               p = talloc_zero_array(vp, uint8_t, 17);
+               lg_response = 16;
+               memcpy(p, &lg_response, 1);
+               memcpy(p + 1, response, 16);
+               pairmemsteal(vp, p);
+       }
        pairreplace(&(rep->vps), vp);
 
        pairreplace(&(rep->vps), id);
@@ -777,19 +824,21 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
        VALUE_PAIR *vp, *vpnext;
        int tried_eap_md5 = 0;
 
+       if (!rep) return -1;
+
        /*
         *      Keep a copy of the the User-Password attribute.
         */
-       if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD)) != NULL) {
-               strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue));
+       if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
+               strlcpy(password, vp->vp_strvalue, sizeof(password));
 
-       } else  if ((vp = pairfind(rep->vps, PW_USER_PASSWORD)) != NULL) {
-               strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue));
+       } else  if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
+               strlcpy(password, vp->vp_strvalue, sizeof(password));
                /*
                 *      Otherwise keep a copy of the CHAP-Password attribute.
                 */
-       } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD)) != NULL) {
-               strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue));
+       } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
+               strlcpy(password, vp->vp_strvalue, sizeof(password));
        } else {
                *password = '\0';
        }
@@ -797,20 +846,17 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
  again:
        rep->id++;
 
-       printf("\n+++> About to send encoded packet:\n");
-       vp_printlist(stdout, rep->vps);
-
        /*
         * if there are EAP types, encode them into an EAP-Message
         *
         */
-       map_eap_types(rep);
+       map_eap_methods(rep);
 
        /*
         *  Fix up Digest-Attributes issues
         */
        for (vp = rep->vps; vp != NULL; vp = vp->next) {
-               switch (vp->attribute) {
+               switch (vp->da->attr) {
                default:
                        break;
 
@@ -824,12 +870,38 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
                case PW_DIGEST_CNONCE:
                case PW_DIGEST_NONCE_COUNT:
                case PW_DIGEST_USER_NAME:
-                       /* overlapping! */
-                       memmove(&vp->vp_strvalue[2], &vp->vp_octets[0], vp->length);
-                       vp->vp_octets[0] = vp->attribute - PW_DIGEST_REALM + 1;
+               /* overlapping! */
+               {
+                       DICT_ATTR const *da;
+                       uint8_t *p, *q;
+
+                       p = talloc_array(vp, uint8_t, vp->length + 2);
+
+                       memcpy(p + 2, vp->vp_octets, vp->length);
+                       p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
                        vp->length += 2;
-                       vp->vp_octets[1] = vp->length;
-                       vp->attribute = PW_DIGEST_ATTRIBUTES;
+                       p[1] = vp->length;
+
+                       da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
+                       vp->da = da;
+
+                       /*
+                        *      Re-do pairmemsteal ourselves,
+                        *      because we play games with
+                        *      vp->da, and pairmemsteal goes
+                        *      to GREAT lengths to sanitize
+                        *      and fix and change and
+                        *      double-check the various
+                        *      fields.
+                        */
+                       memcpy(&q, &vp->vp_octets, sizeof(q));
+                       talloc_free(q);
+
+                       vp->vp_octets = talloc_steal(vp, p);
+                       vp->type = VT_DATA;
+
+                       VERIFY_VP(vp);
+               }
                        break;
                }
        }
@@ -840,59 +912,58 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
         *      ID and authentication vector.
         */
        if (rep->data) {
-               free(rep->data);
+               talloc_free(rep->data);
                rep->data = NULL;
        }
 
-       fr_md5_calc(rep->vector, rep->vector,
-                       sizeof(rep->vector));
+       fr_md5_calc(rep->vector, rep->vector, sizeof(rep->vector));
 
        if (*password != '\0') {
-               if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD)) != NULL) {
-                       strlcpy((char *)vp->vp_strvalue, password, sizeof(vp->vp_strvalue));
-                       vp->length = strlen(password);
+               if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
+                       pairstrcpy(vp, password);
 
-               } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD)) != NULL) {
-                       strlcpy((char *)vp->vp_strvalue, password, sizeof(vp->vp_strvalue));
-                       vp->length = strlen(password);
+               } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
+                       pairstrcpy(vp, password);
 
-               } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD)) != NULL) {
-                       strlcpy((char *)vp->vp_strvalue, password, sizeof(vp->vp_strvalue));
-                       vp->length = strlen(password);
+               } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
+                       pairstrcpy(vp, password);
 
-                       rad_chap_encode(rep, vp->vp_octets, rep->id, vp);
-                       vp->length = 17;
+                       uint8_t *p;
+                       p = talloc_zero_array(vp, uint8_t, 17);
+                       rad_chap_encode(rep, p, rep->id, vp);
+                       pairmemsteal(vp, p);
                }
        } /* there WAS a password */
 
        /* send the response, wait for the next request */
-       send_packet(rep, &req);
+       send_packet(req, &rep);
+       if (!rep) {
+               ERROR("Failed getting response");
+               return -1;
+       }
 
        /* okay got back the packet, go and decode the EAP-Message. */
-       unmap_eap_types(req);
+       unmap_eap_methods(req);
 
-       printf("<+++ EAP decoded packet:\n");
-       vp_printlist(stdout, req->vps);
+       debug_packet(req, R_RECV);
 
        /* now look for the code type. */
        for (vp = req->vps; vp != NULL; vp = vpnext) {
                vpnext = vp->next;
 
-               switch (vp->attribute) {
+               switch (vp->da->attr) {
                default:
                        break;
 
-               case ATTRIBUTE_EAP_BASE+PW_EAP_MD5:
-                       if(respond_eap_md5(req, rep) && tried_eap_md5 < 3)
-                       {
+               case PW_EAP_TYPE_BASE + PW_EAP_MD5:
+                       if (respond_eap_md5(req, rep) && tried_eap_md5 < 3) {
                                tried_eap_md5++;
                                goto again;
                        }
                        break;
 
-               case ATTRIBUTE_EAP_BASE+PW_EAP_SIM:
-                       if(respond_eap_sim(req, rep))
-                       {
+               case PW_EAP_TYPE_BASE + PW_EAP_SIM:
+                       if (respond_eap_sim(req, rep)) {
                                goto again;
                        }
                        break;
@@ -903,32 +974,66 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
 }
 
 
+void set_radius_dir(TALLOC_CTX *ctx, char const *path)
+{
+       if (radius_dir) {
+               char *p;
+
+               memcpy(&p, &radius_dir, sizeof(p));
+               talloc_free(p);
+               radius_dir = NULL;
+       }
+       if (path) radius_dir = talloc_strdup(ctx, path);
+}
+
+
 int main(int argc, char **argv)
 {
        RADIUS_PACKET *req;
        char *p;
        int c;
-       int port = 0;
+       uint16_t port = 0;
        char *filename = NULL;
        FILE *fp;
-       int count = 1;
        int id;
+       int force_af = AF_UNSPEC;
+
+       static fr_log_t radclient_log = {
+               .colourise = true,
+               .fd = STDOUT_FILENO,
+               .dst = L_DST_STDOUT,
+               .file = NULL,
+               .debug_file = NULL,
+       };
+
+       radlog_init(&radclient_log, false);
+
+       /*
+        *      We probably don't want to free the talloc autofree context
+        *      directly, so we'll allocate a new context beneath it, and
+        *      free that before any leak reports.
+        */
+       TALLOC_CTX *autofree = talloc_init("main");
 
        id = ((int)getpid() & 0xff);
-       librad_debug = 0;
+       fr_debug_flag = 0;
 
-       radlog_dest = RADLOG_STDERR;
+       set_radius_dir(autofree, RADIUS_DIR);
 
-       while ((c = getopt(argc, argv, "c:d:f:hi:qst:r:S:xXv")) != EOF)
+       while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF)
        {
                switch(c) {
-               case 'c':
-                       if (!isdigit((int) *optarg))
-                               usage();
-                       count = atoi(optarg);
+               case '4':
+                       force_af = AF_INET;
+                       break;
+               case '6':
+                       force_af = AF_INET6;
                        break;
                case 'd':
-                       radius_dir = optarg;
+                       set_radius_dir(autofree, optarg);
+                       break;
+               case 'D':
+                       main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
                        break;
                case 'f':
                        filename = optarg;
@@ -937,8 +1042,8 @@ int main(int argc, char **argv)
                        do_output = 0;
                        break;
                case 'x':
-                       debug_flag++;
-                       librad_debug++;
+                       debug_flag++;
+                       fr_debug_flag++;
                        break;
 
                case 'X':
@@ -971,24 +1076,24 @@ int main(int argc, char **argv)
                        timeout = atof(optarg);
                        break;
                case 'v':
-                       printf("radclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
+                       printf("$Id$ built on "__DATE__ "at "__TIME__ "");
                        exit(0);
                        break;
-               case 'S':
+              case 'S':
                       fp = fopen(optarg, "r");
-                       if (!fp) {
-                               fprintf(stderr, "radclient: Error opening %s: %s\n",
-                                       optarg, strerror(errno));
-                               exit(1);
-                       }
-                       if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
-                               fprintf(stderr, "radclient: Error reading %s: %s\n",
-                                       optarg, strerror(errno));
-                               exit(1);
-                       }
+                      if (!fp) {
+                              ERROR("Error opening %s: %s",
+                                      optarg, fr_syserror(errno));
+                              exit(1);
+                      }
+                      if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
+                              ERROR("Error reading %s: %s",
+                                      optarg, fr_syserror(errno));
+                              exit(1);
+                      }
                       fclose(fp);
 
-                       /* truncate newline */
+                      /* truncate newline */
                       p = filesecret + strlen(filesecret) - 1;
                       while ((p >= filesecret) &&
                              (*p < ' ')) {
@@ -996,11 +1101,11 @@ int main(int argc, char **argv)
                               --p;
                       }
 
-                       if (strlen(filesecret) < 2) {
-                               fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
-                               exit(1);
-                       }
-                       secret = filesecret;
+                      if (strlen(filesecret) < 2) {
+                              ERROR("Secret in %s is too short", optarg);
+                              exit(1);
+                      }
+                      secret = filesecret;
                       break;
                case 'h':
                default:
@@ -1012,43 +1117,90 @@ int main(int argc, char **argv)
        argv += (optind - 1);
 
        if ((argc < 3)  ||
-           ((secret == NULL) && (argc < 4))) {
+           ((!secret) && (argc < 4))) {
                usage();
        }
 
-       if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
-               librad_perror("radclient");
-               return 1;
+
+       if (!main_config.dictionary_dir) {
+               main_config.dictionary_dir = DICTDIR;
        }
 
-       if ((req = rad_alloc(1)) == NULL) {
-               librad_perror("radclient");
+       /*
+        *      Read the distribution dictionaries first, then
+        *      the ones in raddb.
+        */
+       DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
+       if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
+               ERROR("Errors reading dictionary: %s", fr_strerror());
                exit(1);
        }
 
-#if 0
-       {
-               FILE *randinit;
+       /*
+        *      It's OK if this one doesn't exist.
+        */
+       int rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
+       if (rcode == -1) {
+               ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY, fr_strerror());
+               exit(1);
+       }
 
-               if((randinit = fopen("/dev/urandom", "r")) == NULL)
-               {
-                       perror("/dev/urandom");
-               } else {
-                       fread(randctx.randrsl, 256, 1, randinit);
-                       fclose(randinit);
-               }
+       /*
+        *      We print this after reading it.  That way if
+        *      it doesn't exist, it's OK, and we don't print
+        *      anything.
+        */
+       if (rcode == 0) {
+               DEBUG2("Including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
        }
-       fr_randinit(&randctx, 1);
-#endif
 
+       req = rad_alloc(NULL, true);
+       if (!req) {
+               ERROR("%s", fr_strerror());
+               exit(1);
+       }
        req->id = id;
 
        /*
-        *      Strip port from hostname if needed.
+        *      Resolve hostname.
         */
-       if ((p = strchr(argv[1], ':')) != NULL) {
-               *p++ = 0;
-               port = atoi(p);
+       if (force_af == AF_UNSPEC) force_af = AF_INET;
+       req->dst_ipaddr.af = force_af;
+       if (strcmp(argv[1], "-") != 0) {
+               char const *hostname = argv[1];
+               char const *portname = argv[1];
+               char buffer[256];
+
+               if (*argv[1] == '[') { /* IPv6 URL encoded */
+                       p = strchr(argv[1], ']');
+                       if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
+                               usage();
+                       }
+
+                       memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
+                       buffer[p - argv[1] - 1] = '\0';
+
+                       hostname = buffer;
+                       portname = p + 1;
+
+               }
+               p = strchr(portname, ':');
+               if (p && (strchr(p + 1, ':') == NULL)) {
+                       *p = '\0';
+                       portname = p + 1;
+               } else {
+                       portname = NULL;
+               }
+
+               if (ip_hton(&req->dst_ipaddr, force_af, hostname, false) < 0) {
+                       ERROR("Failed to find IP address for host %s: %s", hostname, fr_syserror(errno));
+                       exit(1);
+               }
+
+               /*
+                *      Strip port from hostname if needed.
+                */
+               if (portname) port = atoi(portname);
        }
 
        /*
@@ -1057,22 +1209,22 @@ int main(int argc, char **argv)
        if (strcmp(argv[2], "auth") == 0) {
                if (port == 0) port = getport("radius");
                if (port == 0) port = PW_AUTH_UDP_PORT;
-               req->code = PW_AUTHENTICATION_REQUEST;
+               req->code = PW_CODE_ACCESS_REQUEST;
 
        } else if (strcmp(argv[2], "acct") == 0) {
                if (port == 0) port = getport("radacct");
                if (port == 0) port = PW_ACCT_UDP_PORT;
-               req->code = PW_ACCOUNTING_REQUEST;
+               req->code = PW_CODE_ACCOUNTING_REQUEST;
                do_summary = 0;
 
        } else if (strcmp(argv[2], "status") == 0) {
                if (port == 0) port = getport("radius");
                if (port == 0) port = PW_AUTH_UDP_PORT;
-               req->code = PW_STATUS_SERVER;
+               req->code = PW_CODE_STATUS_SERVER;
 
        } else if (strcmp(argv[2], "disconnect") == 0) {
                if (port == 0) port = PW_POD_UDP_PORT;
-               req->code = PW_DISCONNECT_REQUEST;
+               req->code = PW_CODE_DISCONNECT_REQUEST;
 
        } else if (isdigit((int) argv[2][0])) {
                if (port == 0) port = getport("radius");
@@ -1081,15 +1233,7 @@ int main(int argc, char **argv)
        } else {
                usage();
        }
-
-       /*
-        *      Resolve hostname.
-        */
        req->dst_port = port;
-       if (ip_hton(argv[1], AF_INET, &req->dst_ipaddr) < 0) {
-               fprintf(stderr, "radclient: Failed to find IP address for host %s\n", argv[1]);
-               exit(1);
-       }
 
        /*
         *      Add the secret.
@@ -1104,8 +1248,7 @@ int main(int argc, char **argv)
        if (filename && (strcmp(filename, "-") != 0)) {
                fp = fopen(filename, "r");
                if (!fp) {
-                       fprintf(stderr, "radclient: Error opening %s: %s\n",
-                               filename, strerror(errno));
+                       ERROR("Error opening %s: %s", filename, fr_syserror(errno));
                        exit(1);
                }
        } else {
@@ -1116,25 +1259,27 @@ int main(int argc, char **argv)
         *      Send request.
         */
        if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-               perror("radclient: socket: ");
+               ERROR("socket: %s", fr_syserror(errno));
                exit(1);
        }
 
-       while(!filedone) {
-               if(req->vps) pairfree(&req->vps);
-
-               if ((req->vps = readvp2(fp, &filedone, "radeapclient:"))
-                   == NULL) {
+       while (!filedone) {
+               if (req->vps) pairfree(&req->vps);
+               if (readvp2(&req->vps, NULL, fp, &filedone) < 0) {
+                       ERROR("%s", fr_strerror());
                        break;
                }
 
                sendrecv_eap(req);
        }
 
-       if(do_summary) {
-               printf("\n\t   Total approved auths:  %d\n", totalapp);
-               printf("\t     Total denied auths:  %d\n", totaldeny);
+       if (do_summary) {
+               INFO("\n\t   Total approved auths:  %d", totalapp);
+               INFO("\t     Total denied auths:  %d", totaldeny);
        }
+
+       talloc_free(autofree);
+
        return 0;
 }
 
@@ -1150,45 +1295,45 @@ int main(int argc, char **argv)
  *       just deserves an assert?
  *
  */
-static void map_eap_types(RADIUS_PACKET *req)
+static void map_eap_methods(RADIUS_PACKET *req)
 {
        VALUE_PAIR *vp, *vpnext;
        int id, eapcode;
-       EAP_PACKET ep;
-       int eap_type;
+       int eap_method;
 
-       vp = pairfind(req->vps, ATTRIBUTE_EAP_ID);
-       if(vp == NULL) {
+       eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t);
+
+       vp = pairfind(req->vps, PW_EAP_ID, 0, TAG_ANY);
+       if(!vp) {
                id = ((int)getpid() & 0xff);
        } else {
                id = vp->vp_integer;
        }
 
-       vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE);
-       if(vp == NULL) {
+       vp = pairfind(req->vps, PW_EAP_CODE, 0, TAG_ANY);
+       if(!vp) {
                eapcode = PW_EAP_REQUEST;
        } else {
                eapcode = vp->vp_integer;
        }
 
-
        for(vp = req->vps; vp != NULL; vp = vpnext) {
                /* save it in case it changes! */
                vpnext = vp->next;
 
-               if(vp->attribute >= ATTRIBUTE_EAP_BASE &&
-                  vp->attribute < ATTRIBUTE_EAP_BASE+256) {
+               if(vp->da->attr >= PW_EAP_TYPE_BASE &&
+                  vp->da->attr < PW_EAP_TYPE_BASE+256) {
                        break;
                }
        }
 
-       if(vp == NULL) {
+       if(!vp) {
                return;
        }
 
-       eap_type = vp->attribute - ATTRIBUTE_EAP_BASE;
+       eap_method = vp->da->attr - PW_EAP_TYPE_BASE;
 
-       switch(eap_type) {
+       switch(eap_method) {
        case PW_EAP_IDENTITY:
        case PW_EAP_NOTIFICATION:
        case PW_EAP_NAK:
@@ -1206,16 +1351,17 @@ static void map_eap_types(RADIUS_PACKET *req)
                 */
 
                /* nuke any existing EAP-Messages */
-               pairdelete(&req->vps, PW_EAP_MESSAGE);
+               pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
 
-               memset(&ep, 0, sizeof(ep));
-               ep.code = eapcode;
-               ep.id   = id;
-               ep.type.type = eap_type;
-               ep.type.length = vp->length;
-               ep.type.data = malloc(vp->length);
-               memcpy(ep.type.data,vp->vp_octets, vp->length);
-               eap_basic_compose(req, &ep);
+               pt_ep->code = eapcode;
+               pt_ep->id = id;
+               pt_ep->type.num = eap_method;
+               pt_ep->type.length = vp->length;
+
+               pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->length);
+               talloc_set_type(pt_ep->type.data, uint8_t);
+
+               eap_basic_compose(req, pt_ep);
        }
 }
 
@@ -1223,30 +1369,31 @@ static void map_eap_types(RADIUS_PACKET *req)
  * given a radius request with an EAP-Message body, decode it specific
  * attributes.
  */
-static void unmap_eap_types(RADIUS_PACKET *rep)
+static void unmap_eap_methods(RADIUS_PACKET *rep)
 {
        VALUE_PAIR *eap1;
-       eap_packet_t *e;
+       eap_packet_raw_t *e;
        int len;
        int type;
 
+       if (!rep) return;
+
        /* find eap message */
-       e = eap_vp2packet(rep->vps);
+       e = eap_vp2packet(NULL, rep->vps);
 
        /* nothing to do! */
-       if(e == NULL) return;
+       if(!e) return;
 
        /* create EAP-ID and EAP-CODE attributes to start */
-       eap1 = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
+       eap1 = paircreate(rep, PW_EAP_ID, 0);
        eap1->vp_integer = e->id;
        pairadd(&(rep->vps), eap1);
 
-       eap1 = paircreate(ATTRIBUTE_EAP_CODE, PW_TYPE_INTEGER);
+       eap1 = paircreate(rep, PW_EAP_CODE, 0);
        eap1->vp_integer = e->code;
        pairadd(&(rep->vps), eap1);
 
-       switch(e->code)
-       {
+       switch(e->code) {
        default:
        case PW_EAP_SUCCESS:
        case PW_EAP_FAILURE:
@@ -1267,74 +1414,73 @@ static void unmap_eap_types(RADIUS_PACKET *rep)
                /* verify the length is big enough to hold type */
                if(len < 5)
                {
+                       talloc_free(e);
                        return;
                }
 
                type = e->data[0];
 
-               type += ATTRIBUTE_EAP_BASE;
+               type += PW_EAP_TYPE_BASE;
                len -= 5;
 
-               if(len > MAX_STRING_LEN) {
+               if (len > MAX_STRING_LEN) {
                        len = MAX_STRING_LEN;
                }
 
-               eap1 = paircreate(type, PW_TYPE_OCTETS);
-               memcpy(eap1->vp_strvalue, &e->data[1], len);
-               eap1->length = len;
+               eap1 = paircreate(rep, type, 0);
+               pairmemcpy(eap1, e->data + 1, len);
+
                pairadd(&(rep->vps), eap1);
                break;
        }
 
+       talloc_free(e);
        return;
 }
 
 static int map_eapsim_types(RADIUS_PACKET *r)
 {
-       EAP_PACKET ep;
        int ret;
 
-       memset(&ep, 0, sizeof(ep));
-       ret = map_eapsim_basictypes(r, &ep);
+       eap_packet_t *pt_ep = talloc_zero(r, eap_packet_t);
+
+       ret = map_eapsim_basictypes(r, pt_ep);
+
        if(ret != 1) {
                return ret;
        }
-       eap_basic_compose(r, &ep);
+
+       eap_basic_compose(r, pt_ep);
 
        return 1;
 }
 
 static int unmap_eapsim_types(RADIUS_PACKET *r)
 {
-       VALUE_PAIR             *esvp;
+       VALUE_PAIR           *esvp;
+       uint8_t *eap_data;
+       int rcode_unmap;
 
-       esvp = pairfind(r->vps, ATTRIBUTE_EAP_BASE+PW_EAP_SIM);
-       if (esvp == NULL) {
-               radlog(L_ERR, "eap: EAP-Sim attribute not found");
+       esvp = pairfind(r->vps, PW_EAP_TYPE_BASE+PW_EAP_SIM, 0, TAG_ANY);
+       if (!esvp) {
+               ERROR("eap: EAP-Sim attribute not found");
                return 0;
        }
 
-       return unmap_eapsim_basictypes(r, esvp->vp_octets, esvp->length);
+       eap_data = talloc_memdup(esvp, esvp->vp_octets, esvp->length);
+       talloc_set_type(eap_data, uint8_t);
+
+       rcode_unmap = unmap_eapsim_basictypes(r, eap_data, esvp->length);
+
+       talloc_free(eap_data);
+       return rcode_unmap;
 }
 
 #ifdef TEST_CASE
 
 #include <assert.h>
 
-const char *radius_dir = RADDBDIR;
-
-int radlog(int lvl, const char *msg, ...)
-{
-       va_list ap;
-       int r;
-
-       va_start(ap, msg);
-       r = vfprintf(stderr, msg, ap);
-       va_end(ap);
-       fputc('\n', stderr);
-
-       return r;
-}
+char const *radius_dir = RADDBDIR;
 
 main(int argc, char *argv[])
 {
@@ -1352,54 +1498,63 @@ main(int argc, char *argv[])
        }
 
        if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
-               librad_perror("radclient");
+               ERROR("%s", fr_strerror());
                return 1;
        }
 
-       if ((req = rad_alloc(1)) == NULL) {
-               librad_perror("radclient");
+       req = rad_alloc(NULL, true)
+       if (!req) {
+               ERROR("%s", fr_strerror());
                exit(1);
        }
 
-       if ((req2 = rad_alloc(1)) == NULL) {
-               librad_perror("radclient");
+       req2 = rad_alloc(NULL, true);
+       if (!req2) {
+               ERROR("%s", fr_strerror());
                exit(1);
        }
 
        while(!filedone) {
-               if(req->vps) pairfree(&req->vps);
-               if(req2->vps) pairfree(&req2->vps);
+               if (req->vps) pairfree(&req->vps);
+               if (req2->vps) pairfree(&req2->vps);
 
-               if ((req->vps = readvp2(stdin, &filedone, "eapsimlib:")) == NULL) {
+               if (readvp2(&req->vps, NULL, stdin, &filedone) < 0) {
+                       ERROR("%s", fr_strerror());
                        break;
                }
 
-               printf("\nRead:\n");
-               vp_printlist(stdout, req->vps);
+               if (fr_debug_flag > 1) {
+                       DEBUG("Read:");
+                       vp_printlist(stdout, req->vps);
+               }
 
                map_eapsim_types(req);
-               map_eap_types(req);
-               printf("Mapped to:\n");
-               vp_printlist(stdout, req->vps);
+               map_eap_methods(req);
+
+               if (fr_debug_flag > 1) {
+                       DEBUG("Mapped to:");
+                       vp_printlist(stdout, req->vps);
+               }
 
                /* find the EAP-Message, copy it to req2 */
-               vp = paircopy2(req->vps, PW_EAP_MESSAGE);
+               vp = paircopy2(NULL, req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
 
-               if(vp == NULL) continue;
+               if(!vp) continue;
 
                pairadd(&req2->vps, vp);
 
                /* only call unmap for sim types here */
-               unmap_eap_types(req2);
+               unmap_eap_methods(req2);
                unmap_eapsim_types(req2);
 
-               printf("Unmapped to:\n");
-               vp_printlist(stdout, req2->vps);
+               if (fr_debug_flag > 1) {
+                       DEBUG("Unmapped to:");
+                       vp_printlist(stdout, req2->vps);
+               }
 
-               vp = pairfind(req2->vps,
-                             ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC);
-               vpkey   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_KEY);
-               vpextra = pairfind(req->vps, ATTRIBUTE_EAP_SIM_EXTRA);
+               vp = pairfind(req2->vps, PW_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY);
+               vpkey   = pairfind(req->vps, PW_EAP_SIM_KEY, 0, TAG_ANY);
+               vpextra = pairfind(req->vps, PW_EAP_SIM_EXTRA, 0, TAG_ANY);
 
                if(vp != NULL && vpkey != NULL && vpextra!=NULL) {
                        uint8_t calcmac[16];
@@ -1407,25 +1562,25 @@ main(int argc, char *argv[])
                        /* find the EAP-Message, copy it to req2 */
 
                        memset(calcmac, 0, sizeof(calcmac));
-                       printf("Confirming MAC...");
+                       DEBUG("Confirming MAC...");
                        if(eapsim_checkmac(req2->vps, vpkey->vp_strvalue,
                                           vpextra->vp_strvalue, vpextra->length,
                                           calcmac)) {
-                               printf("succeed\n");
+                               DEBUG("succeed");
                        } else {
                                int i, j;
 
-                               printf("calculated MAC (");
+                               DEBUG("calculated MAC (");
                                for (i = 0; i < 20; i++) {
                                        if(j==4) {
-                                               printf("_");
+                                               DEBUG("_");
                                                j=0;
                                        }
                                        j++;
 
-                                       printf("%02x", calcmac[i]);
+                                       DEBUG("%02x", calcmac[i]);
                                }
-                               printf(" did not match\n");
+                               DEBUG("did not match");
                        }
                }