]>
Commit | Line | Data |
---|---|---|
5e69ef1a MT |
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 | |
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(®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); | |
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(®threadid, NULL, auth_thread, ®q); | |
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(®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; | |
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(®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); | |
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); | |
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 | |
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 | |
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 | |
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 | +} |