1 diff -urN asterisk-1.2.4/channels/chan_iax2.c asterisk-1.2.4.carrier/channels/chan_iax2.c
2 --- asterisk-1.2.4/channels/chan_iax2.c 2006-01-31 09:41:43.000000000 +0100
3 +++ asterisk-1.2.4.carrier/channels/chan_iax2.c 2006-02-08 09:49:45.000000000 +0100
5 static struct ast_netsock_list *netsock;
6 static int defaultsockfd = -1;
8 +static char servername[80];
11 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
15 static pthread_t netthreadid = AST_PTHREADT_NULL;
17 +static pthread_t regthreadid = AST_PTHREADT_NULL;
19 +static pthread_t auththreadid = AST_PTHREADT_NULL;
22 IAX_STATE_STARTED = (1 << 0),
23 IAX_STATE_AUTHENTICATED = (1 << 1),
28 +typedef struct iax_auth_frame {
29 + struct iax_auth_frame *prev;
30 + struct iax_auth_frame *next;
31 + struct iax_frame *frame;
32 + struct sockaddr_in sin;
36 + unsigned char iebuf[4096];
39 +static struct ast_iax2_auth_queue {
40 + struct iax_auth_frame *head;
41 + struct iax_auth_frame *tail;
47 static struct ast_user_list {
48 struct iax2_user *users;
51 static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
52 static ast_mutex_t iaxsl[IAX_MAX_CALLS];
53 static struct timeval lastused[IAX_MAX_CALLS];
54 +/* some packets have been queued for asynchronous processing */
55 +static int iaxs_queued[IAX_MAX_CALLS];
58 static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
60 static int send_lagrq(void *data)
62 int callno = (long)data;
63 - /* Ping only if it's real not if it's bridged */
64 + /* Ping only if it's real not if it's bridged (and not if it's queued!)*/
66 #ifdef BRIDGE_OPTIMIZATION
67 if (!iaxs[callno]->bridgecallno)
69 + if (!iaxs_queued[callno])
70 send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
76 if ((res < 1) && (new >= NEW_ALLOW)) {
77 - if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer))
78 - snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port));
79 + /* We are NOT calling iax2_getpeernme() here, because it WILL BLOCK when using realtime.
80 + Instead we will overwrite the host filed in the iax_pvt for each new call or registration. */
81 + snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port));
82 gettimeofday(&now, NULL);
83 for (x=1;x<TRUNK_CALL_START;x++) {
84 /* Find first unused call number that hasn't been used in a while */
85 @@ -1141,6 +1170,58 @@
89 +static int iax2_queue_auth_frame(struct ast_iax2_auth_queue *authq, int subclass, struct iax_frame *ifr, unsigned char *buf, unsigned char *iebuf, int iebuflen, struct sockaddr_in *sin, int fd, int maydrop)
91 + struct iax_auth_frame *fr = NULL;
93 + ok, we get here with the iaxsl[callno] still locked.
94 + in any case we have to unlock it before returning.
97 + if (!authq || !ifr || !ifr->callno) return 1;
99 + fr = malloc(sizeof(struct iax_auth_frame));
101 + ast_log(LOG_ERROR, "Unable to malloc!\n");
104 + memset(fr,0,sizeof(fr));
108 + fr->subclass = subclass;
110 + memcpy(fr->iebuf, iebuf, iebuflen);
112 + iax_parse_ies(&fr->ies, fr->iebuf, iebuflen);
113 + memcpy(&fr->sin, sin, sizeof(struct sockaddr_in));
114 + ast_mutex_lock(&authq->lock);
115 + if (maydrop && (authq->count > 5000)) {
116 + ast_mutex_unlock(&authq->lock);
117 + ast_mutex_unlock(&iaxsl[ifr->callno]);
119 + if (option_verbose > 5)
120 + ast_log(LOG_WARNING, "Queue too long, not queueing frame.\n");
123 + if (!authq->head) {
129 + authq->tail->next = fr;
130 + fr->prev = authq->tail;
133 + iaxs_queued[ifr->callno]++;
135 + ast_mutex_unlock(&iaxsl[ifr->callno]);
136 + ast_cond_signal(&authq->cond);
137 + ast_mutex_unlock(&authq->lock);
141 static void destroy_firmware(struct iax_firmware *cur)
144 @@ -1713,8 +1794,10 @@
145 /* Transfer timeout */
146 send_command(iaxs[f->callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
147 } else if (f->final) {
149 + if (!iaxs_queued[f->callno]) {
151 iax2_destroy_nolock(f->callno);
154 if (iaxs[f->callno]->owner)
155 ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass, f->ts, f->oseqno);
156 @@ -1729,12 +1812,14 @@
157 if (iaxs[f->callno]->owner)
158 iaxs[f->callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
160 - if (iaxs[f->callno]->reg) {
161 + if (!iaxs_queued[f->callno]) {
162 + if (iaxs[f->callno]->reg) {
163 memset(&iaxs[f->callno]->reg->us, 0, sizeof(iaxs[f->callno]->reg->us));
164 iaxs[f->callno]->reg->regstate = REG_STATE_TIMEOUT;
165 iaxs[f->callno]->reg->refresh = IAX_DEFAULT_REG_EXPIRE;
167 + iax2_destroy_nolock(f->callno);
169 - iax2_destroy_nolock(f->callno);
173 @@ -2706,6 +2791,8 @@
174 snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);
175 ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin->sin_addr);
176 snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
177 +// the following line is commented out for compatibility
178 +// ast_update_realtime("iaxpeers", "name", peername, "ipaddr", ipaddr, "port", port, "regseconds", regseconds, "servername", servername, NULL);
179 ast_update_realtime("iaxpeers", "name", peername, "ipaddr", ipaddr, "port", port, "regseconds", regseconds, NULL);
182 @@ -5034,16 +5121,17 @@
184 /* We release the lock for the call to prevent a deadlock, but it's okay because
185 only the current thread could possibly make it go away or make changes */
186 - ast_mutex_unlock(&iaxsl[callno]);
187 +// ast_mutex_unlock(&iaxsl[callno]);
188 /* SLD: first call to lookup peer during registration */
189 p = find_peer(peer, 1);
190 - ast_mutex_lock(&iaxsl[callno]);
192 +// ast_mutex_lock(&iaxsl[callno]);
195 ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
198 + /* set the host name here. instead of doing it in find_callno() */
199 + ast_copy_string(iaxs[callno]->host, p->name, sizeof(iaxs[callno]->host));
201 if (!ast_test_flag(p, IAX_DYNAMIC)) {
203 @@ -5677,11 +5765,13 @@
205 refresh = min_reg_expire;
206 if (refresh > max_reg_expire) {
207 - ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
208 + if (option_verbose > 5)
209 + ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
210 p->name, max_reg_expire, refresh);
211 p->expiry = max_reg_expire;
212 } else if (refresh < min_reg_expire) {
213 - ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
214 + if (option_verbose > 5)
215 + ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
216 p->name, min_reg_expire, refresh);
217 p->expiry = min_reg_expire;
219 @@ -5720,6 +5810,7 @@
220 iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
221 if (ast_test_flag(p, IAX_TEMPONLY))
223 + ast_mutex_lock(&iaxsl[callno]);
224 return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
227 @@ -6279,6 +6370,8 @@
228 struct timeval rxtrunktime;
230 struct iax_ie_data ied0, ied1;
231 + unsigned char iebuf[4096];
236 @@ -6609,9 +6702,11 @@
238 /* Destroy call if this is the end */
240 - if (iaxdebug && option_debug)
241 + if (!iaxs_queued[fr.callno]) {
242 + if (iaxdebug && option_debug)
243 ast_log(LOG_DEBUG, "Really destroying %d, having been acked on final message\n", fr.callno);
244 - iax2_destroy_nolock(fr.callno);
245 + iax2_destroy_nolock(fr.callno);
250 @@ -6639,6 +6734,8 @@
253 if (f.frametype == AST_FRAME_IAX) {
254 + memcpy(iebuf, buf + sizeof(struct ast_iax2_full_hdr), f.datalen);
255 + iebuflen = f.datalen;
256 if (iax_parse_ies(&ies, buf + sizeof(struct ast_iax2_full_hdr), f.datalen)) {
257 ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
258 ast_mutex_unlock(&iaxsl[fr.callno]);
259 @@ -6772,166 +6869,11 @@
261 if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr)
262 check_provisioning(&sin, fd, ies.serviceident, ies.provver);
263 - /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */
264 - if (ast_test_flag(iaxs[fr.callno], IAX_TRUNK)) {
265 - fr.callno = make_trunk(fr.callno, 1);
267 /* For security, always ack immediately */
269 send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
270 - if (check_access(fr.callno, &sin, &ies)) {
271 - /* They're not allowed on */
272 - auth_fail(fr.callno, IAX_COMMAND_REJECT);
274 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
277 - /* This might re-enter the IAX code and need the lock */
278 - if (strcasecmp(iaxs[fr.callno]->exten, "TBD")) {
279 - ast_mutex_unlock(&iaxsl[fr.callno]);
280 - exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->cid_num);
281 - ast_mutex_lock(&iaxsl[fr.callno]);
284 - if (ast_strlen_zero(iaxs[fr.callno]->secret) && ast_strlen_zero(iaxs[fr.callno]->inkeys)) {
285 - if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
286 - memset(&ied0, 0, sizeof(ied0));
287 - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
288 - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
289 - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
291 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
293 - /* Select an appropriate format */
295 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOPREFS)) {
296 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP)) {
297 - using_prefs = "reqonly";
299 - using_prefs = "disabled";
301 - format = iaxs[fr.callno]->peerformat & iaxs[fr.callno]->capability;
302 - memset(&pref, 0, sizeof(pref));
303 - strcpy(caller_pref_buf, "disabled");
304 - strcpy(host_pref_buf, "disabled");
306 - using_prefs = "mine";
307 - if(ies.codec_prefs) {
308 - ast_codec_pref_convert(&rpref, ies.codec_prefs, 32, 0);
309 - /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
310 - if (ast_test_flag(iaxs[fr.callno], IAX_CODEC_USER_FIRST)) {
312 - using_prefs = "caller";
314 - pref = iaxs[fr.callno]->prefs;
317 - pref = iaxs[fr.callno]->prefs;
319 - format = ast_codec_choose(&pref, iaxs[fr.callno]->capability & iaxs[fr.callno]->peercapability, 0);
320 - ast_codec_pref_string(&rpref, caller_pref_buf, sizeof(caller_pref_buf) - 1);
321 - ast_codec_pref_string(&iaxs[fr.callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
324 - if(!ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP))
325 - format = iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability;
327 - memset(&ied0, 0, sizeof(ied0));
328 - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
329 - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
330 - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
332 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP))
333 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->capability);
335 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iaxs[fr.callno]->capability);
339 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP)) {
340 - if(!(iaxs[fr.callno]->peerformat & iaxs[fr.callno]->capability))
343 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOPREFS)) {
344 - using_prefs = ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
345 - memset(&pref, 0, sizeof(pref));
346 - format = ast_best_codec(iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability);
347 - strcpy(caller_pref_buf,"disabled");
348 - strcpy(host_pref_buf,"disabled");
350 - using_prefs = "mine";
351 - if(ies.codec_prefs) {
352 - /* Do the opposite of what we tried above. */
353 - if (ast_test_flag(iaxs[fr.callno], IAX_CODEC_USER_FIRST)) {
354 - pref = iaxs[fr.callno]->prefs;
357 - using_prefs = "caller";
359 - format = ast_codec_choose(&pref, iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability, 1);
361 - } else /* if no codec_prefs IE do it the old way */
362 - format = ast_best_codec(iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability);
367 - memset(&ied0, 0, sizeof(ied0));
368 - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
369 - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
370 - ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability);
371 - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
373 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iaxs[fr.callno]->capability);
374 - ast_set_flag(iaxs[fr.callno], IAX_ALREADYGONE);
380 - /* No authentication required, let them in */
381 - memset(&ied1, 0, sizeof(ied1));
382 - iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
383 - send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
384 - if (strcmp(iaxs[fr.callno]->exten, "TBD")) {
385 - ast_set_flag(&iaxs[fr.callno]->state, IAX_STATE_STARTED);
386 - if (option_verbose > 2)
387 - ast_verbose(VERBOSE_PREFIX_3 "Accepting UNAUTHENTICATED call from %s:\n"
388 - "%srequested format = %s,\n"
389 - "%srequested prefs = %s,\n"
390 - "%sactual format = %s,\n"
391 - "%shost prefs = %s,\n"
392 - "%spriority = %s\n",
393 - ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr),
395 - ast_getformatname(iaxs[fr.callno]->peerformat),
399 - ast_getformatname(format),
405 - if(!(c = ast_iax2_new(fr.callno, AST_STATE_RING, format)))
406 - iax2_destroy_nolock(fr.callno);
408 - ast_set_flag(&iaxs[fr.callno]->state, IAX_STATE_TBD);
409 - /* If this is a TBD call, we're ready but now what... */
410 - if (option_verbose > 2)
411 - ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
417 - if (iaxs[fr.callno]->authmethods & IAX_AUTH_MD5)
418 - merge_encryption(iaxs[fr.callno],ies.encmethods);
420 - iaxs[fr.callno]->encmethods = 0;
421 - authenticate_request(iaxs[fr.callno]);
422 - ast_set_flag(&iaxs[fr.callno]->state, IAX_STATE_AUTHENTICATED);
424 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
426 case IAX_COMMAND_DPREQ:
427 /* Request status in the dialplan */
428 @@ -6947,14 +6889,22 @@
431 case IAX_COMMAND_HANGUP:
432 - ast_set_flag(iaxs[fr.callno], IAX_ALREADYGONE);
433 - ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr.callno);
434 - /* Set hangup cause according to remote */
435 - if (ies.causecode && iaxs[fr.callno]->owner)
436 - iaxs[fr.callno]->owner->hangupcause = ies.causecode;
437 - /* Send ack immediately, before we destroy */
438 - send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
439 - iax2_destroy_nolock(fr.callno);
440 + if (iaxs_queued[fr.callno]) {
441 + /* there is something queued, maybe the call has not been authorized yet. */
442 + /* Send ack immediately, before we destroy */
443 + send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
445 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
447 + ast_set_flag(iaxs[fr.callno], IAX_ALREADYGONE);
448 + ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr.callno);
449 + /* Set hangup cause according to remote */
450 + if (ies.causecode && iaxs[fr.callno]->owner)
451 + iaxs[fr.callno]->owner->hangupcause = ies.causecode;
452 + /* Send ack immediately, before we destroy */
453 + send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
454 + iax2_destroy_nolock(fr.callno);
457 case IAX_COMMAND_REJECT:
458 memset(&f, 0, sizeof(f));
459 @@ -7183,31 +7133,36 @@
460 /* For security, always ack immediately */
462 send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
463 - /* Ignore once we've started */
464 - if (ast_test_flag(&iaxs[fr.callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
465 + if (iaxs_queued[fr.callno]) {
466 + /* there is something queued, take the same path */
468 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
470 + /* Ignore once we've started */
471 + if (ast_test_flag(&iaxs[fr.callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
472 ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr.callno]->owner ? iaxs[fr.callno]->owner->name : "<Unknown>");
475 - if (authenticate_verify(iaxs[fr.callno], &ies)) {
477 + if (authenticate_verify(iaxs[fr.callno], &ies)) {
479 ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[fr.callno]->addr.sin_addr), iaxs[fr.callno]->username);
480 memset(&ied0, 0, sizeof(ied0));
481 auth_fail(fr.callno, IAX_COMMAND_REJECT);
484 - if (strcasecmp(iaxs[fr.callno]->exten, "TBD")) {
486 + if (strcasecmp(iaxs[fr.callno]->exten, "TBD")) {
487 /* This might re-enter the IAX code and need the lock */
488 exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->cid_num);
492 - if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
493 + if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
495 ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
496 memset(&ied0, 0, sizeof(ied0));
497 iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
498 iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
499 send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
502 /* Select an appropriate format */
503 if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOPREFS)) {
504 if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP)) {
505 @@ -7332,6 +7287,7 @@
506 ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
512 case IAX_COMMAND_DIAL:
513 @@ -7357,11 +7313,16 @@
516 case IAX_COMMAND_INVAL:
517 - iaxs[fr.callno]->error = ENOTCONN;
518 - ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr.callno);
519 - iax2_destroy_nolock(fr.callno);
521 + if (iaxs_queued[fr.callno]) {
523 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
525 + iaxs[fr.callno]->error = ENOTCONN;
526 + ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr.callno);
527 + iax2_destroy_nolock(fr.callno);
529 ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
532 case IAX_COMMAND_VNAK:
533 ast_log(LOG_DEBUG, "Received VNAK: resending outstanding frames\n");
534 @@ -7373,21 +7334,11 @@
535 /* For security, always ack immediately */
537 send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
538 - if (register_verify(fr.callno, &sin, &ies)) {
539 - /* Send delayed failure */
540 - auth_fail(fr.callno, IAX_COMMAND_REGREJ);
543 - if ((ast_strlen_zero(iaxs[fr.callno]->secret) && ast_strlen_zero(iaxs[fr.callno]->inkeys)) || ast_test_flag(&iaxs[fr.callno]->state, IAX_STATE_AUTHENTICATED)) {
544 - if (f.subclass == IAX_COMMAND_REGREL)
545 - memset(&sin, 0, sizeof(sin));
546 - if (update_registry(iaxs[fr.callno]->peer, &sin, fr.callno, ies.devicetype, fd, ies.refresh))
547 - ast_log(LOG_WARNING, "Registry error\n");
548 - if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr)
549 - check_provisioning(&sin, fd, ies.serviceident, ies.provver);
552 + if (iax2_queue_auth_frame(®q, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 1)) {
553 + /* queueing failed due to a full queue */
554 + iax2_destroy_nolock(fr.callno);
556 - registry_authrequest(iaxs[fr.callno]->peer, fr.callno);
558 case IAX_COMMAND_REGACK:
559 if (iax2_ack_registry(&ies, &sin, fr.callno))
560 @@ -7989,6 +7940,478 @@
561 return ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
564 +struct iax_auth_frame *iax2_get_next_auth_frame(struct ast_iax2_auth_queue *authq) {
565 + struct iax_auth_frame *frame = NULL;
567 + /* sleep until the network_thread queues us a frame, then grab it and release the lock A.S.A.P. */
568 + ast_mutex_lock(&authq->lock);
569 + if (authq->count == 0)
570 + ast_cond_wait(&authq->cond, &authq->lock);
571 + frame = authq->head;
574 +// frame->next->prev = NULL;
575 + authq->head = frame->next;
577 + authq->head = NULL;
578 + authq->tail = NULL;
582 + ast_log(LOG_ERROR, "I SHOULD NEVER HAPPEN! EXPECT SOME MAJOR KABOOM! DUCK AND COVER!\n");
584 + ast_mutex_unlock(&authq->lock);
589 +void *auth_thread(void *data)
592 + The network_thread queues iax_frames into our queue and wakes us up.
593 + We will authenticate IAX_COMMAND_NEWs and IAX_COMMAND_REGREQs.
595 + We also have to process IAX_COMMAND_HANGUP when somebody hangs up a call
596 + before it has been authorized!
598 + If ever possible we shall not lock any iaxsl[...].
600 + struct ast_iax2_auth_queue *authq = (struct ast_iax2_auth_queue *)data;
601 + struct iax_frame *ifr;
602 + struct iax_auth_frame *fr;
603 + struct iax_ies *ies;
604 + struct iax_ie_data ied0, ied1;
605 + struct ast_channel *c;
606 + char iabuf[INET_ADDRSTRLEN];
610 + char host_pref_buf[128];
611 + char caller_pref_buf[128];
612 + struct ast_codec_pref pref,rpref;
613 + char *using_prefs = "mine";
616 + ast_log(LOG_ERROR, "no queue!\n");
621 + fr = iax2_get_next_auth_frame(authq);
625 + if (ifr->callno > 0) {
626 + /* we will not lock iaxsl[ifr->callno], instead we will haveset a flag in iaxs_queued[ifr->callno].
627 + if this flag is set the socket_read thread may not touch iaxs[ifr->callno]. instead of
628 + processing the incoming packets there it has to queue them to us!
629 + This should only happen with IAX_COMMAND_HANGUP when hanging up a not-yet-established call.
631 + However we will try to deliver audio frames in socket_read. If we get audio data for call
632 + whos signalling frames have been queued we should be safe to just drop them.
634 + When we really want to destroy something we will aquire the lock first.
636 + switch(fr->subclass) {
637 + case IAX_COMMAND_NEW:
638 + iax2_getpeername(fr->sin, iaxs[ifr->callno]->host, sizeof(iaxs[ifr->callno]->host), 1);
639 + if (check_access(ifr->callno, &fr->sin, ies)) {
640 + /* They're not allowed on */
642 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->exten, iaxs[ifr->callno]->context);
643 + auth_fail(ifr->callno, IAX_COMMAND_REJECT);
646 + /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */
647 + if (ast_test_flag(iaxs[ifr->callno], IAX_TRUNK)) {
648 + ifr->callno = make_trunk(ifr->callno, 1);
650 + /* This might re-enter the IAX code and need the lock */
651 + if (strcasecmp(iaxs[ifr->callno]->exten, "TBD")) {
652 + // ast_mutex_unlock(&iaxsl[ifr->callno]);
653 + exists = ast_exists_extension(NULL, iaxs[ifr->callno]->context, iaxs[ifr->callno]->exten, 1, iaxs[ifr->callno]->cid_num);
654 + // ast_mutex_lock(&iaxsl[ifr->callno]);
657 + if (ast_strlen_zero(iaxs[ifr->callno]->secret) && ast_strlen_zero(iaxs[ifr->callno]->inkeys)) {
658 + if (strcmp(iaxs[ifr->callno]->exten, "TBD") && !exists) {
659 + memset(&ied0, 0, sizeof(ied0));
660 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
661 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
662 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
664 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->exten, iaxs[ifr->callno]->context);
666 + /* Select an appropriate format */
667 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
668 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
669 + using_prefs = "reqonly";
671 + using_prefs = "disabled";
673 + format = iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability;
674 + memset(&pref, 0, sizeof(pref));
675 + strcpy(caller_pref_buf, "disabled");
676 + strcpy(host_pref_buf, "disabled");
678 + using_prefs = "mine";
679 + if(ies->codec_prefs) {
680 + ast_codec_pref_convert(&rpref, ies->codec_prefs, 32, 0);
681 + /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
682 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
684 + using_prefs = "caller";
686 + pref = iaxs[ifr->callno]->prefs;
689 + pref = iaxs[ifr->callno]->prefs;
691 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->capability & iaxs[ifr->callno]->peercapability, 0);
692 + ast_codec_pref_string(&rpref, caller_pref_buf, sizeof(caller_pref_buf) - 1);
693 + ast_codec_pref_string(&iaxs[ifr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
696 + if(!ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
697 + format = iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability;
699 + memset(&ied0, 0, sizeof(ied0));
700 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
701 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
702 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
704 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
705 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->capability);
707 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
711 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
712 + if(!(iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability))
715 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
716 + using_prefs = ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
717 + memset(&pref, 0, sizeof(pref));
718 + format = ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
719 + strcpy(caller_pref_buf,"disabled");
720 + strcpy(host_pref_buf,"disabled");
722 + using_prefs = "mine";
723 + if(ies->codec_prefs) {
724 + /* Do the opposite of what we tried above. */
725 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
726 + pref = iaxs[ifr->callno]->prefs;
729 + using_prefs = "caller";
731 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability, 1);
733 + } else /* if no codec_prefs IE do it the old way */
734 + format = ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
739 + memset(&ied0, 0, sizeof(ied0));
740 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
741 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
742 + ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
743 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
745 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
746 + ast_set_flag(iaxs[ifr->callno], IAX_ALREADYGONE);
752 + /* No authentication required, let them in */
753 + memset(&ied1, 0, sizeof(ied1));
754 + iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
755 + send_command(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
756 + if (strcmp(iaxs[ifr->callno]->exten, "TBD")) {
757 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED);
758 + if (option_verbose > 2)
759 + ast_verbose(VERBOSE_PREFIX_3 "Accepting UNAUTHENTICATED call from %s:\n"
760 + "%srequested format = %s,\n"
761 + "%srequested prefs = %s,\n"
762 + "%sactual format = %s,\n"
763 + "%shost prefs = %s,\n"
764 + "%spriority = %s\n",
765 + ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr),
767 + ast_getformatname(iaxs[ifr->callno]->peerformat),
771 + ast_getformatname(format),
777 + if(!(c = ast_iax2_new(ifr->callno, AST_STATE_RING, format))) {
778 + ast_mutex_lock(&iaxsl[ifr->callno]);
779 + iax2_destroy_nolock(ifr->callno);
780 + ast_mutex_unlock(&iaxsl[ifr->callno]);
783 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_TBD);
784 + /* If this is a TBD call, we're ready but now what... */
785 + if (option_verbose > 2)
786 + ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr));
792 + if (iaxs[ifr->callno]->authmethods & IAX_AUTH_MD5)
793 + merge_encryption(iaxs[ifr->callno],ies->encmethods);
795 + iaxs[ifr->callno]->encmethods = 0;
796 + authenticate_request(iaxs[ifr->callno]);
797 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_AUTHENTICATED);
799 + case IAX_COMMAND_REGREQ:
800 + case IAX_COMMAND_REGREL:
801 + // usleep(1000000);
802 + if (register_verify(ifr->callno, &fr->sin, ies)) {
803 + /* Send delayed failure */
804 + auth_fail(ifr->callno, IAX_COMMAND_REGREJ);
807 + if ((ast_strlen_zero(iaxs[ifr->callno]->secret) && ast_strlen_zero(iaxs[ifr->callno]->inkeys)) || ast_test_flag(&iaxs[ifr->callno]->state, IAX_STATE_AUTHENTICATED)) {
808 + if (fr->subclass == IAX_COMMAND_REGREL)
809 + memset(&fr->sin, 0, sizeof(struct sockaddr_in));
810 + if (update_registry(iaxs[ifr->callno]->peer, &fr->sin, ifr->callno, ies->devicetype, fr->fd, ies->refresh))
811 + ast_log(LOG_WARNING, "Registry error\n");
812 + if (ies->provverpres && ies->serviceident && fr->sin.sin_addr.s_addr)
813 + check_provisioning(&fr->sin, fr->fd, ies->serviceident, ies->provver);
814 + /* update registry leaves us locked, so we have to unlock to not deadlock */
815 + iaxs_queued[ifr->callno]--;
816 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
817 + ast_mutex_unlock(&iaxsl[ifr->callno]);
820 + registry_authrequest(iaxs[ifr->callno]->peer, ifr->callno);
822 + case IAX_COMMAND_HANGUP:
823 + /* Here we really have to lock */
824 + ast_mutex_lock(&iaxsl[ifr->callno]);
825 + if (iaxs[ifr->callno]) {
826 + ast_set_flag(iaxs[ifr->callno], IAX_ALREADYGONE);
827 + ast_log(LOG_DEBUG, "Asynchronously destroying %d, having received hangup\n", ifr->callno);
828 + /* Set hangup cause according to remote */
829 + if (ies->causecode && iaxs[ifr->callno]->owner)
830 + iaxs[ifr->callno]->owner->hangupcause = ies->causecode;
831 + iax2_destroy_nolock(ifr->callno);
833 + iaxs_queued[ifr->callno]--;
834 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
836 + ast_mutex_unlock(&iaxsl[ifr->callno]);
838 + case IAX_COMMAND_INVAL:
839 + /* Here we really have to lock */
840 + ast_mutex_lock(&iaxsl[ifr->callno]);
841 + if (iaxs[ifr->callno]) {
842 + iaxs[ifr->callno]->error = ENOTCONN;
843 + ast_log(LOG_DEBUG, "Asynchronously destroying %d, having received INVAL\n", ifr->callno);
844 + iax2_destroy_nolock(ifr->callno);
846 + ast_log(LOG_DEBUG, "Destroying call %d\n", ifr->callno);
848 + iaxs_queued[ifr->callno]--;
849 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
851 + ast_mutex_unlock(&iaxsl[ifr->callno]);
853 + case IAX_COMMAND_AUTHREP:
854 + /* Ignore once we've started */
855 + if (ast_test_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
856 + ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[ifr->callno]->owner ? iaxs[ifr->callno]->owner->name : "<Unknown>");
859 + if (authenticate_verify(iaxs[ifr->callno], ies)) {
861 + ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[ifr->callno]->addr.sin_addr), iaxs[ifr->callno]->username);
862 + memset(&ied0, 0, sizeof(ied0));
863 + auth_fail(ifr->callno, IAX_COMMAND_REJECT);
866 + ast_mutex_lock(&iaxsl[ifr->callno]);
867 + if (strcasecmp(iaxs[ifr->callno]->exten, "TBD")) {
868 + /* This might re-enter the IAX code and need the lock */
869 + exists = ast_exists_extension(NULL, iaxs[ifr->callno]->context, iaxs[ifr->callno]->exten, 1, iaxs[ifr->callno]->cid_num);
872 + if (strcmp(iaxs[ifr->callno]->exten, "TBD") && !exists) {
874 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->exten, iaxs[ifr->callno]->context);
875 + memset(&ied0, 0, sizeof(ied0));
876 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
877 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
878 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
880 + /* Select an appropriate format */
881 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
882 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
883 + using_prefs = "reqonly";
885 + using_prefs = "disabled";
887 + format = iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability;
888 + memset(&pref, 0, sizeof(pref));
889 + strcpy(caller_pref_buf, "disabled");
890 + strcpy(host_pref_buf, "disabled");
892 + using_prefs = "mine";
893 + if(ies->codec_prefs) {
894 + /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
895 + ast_codec_pref_convert(&rpref, ies->codec_prefs, 32, 0);
896 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
897 + ast_codec_pref_convert(&pref, ies->codec_prefs, 32, 0);
898 + using_prefs = "caller";
900 + pref = iaxs[ifr->callno]->prefs;
902 + } else /* if no codec_prefs IE do it the old way */
903 + pref = iaxs[ifr->callno]->prefs;
905 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->capability & iaxs[ifr->callno]->peercapability, 0);
906 + ast_codec_pref_string(&rpref, caller_pref_buf, sizeof(caller_pref_buf) - 1);
907 + ast_codec_pref_string(&iaxs[ifr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
910 + if(!ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
911 + ast_log(LOG_DEBUG, "We don't do requested format %s, falling back to peer capability %d\n", ast_getformatname(iaxs[ifr->callno]->peerformat), iaxs[ifr->callno]->peercapability);
912 + format = iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability;
916 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
917 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->capability);
919 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
921 + memset(&ied0, 0, sizeof(ied0));
922 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
923 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
924 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
927 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
928 + if(!(iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability))
931 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
932 + using_prefs = ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
933 + memset(&pref, 0, sizeof(pref));
934 + format = ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP) ?
935 + iaxs[ifr->callno]->peerformat : ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
936 + strcpy(caller_pref_buf,"disabled");
937 + strcpy(host_pref_buf,"disabled");
939 + using_prefs = "mine";
940 + if(ies->codec_prefs) {
941 + /* Do the opposite of what we tried above. */
942 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
943 + pref = iaxs[ifr->callno]->prefs;
946 + using_prefs = "caller";
948 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability, 1);
949 + } else /* if no codec_prefs IE do it the old way */
950 + format = ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
954 + ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
956 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
957 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->capability);
959 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
961 + memset(&ied0, 0, sizeof(ied0));
962 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
963 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
964 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
969 + /* Authentication received */
970 + memset(&ied1, 0, sizeof(ied1));
971 + iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
972 + send_command(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
973 + if (strcmp(iaxs[ifr->callno]->exten, "TBD")) {
974 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED);
975 + if (option_verbose > 2)
976 + ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s:\n"
977 + "%srequested format = %s,\n"
978 + "%srequested prefs = %s,\n"
979 + "%sactual format = %s,\n"
980 + "%shost prefs = %s,\n"
981 + "%spriority = %s\n",
982 + ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr),
984 + ast_getformatname(iaxs[ifr->callno]->peerformat),
988 + ast_getformatname(format),
994 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED);
995 + if(!(c = ast_iax2_new(ifr->callno, AST_STATE_RING, format)))
996 + iax2_destroy_nolock(ifr->callno);
998 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_TBD);
999 + /* If this is a TBD call, we're ready but now what... */
1000 + if (option_verbose > 2)
1001 + ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr));
1005 + iaxs_queued[ifr->callno]--;
1006 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
1007 + ast_mutex_unlock(&iaxsl[ifr->callno]);
1010 + ast_mutex_lock(&iaxsl[ifr->callno]);
1011 + iaxs_queued[ifr->callno]--;
1012 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
1013 + ast_mutex_unlock(&iaxsl[ifr->callno]);
1016 + iax_frame_free(ifr);
1026 +static int start_auth_thread(void)
1028 + return ast_pthread_create(&auththreadid, NULL, auth_thread, &callq);
1031 +static int start_reg_thread(void)
1033 + return ast_pthread_create(®threadid, NULL, auth_thread, ®q);
1036 static struct iax2_context *build_context(char *context)
1038 struct iax2_context *con = malloc(sizeof(struct iax2_context));
1039 @@ -8882,6 +9305,7 @@
1040 tmpstr = ast_strdupa(data);
1041 parse_dial_string(tmpstr, &pds);
1043 +ast_log(LOG_NOTICE, "calling create_addr here\n");
1044 /* Populate our address from the given */
1045 if (create_addr(pds.peer, &sin, &cai))
1047 @@ -9492,6 +9916,11 @@
1048 static int __unload_module(void)
1051 + /* Cancel the authentication thread */
1052 + if (auththreadid != AST_PTHREADT_NULL) {
1053 + pthread_cancel(auththreadid);
1054 + pthread_join(auththreadid, NULL);
1056 /* Cancel the network thread, close the net socket */
1057 if (netthreadid != AST_PTHREADT_NULL) {
1058 pthread_cancel(netthreadid);
1059 @@ -9514,6 +9943,8 @@
1063 + ast_mutex_destroy(®q.lock);
1064 + ast_mutex_destroy(&callq.lock);
1065 ast_mutex_destroy(&iaxq.lock);
1066 ast_mutex_destroy(&userl.lock);
1067 ast_mutex_destroy(&peerl.lock);
1068 @@ -9535,6 +9966,9 @@
1069 struct ast_netsock *ns;
1070 struct sockaddr_in sin;
1072 + /* you never can tell */
1073 + memset(iaxs_queued, 0, sizeof(iaxs_queued));
1075 ast_custom_function_register(&iaxpeer_function);
1077 iax_set_output(iax_debug_output);
1078 @@ -9577,7 +10011,14 @@
1080 ast_netsock_init(netsock);
1082 + gethostname(servername, sizeof(servername) - 1);
1083 + ast_log(LOG_NOTICE, "servername = %s\n", servername);
1085 ast_mutex_init(&iaxq.lock);
1086 + ast_mutex_init(®q.lock);
1087 + ast_cond_init(®q.cond, NULL);
1088 + ast_mutex_init(&callq.lock);
1089 + ast_cond_init(&callq.cond, NULL);
1090 ast_mutex_init(&userl.lock);
1091 ast_mutex_init(&peerl.lock);
1092 ast_mutex_init(&waresl.lock);
1093 @@ -9621,6 +10062,16 @@
1094 ast_netsock_release(netsock);
1097 + res = start_reg_thread();
1099 + ast_log(LOG_ERROR, "Unable to start registration thread\n");
1102 + res = start_auth_thread();
1104 + ast_log(LOG_ERROR, "Unable to start authentication thread\n");
1107 for (reg = registrations; reg; reg = reg->next)
1108 iax2_do_register(reg);
1109 ast_mutex_lock(&peerl.lock);
1110 diff -urN asterisk-1.2.4/res/Makefile asterisk-1.2.4.carrier/res/Makefile
1111 --- asterisk-1.2.4/res/Makefile 2006-01-31 09:41:43.000000000 +0100
1112 +++ asterisk-1.2.4.carrier/res/Makefile 2006-01-31 15:04:30.000000000 +0100
1115 MODS=res_indications.so res_monitor.so res_adsi.so res_agi.so res_features.so res_watchdog.so
1118 +# MySQL stuff... Autoconf anyone??
1120 +MODS+=$(shell if [ -d /usr/local/mysql/include ] || [ -d /usr/include/mysql ] || [ -d /usr/local/include/mysql ] || [ -d /opt/mysql/include ]; then echo "res_config_mysql.so"; fi)
1121 +CFLAGS+=$(shell if [ -d /usr/local/mysql/include ]; then echo "-I/usr/local/mysql/include"; fi)
1122 +CFLAGS+=$(shell if [ -d /usr/include/mysql ]; then echo "-I/usr/include/mysql"; fi)
1123 +CFLAGS+=$(shell if [ -d /usr/local/include/mysql ]; then echo "-I/usr/local/include/mysql"; fi)
1124 +CFLAGS+=$(shell if [ -d /opt/mysql/include/mysql ]; then echo "-I/opt/mysql/include/mysql"; fi)
1126 +MLFLAGS+=$(shell if [ -d /usr/lib/mysql ]; then echo "-L/usr/lib/mysql"; fi)
1127 +MLFLAGS+=$(shell if [ -d /usr/lib64/mysql ]; then echo "-L/usr/lib64/mysql"; fi)
1128 +MLFLAGS+=$(shell if [ -d /usr/local/mysql/lib ]; then echo "-L/usr/local/mysql/lib"; fi)
1129 +MLFLAGS+=$(shell if [ -d /usr/local/lib/mysql ]; then echo "-L/usr/local/lib/mysql"; fi)
1130 +MLFLAGS+=$(shell if [ -d /opt/mysql/lib/mysql ]; then echo "-L/opt/mysql/lib/mysql"; fi)
1132 ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/odbcinst.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/odbcinst.h),)
1133 ifneq (${OSARCH},FreeBSD)
1134 MODS+=res_config_odbc.so
1136 res_config_odbc.so: res_config_odbc.o
1137 $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${CYG_RES_CONFIG_ODBC_LIB}
1139 +res_config_mysql.so: res_config_mysql.o
1140 + $(CC) $(SOLINK) -o $@ $< -lmysqlclient -lz $(MLFLAGS)
1142 ifneq ($(wildcard .depend),)
1145 diff -urN asterisk-1.2.4/res/res_config_mysql.c asterisk-1.2.4.carrier/res/res_config_mysql.c
1146 --- asterisk-1.2.4/res/res_config_mysql.c 1970-01-01 01:00:00.000000000 +0100
1147 +++ asterisk-1.2.4.carrier/res/res_config_mysql.c 2006-01-31 14:54:49.000000000 +0100
1150 + * Asterisk -- A telephony toolkit for Linux.
1152 + * Copyright (C) 1999-2005, Digium, Inc.
1154 + * Mark Spencer <markster@digium.com> - Asterisk Author
1155 + * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
1157 + * res_config_mysql.c <mysql plugin for RealTime configuration engine>
1159 + * v2.0 - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
1161 + * v1.9 - (08-19-05) - Added support to correctly honor the family database specified
1162 + * in extconfig.conf (bug #4973)
1164 + * v1.8 - (04-21-05) - Modified return values of update_mysql to better indicate
1165 + * what really happened.
1167 + * v1.7 - (01-28-05) - Fixed non-initialization of ast_category struct
1168 + * in realtime_multi_mysql function which caused segfault.
1170 + * v1.6 - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
1172 + * v1.5.1 - (01-26-05) - Added better(?) locking stuff
1174 + * v1.5 - (01-26-05) - Brought up to date with new config.h changes (bug #3406)
1175 + * - Added in extra locking provided by georg (bug #3248)
1177 + * v1.4 - (12-02-04) - Added realtime_multi_mysql function
1178 + * This function will return an ast_config with categories,
1179 + * unlike standard realtime_mysql which only returns
1180 + * a linked list of ast_variables
1182 + * v1.3 - (12-01-04) - Added support other operators
1183 + * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc...
1185 + * v1.2 - (11-DD-04) - Added reload. Updated load and unload.
1186 + * Code beautification (doc/CODING-GUIDELINES)
1189 +#include <asterisk/channel.h>
1190 +#include <asterisk/logger.h>
1191 +#include <asterisk/config.h>
1192 +#include <asterisk/module.h>
1193 +#include <asterisk/lock.h>
1194 +#include <asterisk/options.h>
1195 +#include <asterisk/cli.h>
1196 +#include <asterisk/utils.h>
1197 +#include <stdlib.h>
1198 +#include <string.h>
1200 +#include <mysql_version.h>
1201 +#include <errmsg.h>
1203 +static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver";
1205 +AST_MUTEX_DEFINE_STATIC(mysql_lock);
1206 +#define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
1208 +static char dbhost[50];
1209 +static char dbuser[50];
1210 +static char dbpass[50];
1211 +static char dbname[50];
1212 +static char dbsock[50];
1214 +static int connected;
1215 +static time_t connect_time;
1217 +static int parse_config(void);
1218 +static int mysql_reconnect(const char *database);
1219 +static int realtime_mysql_status(int fd, int argc, char **argv);
1221 +STANDARD_LOCAL_USER;
1225 +static char cli_realtime_mysql_status_usage[] =
1226 +"Usage: realtime mysql status\n"
1227 +" Shows connection information for the MySQL RealTime driver\n";
1229 +static struct ast_cli_entry cli_realtime_mysql_status = {
1230 + { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
1231 + "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };
1233 +static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
1235 + MYSQL_RES *result;
1237 + MYSQL_FIELD *fields;
1243 + const char *newparam, *newval;
1244 + struct ast_variable *var=NULL, *prev=NULL;
1247 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1251 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
1252 + newparam = va_arg(ap, const char *);
1253 + newval = va_arg(ap, const char *);
1254 + if(!newparam || !newval) {
1255 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
1256 + mysql_close(&mysql);
1260 + /* Create the first part of the query using the first parameter/value pairs we just extracted
1261 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1263 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1265 + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
1266 + while((newparam = va_arg(ap, const char *))) {
1267 + newval = va_arg(ap, const char *);
1268 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1269 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
1273 + ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
1275 + /* We now have our complete statement; Lets connect to the server and execute it. */
1276 + ast_mutex_lock(&mysql_lock);
1277 + if(!mysql_reconnect(database)) {
1278 + ast_mutex_unlock(&mysql_lock);
1282 +// ast_log(LOG_NOTICE, "SQL: %s\m", sql);
1284 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1285 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1286 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1287 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1288 + ast_mutex_unlock(&mysql_lock);
1292 + if((result = mysql_store_result(&mysql))) {
1293 + numFields = mysql_num_fields(result);
1294 + fields = mysql_fetch_fields(result);
1296 + while((row = mysql_fetch_row(result))) {
1297 + for(i = 0; i < numFields; i++) {
1300 + chunk = strsep(&stringp, ";");
1301 + if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
1303 + prev->next = ast_variable_new(fields[i].name, chunk);
1305 + prev = prev->next;
1308 + prev = var = ast_variable_new(fields[i].name, chunk);
1315 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
1318 + mysql_free_result(result);
1319 + ast_mutex_unlock(&mysql_lock);
1324 +static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
1326 + MYSQL_RES *result;
1328 + MYSQL_FIELD *fields;
1331 + const char *initfield = NULL;
1335 + const char *newparam, *newval;
1336 + struct ast_realloca ra;
1337 + struct ast_variable *var=NULL;
1338 + struct ast_config *cfg = NULL;
1339 + struct ast_category *cat = NULL;
1342 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1346 + memset(&ra, 0, sizeof(ra));
1348 + cfg = ast_config_new();
1350 + /* If I can't alloc memory at this point, why bother doing anything else? */
1351 + ast_log(LOG_WARNING, "Out of memory!\n");
1355 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
1356 + newparam = va_arg(ap, const char *);
1357 + newval = va_arg(ap, const char *);
1358 + if(!newparam || !newval) {
1359 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
1360 + mysql_close(&mysql);
1364 + initfield = ast_strdupa(newparam);
1365 + if(initfield && (op = strchr(initfield, ' '))) {
1369 + /* Create the first part of the query using the first parameter/value pairs we just extracted
1370 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1372 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1374 + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
1375 + while((newparam = va_arg(ap, const char *))) {
1376 + newval = va_arg(ap, const char *);
1377 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1378 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
1382 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
1387 + ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
1389 + /* We now have our complete statement; Lets connect to the server and execute it. */
1390 + ast_mutex_lock(&mysql_lock);
1391 + if(!mysql_reconnect(database)) {
1392 + ast_mutex_unlock(&mysql_lock);
1396 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1397 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1398 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1399 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1400 + ast_mutex_unlock(&mysql_lock);
1404 + if((result = mysql_store_result(&mysql))) {
1405 + numFields = mysql_num_fields(result);
1406 + fields = mysql_fetch_fields(result);
1408 + while((row = mysql_fetch_row(result))) {
1410 + cat = ast_category_new("");
1412 + ast_log(LOG_WARNING, "Out of memory!\n");
1415 + for(i = 0; i < numFields; i++) {
1418 + chunk = strsep(&stringp, ";");
1419 + if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
1420 + if(initfield && !strcmp(initfield, fields[i].name)) {
1421 + ast_category_rename(cat, chunk);
1423 + var = ast_variable_new(fields[i].name, chunk);
1424 + ast_variable_append(cat, var);
1428 + ast_category_append(cfg, cat);
1431 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
1434 + mysql_free_result(result);
1435 + ast_mutex_unlock(&mysql_lock);
1440 +static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
1442 + my_ulonglong numrows;
1444 + const char *newparam, *newval;
1447 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1451 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
1452 + newparam = va_arg(ap, const char *);
1453 + newval = va_arg(ap, const char *);
1454 + if(!newparam || !newval) {
1455 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
1456 + mysql_close(&mysql);
1460 + /* Create the first part of the query using the first parameter/value pairs we just extracted
1461 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1463 + snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, newval);
1464 + while((newparam = va_arg(ap, const char *))) {
1465 + newval = va_arg(ap, const char *);
1466 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, newval);
1469 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup);
1471 + ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql);
1473 + /* We now have our complete statement; Lets connect to the server and execute it. */
1474 + ast_mutex_lock(&mysql_lock);
1475 + if(!mysql_reconnect(database)) {
1476 + ast_mutex_unlock(&mysql_lock);
1480 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1481 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1482 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1483 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1484 + ast_mutex_unlock(&mysql_lock);
1488 + numrows = mysql_affected_rows(&mysql);
1489 + ast_mutex_unlock(&mysql_lock);
1491 + ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
1493 + /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
1494 + * An integer greater than zero indicates the number of rows affected
1495 + * Zero indicates that no records were updated
1496 + * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1500 + return (int)numrows;
1505 +static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg)
1507 + MYSQL_RES *result;
1509 + my_ulonglong num_rows;
1510 + struct ast_config *new;
1511 + struct ast_variable *cur_v, *new_v;
1512 + struct ast_category *cur_cat, *new_cat;
1513 + char sql[250] = "";
1514 + char last[80] = "";
1515 + int cat_started = 0;
1516 + int var_started = 0;
1517 + int last_cat_metric = 0;
1521 + if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
1522 + ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
1526 + snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
1528 + ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql);
1530 + /* We now have our complete statement; Lets connect to the server and execute it. */
1531 + ast_mutex_lock(&mysql_lock);
1532 + if(!mysql_reconnect(database)) {
1533 + ast_mutex_unlock(&mysql_lock);
1537 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1538 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1539 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1540 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1541 + ast_mutex_unlock(&mysql_lock);
1545 + if((result = mysql_store_result(&mysql))) {
1546 + num_rows = mysql_num_rows(result);
1547 + ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows);
1549 + /* There might exist a better way to access the column names other than counting,
1550 + but I believe that would require another loop that we don't need. */
1552 + while((row = mysql_fetch_row(result))) {
1553 + if(!strcmp(row[1], "#include")) {
1554 + if (!ast_config_internal_load(row[2], cfg)) {
1555 + mysql_free_result(result);
1556 + ast_mutex_unlock(&mysql_lock);
1562 + if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
1563 + cur_cat = ast_category_new(row[0]);
1565 + ast_log(LOG_WARNING, "Out of memory!\n");
1568 + strcpy(last, row[0]);
1569 + last_cat_metric = atoi(row[3]);
1570 + ast_category_append(cfg, cur_cat);
1572 + new_v = ast_variable_new(row[1], row[2]);
1573 + ast_variable_append(cur_cat, new_v);
1576 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
1579 + mysql_free_result(result);
1580 + ast_mutex_unlock(&mysql_lock);
1585 +static struct ast_config_engine mysql_engine = {
1587 + .load_func = config_mysql,
1588 + .realtime_func = realtime_mysql,
1589 + .realtime_multi_func = realtime_multi_mysql,
1590 + .update_func = update_mysql
1593 +int load_module (void)
1597 + ast_mutex_lock(&mysql_lock);
1599 + if(!mysql_reconnect(NULL)) {
1600 + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
1601 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
1604 + ast_config_engine_register(&mysql_engine);
1605 + if(option_verbose) {
1606 + ast_verbose("MySQL RealTime driver loaded.\n");
1608 + ast_cli_register(&cli_realtime_mysql_status);
1610 + ast_mutex_unlock(&mysql_lock);
1615 +int unload_module (void)
1617 + /* Aquire control before doing anything to the module itself. */
1618 + ast_mutex_lock(&mysql_lock);
1620 + mysql_close(&mysql);
1621 + ast_cli_unregister(&cli_realtime_mysql_status);
1622 + ast_config_engine_deregister(&mysql_engine);
1623 + if(option_verbose) {
1624 + ast_verbose("MySQL RealTime unloaded.\n");
1627 + STANDARD_HANGUP_LOCALUSERS;
1629 + /* Unlock so something else can destroy the lock. */
1630 + ast_mutex_unlock(&mysql_lock);
1637 + /* Aquire control before doing anything to the module itself. */
1638 + ast_mutex_lock(&mysql_lock);
1640 + mysql_close(&mysql);
1644 + if(!mysql_reconnect(NULL)) {
1645 + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
1646 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
1649 + ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n");
1651 + /* Done reloading. Release lock so others can now use driver. */
1652 + ast_mutex_unlock(&mysql_lock);
1657 +int parse_config (void)
1659 + struct ast_config *config;
1662 + config = ast_config_load(RES_CONFIG_MYSQL_CONF);
1665 + if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) {
1666 + ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n");
1667 + strncpy(dbuser, "asterisk", sizeof(dbuser) - 1);
1669 + strncpy(dbuser, s, sizeof(dbuser) - 1);
1672 + if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) {
1673 + ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n");
1674 + strncpy(dbpass, "asterisk", sizeof(dbpass) - 1);
1676 + strncpy(dbpass, s, sizeof(dbpass) - 1);
1679 + if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) {
1680 + ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n");
1683 + strncpy(dbhost, s, sizeof(dbhost) - 1);
1686 + if(!(s=ast_variable_retrieve(config, "general", "dbname"))) {
1687 + ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n");
1688 + strncpy(dbname, "asterisk", sizeof(dbname) - 1);
1690 + strncpy(dbname, s, sizeof(dbname) - 1);
1693 + if(!(s=ast_variable_retrieve(config, "general", "dbport"))) {
1694 + ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n");
1700 + if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) {
1701 + ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n");
1702 + strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1);
1704 + strncpy(dbsock, s, sizeof(dbsock) - 1);
1707 + ast_config_destroy(config);
1710 + ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost);
1711 + ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport);
1713 + ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock);
1715 + ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser);
1716 + ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass);
1721 +char *description (void)
1723 + return res_config_mysql_desc;
1726 +int usecount (void)
1728 + /* Try and get a lock. If unsuccessful, than that means another thread is using the mysql object. */
1729 + if(ast_mutex_trylock(&mysql_lock)) {
1730 + ast_log(LOG_DEBUG, "MySQL RealTime: Module usage count is 1.\n");
1733 + ast_mutex_unlock(&mysql_lock);
1739 + return ASTERISK_GPL_KEY;
1742 +static int mysql_reconnect(const char *database)
1744 + char my_database[50];
1746 + if(!database || ast_strlen_zero(database))
1747 + ast_copy_string(my_database, dbname, sizeof(my_database));
1749 + ast_copy_string(my_database, database, sizeof(my_database));
1751 + /* mutex lock should have been locked before calling this function. */
1753 + if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) {
1754 + if(!mysql_init(&mysql)) {
1755 + ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
1759 + if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) {
1760 + ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n");
1762 + connect_time = time(NULL);
1765 + ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost);
1766 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
1771 + if(mysql_ping(&mysql) != 0) {
1773 + ast_log(LOG_ERROR, "MySQL RealTime: Failed to reconnect. Check debug for more info.\n");
1774 + ast_log(LOG_DEBUG, "MySQL RealTime: Server Error: %s\n", mysql_error(&mysql));
1780 + if(mysql_select_db(&mysql, my_database) != 0) {
1781 + ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected.\n", my_database);
1782 + ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed: %s\n", mysql_error(&mysql));
1786 + ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n");
1791 +static int realtime_mysql_status(int fd, int argc, char **argv)
1793 + char status[256], status2[100] = "";
1794 + int ctime = time(NULL) - connect_time;
1796 + ast_mutex_lock(&mysql_lock);
1797 + if(mysql_reconnect(NULL)) {
1799 + snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
1800 + } else if(dbsock) {
1801 + snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
1803 + snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
1806 + if(dbuser && *dbuser) {
1807 + snprintf(status2, 99, " with username %s", dbuser);
1810 + if (ctime > 31536000) {
1811 + ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
1812 + } else if (ctime > 86400) {
1813 + ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
1814 + } else if (ctime > 3600) {
1815 + ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
1816 + } else if (ctime > 60) {
1817 + ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
1819 + ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
1822 + ast_mutex_unlock(&mysql_lock);
1823 + return RESULT_SUCCESS;
1825 + ast_mutex_unlock(&mysql_lock);
1826 + return RESULT_FAILURE;