]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/asterisk-1.2.4-iax2-bristuff-0.3.0-PRE-1l.patch
HinzugefĆ¼gt:
[ipfire-2.x.git] / src / patches / asterisk-1.2.4-iax2-bristuff-0.3.0-PRE-1l.patch
CommitLineData
5e69ef1a
MT
1diff -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
4@@ -179,6 +179,8 @@
5 static struct ast_netsock_list *netsock;
6 static int defaultsockfd = -1;
7
8+static char servername[80];
9+
10 static int usecnt;
11 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
12
13@@ -232,6 +234,10 @@
14
15 static pthread_t netthreadid = AST_PTHREADT_NULL;
16
17+static pthread_t regthreadid = AST_PTHREADT_NULL;
18+
19+static pthread_t auththreadid = AST_PTHREADT_NULL;
20+
21 enum {
22 IAX_STATE_STARTED = (1 << 0),
23 IAX_STATE_AUTHENTICATED = (1 << 1),
24@@ -606,6 +612,25 @@
25 ast_mutex_t lock;
26 } iaxq;
27
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;
33+ int fd;
34+ int subclass;
35+ struct iax_ies ies;
36+ unsigned char iebuf[4096];
37+} iax_auth_frame;
38+
39+static struct ast_iax2_auth_queue {
40+ struct iax_auth_frame *head;
41+ struct iax_auth_frame *tail;
42+ int count;
43+ ast_cond_t cond;
44+ ast_mutex_t lock;
45+} callq, regq;
46+
47 static struct ast_user_list {
48 struct iax2_user *users;
49 ast_mutex_t lock;
50@@ -712,6 +737,8 @@
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];
56
57
58 static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
59@@ -798,11 +825,12 @@
60 static int send_lagrq(void *data)
61 {
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!)*/
65 if (iaxs[callno]) {
66 #ifdef BRIDGE_OPTIMIZATION
67 if (!iaxs[callno]->bridgecallno)
68 #endif
69+ if (!iaxs_queued[callno])
70 send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
71 return 1;
72 } else
73@@ -1070,8 +1098,9 @@
74 }
75 }
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 @@
86 return 0;
87 }
88
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)
90+{
91+ struct iax_auth_frame *fr = NULL;
92+ /*
93+ ok, we get here with the iaxsl[callno] still locked.
94+ in any case we have to unlock it before returning.
95+ */
96+
97+ if (!authq || !ifr || !ifr->callno) return 1;
98+
99+ fr = malloc(sizeof(struct iax_auth_frame));
100+ if (!fr) {
101+ ast_log(LOG_ERROR, "Unable to malloc!\n");
102+ return 1;
103+ }
104+ memset(fr,0,sizeof(fr));
105+ fr->next = NULL;
106+ fr->prev = NULL;
107+ fr->frame = ifr;
108+ fr->subclass = subclass;
109+ fr->fd = fd;
110+ memcpy(fr->iebuf, iebuf, iebuflen);
111+ if (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]);
118+ free(fr);
119+ if (option_verbose > 5)
120+ ast_log(LOG_WARNING, "Queue too long, not queueing frame.\n");
121+ return -1;
122+ }
123+ if (!authq->head) {
124+ /* Empty queue */
125+ authq->head = fr;
126+ authq->tail = fr;
127+ } else {
128+ /* Double link */
129+ authq->tail->next = fr;
130+ fr->prev = authq->tail;
131+ authq->tail = fr;
132+ }
133+ iaxs_queued[ifr->callno]++;
134+ authq->count++;
135+ ast_mutex_unlock(&iaxsl[ifr->callno]);
136+ ast_cond_signal(&authq->cond);
137+ ast_mutex_unlock(&authq->lock);
138+ return 0;
139+}
140+
141 static void destroy_firmware(struct iax_firmware *cur)
142 {
143 /* Close firmware */
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) {
148- if (f->final)
149+ if (!iaxs_queued[f->callno]) {
150+ if (f->final)
151 iax2_destroy_nolock(f->callno);
152+ }
153 } else {
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;
159 } else {
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;
166+ }
167+ iax2_destroy_nolock(f->callno);
168 }
169- iax2_destroy_nolock(f->callno);
170 }
171 }
172
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);
180 }
181
182@@ -5034,16 +5121,17 @@
183 }
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]);
191-
192+// ast_mutex_lock(&iaxsl[callno]);
193 if (!p) {
194 if (authdebug)
195 ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
196 return -1;
197 }
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));
200
201 if (!ast_test_flag(p, IAX_DYNAMIC)) {
202 if (authdebug)
203@@ -5677,11 +5765,13 @@
204 if (!refresh)
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;
218 } else {
219@@ -5720,6 +5810,7 @@
220 iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
221 if (ast_test_flag(p, IAX_TEMPONLY))
222 destroy_peer(p);
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);
225 }
226
227@@ -6279,6 +6370,8 @@
228 struct timeval rxtrunktime;
229 struct iax_ies ies;
230 struct iax_ie_data ied0, ied1;
231+ unsigned char iebuf[4096];
232+ int iebuflen = 0;
233 int format;
234 int exists;
235 int minivid = 0;
236@@ -6609,9 +6702,11 @@
237 cur->retries = -1;
238 /* Destroy call if this is the end */
239 if (cur->final) {
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);
246+ }
247 }
248 }
249 }
250@@ -6639,6 +6734,8 @@
251
252 if (f.datalen) {
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 @@
260 break;
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);
266- }
267 /* For security, always ack immediately */
268 if (delayreject)
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);
273- if (authdebug)
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);
275- break;
276- }
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]);
282- } else
283- exists = 0;
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);
290- if (authdebug)
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);
292- } else {
293- /* Select an appropriate format */
294-
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";
298- } else {
299- using_prefs = "disabled";
300- }
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");
305- } else {
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)) {
311- pref = rpref;
312- using_prefs = "caller";
313- } else {
314- pref = iaxs[fr.callno]->prefs;
315- }
316- } else
317- pref = iaxs[fr.callno]->prefs;
318-
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);
322- }
323- if (!format) {
324- if(!ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP))
325- format = iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability;
326- if (!format) {
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);
331- if (authdebug) {
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);
334- else
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);
336- }
337- } else {
338- /* Pick one... */
339- if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP)) {
340- if(!(iaxs[fr.callno]->peerformat & iaxs[fr.callno]->capability))
341- format = 0;
342- } else {
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");
349- } else {
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;
355- } else {
356- pref = rpref;
357- using_prefs = "caller";
358- }
359- format = ast_codec_choose(&pref, iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability, 1);
360-
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);
363- }
364- }
365-
366- if (!format) {
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);
372- if (authdebug)
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);
375- break;
376- }
377- }
378- }
379- if (format) {
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),
394- VERBOSE_PREFIX_4,
395- ast_getformatname(iaxs[fr.callno]->peerformat),
396- VERBOSE_PREFIX_4,
397- caller_pref_buf,
398- VERBOSE_PREFIX_4,
399- ast_getformatname(format),
400- VERBOSE_PREFIX_4,
401- host_pref_buf,
402- VERBOSE_PREFIX_4,
403- using_prefs);
404-
405- if(!(c = ast_iax2_new(fr.callno, AST_STATE_RING, format)))
406- iax2_destroy_nolock(fr.callno);
407- } else {
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));
412- }
413- }
414- }
415- break;
416- }
417- if (iaxs[fr.callno]->authmethods & IAX_AUTH_MD5)
418- merge_encryption(iaxs[fr.callno],ies.encmethods);
419- else
420- iaxs[fr.callno]->encmethods = 0;
421- authenticate_request(iaxs[fr.callno]);
422- ast_set_flag(&iaxs[fr.callno]->state, IAX_STATE_AUTHENTICATED);
423+ fr.af.datalen = 0;
424+ iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
425 break;
426 case IAX_COMMAND_DPREQ:
427 /* Request status in the dialplan */
428@@ -6947,14 +6889,22 @@
429 }
430 break;
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);
444+ fr.af.datalen = 0;
445+ iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
446+ } else {
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);
455+ }
456 break;
457 case IAX_COMMAND_REJECT:
458 memset(&f, 0, sizeof(f));
459@@ -7183,31 +7133,36 @@
460 /* For security, always ack immediately */
461 if (delayreject)
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 */
467+ fr.af.datalen = 0;
468+ iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
469+ } else {
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>");
473 break;
474- }
475- if (authenticate_verify(iaxs[fr.callno], &ies)) {
476+ }
477+ if (authenticate_verify(iaxs[fr.callno], &ies)) {
478 if (authdebug)
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);
482 break;
483- }
484- if (strcasecmp(iaxs[fr.callno]->exten, "TBD")) {
485+ }
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);
489- } else
490+ } else
491 exists = 0;
492- if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
493+ if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
494 if (authdebug)
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);
500- } else {
501+ } else {
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));
507 }
508 }
509+ }
510 }
511 break;
512 case IAX_COMMAND_DIAL:
513@@ -7357,11 +7313,16 @@
514 }
515 break;
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);
520- if (option_debug)
521+ if (iaxs_queued[fr.callno]) {
522+ fr.af.datalen = 0;
523+ iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
524+ } else {
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);
528+ if (option_debug)
529 ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
530+ }
531 break;
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 */
536 if (delayreject)
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);
541- break;
542- }
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);
550- break;
551+ fr.af.datalen = 0;
552+ if (iax2_queue_auth_frame(&regq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 1)) {
553+ /* queueing failed due to a full queue */
554+ iax2_destroy_nolock(fr.callno);
555 }
556- registry_authrequest(iaxs[fr.callno]->peer, fr.callno);
557 break;
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);
562 }
563
564+struct iax_auth_frame *iax2_get_next_auth_frame(struct ast_iax2_auth_queue *authq) {
565+ struct iax_auth_frame *frame = NULL;
566+
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;
572+ if (frame) {
573+ if (frame->next) {
574+// frame->next->prev = NULL;
575+ authq->head = frame->next;
576+ } else {
577+ authq->head = NULL;
578+ authq->tail = NULL;
579+ }
580+ authq->count--;
581+ } else {
582+ ast_log(LOG_ERROR, "I SHOULD NEVER HAPPEN! EXPECT SOME MAJOR KABOOM! DUCK AND COVER!\n");
583+ }
584+ ast_mutex_unlock(&authq->lock);
585+ return frame;
586+}
587+
588+
589+void *auth_thread(void *data)
590+{
591+ /*
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.
594+
595+ We also have to process IAX_COMMAND_HANGUP when somebody hangs up a call
596+ before it has been authorized!
597+
598+ If ever possible we shall not lock any iaxsl[...].
599+ */
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];
607+ int exists;
608+ int format;
609+ int dequeued = 0;
610+ char host_pref_buf[128];
611+ char caller_pref_buf[128];
612+ struct ast_codec_pref pref,rpref;
613+ char *using_prefs = "mine";
614+
615+ if (!authq) {
616+ ast_log(LOG_ERROR, "no queue!\n");
617+ return NULL;
618+ }
619+
620+ for(;;) {
621+ fr = iax2_get_next_auth_frame(authq);
622+ ifr = fr->frame;
623+ ies = &fr->ies;
624+ if (ifr) {
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.
630+
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.
633+
634+ When we really want to destroy something we will aquire the lock first.
635+ */
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 */
641+ if (authdebug)
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);
644+ break;
645+ }
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);
649+ }
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]);
655+ } else
656+ exists = 0;
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);
663+ if (authdebug)
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);
665+ } else {
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";
670+ } else {
671+ using_prefs = "disabled";
672+ }
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");
677+ } else {
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)) {
683+ pref = rpref;
684+ using_prefs = "caller";
685+ } else {
686+ pref = iaxs[ifr->callno]->prefs;
687+ }
688+ } else
689+ pref = iaxs[ifr->callno]->prefs;
690+
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);
694+ }
695+ if (!format) {
696+ if(!ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
697+ format = iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability;
698+ if (!format) {
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);
703+ if (authdebug) {
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);
706+ else
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);
708+ }
709+ } else {
710+ /* Pick one... */
711+ if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
712+ if(!(iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability))
713+ format = 0;
714+ } else {
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");
721+ } else {
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;
727+ } else {
728+ pref = rpref;
729+ using_prefs = "caller";
730+ }
731+ format = ast_codec_choose(&pref, iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability, 1);
732+
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);
735+ }
736+ }
737+
738+ if (!format) {
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);
744+ if (authdebug)
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);
747+ break;
748+ }
749+ }
750+ }
751+ if (format) {
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),
766+ VERBOSE_PREFIX_4,
767+ ast_getformatname(iaxs[ifr->callno]->peerformat),
768+ VERBOSE_PREFIX_4,
769+ caller_pref_buf,
770+ VERBOSE_PREFIX_4,
771+ ast_getformatname(format),
772+ VERBOSE_PREFIX_4,
773+ host_pref_buf,
774+ VERBOSE_PREFIX_4,
775+ using_prefs);
776+
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]);
781+ }
782+ } else {
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));
787+ }
788+ }
789+ }
790+ break;
791+ }
792+ if (iaxs[ifr->callno]->authmethods & IAX_AUTH_MD5)
793+ merge_encryption(iaxs[ifr->callno],ies->encmethods);
794+ else
795+ iaxs[ifr->callno]->encmethods = 0;
796+ authenticate_request(iaxs[ifr->callno]);
797+ ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_AUTHENTICATED);
798+ break;
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);
805+ break;
806+ }
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]);
818+ break;
819+ }
820+ registry_authrequest(iaxs[ifr->callno]->peer, ifr->callno);
821+ break;
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);
832+ }
833+ iaxs_queued[ifr->callno]--;
834+ if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
835+ dequeued = 1;
836+ ast_mutex_unlock(&iaxsl[ifr->callno]);
837+ break;
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);
845+ if (option_debug)
846+ ast_log(LOG_DEBUG, "Destroying call %d\n", ifr->callno);
847+ }
848+ iaxs_queued[ifr->callno]--;
849+ if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
850+ dequeued = 1;
851+ ast_mutex_unlock(&iaxsl[ifr->callno]);
852+ break;
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>");
857+ break;
858+ }
859+ if (authenticate_verify(iaxs[ifr->callno], ies)) {
860+ if (authdebug)
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);
864+ break;
865+ }
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);
870+ } else
871+ exists = 0;
872+ if (strcmp(iaxs[ifr->callno]->exten, "TBD") && !exists) {
873+ if (authdebug)
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);
879+ } else {
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";
884+ } else {
885+ using_prefs = "disabled";
886+ }
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");
891+ } else {
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";
899+ } else {
900+ pref = iaxs[ifr->callno]->prefs;
901+ }
902+ } else /* if no codec_prefs IE do it the old way */
903+ pref = iaxs[ifr->callno]->prefs;
904+
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);
908+ }
909+ if (!format) {
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;
913+ }
914+ if (!format) {
915+ if (authdebug) {
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);
918+ else
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);
920+ }
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);
925+ } else {
926+ /* Pick one... */
927+ if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
928+ if(!(iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability))
929+ format = 0;
930+ } else {
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");
938+ } else {
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;
944+ } else {
945+ pref = rpref;
946+ using_prefs = "caller";
947+ }
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);
951+ }
952+ }
953+ if (!format) {
954+ ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
955+ if (authdebug) {
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);
958+ else
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);
960+ }
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);
965+ }
966+ }
967+ }
968+ if (format) {
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),
983+ VERBOSE_PREFIX_4,
984+ ast_getformatname(iaxs[ifr->callno]->peerformat),
985+ VERBOSE_PREFIX_4,
986+ caller_pref_buf,
987+ VERBOSE_PREFIX_4,
988+ ast_getformatname(format),
989+ VERBOSE_PREFIX_4,
990+ host_pref_buf,
991+ VERBOSE_PREFIX_4,
992+ using_prefs);
993+
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);
997+ } else {
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));
1002+ }
1003+ }
1004+ }
1005+ iaxs_queued[ifr->callno]--;
1006+ if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
1007+ ast_mutex_unlock(&iaxsl[ifr->callno]);
1008+ }
1009+ if (!dequeued) {
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]);
1014+ }
1015+ }
1016+ iax_frame_free(ifr);
1017+ free(fr);
1018+ fr = NULL;
1019+ }
1020+
1021+ }
1022+ return NULL;
1023+}
1024+
1025+
1026+static int start_auth_thread(void)
1027+{
1028+ return ast_pthread_create(&auththreadid, NULL, auth_thread, &callq);
1029+}
1030+
1031+static int start_reg_thread(void)
1032+{
1033+ return ast_pthread_create(&regthreadid, NULL, auth_thread, &regq);
1034+}
1035+
1036 static struct iax2_context *build_context(char *context)
1037 {
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);
1042
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))
1046 return -1;
1047@@ -9492,6 +9916,11 @@
1048 static int __unload_module(void)
1049 {
1050 int x;
1051+ /* Cancel the authentication thread */
1052+ if (auththreadid != AST_PTHREADT_NULL) {
1053+ pthread_cancel(auththreadid);
1054+ pthread_join(auththreadid, NULL);
1055+ }
1056 /* Cancel the network thread, close the net socket */
1057 if (netthreadid != AST_PTHREADT_NULL) {
1058 pthread_cancel(netthreadid);
1059@@ -9514,6 +9943,8 @@
1060
1061 int unload_module()
1062 {
1063+ ast_mutex_destroy(&regq.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;
1071
1072+ /* you never can tell */
1073+ memset(iaxs_queued, 0, sizeof(iaxs_queued));
1074+
1075 ast_custom_function_register(&iaxpeer_function);
1076
1077 iax_set_output(iax_debug_output);
1078@@ -9577,7 +10011,14 @@
1079 }
1080 ast_netsock_init(netsock);
1081
1082+ gethostname(servername, sizeof(servername) - 1);
1083+ ast_log(LOG_NOTICE, "servername = %s\n", servername);
1084+
1085 ast_mutex_init(&iaxq.lock);
1086+ ast_mutex_init(&regq.lock);
1087+ ast_cond_init(&regq.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);
1095 }
1096
1097+ res = start_reg_thread();
1098+ if (res) {
1099+ ast_log(LOG_ERROR, "Unable to start registration thread\n");
1100+ }
1101+
1102+ res = start_auth_thread();
1103+ if (res) {
1104+ ast_log(LOG_ERROR, "Unable to start authentication thread\n");
1105+ }
1106+
1107 for (reg = registrations; reg; reg = reg->next)
1108 iax2_do_register(reg);
1109 ast_mutex_lock(&peerl.lock);
1110diff -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
1113@@ -13,6 +13,21 @@
1114
1115 MODS=res_indications.so res_monitor.so res_adsi.so res_agi.so res_features.so res_watchdog.so
1116
1117+#
1118+# MySQL stuff... Autoconf anyone??
1119+#
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)
1125+MLFLAGS=
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)
1131+
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
1135@@ -109,6 +124,9 @@
1136 res_config_odbc.so: res_config_odbc.o
1137 $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${CYG_RES_CONFIG_ODBC_LIB}
1138
1139+res_config_mysql.so: res_config_mysql.o
1140+ $(CC) $(SOLINK) -o $@ $< -lmysqlclient -lz $(MLFLAGS)
1141+
1142 ifneq ($(wildcard .depend),)
1143 include .depend
1144 endif
1145diff -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
1148@@ -0,0 +1,680 @@
1149+/*
1150+ * Asterisk -- A telephony toolkit for Linux.
1151+ *
1152+ * Copyright (C) 1999-2005, Digium, Inc.
1153+ *
1154+ * Mark Spencer <markster@digium.com> - Asterisk Author
1155+ * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
1156+ *
1157+ * res_config_mysql.c <mysql plugin for RealTime configuration engine>
1158+ *
1159+ * v2.0 - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
1160+ *
1161+ * v1.9 - (08-19-05) - Added support to correctly honor the family database specified
1162+ * in extconfig.conf (bug #4973)
1163+ *
1164+ * v1.8 - (04-21-05) - Modified return values of update_mysql to better indicate
1165+ * what really happened.
1166+ *
1167+ * v1.7 - (01-28-05) - Fixed non-initialization of ast_category struct
1168+ * in realtime_multi_mysql function which caused segfault.
1169+ *
1170+ * v1.6 - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
1171+ *
1172+ * v1.5.1 - (01-26-05) - Added better(?) locking stuff
1173+ *
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)
1176+ *
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
1181+ *
1182+ * v1.3 - (12-01-04) - Added support other operators
1183+ * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc...
1184+ *
1185+ * v1.2 - (11-DD-04) - Added reload. Updated load and unload.
1186+ * Code beautification (doc/CODING-GUIDELINES)
1187+ */
1188+
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>
1199+#include <mysql.h>
1200+#include <mysql_version.h>
1201+#include <errmsg.h>
1202+
1203+static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver";
1204+
1205+AST_MUTEX_DEFINE_STATIC(mysql_lock);
1206+#define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
1207+MYSQL mysql;
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];
1213+static int dbport;
1214+static int connected;
1215+static time_t connect_time;
1216+
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);
1220+
1221+STANDARD_LOCAL_USER;
1222+
1223+LOCAL_USER_DECL;
1224+
1225+static char cli_realtime_mysql_status_usage[] =
1226+"Usage: realtime mysql status\n"
1227+" Shows connection information for the MySQL RealTime driver\n";
1228+
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 };
1232+
1233+static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
1234+{
1235+ MYSQL_RES *result;
1236+ MYSQL_ROW row;
1237+ MYSQL_FIELD *fields;
1238+ int numFields, i;
1239+ char sql[256];
1240+ char *stringp;
1241+ char *chunk;
1242+ char *op;
1243+ const char *newparam, *newval;
1244+ struct ast_variable *var=NULL, *prev=NULL;
1245+
1246+ if(!table) {
1247+ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1248+ return NULL;
1249+ }
1250+
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);
1257+ return NULL;
1258+ }
1259+
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 */
1262+
1263+ if(!strchr(newparam, ' ')) op = " ="; else op = "";
1264+
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);
1270+ }
1271+ va_end(ap);
1272+
1273+ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
1274+
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);
1279+ return NULL;
1280+ }
1281+
1282+// ast_log(LOG_NOTICE, "SQL: %s\m", sql);
1283+
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);
1289+ return NULL;
1290+ }
1291+
1292+ if((result = mysql_store_result(&mysql))) {
1293+ numFields = mysql_num_fields(result);
1294+ fields = mysql_fetch_fields(result);
1295+
1296+ while((row = mysql_fetch_row(result))) {
1297+ for(i = 0; i < numFields; i++) {
1298+ stringp = row[i];
1299+ while(stringp) {
1300+ chunk = strsep(&stringp, ";");
1301+ if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
1302+ if(prev) {
1303+ prev->next = ast_variable_new(fields[i].name, chunk);
1304+ if (prev->next) {
1305+ prev = prev->next;
1306+ }
1307+ } else {
1308+ prev = var = ast_variable_new(fields[i].name, chunk);
1309+ }
1310+ }
1311+ }
1312+ }
1313+ }
1314+ } else {
1315+ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
1316+ }
1317+
1318+ mysql_free_result(result);
1319+ ast_mutex_unlock(&mysql_lock);
1320+
1321+ return var;
1322+}
1323+
1324+static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
1325+{
1326+ MYSQL_RES *result;
1327+ MYSQL_ROW row;
1328+ MYSQL_FIELD *fields;
1329+ int numFields, i;
1330+ char sql[256];
1331+ const char *initfield = NULL;
1332+ char *stringp;
1333+ char *chunk;
1334+ char *op;
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;
1340+
1341+ if(!table) {
1342+ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1343+ return NULL;
1344+ }
1345+
1346+ memset(&ra, 0, sizeof(ra));
1347+
1348+ cfg = ast_config_new();
1349+ if (!cfg) {
1350+ /* If I can't alloc memory at this point, why bother doing anything else? */
1351+ ast_log(LOG_WARNING, "Out of memory!\n");
1352+ return NULL;
1353+ }
1354+
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);
1361+ return NULL;
1362+ }
1363+
1364+ initfield = ast_strdupa(newparam);
1365+ if(initfield && (op = strchr(initfield, ' '))) {
1366+ *op = '\0';
1367+ }
1368+
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 */
1371+
1372+ if(!strchr(newparam, ' ')) op = " ="; else op = "";
1373+
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);
1379+ }
1380+
1381+ if(initfield) {
1382+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
1383+ }
1384+
1385+ va_end(ap);
1386+
1387+ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
1388+
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);
1393+ return NULL;
1394+ }
1395+
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);
1401+ return NULL;
1402+ }
1403+
1404+ if((result = mysql_store_result(&mysql))) {
1405+ numFields = mysql_num_fields(result);
1406+ fields = mysql_fetch_fields(result);
1407+
1408+ while((row = mysql_fetch_row(result))) {
1409+ var = NULL;
1410+ cat = ast_category_new("");
1411+ if(!cat) {
1412+ ast_log(LOG_WARNING, "Out of memory!\n");
1413+ continue;
1414+ }
1415+ for(i = 0; i < numFields; i++) {
1416+ stringp = row[i];
1417+ while(stringp) {
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);
1422+ }
1423+ var = ast_variable_new(fields[i].name, chunk);
1424+ ast_variable_append(cat, var);
1425+ }
1426+ }
1427+ }
1428+ ast_category_append(cfg, cat);
1429+ }
1430+ } else {
1431+ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
1432+ }
1433+
1434+ mysql_free_result(result);
1435+ ast_mutex_unlock(&mysql_lock);
1436+
1437+ return cfg;
1438+}
1439+
1440+static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
1441+{
1442+ my_ulonglong numrows;
1443+ char sql[256];
1444+ const char *newparam, *newval;
1445+
1446+ if(!table) {
1447+ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1448+ return -1;
1449+ }
1450+
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);
1457+ return -1;
1458+ }
1459+
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 */
1462+
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);
1467+ }
1468+ va_end(ap);
1469+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup);
1470+
1471+ ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql);
1472+
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);
1477+ return -1;
1478+ }
1479+
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);
1485+ return -1;
1486+ }
1487+
1488+ numrows = mysql_affected_rows(&mysql);
1489+ ast_mutex_unlock(&mysql_lock);
1490+
1491+ ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
1492+
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.)
1497+ */
1498+
1499+ if(numrows >= 0)
1500+ return (int)numrows;
1501+
1502+ return -1;
1503+}
1504+
1505+static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg)
1506+{
1507+ MYSQL_RES *result;
1508+ MYSQL_ROW row;
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;
1518+
1519+ last[0] = '\0';
1520+
1521+ if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
1522+ ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
1523+ return NULL;
1524+ }
1525+
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);
1527+
1528+ ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql);
1529+
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);
1534+ return NULL;
1535+ }
1536+
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);
1542+ return NULL;
1543+ }
1544+
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);
1548+
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. */
1551+
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);
1557+ return NULL;
1558+ }
1559+ continue;
1560+ }
1561+
1562+ if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
1563+ cur_cat = ast_category_new(row[0]);
1564+ if (!cur_cat) {
1565+ ast_log(LOG_WARNING, "Out of memory!\n");
1566+ break;
1567+ }
1568+ strcpy(last, row[0]);
1569+ last_cat_metric = atoi(row[3]);
1570+ ast_category_append(cfg, cur_cat);
1571+ }
1572+ new_v = ast_variable_new(row[1], row[2]);
1573+ ast_variable_append(cur_cat, new_v);
1574+ }
1575+ } else {
1576+ ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
1577+ }
1578+
1579+ mysql_free_result(result);
1580+ ast_mutex_unlock(&mysql_lock);
1581+
1582+ return cfg;
1583+}
1584+
1585+static struct ast_config_engine mysql_engine = {
1586+ .name = "mysql",
1587+ .load_func = config_mysql,
1588+ .realtime_func = realtime_mysql,
1589+ .realtime_multi_func = realtime_multi_mysql,
1590+ .update_func = update_mysql
1591+};
1592+
1593+int load_module (void)
1594+{
1595+ parse_config();
1596+
1597+ ast_mutex_lock(&mysql_lock);
1598+
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));
1602+ }
1603+
1604+ ast_config_engine_register(&mysql_engine);
1605+ if(option_verbose) {
1606+ ast_verbose("MySQL RealTime driver loaded.\n");
1607+ }
1608+ ast_cli_register(&cli_realtime_mysql_status);
1609+
1610+ ast_mutex_unlock(&mysql_lock);
1611+
1612+ return 0;
1613+}
1614+
1615+int unload_module (void)
1616+{
1617+ /* Aquire control before doing anything to the module itself. */
1618+ ast_mutex_lock(&mysql_lock);
1619+
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");
1625+ }
1626+
1627+ STANDARD_HANGUP_LOCALUSERS;
1628+
1629+ /* Unlock so something else can destroy the lock. */
1630+ ast_mutex_unlock(&mysql_lock);
1631+
1632+ return 0;
1633+}
1634+
1635+int reload (void)
1636+{
1637+ /* Aquire control before doing anything to the module itself. */
1638+ ast_mutex_lock(&mysql_lock);
1639+
1640+ mysql_close(&mysql);
1641+ connected = 0;
1642+ parse_config();
1643+
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));
1647+ }
1648+
1649+ ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n");
1650+
1651+ /* Done reloading. Release lock so others can now use driver. */
1652+ ast_mutex_unlock(&mysql_lock);
1653+
1654+ return 0;
1655+}
1656+
1657+int parse_config (void)
1658+{
1659+ struct ast_config *config;
1660+ char *s;
1661+
1662+ config = ast_config_load(RES_CONFIG_MYSQL_CONF);
1663+
1664+ if(config) {
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);
1668+ } else {
1669+ strncpy(dbuser, s, sizeof(dbuser) - 1);
1670+ }
1671+
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);
1675+ } else {
1676+ strncpy(dbpass, s, sizeof(dbpass) - 1);
1677+ }
1678+
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");
1681+ dbhost[0] = '\0';
1682+ } else {
1683+ strncpy(dbhost, s, sizeof(dbhost) - 1);
1684+ }
1685+
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);
1689+ } else {
1690+ strncpy(dbname, s, sizeof(dbname) - 1);
1691+ }
1692+
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");
1695+ dbport = 3306;
1696+ } else {
1697+ dbport = atoi(s);
1698+ }
1699+
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);
1703+ } else {
1704+ strncpy(dbsock, s, sizeof(dbsock) - 1);
1705+ }
1706+ }
1707+ ast_config_destroy(config);
1708+
1709+ if(dbhost) {
1710+ ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost);
1711+ ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport);
1712+ } else {
1713+ ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock);
1714+ }
1715+ ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser);
1716+ ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass);
1717+
1718+ return 1;
1719+}
1720+
1721+char *description (void)
1722+{
1723+ return res_config_mysql_desc;
1724+}
1725+
1726+int usecount (void)
1727+{
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");
1731+ return 1;
1732+ }
1733+ ast_mutex_unlock(&mysql_lock);
1734+ return 0;
1735+}
1736+
1737+char *key ()
1738+{
1739+ return ASTERISK_GPL_KEY;
1740+}
1741+
1742+static int mysql_reconnect(const char *database)
1743+{
1744+ char my_database[50];
1745+
1746+ if(!database || ast_strlen_zero(database))
1747+ ast_copy_string(my_database, dbname, sizeof(my_database));
1748+ else
1749+ ast_copy_string(my_database, database, sizeof(my_database));
1750+
1751+ /* mutex lock should have been locked before calling this function. */
1752+
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");
1756+ connected = 0;
1757+ return 0;
1758+ }
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");
1761+ connected = 1;
1762+ connect_time = time(NULL);
1763+ return 1;
1764+ } else {
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));
1767+ connected = 0;
1768+ return 0;
1769+ }
1770+ } else {
1771+ if(mysql_ping(&mysql) != 0) {
1772+ connected = 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));
1775+ return 0;
1776+ }
1777+
1778+ connected = 1;
1779+
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));
1783+ return 0;
1784+ }
1785+
1786+ ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n");
1787+ return 1;
1788+ }
1789+}
1790+
1791+static int realtime_mysql_status(int fd, int argc, char **argv)
1792+{
1793+ char status[256], status2[100] = "";
1794+ int ctime = time(NULL) - connect_time;
1795+
1796+ ast_mutex_lock(&mysql_lock);
1797+ if(mysql_reconnect(NULL)) {
1798+ if(dbhost) {
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);
1802+ } else {
1803+ snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
1804+ }
1805+
1806+ if(dbuser && *dbuser) {
1807+ snprintf(status2, 99, " with username %s", dbuser);
1808+ }
1809+
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);
1818+ } else {
1819+ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
1820+ }
1821+
1822+ ast_mutex_unlock(&mysql_lock);
1823+ return RESULT_SUCCESS;
1824+ } else {
1825+ ast_mutex_unlock(&mysql_lock);
1826+ return RESULT_FAILURE;
1827+ }
1828+}