+ debug(DBG_DBG, "dtlsservernew: exiting");
+}
+
+void cacheexpire(struct hash *cache, struct timeval *last) {
+ struct timeval now;
+ struct hash_entry *he;
+ struct sessioncacheentry *e;
+
+ gettimeofday(&now, NULL);
+ if (now.tv_sec - last->tv_sec < 19)
+ return;
+
+ for (he = hash_first(cache); he; he = hash_next(he)) {
+ e = (struct sessioncacheentry *)he->data;
+ pthread_mutex_lock(&e->mutex);
+ if (!e->expiry.tv_sec || e->expiry.tv_sec > now.tv_sec) {
+ pthread_mutex_unlock(&e->mutex);
+ continue;
+ }
+ debug(DBG_DBG, "cacheexpire: freeing entry");
+ hash_extract(cache, he->key, he->keylen);
+ if (e->rbios) {
+ freebios(e->rbios);
+ e->rbios = NULL;
+ }
+ pthread_mutex_unlock(&e->mutex);
+ pthread_mutex_destroy(&e->mutex);
+ }
+ last->tv_sec = now.tv_sec;
+}
+
+void *udpdtlsserverrd(void *arg) {
+ int ndesc, cnt, s = *(int *)arg;
+ unsigned char buf[4];
+ struct sockaddr_storage from;
+ socklen_t fromlen = sizeof(from);
+ struct dtlsservernewparams *params;
+ fd_set readfds;
+ struct timeval timeout, lastexpiry;
+ pthread_t dtlsserverth;
+ struct hash *sessioncache;
+ struct sessioncacheentry *cacheentry;
+
+ sessioncache = hash_create();
+ if (!sessioncache)
+ debugx(1, DBG_ERR, "udpdtlsserverrd: malloc failed");
+ gettimeofday(&lastexpiry, NULL);
+
+ for (;;) {
+ FD_ZERO(&readfds);
+ FD_SET(s, &readfds);
+ memset(&timeout, 0, sizeof(struct timeval));
+ timeout.tv_sec = 60;
+ ndesc = select(s + 1, &readfds, NULL, NULL, &timeout);
+ if (ndesc < 1) {
+ cacheexpire(sessioncache, &lastexpiry);
+ continue;
+ }
+ cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
+ if (cnt == -1) {
+ debug(DBG_WARN, "udpdtlsserverrd: recv failed");
+ cacheexpire(sessioncache, &lastexpiry);
+ continue;
+ }
+ cacheentry = hash_read(sessioncache, &from, fromlen);
+ if (cacheentry) {
+ debug(DBG_DBG, "udpdtlsserverrd: cache hit");
+ pthread_mutex_lock(&cacheentry->mutex);
+ if (cacheentry->rbios) {
+ if (udp2bio(s, cacheentry->rbios, cnt))
+ debug(DBG_DBG, "udpdtlsserverrd: got DTLS in UDP from %s", addr2string((struct sockaddr *)&from));
+ } else
+ recv(s, buf, 1, 0);
+ pthread_mutex_unlock(&cacheentry->mutex);
+ cacheexpire(sessioncache, &lastexpiry);
+ continue;
+ }
+
+ /* from new source */
+ debug(DBG_DBG, "udpdtlsserverrd: cache miss");
+ params = malloc(sizeof(struct dtlsservernewparams));
+ if (!params) {
+ cacheexpire(sessioncache, &lastexpiry);
+ recv(s, buf, 1, 0);
+ continue;
+ }
+ memset(params, 0, sizeof(struct dtlsservernewparams));
+ params->sesscache = malloc(sizeof(struct sessioncacheentry));
+ if (!params->sesscache) {
+ free(params);
+ cacheexpire(sessioncache, &lastexpiry);
+ recv(s, buf, 1, 0);
+ continue;
+ }
+ memset(params->sesscache, 0, sizeof(struct sessioncacheentry));
+ pthread_mutex_init(¶ms->sesscache->mutex, NULL);
+ params->sesscache->rbios = newqueue();
+ if (hash_insert(sessioncache, &from, fromlen, params->sesscache)) {
+ params->sock = s;
+ memcpy(¶ms->addr, &from, fromlen);
+
+ if (udp2bio(s, params->sesscache->rbios, cnt)) {
+ debug(DBG_DBG, "udpdtlsserverrd: got DTLS in UDP from %s", addr2string((struct sockaddr *)&from));
+ if (!pthread_create(&dtlsserverth, NULL, dtlsservernew, (void *)params)) {
+ pthread_detach(dtlsserverth);
+ cacheexpire(sessioncache, &lastexpiry);
+ continue;
+ }
+ debug(DBG_ERR, "udpdtlsserverrd: pthread_create failed");
+ }
+ hash_extract(sessioncache, &from, fromlen);
+ }
+ freebios(params->sesscache->rbios);
+ pthread_mutex_destroy(¶ms->sesscache->mutex);
+ free(params->sesscache);
+ free(params);
+ cacheexpire(sessioncache, &lastexpiry);
+ }