new debug version with timestamp off by default
[radsecproxy.git] / radsecproxy.c
index b59b3f2..80f6a47 100644 (file)
@@ -6,6 +6,11 @@
  * copyright notice and this permission notice appear in all copies.
  */
 
+/* Code contributions from:
+ *
+ * Arne Schwabe <schwabe at uni-paderborn.de>
+ */
+
 /* For UDP there is one server instance consisting of udpserverrd and udpserverth
  *              rd is responsible for init and launching wr
  * For TLS there is a server instance that launches tlsserverrd for each TLS peer
 */
 
 /* Bugs:
- * TCP accounting not yet supported
+ * Multiple outgoing connections if not enough IDs? (multiple servers per conf?)
+ * Also useful for TCP accounting which is not yet supported?
  * We are not removing client requests from dynamic servers, see removeclientrqs()
+ * Reserve ID 0 for statusserver requests?
  * Need to remove UDP clients when no activity for a while...
  * Remove expired stuff from clients request list?
  */
@@ -92,6 +99,7 @@ void freerealm(struct realm *realm);
 void freeclsrvconf(struct clsrvconf *conf);
 void freerq(struct request *rq);
 void freerqoutdata(struct rqout *rqout);
+void rmclientrq(struct request *rq, uint8_t id);
 
 static const struct protodefs protodefs[] = {
     {   "udp", /* UDP, assuming RAD_UDP defined as 0 */
@@ -278,16 +286,8 @@ int resolvepeer(struct clsrvconf *conf, int ai_flags) {
            debug(DBG_WARN, "resolvepeer: can't resolve (null) port (null)");
            return 0;
        }
-       for (res = addrinfo; res; res = res->ai_next) {
-           switch (res->ai_family) {
-           case AF_INET:
-               ((struct sockaddr_in *)res->ai_addr)->sin_port = 0;
-               break;
-           case AF_INET6:
-               ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = 0;
-               break;
-           }
-       }
+       for (res = addrinfo; res; res = res->ai_next)
+           port_set(res->ai_addr, 0);
     } else {
        if (slash)
            hints.ai_flags |= AI_NUMERICHOST;
@@ -540,7 +540,7 @@ void removequeue(struct queue *q) {
        return;
     pthread_mutex_lock(&q->mutex);
     for (entry = list_first(q->entries); entry; entry = list_next(entry))
-       free(((struct reply *)entry)->buf);
+       freerq((struct request *)entry);
     list_destroy(q->entries);
     pthread_cond_destroy(&q->cond);
     pthread_mutex_unlock(&q->mutex);
@@ -579,7 +579,6 @@ struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
     }
     
     memset(new, 0, sizeof(struct client));
-    pthread_mutex_init(&new->lock, NULL);
     new->conf = conf;
     if (conf->pdef->addclient)
        conf->pdef->addclient(new);
@@ -592,20 +591,19 @@ struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
 }
 
 void removeclient(struct client *client) {
+    struct clsrvconf *conf;
+    
     if (!client)
        return;
-
-    pthread_mutex_lock(client->conf->lock);
-    if (client->conf->clients) {
-       pthread_mutex_lock(&client->lock);
+    conf = client->conf;
+    pthread_mutex_lock(conf->lock);
+    if (conf->clients) {
        removequeue(client->replyq);
-       list_removedata(client->conf->clients, client);
-       pthread_mutex_unlock(&client->lock);
-       pthread_mutex_destroy(&client->lock);
+       list_removedata(conf->clients, client);
        free(client->addr);
        free(client);
     }
-    pthread_mutex_unlock(client->conf->lock);
+    pthread_mutex_unlock(conf->lock);
 }
 
 void removeclientrqs(struct client *client) {
@@ -618,15 +616,13 @@ void removeclientrqs(struct client *client) {
        server = ((struct clsrvconf *)entry->data)->servers;
        if (!server)
            continue;
-       pthread_mutex_lock(&server->newrq_mutex);
        for (i = 0; i < MAX_REQUESTS; i++) {
            rqout = server->requests + i;
-           if (rqout->rq && rqout->rq->from == client) {
-               freerq(rqout->rq);
-               rqout->rq = NULL;
-           }
+           pthread_mutex_lock(rqout->lock);
+           if (rqout->rq && rqout->rq->from == client)
+               freerqoutdata(rqout);
+           pthread_mutex_unlock(rqout->lock);
        }
-       pthread_mutex_unlock(&server->newrq_mutex);
     }
 }
 
@@ -638,8 +634,11 @@ void freeserver(struct server *server, uint8_t destroymutex) {
 
     if (server->requests) {
        rqout = server->requests;
-       for (end = rqout + MAX_REQUESTS; rqout < end; rqout++)
+       for (end = rqout + MAX_REQUESTS; rqout < end; rqout++) {
            freerqoutdata(rqout);
+           pthread_mutex_destroy(rqout->lock);
+           free(rqout->lock);
+       }
        free(server->requests);
     }
     if (server->rbios)
@@ -658,6 +657,7 @@ void freeserver(struct server *server, uint8_t destroymutex) {
 int addserver(struct clsrvconf *conf) {
     struct clsrvconf *res;
     uint8_t type;
+    int i;
     
     if (conf->servers) {
        debug(DBG_ERR, "addserver: currently works with just one server per conf");
@@ -691,6 +691,19 @@ int addserver(struct clsrvconf *conf) {
        debug(DBG_ERR, "malloc failed");
        goto errexit;
     }
+    for (i = 0; i < MAX_REQUESTS; i++) {
+       conf->servers->requests[i].lock = malloc(sizeof(pthread_mutex_t));
+       if (!conf->servers->requests[i].lock) {
+           debug(DBG_ERR, "malloc failed");
+           goto errexit;
+       }
+       if (pthread_mutex_init(conf->servers->requests[i].lock, NULL)) {
+           debug(DBG_ERR, "mutex init failed");
+           free(conf->servers->requests[i].lock);
+           conf->servers->requests[i].lock = NULL;
+           goto errexit;
+       }
+    }
     if (pthread_mutex_init(&conf->servers->lock, NULL)) {
        debug(DBG_ERR, "mutex init failed");
        goto errexit;
@@ -914,65 +927,77 @@ unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
     return NULL;
 }
 
-void freerqdata(struct request *rq) {
-    if (!rq)
-       return;
-    if (rq->origusername)
-       free(rq->origusername);
-    if (rq->buf)
-       free(rq->buf);
-}
-
 void freerq(struct request *rq) {
     if (!rq)
        return;
     debug(DBG_DBG, "freerq: called with refcount %d", rq->refcount);
     if (--rq->refcount)
        return;
-    freerqdata(rq);
+    if (rq->origusername)
+       free(rq->origusername);
+    if (rq->buf)
+       free(rq->buf);
+    if (rq->replybuf)
+       free(rq->replybuf);
+    if (rq->msg)
+       radmsg_free(rq->msg);
     free(rq);
 }
 
 void freerqoutdata(struct rqout *rqout) {
     if (!rqout)
        return;
-    if (rqout->msg)
-       radmsg_free(rqout->msg);
-    if (rqout->buf)
-       free(rqout->buf);
-    if (rqout->rq)
+    if (rqout->rq) {
        freerq(rqout->rq);
+       rqout->rq = NULL;
+    }
+    rqout->tries = 0;
+    memset(&rqout->expiry, 0, sizeof(struct timeval));
 }
 
-void sendrq(struct server *to, struct rqout *rqout) {
+void sendrq(struct server *to, struct request *rq) {
     int i;
-
+    
     pthread_mutex_lock(&to->newrq_mutex);
     /* might simplify if only try nextid, might be ok */
-    for (i = to->nextid; i < MAX_REQUESTS; i++)
-       if (!to->requests[i].buf)
-           break;
-    if (i == MAX_REQUESTS) {
-       for (i = 0; i < to->nextid; i++)
-           if (!to->requests[i].buf)
+    for (i = to->nextid; i < MAX_REQUESTS; i++) {
+       if (!to->requests[i].rq) {
+           pthread_mutex_lock(to->requests[i].lock);
+           if (!to->requests[i].rq)
                break;
+           pthread_mutex_unlock(to->requests[i].lock);
+       }
+    }
+    if (i == MAX_REQUESTS) {
+       for (i = 0; i < to->nextid; i++) {
+           if (!to->requests[i].rq) {
+               pthread_mutex_lock(to->requests[i].lock);
+               if (!to->requests[i].rq)
+                   break;
+               pthread_mutex_unlock(to->requests[i].lock);
+           }
+       }
        if (i == to->nextid) {
            debug(DBG_WARN, "sendrq: no room in queue, dropping request");
-           freerqoutdata(rqout);
+           rmclientrq(rq, rq->msg->id);
+           freerq(rq);
            goto exit;
        }
     }
 
-    rqout->msg->id = (uint8_t)i;
-    rqout->buf = radmsg2buf(rqout->msg, (uint8_t *)to->conf->secret);
-    if (!rqout->buf) {
+    rq->msg->id = (uint8_t)i;
+    rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
+    if (!rq->buf) {
+       pthread_mutex_unlock(to->requests[i].lock);
        debug(DBG_ERR, "sendrq: radmsg2buf failed");
-       freerqoutdata(rqout);
+       rmclientrq(rq, rq->msg->id);
+       freerq(rq);
        goto exit;
     }
     
     debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->host);
-    to->requests[i] = *rqout;
+    to->requests[i].rq = rq;
+    pthread_mutex_unlock(to->requests[i].lock);
     to->nextid = i + 1;
 
     if (!to->newrq) {
@@ -985,38 +1010,26 @@ void sendrq(struct server *to, struct rqout *rqout) {
     pthread_mutex_unlock(&to->newrq_mutex);
 }
 
-void sendreply(struct client *to, struct radmsg *msg, struct sockaddr_storage *tosa, int toudpsock) {
-    uint8_t *buf;
-    struct reply *reply;
+void sendreply(struct request *rq) {
     uint8_t first;
-
-    buf = radmsg2buf(msg, (uint8_t *)to->conf->secret);
-    radmsg_free(msg);
-    if (!buf) {
+    struct client *to = rq->from;
+    
+    if (!rq->replybuf)
+       rq->replybuf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
+    radmsg_free(rq->msg);
+    rq->msg = NULL;
+    if (!rq->replybuf) {
+       freerq(rq);
        debug(DBG_ERR, "sendreply: radmsg2buf failed");
        return;
     }
 
-    reply = malloc(sizeof(struct reply));
-    if (!reply) {
-       free(buf);
-       debug(DBG_ERR, "sendreply: malloc failed");
-       return;
-    }
-    memset(reply, 0, sizeof(struct reply));
-    reply->buf = buf;
-    if (tosa)
-       reply->tosa = *tosa;
-    reply->toudpsock = toudpsock;
-    
     pthread_mutex_lock(&to->replyq->mutex);
-
     first = list_first(to->replyq->entries) == NULL;
     
-    if (!list_push(to->replyq->entries, reply)) {
+    if (!list_push(to->replyq->entries, rq)) {
        pthread_mutex_unlock(&to->replyq->mutex);
-       free(reply);
-       free(buf);
+       freerq(rq);
        debug(DBG_ERR, "sendreply: malloc failed");
        return;
     }
@@ -1299,18 +1312,6 @@ void removeserversubrealms(struct list *realmlist, struct clsrvconf *srv) {
     }
 }
                        
-int rqinqueue(struct server *to, struct client *from, uint8_t id, uint8_t code) {
-    struct rqout *rqout = to->requests, *end;
-    
-    pthread_mutex_lock(&to->newrq_mutex);
-    for (end = rqout + MAX_REQUESTS; rqout < end; rqout++)
-       if (rqout->buf && !rqout->received && rqout->rq && rqout->rq->origid == id && rqout->rq->from == from && *rqout->buf == code)
-           break;
-    pthread_mutex_unlock(&to->newrq_mutex);
-    
-    return rqout < end;
-}
-
 int attrvalidate(unsigned char *attrs, int length) {
     while (length > 1) {
        if (ATTRLEN(attrs) < 2) {
@@ -1349,27 +1350,27 @@ int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, uint
     return 1;
 }
 
-int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, unsigned char *oldauth, char *newauth) {
+int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) {
     if (len < 18)
        return 0;
-    if (!msmppdecrypt(msmpp + 2, len - 2, (unsigned char *)oldsecret, strlen(oldsecret), oldauth, msmpp)) {
+    if (!msmppdecrypt(msmpp + 2, len - 2, (uint8_t *)oldsecret, strlen(oldsecret), oldauth, msmpp)) {
        debug(DBG_WARN, "msmpprecrypt: failed to decrypt msppe key");
        return 0;
     }
-    if (!msmppencrypt(msmpp + 2, len - 2, (unsigned char *)newsecret, strlen(newsecret), (unsigned char *)newauth, msmpp)) {
+    if (!msmppencrypt(msmpp + 2, len - 2, (uint8_t *)newsecret, strlen(newsecret), newauth, msmpp)) {
        debug(DBG_WARN, "msmpprecrypt: failed to encrypt msppe key");
        return 0;
     }
     return 1;
 }
 
-int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct rqout *rqout,
+int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct request *rq,
           char *oldsecret, char *newsecret) {
     unsigned char *attr;
     
     for (attr = attrs; (attr = attrget(attr, length - (attr - attrs), type)); attr += ATTRLEN(attr)) {
        debug(DBG_DBG, "msmppe: Got %s", attrtxt);
-       if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rqout->buf + 4, rqout->rq->origauth))
+       if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rq->buf + 4, rq->rqauth))
            return 0;
     }
     return 1;
@@ -1554,14 +1555,14 @@ int dorewrite(struct radmsg *msg, struct rewrite *rewrite) {
     return 1;
 }
 
-int rewriteusername(struct rqout *rqout, struct tlv *attr) {
+int rewriteusername(struct request *rq, struct tlv *attr) {
     char *orig = (char *)tlv2str(attr);
-    if (!dorewritemodattr(attr, rqout->rq->from->conf->rewriteusername)) {
+    if (!dorewritemodattr(attr, rq->from->conf->rewriteusername)) {
        free(orig);
        return 0;
     }
     if (strlen(orig) != attr->l || memcmp(orig, attr->v, attr->l))
-       rqout->rq->origusername = (char *)orig;
+       rq->origusername = (char *)orig;
     else
        free(orig);
     return 1;
@@ -1631,35 +1632,13 @@ void acclog(struct radmsg *msg, char *host) {
     }
 }
 
-void respondaccounting(struct rqout *rqout) {
-    struct radmsg *msg;
-
-    msg = radmsg_init(RAD_Accounting_Response, rqout->msg->id, rqout->msg->auth);
-    if (msg) {
-       debug(DBG_DBG, "respondaccounting: responding to %s", rqout->rq->from->conf->host);
-       sendreply(rqout->rq->from, msg, &rqout->rq->fromsa, rqout->rq->fromudpsock);
-    } else     
-       debug(DBG_ERR, "respondaccounting: malloc failed");
-}
-
-void respondstatusserver(struct rqout *rqout) {
-    struct radmsg *msg;
-
-    msg = radmsg_init(RAD_Access_Accept, rqout->msg->id, rqout->msg->auth);
-    if (msg) {
-       debug(DBG_DBG, "respondstatusserver: responding to %s", rqout->rq->from->conf->host);
-       sendreply(rqout->rq->from, msg, &rqout->rq->fromsa, rqout->rq->fromudpsock);
-    } else
-       debug(DBG_ERR, "respondstatusserver: malloc failed");
-}
-
-void respondreject(struct rqout *rqout, char *message) {
+void respond(struct request *rq, uint8_t code, char *message) {
     struct radmsg *msg;
     struct tlv *attr;
 
-    msg = radmsg_init(RAD_Access_Reject, rqout->msg->id, rqout->msg->auth);
+    msg = radmsg_init(code, rq->msg->id, rq->msg->auth);
     if (!msg) {
-       debug(DBG_ERR, "respondreject: malloc failed");
+       debug(DBG_ERR, "respond: malloc failed");
        return;
     }
     if (message && *message) {
@@ -1667,13 +1646,16 @@ void respondreject(struct rqout *rqout, char *message) {
        if (!attr || !radmsg_add(msg, attr)) {
            freetlv(attr);
            radmsg_free(msg);
-           debug(DBG_ERR, "respondreject: malloc failed");
+           debug(DBG_ERR, "respond: malloc failed");
            return;
        }
     }
 
-    debug(DBG_DBG, "respondreject: responding to %s", rqout->rq->from->conf->host);
-    sendreply(rqout->rq->from, msg, &rqout->rq->fromsa, rqout->rq->fromudpsock);
+    radmsg_free(rq->msg);
+    rq->msg = msg;
+    debug(DBG_DBG, "respond: sending %s to %s", radmsgtype2string(msg->code), rq->from->conf->host);
+    rq->refcount++;
+    sendreply(rq);
 }
 
 struct clsrvconf *choosesrvconf(struct list *srvconfs) {
@@ -1738,45 +1720,46 @@ struct request *newrequest() {
     return rq;
 }
 
-int addclientrq(struct request *rq, uint8_t id) {
+int addclientrq(struct request *rq) {
     struct request *r;
     struct timeval now;
-
-    pthread_mutex_lock(&rq->from->lock);
-    gettimeofday(&now, NULL);
-    r = rq->from->rqs[id];
+    
+    r = rq->from->rqs[rq->rqid];
     if (r) {
-       if (r->refcount > 1 && now.tv_sec - r->created.tv_sec < r->from->conf->dupinterval) {
-           pthread_mutex_unlock(&rq->from->lock);
-           return 0;
+       if (rq->udpport == r->udpport && !memcmp(rq->rqauth, r->rqauth, 16)) {
+           gettimeofday(&now, NULL);
+           if (now.tv_sec - r->created.tv_sec < r->from->conf->dupinterval) {
+               if (r->replybuf) {
+                   debug(DBG_INFO, "addclientrq: already sent reply to request with id %d from %s, resending", rq->rqid, addr2string(r->from->addr));
+                   r->refcount++;
+                   sendreply(r);
+               } else
+                   debug(DBG_INFO, "addclientrq: already got request with id %d from %s, ignoring", rq->rqid, addr2string(r->from->addr));
+               return 0;
+           }
        }
        freerq(r);
     }
     rq->refcount++;
-    rq->from->rqs[id] = rq;
-    pthread_mutex_unlock(&rq->from->lock);
+    rq->from->rqs[rq->rqid] = rq;
     return 1;
 }
 
 void rmclientrq(struct request *rq, uint8_t id) {
     struct request *r;
 
-    pthread_mutex_lock(&rq->from->lock);
     r = rq->from->rqs[id];
     if (r) {
        freerq(r);
        rq->from->rqs[id] = NULL;
     }
-    pthread_mutex_unlock(&rq->from->lock);
 }
 
 /* returns 0 if validation/authentication fails, else 1 */
 int radsrv(struct request *rq) {
     struct radmsg *msg = NULL;
-    struct rqout *rqout, rqdata;
     struct tlv *attr;
     uint8_t *userascii = NULL;
-    unsigned char newauth[16];
     struct realm *realm = NULL;
     struct server *to = NULL;
     struct client *from = rq->from;
@@ -1787,57 +1770,51 @@ int radsrv(struct request *rq) {
 
     if (!msg) {
        debug(DBG_WARN, "radsrv: message validation failed, ignoring packet");
+       freerq(rq);
        return 0;
     }
+    
+    rq->msg = msg;
+    rq->rqid = msg->id;
+    memcpy(rq->rqauth, msg->auth, 16);
 
-    rqout = &rqdata;
-    memset(rqout, 0, sizeof(struct rqout));
-    rqout->msg = msg;
     debug(DBG_DBG, "radsrv: code %d, id %d", msg->code, msg->id);
-    
     if (msg->code != RAD_Access_Request && msg->code != RAD_Status_Server && msg->code != RAD_Accounting_Request) {
-       debug(DBG_INFO, "radsrv: server currently accepts only access-requests, accounting-requests and status-server, ignoring");
-       freerqoutdata(rqout);
-       return 1;
-    }
-
-    if (!addclientrq(rq, msg->id)) {
-       debug(DBG_INFO, "radsrv: already got request with id %d from %s, ignoring", msg->id, from->conf->host);
-       freerqoutdata(rqout);
-       return 1;
+       debug(DBG_INFO, "radsrv: server currently accepts only access-requests, accounting-requests and status-server, ignoring");      
+       goto exit;
     }
-    rqout->rq = rq;
-    rq->refcount++;
     
+    if (!addclientrq(rq))
+       goto exit;
+
     if (msg->code == RAD_Status_Server) {
-       respondstatusserver(rqout);
-       goto respexit;
+       respond(rq, RAD_Access_Accept, NULL);
+       goto exit;
     }
 
     /* below: code == RAD_Access_Request || code == RAD_Accounting_Request */
 
     if (from->conf->rewritein && !dorewrite(msg, from->conf->rewritein))
-       goto exit;
+       goto rmclrqexit;
 
     attr = radmsg_gettype(msg, RAD_Attr_User_Name);
     if (!attr) {
        if (msg->code == RAD_Accounting_Request) {
            acclog(msg, from->conf->host);
-           respondaccounting(rqout);
-           goto respexit;
-       } 
-       debug(DBG_WARN, "radsrv: ignoring access request, no username attribute");
+           respond(rq, RAD_Accounting_Response, NULL);
+       } else
+           debug(DBG_WARN, "radsrv: ignoring access request, no username attribute");
        goto exit;
     }
     
-    if (from->conf->rewriteusername && !rewriteusername(rqout, attr)) {
+    if (from->conf->rewriteusername && !rewriteusername(rq, attr)) {
        debug(DBG_WARN, "radsrv: username malloc failed, ignoring request");
-       goto exit;
+       goto rmclrqexit;
     }
     
     userascii = radattr2ascii(attr);
     if (!userascii)
-       goto exit;
+       goto rmclrqexit;
     debug(DBG_DBG, "%s with username: %s", radmsgtype2string(msg->code), userascii);
 
     to = findserver(&realm, attr, msg->code == RAD_Accounting_Request);
@@ -1849,12 +1826,12 @@ int radsrv(struct request *rq) {
     if (!to) {
        if (realm->message && msg->code == RAD_Access_Request) {
            debug(DBG_INFO, "radsrv: sending reject to %s for %s", from->conf->host, userascii);
-           respondreject(rqout, realm->message);
+           respond(rq, RAD_Access_Reject, realm->message);
        } else if (realm->accresp && msg->code == RAD_Accounting_Request) {
            acclog(msg, from->conf->host);
-           respondaccounting(rqout);
+           respond(rq, RAD_Accounting_Response, NULL);
        }
-       goto respexit;
+       goto exit;
     }
     
     if (options.loopprevention && !strcmp(from->conf->name, to->conf->name)) {
@@ -1863,20 +1840,11 @@ int radsrv(struct request *rq) {
        goto exit;
     }
 
-#if 0
-    skip this now that we have rqrcv... per client?
-    if (rqinqueue(to, from, msg->id, msg->code)) {
-       debug(DBG_INFO, "radsrv: already got %s from host %s with id %d, ignoring",
-             radmsgtype2string(msg->code), from->conf->host, msg->id);
-       goto exit;
-    }
-#endif
-    
-    if (msg->code != RAD_Accounting_Request) {
-       if (!RAND_bytes(newauth, 16)) {
-           debug(DBG_WARN, "radsrv: failed to generate random auth");
-           goto exit;
-       }
+    if (msg->code == RAD_Accounting_Request)
+       memset(msg->auth, 0, 16);
+    else if (!RAND_bytes(msg->auth, 16)) {
+       debug(DBG_WARN, "radsrv: failed to generate random auth");
+       goto rmclrqexit;
     }
     
 #ifdef DEBUG
@@ -1886,39 +1854,30 @@ int radsrv(struct request *rq) {
     attr = radmsg_gettype(msg, RAD_Attr_User_Password);
     if (attr) {
        debug(DBG_DBG, "radsrv: found userpwdattr with value length %d", attr->l);
-       if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, msg->auth, newauth))
-           goto exit;
+       if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, rq->rqauth, msg->auth))
+           goto rmclrqexit;
     }
 
     attr = radmsg_gettype(msg, RAD_Attr_Tunnel_Password);
     if (attr) {
        debug(DBG_DBG, "radsrv: found tunnelpwdattr with value length %d", attr->l);
-       if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, msg->auth, newauth))
-           goto exit;
+       if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, rq->rqauth, msg->auth))
+           goto rmclrqexit;
     }
 
-    rqout->rq->origid = msg->id;
-    memcpy(rqout->rq->origauth, msg->auth, 16);
-    memcpy(msg->auth, newauth, 16);    
-
     if (to->conf->rewriteout && !dorewrite(msg, to->conf->rewriteout))
-       goto exit;
+       goto rmclrqexit;
     
     free(userascii);
-    sendrq(to, rqout);
+    sendrq(to, rq);
     return 1;
     
- exit:
rmclrqexit:
     rmclientrq(rq, msg->id);
+ exit:
+    freerq(rq);
     free(userascii);
-    freerqoutdata(rqout);
-    return 1;
-    
- respexit:
-    free(userascii);
-    freerqoutdata(rqout);
     return 1;
-    
 }
 
 void replyh(struct server *server, unsigned char *buf) {
@@ -1926,8 +1885,7 @@ void replyh(struct server *server, unsigned char *buf) {
     struct rqout *rqout;
     int sublen;
     unsigned char *subattrs;
-    struct sockaddr_storage fromsa;
-    uint8_t *username, *stationid;
+    uint8_t *username, *stationid, *replymsg;
     struct radmsg *msg = NULL;
     struct tlv *attr;
     struct list_node *node;
@@ -1936,46 +1894,37 @@ void replyh(struct server *server, unsigned char *buf) {
     server->lostrqs = 0;
 
     rqout = server->requests + buf[1];
-    msg = buf2radmsg(buf, (uint8_t *)server->conf->secret, rqout->msg->auth);
+    pthread_mutex_lock(rqout->lock);
+    if (!rqout->tries) {
+       free(buf);
+       buf = NULL;
+       debug(DBG_INFO, "replyh: no outstanding request with this id, ignoring reply");
+       goto errunlock;
+    }
+       
+    msg = buf2radmsg(buf, (uint8_t *)server->conf->secret, rqout->rq->msg->auth);
     free(buf);
     buf = NULL;
     if (!msg) {
         debug(DBG_WARN, "replyh: message validation failed, ignoring packet");
-       return;
+       goto errunlock;
     }
     if (msg->code != RAD_Access_Accept && msg->code != RAD_Access_Reject && msg->code != RAD_Access_Challenge
        && msg->code != RAD_Accounting_Response) {
        debug(DBG_INFO, "replyh: discarding message type %s, accepting only access accept, access reject, access challenge and accounting response messages", radmsgtype2string(msg->code));
-       radmsg_free(msg);
-       return;
-    }
-    debug(DBG_DBG, "got %s message with id %d", radmsgtype2string(msg->code), msg->id);
-
-    pthread_mutex_lock(&server->newrq_mutex);
-    if (!rqout->buf || !rqout->tries) {
-       debug(DBG_INFO, "replyh: no matching request sent with this id, ignoring reply");
        goto errunlock;
     }
+    debug(DBG_DBG, "got %s message with id %d", radmsgtype2string(msg->code), msg->id);
 
-    if (rqout->received) {
-       debug(DBG_INFO, "replyh: already received, ignoring reply");
-       goto errunlock;
-    }
-       
     gettimeofday(&server->lastrcv, NULL);
     
-    if (rqout->msg->code == RAD_Status_Server) {
-       rqout->received = 1;
+    if (rqout->rq->msg->code == RAD_Status_Server) {
+       freerqoutdata(rqout);
        debug(DBG_DBG, "replyh: got status server response from %s", server->conf->host);
        goto errunlock;
     }
 
     gettimeofday(&server->lastreply, NULL);
-    
-    if (!rqout->rq) {
-       debug(DBG_INFO, "replyh: client gone, ignoring reply");
-       goto errunlock;
-    }
     from = rqout->rq->from;
        
     if (server->conf->rewritein && !dorewrite(msg, from->conf->rewritein)) {
@@ -1997,9 +1946,9 @@ void replyh(struct server *server, unsigned char *buf) {
        subattrs = attr->v + 4;  
        if (!attrvalidate(subattrs, sublen) ||
            !msmppe(subattrs, sublen, RAD_VS_ATTR_MS_MPPE_Send_Key, "MS MPPE Send Key",
-                   rqout, server->conf->secret, from->conf->secret) ||
+                   rqout->rq, server->conf->secret, from->conf->secret) ||
            !msmppe(subattrs, sublen, RAD_VS_ATTR_MS_MPPE_Recv_Key, "MS MPPE Recv Key",
-                   rqout, server->conf->secret, from->conf->secret))
+                   rqout->rq, server->conf->secret, from->conf->secret))
            break;
     }
     if (node) {
@@ -2008,21 +1957,34 @@ void replyh(struct server *server, unsigned char *buf) {
     }
 
     if (msg->code == RAD_Access_Accept || msg->code == RAD_Access_Reject || msg->code == RAD_Accounting_Response) {
-       username = radattr2ascii(radmsg_gettype(rqout->msg, RAD_Attr_User_Name));
+       username = radattr2ascii(radmsg_gettype(rqout->rq->msg, RAD_Attr_User_Name));
        if (username) {
-           stationid = radattr2ascii(radmsg_gettype(rqout->msg, RAD_Attr_Calling_Station_Id));
+           stationid = radattr2ascii(radmsg_gettype(rqout->rq->msg, RAD_Attr_Calling_Station_Id));
+           replymsg = radattr2ascii(radmsg_gettype(msg, RAD_Attr_Reply_Message));
            if (stationid) {
-               debug(DBG_INFO, "%s for user %s stationid %s from %s",
-                     radmsgtype2string(msg->code), username, stationid, server->conf->host);
+               if (replymsg) {
+                   debug(DBG_INFO, "%s for user %s stationid %s from %s (%s)",
+                         radmsgtype2string(msg->code), username, stationid, server->conf->host, replymsg);
+                   free(replymsg);
+               } else
+                   debug(DBG_INFO, "%s for user %s stationid %s from %s",
+                         radmsgtype2string(msg->code), username, stationid, server->conf->host);
                free(stationid);
-           } else
-               debug(DBG_INFO, "%s for user %s from %s", radmsgtype2string(msg->code), username, server->conf->host);
+           } else {
+               if (replymsg) {
+                   debug(DBG_INFO, "%s for user %s from %s (%s)",
+                         radmsgtype2string(msg->code), username, server->conf->host, replymsg);
+                   free(replymsg);
+               } else
+                   debug(DBG_INFO, "%s for user %s from %s",
+                         radmsgtype2string(msg->code), username, server->conf->host);
+           }
            free(username);
        }
     }
 
-    msg->id = (char)rqout->rq->origid;
-    memcpy(msg->auth, rqout->rq->origauth, 16);
+    msg->id = (char)rqout->rq->rqid;
+    memcpy(msg->auth, rqout->rq->rqauth, 16);
 
 #ifdef DEBUG   
     printfchars(NULL, "origauth/buf+4", "%02x ", buf + 4, 16);
@@ -2041,52 +2003,55 @@ void replyh(struct server *server, unsigned char *buf) {
        goto errunlock;
     }
 
-    fromsa = rqout->rq->fromsa; /* only needed for UDP */
-    /* once we set received = 1, rq may be reused */
-    rqout->received = 1;
-
     debug(DBG_INFO, "replyh: passing reply to client %s", from->conf->name);
-    sendreply(from, msg, &fromsa, rqout->rq->fromudpsock);
-    pthread_mutex_unlock(&server->newrq_mutex);
+    radmsg_free(rqout->rq->msg);
+    rqout->rq->msg = msg;
+    rqout->rq->refcount++;
+    sendreply(rqout->rq);
+    freerqoutdata(rqout);
+    pthread_mutex_unlock(rqout->lock);
     return;
 
  errunlock:
     radmsg_free(msg);
-    pthread_mutex_unlock(&server->newrq_mutex);
+    pthread_mutex_unlock(rqout->lock);
     return;
 }
 
-struct radmsg *createstatsrvmsg() {
-    struct radmsg *msg;
+struct request *createstatsrvrq() {
+    struct request *rq;
     struct tlv *attr;
 
-    msg = radmsg_init(RAD_Status_Server, 0, NULL);
-    if (!msg)
+    rq = newrequest();
+    if (!rq)
        return NULL;
-    
+    rq->msg = radmsg_init(RAD_Status_Server, 0, NULL);
+    if (!rq->msg)
+       goto exit;
     attr = maketlv(RAD_Attr_Message_Authenticator, 16, NULL);
-    if (!attr) {
-       radmsg_free(msg);
-       return NULL;
-    }
-    if (!radmsg_add(msg, attr)) {
+    if (!attr)
+       goto exit;
+    if (!radmsg_add(rq->msg, attr)) {
        freetlv(attr);
-       radmsg_free(msg);
-       return NULL;
+       goto exit;
     }
-    return msg;
+    return rq;
+
+ exit:
+    freerq(rq);
+    return NULL;
 }
 
 /* code for removing state not finished */
 void *clientwr(void *arg) {
     struct server *server = (struct server *)arg;
-    struct rqout *rqout;
+    struct rqout *rqout = NULL;
     pthread_t clientrdth;
     int i, secs, dynconffail = 0;
     uint8_t rnd;
     struct timeval now, laststatsrv;
     struct timespec timeout;
-    struct rqout statsrvrq;
+    struct request *statsrvrq;
     struct clsrvconf *conf;
     
     conf = server->conf;
@@ -2156,39 +2121,32 @@ void *clientwr(void *arg) {
                pthread_join(clientrdth, NULL);
                goto errexit;
            }
-           pthread_mutex_lock(&server->newrq_mutex);
-           while (i < MAX_REQUESTS && !server->requests[i].buf)
-               i++;
-           if (i == MAX_REQUESTS) {
-               pthread_mutex_unlock(&server->newrq_mutex);
-               break;
-           }
-           rqout = server->requests + i;
 
-            if (rqout->received) {
-               debug(DBG_DBG, "clientwr: packet %d in queue is marked as received", i);
-               if (rqout->buf) {
-                   debug(DBG_DBG, "clientwr: freeing received packet %d from queue", i);
-                   freerqoutdata(rqout);
-                   /* setting this to NULL means that it can be reused */
-                   rqout->buf = NULL;
+           for (; i < MAX_REQUESTS; i++) {
+               rqout = server->requests + i;
+               if (rqout->rq) {
+                   pthread_mutex_lock(rqout->lock);
+                   if (rqout->rq)
+                       break;
+                   pthread_mutex_unlock(rqout->lock);
                }
-                pthread_mutex_unlock(&server->newrq_mutex);
-                continue;
-            }
-           
+           }
+               
+           if (i == MAX_REQUESTS)
+               break;
+
            gettimeofday(&now, NULL);
             if (now.tv_sec < rqout->expiry.tv_sec) {
                if (!timeout.tv_sec || rqout->expiry.tv_sec < timeout.tv_sec)
                    timeout.tv_sec = rqout->expiry.tv_sec;
-               pthread_mutex_unlock(&server->newrq_mutex);
+                pthread_mutex_unlock(rqout->lock);
                continue;
            }
 
-           if (rqout->tries == (*rqout->buf == RAD_Status_Server ? 1 : conf->retrycount + 1)) {
+           if (rqout->tries == (*rqout->rq->buf == RAD_Status_Server ? 1 : conf->retrycount + 1)) {
                debug(DBG_DBG, "clientwr: removing expired packet from queue");
                if (conf->statusserver) {
-                   if (*rqout->buf == RAD_Status_Server) {
+                   if (*rqout->rq->buf == RAD_Status_Server) {
                        debug(DBG_WARN, "clientwr: no status server response, %s dead?", conf->host);
                        if (server->lostrqs < 255)
                            server->lostrqs++;
@@ -2199,29 +2157,26 @@ void *clientwr(void *arg) {
                        server->lostrqs++;
                }
                freerqoutdata(rqout);
-               /* setting this to NULL means that it can be reused */
-               rqout->buf = NULL;
-               pthread_mutex_unlock(&server->newrq_mutex);
+                pthread_mutex_unlock(rqout->lock);
                continue;
            }
-            pthread_mutex_unlock(&server->newrq_mutex);
 
            rqout->expiry.tv_sec = now.tv_sec + conf->retryinterval;
            if (!timeout.tv_sec || rqout->expiry.tv_sec < timeout.tv_sec)
                timeout.tv_sec = rqout->expiry.tv_sec;
            rqout->tries++;
-           conf->pdef->clientradput(server, server->requests[i].buf);
+           conf->pdef->clientradput(server, rqout->rq->buf);
+           pthread_mutex_unlock(rqout->lock);
        }
        if (conf->statusserver) {
            secs = server->lastrcv.tv_sec > laststatsrv.tv_sec ? server->lastrcv.tv_sec : laststatsrv.tv_sec;
            gettimeofday(&now, NULL);
            if (now.tv_sec - secs > STATUS_SERVER_PERIOD) {
                laststatsrv = now;
-               memset(&statsrvrq, 0, sizeof(struct rqout));
-               statsrvrq.msg = createstatsrvmsg();
-               if (statsrvrq.msg) {
+               statsrvrq = createstatsrvrq();
+               if (statsrvrq) {
                    debug(DBG_DBG, "clientwr: sending status server to %s", conf->host);
-                   sendrq(server, &statsrvrq);
+                   sendrq(server, statsrvrq);
                }
            }
        }
@@ -3571,6 +3526,7 @@ int main(int argc, char **argv) {
     if (!foreground && (daemon(0, 0) < 0))
        debugx(1, DBG_ERR, "daemon() failed: %s", strerror(errno));
     
+    debug_timestamp_on();
     debug(DBG_INFO, "radsecproxy revision $Rev$ starting");
 
     sigemptyset(&sigset);