diff options
-rwxr-xr-x | channels/Makefile | 10 | ||||
-rwxr-xr-x | channels/chan_iax2.c | 321 |
2 files changed, 259 insertions, 72 deletions
diff --git a/channels/Makefile b/channels/Makefile index b35569a3c..c297992b0 100755 --- a/channels/Makefile +++ b/channels/Makefile @@ -13,6 +13,8 @@ OSARCH=$(shell uname -s) +USE_MYSQL_FRIENDS=0 + CHANNEL_LIBS=chan_modem.so chan_iax.so chan_sip.so \ chan_modem_aopen.so \ chan_modem_bestdata.so chan_modem_i4l.so \ @@ -50,6 +52,10 @@ ALSA_SRC+=$(shell [ -f alsa-monitor.h ] && echo "alsa-monitor.h") CFLAGS+=-DCRYPTO CFLAGS+=-fPIC +ifeq ($(USE_MYSQL_FRIENDS),1) +CFLAGS+=-DMYSQL_FRIENDS +endif + CFLAGS+=#-DVOFRDUMPER ZAPDIR=/usr/lib @@ -101,7 +107,11 @@ chan_oss.so: chan_oss.o endif chan_iax2.so: chan_iax2.o iax2-parser.o +ifeq ($(USE_MYSQL_FRIENDS),1) + $(CC) $(SOLINK) -o $@ chan_iax2.o iax2-parser.o -lmysqlclient -lz +else $(CC) $(SOLINK) -o $@ chan_iax2.o iax2-parser.o +endif chan_zap.o: $(CHANZAP) $(CC) -c $(CFLAGS) -o chan_zap.o $(CHANZAP) diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index c34d016f7..8f0e40fb6 100755 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -51,7 +51,9 @@ #include <sys/ioctl.h> #include <linux/zaptel.h> #endif - +#ifdef MYSQL_FRIENDS +#include <mysql/mysql.h> +#endif #include "iax2.h" #include "iax2-parser.h" @@ -82,6 +84,14 @@ /* Sample over last 100 units to determine historic jitter */ #define GAMMA (0.01) +#ifdef MYSQL_FRIENDS +static ast_mutex_t mysqllock = AST_MUTEX_INITIALIZER; +static MYSQL *mysql; +static char mydbuser[80]; +static char mydbpass[80]; +static char mydbhost[80]; +static char mydbname[80]; +#endif static char *desc = "Inter Asterisk eXchange (Ver 2)"; static char *tdesc = "Inter Asterisk eXchange Driver (Ver 2)"; static char *type = "IAX2"; @@ -192,7 +202,6 @@ struct iax2_peer { /* Dynamic Registration fields */ int dynamic; /* If this is a dynamic peer */ struct sockaddr_in defaddr; /* Default address if there is one */ - char challenge[80]; /* Challenge used to authenticate the secret */ int authmethods; /* Authentication methods (IAX_AUTH_*) */ char inkeys[80]; /* Key(s) this peer can use to authenticate to us */ @@ -1507,6 +1516,92 @@ static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newcha return 0; } +#ifdef MYSQL_FRIENDS + +static void mysql_update_peer(char *peer, struct sockaddr_in *sin) +{ + if (mysql && (strlen(peer) < 128)) { + char query[512]; + char *name; + time_t nowtime; + name = alloca(strlen(peer) * 2 + 1); + time(&nowtime); + mysql_real_escape_string(mysql, name, peer, strlen(peer)); + snprintf(query, sizeof(query), "UPDATE iaxfriends SET ipaddr=\"%s\", port=\"%d\", regseconds=\"%ld\" WHERE name=\"%s\"", + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), nowtime, name); + ast_mutex_lock(&mysqllock); + if (mysql_real_query(mysql, query, strlen(query))) + ast_log(LOG_WARNING, "Unable to update database\n"); + + ast_mutex_unlock(&mysqllock); + } +} + +static struct iax2_peer *mysql_peer(char *peer) +{ + struct iax2_peer *p; + int success = 0; + + p = malloc(sizeof(struct iax2_peer)); + memset(p, 0, sizeof(struct iax2_peer)); + if (mysql && (strlen(peer) < 128)) { + char query[512]; + char *name; + int numfields, x; + int port; + time_t regseconds, nowtime; + MYSQL_RES *result; + MYSQL_FIELD *fields; + MYSQL_ROW rowval; + name = alloca(strlen(peer) * 2 + 1); + mysql_real_escape_string(mysql, name, peer, strlen(peer)); + snprintf(query, sizeof(query), "SELECT * FROM iaxfriends WHERE name=\"%s\"", name); + ast_mutex_lock(&mysqllock); + mysql_query(mysql, query); + if ((result = mysql_store_result(mysql))) { + if ((rowval = mysql_fetch_row(result))) { + numfields = mysql_num_fields(result); + fields = mysql_fetch_fields(result); + success = 1; + for (x=0;x<numfields;x++) { + if (rowval[x]) { + if (!strcasecmp(fields[x].name, "secret")) { + strncpy(p->secret, rowval[x], sizeof(p->secret)); + } else if (!strcasecmp(fields[x].name, "context")) { + strncpy(p->context, rowval[x], sizeof(p->context) - 1); + } else if (!strcasecmp(fields[x].name, "ipaddr")) { + inet_aton(rowval[x], &p->addr.sin_addr); + } else if (!strcasecmp(fields[x].name, "port")) { + if (sscanf(rowval[x], "%i", &port) != 1) + port = 0; + p->addr.sin_port = htons(port); + } else if (!strcasecmp(fields[x].name, "regseconds")) { + if (sscanf(rowval[x], "%li", ®seconds) != 1) + regseconds = 0; + } + } + } + time(&nowtime); + if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) + memset(&p->addr, 0, sizeof(p->addr)); + } + } + ast_mutex_unlock(&mysqllock); + } + if (!success) { + free(p); + p = NULL; + } else { + strncpy(p->name, peer, sizeof(p->name) - 1); + p->dynamic = 1; + p->delme = 1; + p->capability = iax2_capability; + p->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT; + } + return p; +} +#endif /* MYSQL_FRIENDS */ + static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, int *maxtime, char *peer, char *context, int *trunk, int *notransfer, char *secret, int seclen) { struct hostent *hp; @@ -1523,36 +1618,46 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, i p = peerl.peers; while(p) { if (!strcasecmp(p->name, peer)) { - found++; - if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && - (!p->maxms || ((p->lastms > 0) && (p->lastms <= p->maxms)))) { - if (sendani) - *sendani = p->sendani; /* Whether we transmit ANI */ - if (maxtime) - *maxtime = p->maxms; /* Max time they should take */ - if (context) - strncpy(context, p->context, AST_MAX_EXTENSION - 1); - if (trunk) - *trunk = p->trunk; - if (capability) - *capability = p->capability; - if (secret) - strncpy(secret, p->secret, seclen); - if (p->addr.sin_addr.s_addr) { - sin->sin_addr = p->addr.sin_addr; - sin->sin_port = p->addr.sin_port; - } else { - sin->sin_addr = p->defaddr.sin_addr; - sin->sin_port = p->defaddr.sin_port; - } - if (notransfer) - *notransfer=p->notransfer; - break; - } + break; } p = p->next; } ast_mutex_unlock(&peerl.lock); +#ifdef MYSQL_FRIENDS + if (!p) + p = mysql_peer(peer); +#endif + if (p) { + found++; + if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && + (!p->maxms || ((p->lastms > 0) && (p->lastms <= p->maxms)))) { + if (sendani) + *sendani = p->sendani; /* Whether we transmit ANI */ + if (maxtime) + *maxtime = p->maxms; /* Max time they should take */ + if (context) + strncpy(context, p->context, AST_MAX_EXTENSION - 1); + if (trunk) + *trunk = p->trunk; + if (capability) + *capability = p->capability; + if (secret) + strncpy(secret, p->secret, seclen); + if (p->addr.sin_addr.s_addr) { + sin->sin_addr = p->addr.sin_addr; + sin->sin_port = p->addr.sin_port; + } else { + sin->sin_addr = p->defaddr.sin_addr; + sin->sin_port = p->defaddr.sin_port; + } + if (notransfer) + *notransfer=p->notransfer; + } else { + if (p->delme) + free(p); + p = NULL; + } + } if (!p && !found) { hp = gethostbyname(peer); if (hp) { @@ -1565,8 +1670,9 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, i } } else if (!p) return -1; - else - return 0; + if (p->delme) + free(p); + return 0; } static int auto_congest(void *nothing) @@ -2849,9 +2955,15 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies * return -1; } + ast_mutex_lock(&peerl.lock); for (p = peerl.peers; p ; p = p->next) if (!strcasecmp(p->name, peer)) break; + ast_mutex_unlock(&peerl.lock); +#ifdef MYSQL_FRIENDS + if (!p) + p = mysql_peer(peer); +#endif if (!p) { ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, inet_ntoa(sin->sin_addr)); @@ -2860,17 +2972,21 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies * if (!p->dynamic) { ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, inet_ntoa(sin->sin_addr)); + if (p->delme) + free(p); return -1; } if (!ast_apply_ha(p->ha, sin)) { ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", inet_ntoa(sin->sin_addr), p->name); + if (p->delme) + free(p); return -1; } strncpy(iaxs[callno]->secret, p->secret, sizeof(iaxs[callno]->secret)-1); strncpy(iaxs[callno]->inkeys, p->inkeys, sizeof(iaxs[callno]->inkeys)-1); /* Check secret against what we have on file */ - if (strlen(rsasecret) && (p->authmethods & IAX_AUTH_RSA) && strlen(p->challenge)) { + if (strlen(rsasecret) && (p->authmethods & IAX_AUTH_RSA) && strlen(iaxs[callno]->challenge)) { if (strlen(p->inkeys)) { char tmpkeys[256]; char *stringp=NULL; @@ -2879,7 +2995,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies * keyn = strsep(&stringp, ":"); while(keyn) { key = ast_key_get(keyn, AST_KEY_PUBLIC); - if (key && !ast_check_signature(key, p->challenge, rsasecret)) { + if (key && !ast_check_signature(key, iaxs[callno]->challenge, rsasecret)) { iaxs[callno]->state |= IAX_STATE_AUTHENTICATED; break; } else if (!key) @@ -2888,24 +3004,30 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies * } if (!keyn) { ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys); + if (p->delme) + free(p); return -1; } } else { ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer); + if (p->delme) + free(p); return -1; } } else if (strlen(secret) && (p->authmethods & IAX_AUTH_PLAINTEXT)) { /* They've provided a plain text password and we support that */ if (strcmp(secret, p->secret)) { ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", inet_ntoa(sin->sin_addr), p->name); + if (p->delme) + free(p); return -1; } else iaxs[callno]->state |= IAX_STATE_AUTHENTICATED; - } else if (strlen(md5secret) && (p->authmethods & IAX_AUTH_MD5) && strlen(p->challenge)) { + } else if (strlen(md5secret) && (p->authmethods & IAX_AUTH_MD5) && strlen(iaxs[callno]->challenge)) { struct MD5Context md5; unsigned char digest[16]; MD5Init(&md5); - MD5Update(&md5, p->challenge, strlen(p->challenge)); + MD5Update(&md5, iaxs[callno]->challenge, strlen(iaxs[callno]->challenge)); MD5Update(&md5, p->secret, strlen(p->secret)); MD5Final(digest, &md5); for (x=0;x<16;x++) @@ -2917,12 +3039,16 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies * iaxs[callno]->state |= IAX_STATE_AUTHENTICATED; } else if (strlen(md5secret) || strlen(secret)) { ast_log(LOG_NOTICE, "Inappropriate authentication received\n"); + if (p->delme) + free(p); return -1; } strncpy(iaxs[callno]->peer, peer, sizeof(iaxs[callno]->peer)-1); /* Choose lowest expirey number */ if (expire && (expire < iaxs[callno]->expirey)) iaxs[callno]->expirey = expire; + if (p->delme) + free(p); return 0; } @@ -3343,35 +3469,48 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno) memset(&ied, 0, sizeof(ied)); for (p = peerl.peers;p;p = p->next) { if (!strcasecmp(name, p->name)) { - if (inaddrcmp(&p->addr, sin)) { - if (iax2_regfunk) - iax2_regfunk(p->name, 1); - snprintf(data, sizeof(data), "%s:%d:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expirey); - ast_db_put("IAX/Registry", p->name, data); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Registered '%s' (%s) at %s:%d\n", p->name, - iaxs[callno]->state & IAX_STATE_AUTHENTICATED ? "AUTHENTICATED" : "UNAUTHENTICATED", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); - iax2_poke_peer(p); - } - /* Update the host */ - memcpy(&p->addr, sin, sizeof(p->addr)); - /* Setup the expirey */ - if (p->expire > -1) - ast_sched_del(sched, p->expire); - p->expire = ast_sched_add(sched, p->expirey * 1000, expire_registry, (void *)p); - iax_ie_append_str(&ied, IAX_IE_USERNAME, p->name); - iax_ie_append_short(&ied, IAX_IE_REFRESH, p->expirey); - iax_ie_append_addr(&ied, IAX_IE_APPARENT_ADDR, &p->addr); - if (strlen(p->mailbox)) { - msgcount = ast_app_has_voicemail(p->mailbox); - if (msgcount) - msgcount = 65535; - iax_ie_append_short(&ied, IAX_IE_MSGCOUNT, msgcount); - } - if (p->hascallerid) - iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->callerid); - return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);; + break; + } + } +#ifdef MYSQL_FRIENDS + if (!p) + p = mysql_peer(name); +#endif + if (p) { +#ifdef MYSQL_FRIENDS + if (p->delme) + mysql_update_peer(name, sin); +#endif + if (inaddrcmp(&p->addr, sin)) { + if (iax2_regfunk) + iax2_regfunk(p->name, 1); + snprintf(data, sizeof(data), "%s:%d:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expirey); + ast_db_put("IAX/Registry", p->name, data); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Registered '%s' (%s) at %s:%d\n", p->name, + iaxs[callno]->state & IAX_STATE_AUTHENTICATED ? "AUTHENTICATED" : "UNAUTHENTICATED", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + iax2_poke_peer(p); + } + /* Update the host */ + memcpy(&p->addr, sin, sizeof(p->addr)); + /* Setup the expirey */ + if (p->expire > -1) + ast_sched_del(sched, p->expire); + p->expire = ast_sched_add(sched, p->expirey * 1000, expire_registry, (void *)p); + iax_ie_append_str(&ied, IAX_IE_USERNAME, p->name); + iax_ie_append_short(&ied, IAX_IE_REFRESH, p->expirey); + iax_ie_append_addr(&ied, IAX_IE_APPARENT_ADDR, &p->addr); + if (strlen(p->mailbox)) { + msgcount = ast_app_has_voicemail(p->mailbox); + if (msgcount) + msgcount = 65535; + iax_ie_append_short(&ied, IAX_IE_MSGCOUNT, msgcount); } + if (p->hascallerid) + iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->callerid); + if (p->delme) + free(p); + return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);; } ast_log(LOG_WARNING, "No such peer '%s'\n", name); return -1; @@ -3381,19 +3520,30 @@ static int registry_authrequest(char *name, int callno) { struct iax_ie_data ied; struct iax2_peer *p; + ast_mutex_lock(&peerl.lock); for (p = peerl.peers;p;p = p->next) { if (!strcasecmp(name, p->name)) { - memset(&ied, 0, sizeof(ied)); - iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods); - if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) { - /* Build the challenge */ - snprintf(p->challenge, sizeof(p->challenge), "%d", rand()); - iax_ie_append_str(&ied, IAX_IE_CHALLENGE, p->challenge); - } - iax_ie_append_str(&ied, IAX_IE_USERNAME, name); - return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);; + break; } } + ast_mutex_unlock(&peerl.lock); +#ifdef MYSQL_FRIENDS + if (!p) + p = mysql_peer(name); +#endif + if (p) { + memset(&ied, 0, sizeof(ied)); + iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods); + if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) { + /* Build the challenge */ + snprintf(iaxs[callno]->challenge, sizeof(iaxs[callno]->challenge), "%d", rand()); + iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge); + } + iax_ie_append_str(&ied, IAX_IE_USERNAME, name); + if (p->delme) + free(p); + return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);; + } ast_log(LOG_WARNING, "No such peer '%s'\n", name); return 0; } @@ -5245,7 +5395,19 @@ static int set_config(char *config_file, struct sockaddr_in* sin){ } else { amaflags = format; } - } //else if (strcasecmp(v->name,"type")) + } +#ifdef MYSQL_FRIENDS + else if (!strcasecmp(v->name, "dbuser")) { + strncpy(mydbuser, v->value, sizeof(mydbuser) - 1); + } else if (!strcasecmp(v->name, "dbpass")) { + strncpy(mydbpass, v->value, sizeof(mydbpass) - 1); + } else if (!strcasecmp(v->name, "dbhost")) { + strncpy(mydbhost, v->value, sizeof(mydbhost) - 1); + } else if (!strcasecmp(v->name, "dbname")) { + strncpy(mydbname, v->value, sizeof(mydbname) - 1); + } +#endif + //else if (strcasecmp(v->name,"type")) // ast_log(LOG_WARNING, "Ignoring %s\n", v->name); v = v->next; } @@ -5282,6 +5444,21 @@ static int set_config(char *config_file, struct sockaddr_in* sin){ } ast_destroy(cfg); set_timing(); +#ifdef MYSQL_FRIENDS + /* Connect to db if appropriate */ + if (strlen(mydbname)) { + mysql = mysql_init(NULL); + if (!mysql_real_connect(mysql, mydbhost[0] ? mydbhost : NULL, mydbuser, mydbpass, mydbname, 0, NULL, 0)) { + memset(mydbpass, '*', strlen(mydbpass)); + ast_log(LOG_WARNING, "Database connection failed (db=%s, host=%s, user=%s, pass=%s)!\n", + mydbname, mydbhost, mydbuser, mydbpass); + free(mysql); + mysql = NULL; + } else + ast_verbose(VERBOSE_PREFIX_1 "Connected to database '%s' on '%s' as '%s'\n", + mydbname, mydbhost, mydbuser); + } +#endif return capability; } |