janus-gateway / plugins / janus_sipre.c @ 38109c95
History | View | Annotate | Download (165 KB)
1 |
/*! \file janus_sipre.c
|
---|---|
2 |
* \author Lorenzo Miniero <lorenzo@meetecho.com>
|
3 |
* \copyright GNU General Public License v3
|
4 |
* \brief Janus SIPre plugin (libre)
|
5 |
* \details This is basically a clone of the SIPre plugin, with the key
|
6 |
* difference being that it uses \c libre (http://creytiv.com/re.html)
|
7 |
* instead of Sofia SIP for its internal stack. As such, it provides an
|
8 |
* alternative for those who don't want to, or can't, use the Sofia-based
|
9 |
* SIP plugin. The API it exposes is exactly the same, meaning it should
|
10 |
* be pretty straightforward to switch from one plugin to another on the
|
11 |
* client side. The configuration file looks exactly the same as well.
|
12 |
*
|
13 |
* \section sipapi SIPre Plugin API
|
14 |
*
|
15 |
* All requests you can send in the SIPre Plugin API are asynchronous,
|
16 |
* which means all responses (successes and errors) will be delivered
|
17 |
* as events with the same transaction.
|
18 |
*
|
19 |
* The supported requests are \c register , \c unregister , \c call ,
|
20 |
* \c accept, \c info , \c message , \c dtmf_info , \c recording ,
|
21 |
* \c hold , \c unhold and \c hangup . \c register can be used,
|
22 |
* as the name suggests, to register a username at a SIP registrar to
|
23 |
* call and be called, while \c unregister unregisters it; \c call is used
|
24 |
* to send an INVITE to a different SIP URI through the plugin, while
|
25 |
* \c accept is used to accept the call in case one is invited instead
|
26 |
* of inviting; \c hold and \c unhold can be used respectively to put a
|
27 |
* call on-hold and to resume it; \c info allows you to send a generic
|
28 |
* SIP INFO request, while \c dtmf_info is focused on using INFO for DTMF
|
29 |
* instead; \c message is the method you use to send a SIP message
|
30 |
* to the other peer; \c recording is used, instead, to record the
|
31 |
* conversation to one or more .mjr files (depending on the direction you
|
32 |
* want to record); finally, \c hangup can be used to terminate the
|
33 |
* communication at any time, either to hangup (BYE) an ongoing call or
|
34 |
* to cancel/decline (CANCEL/BYE) a call that hasn't started yet.
|
35 |
*
|
36 |
* Actual API docs: TBD.
|
37 |
*
|
38 |
* \ingroup plugins
|
39 |
* \ref plugins
|
40 |
*/
|
41 |
|
42 |
#include "plugin.h" |
43 |
|
44 |
#include <arpa/inet.h> |
45 |
#include <net/if.h> |
46 |
#include <sys/socket.h> |
47 |
#include <netdb.h> |
48 |
#include <poll.h> |
49 |
|
50 |
#include <jansson.h> |
51 |
|
52 |
#include <re_types.h> |
53 |
#include <re_fmt.h> |
54 |
#include <re_mbuf.h> |
55 |
#include <re_msg.h> |
56 |
#include <re_list.h> |
57 |
#include <re_sa.h> |
58 |
#include <re_main.h> |
59 |
#include <re_mem.h> |
60 |
#include <re_mqueue.h> |
61 |
#include <re_sdp.h> |
62 |
#include <re_uri.h> |
63 |
#include <re_sip.h> |
64 |
#include <re_sipreg.h> |
65 |
#include <re_sipsess.h> |
66 |
#include <re_srtp.h> |
67 |
#include <re_tmr.h> |
68 |
#include <re_tls.h> |
69 |
#include <re_dns.h> |
70 |
|
71 |
#include "../debug.h" |
72 |
#include "../apierror.h" |
73 |
#include "../config.h" |
74 |
#include "../mutex.h" |
75 |
#include "../record.h" |
76 |
#include "../rtp.h" |
77 |
#include "../rtcp.h" |
78 |
#include "../sdp-utils.h" |
79 |
#include "../utils.h" |
80 |
#include "../ip-utils.h" |
81 |
|
82 |
|
83 |
/* Plugin information */
|
84 |
#define JANUS_SIPRE_VERSION 1 |
85 |
#define JANUS_SIPRE_VERSION_STRING "0.0.1" |
86 |
#define JANUS_SIPRE_DESCRIPTION "This is a simple SIP plugin for Janus (based on libre instead of Sofia), allowing WebRTC peers to register at a SIP server and call SIP user agents through the gateway." |
87 |
#define JANUS_SIPRE_NAME "JANUS SIPre plugin" |
88 |
#define JANUS_SIPRE_AUTHOR "Meetecho s.r.l." |
89 |
#define JANUS_SIPRE_PACKAGE "janus.plugin.sipre" |
90 |
|
91 |
/* Plugin methods */
|
92 |
janus_plugin *create(void);
|
93 |
int janus_sipre_init(janus_callbacks *callback, const char *config_path); |
94 |
void janus_sipre_destroy(void); |
95 |
int janus_sipre_get_api_compatibility(void); |
96 |
int janus_sipre_get_version(void); |
97 |
const char *janus_sipre_get_version_string(void); |
98 |
const char *janus_sipre_get_description(void); |
99 |
const char *janus_sipre_get_name(void); |
100 |
const char *janus_sipre_get_author(void); |
101 |
const char *janus_sipre_get_package(void); |
102 |
void janus_sipre_create_session(janus_plugin_session *handle, int *error); |
103 |
struct janus_plugin_result *janus_sipre_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep); |
104 |
void janus_sipre_setup_media(janus_plugin_session *handle);
|
105 |
void janus_sipre_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len); |
106 |
void janus_sipre_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len); |
107 |
void janus_sipre_hangup_media(janus_plugin_session *handle);
|
108 |
void janus_sipre_destroy_session(janus_plugin_session *handle, int *error); |
109 |
json_t *janus_sipre_query_session(janus_plugin_session *handle); |
110 |
|
111 |
/* Plugin setup */
|
112 |
static janus_plugin janus_sipre_plugin =
|
113 |
JANUS_PLUGIN_INIT ( |
114 |
.init = janus_sipre_init, |
115 |
.destroy = janus_sipre_destroy, |
116 |
|
117 |
.get_api_compatibility = janus_sipre_get_api_compatibility, |
118 |
.get_version = janus_sipre_get_version, |
119 |
.get_version_string = janus_sipre_get_version_string, |
120 |
.get_description = janus_sipre_get_description, |
121 |
.get_name = janus_sipre_get_name, |
122 |
.get_author = janus_sipre_get_author, |
123 |
.get_package = janus_sipre_get_package, |
124 |
|
125 |
.create_session = janus_sipre_create_session, |
126 |
.handle_message = janus_sipre_handle_message, |
127 |
.setup_media = janus_sipre_setup_media, |
128 |
.incoming_rtp = janus_sipre_incoming_rtp, |
129 |
.incoming_rtcp = janus_sipre_incoming_rtcp, |
130 |
.hangup_media = janus_sipre_hangup_media, |
131 |
.destroy_session = janus_sipre_destroy_session, |
132 |
.query_session = janus_sipre_query_session, |
133 |
); |
134 |
|
135 |
/* Plugin creator */
|
136 |
janus_plugin *create(void) {
|
137 |
JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_SIPRE_NAME);
|
138 |
return &janus_sipre_plugin;
|
139 |
} |
140 |
|
141 |
/* Parameter validation */
|
142 |
static struct janus_json_parameter request_parameters[] = { |
143 |
{"request", JANUS_JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
|
144 |
}; |
145 |
static struct janus_json_parameter register_parameters[] = { |
146 |
{"type", JANUS_JSON_STRING, 0}, |
147 |
{"send_register", JANUS_JSON_BOOL, 0}, |
148 |
{"sips", JANUS_JSON_BOOL, 0}, |
149 |
{"username", JANUS_JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
150 |
{"secret", JANUS_JSON_STRING, 0}, |
151 |
{"authuser", JANUS_JSON_STRING, 0}, |
152 |
{"headers", JANUS_JSON_OBJECT, 0}, |
153 |
{"refresh", JANUS_JSON_BOOL, 0} |
154 |
}; |
155 |
static struct janus_json_parameter proxy_parameters[] = { |
156 |
{"proxy", JSON_STRING, 0}, |
157 |
{"outbound_proxy", JSON_STRING, 0} |
158 |
}; |
159 |
static struct janus_json_parameter call_parameters[] = { |
160 |
{"uri", JANUS_JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
161 |
{"autoack", JANUS_JSON_BOOL, 0}, |
162 |
{"headers", JANUS_JSON_OBJECT, 0}, |
163 |
{"srtp", JANUS_JSON_STRING, 0} |
164 |
}; |
165 |
static struct janus_json_parameter accept_parameters[] = { |
166 |
{"srtp", JANUS_JSON_STRING, 0} |
167 |
}; |
168 |
static struct janus_json_parameter recording_parameters[] = { |
169 |
{"action", JANUS_JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
170 |
{"audio", JANUS_JSON_BOOL, 0}, |
171 |
{"video", JANUS_JSON_BOOL, 0}, |
172 |
{"peer_audio", JANUS_JSON_BOOL, 0}, |
173 |
{"peer_video", JANUS_JSON_BOOL, 0}, |
174 |
{"filename", JANUS_JSON_STRING, 0} |
175 |
}; |
176 |
static struct janus_json_parameter dtmf_info_parameters[] = { |
177 |
{"digit", JANUS_JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
178 |
{"duration", JANUS_JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
|
179 |
}; |
180 |
static struct janus_json_parameter info_parameters[] = { |
181 |
{"type", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
182 |
{"content", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
|
183 |
}; |
184 |
static struct janus_json_parameter sipmessage_parameters[] = { |
185 |
{"content", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
|
186 |
}; |
187 |
|
188 |
/* Useful stuff */
|
189 |
static volatile gint initialized = 0, stopping = 0; |
190 |
static gboolean notify_events = TRUE;
|
191 |
static janus_callbacks *gateway = NULL; |
192 |
|
193 |
static char *local_ip = NULL; |
194 |
static gboolean behind_nat = FALSE;
|
195 |
static char *user_agent; |
196 |
#define JANUS_DEFAULT_REGISTER_TTL 3600 |
197 |
static uint32_t register_ttl = JANUS_DEFAULT_REGISTER_TTL;
|
198 |
|
199 |
static GThread *handler_thread;
|
200 |
static GThread *watchdog;
|
201 |
static void *janus_sipre_handler(void *data); |
202 |
|
203 |
typedef struct janus_sipre_message { |
204 |
janus_plugin_session *handle; |
205 |
char *transaction;
|
206 |
json_t *message; |
207 |
json_t *jsep; |
208 |
} janus_sipre_message; |
209 |
static GAsyncQueue *messages = NULL; |
210 |
static janus_sipre_message exit_message;
|
211 |
|
212 |
static void janus_sipre_message_free(janus_sipre_message *msg) { |
213 |
if(!msg || msg == &exit_message)
|
214 |
return;
|
215 |
|
216 |
msg->handle = NULL;
|
217 |
|
218 |
g_free(msg->transaction); |
219 |
msg->transaction = NULL;
|
220 |
if(msg->message)
|
221 |
json_decref(msg->message); |
222 |
msg->message = NULL;
|
223 |
if(msg->jsep)
|
224 |
json_decref(msg->jsep); |
225 |
msg->jsep = NULL;
|
226 |
|
227 |
g_free(msg); |
228 |
} |
229 |
|
230 |
/* libre SIP stack */
|
231 |
static volatile int libre_inited = 0; |
232 |
GThread *sipstack_thread = NULL;
|
233 |
|
234 |
/* Message queue */
|
235 |
static struct mqueue *mq = NULL; |
236 |
void janus_sipre_mqueue_handler(int id, void *data, void *arg); |
237 |
typedef enum janus_sipre_mqueue_event { |
238 |
janus_sipre_mqueue_event_do_init, |
239 |
janus_sipre_mqueue_event_do_register, |
240 |
janus_sipre_mqueue_event_do_unregister, |
241 |
janus_sipre_mqueue_event_do_call, |
242 |
janus_sipre_mqueue_event_do_accept, |
243 |
janus_sipre_mqueue_event_do_rcode, |
244 |
janus_sipre_mqueue_event_do_update, |
245 |
janus_sipre_mqueue_event_do_info, |
246 |
janus_sipre_mqueue_event_do_message, |
247 |
janus_sipre_mqueue_event_do_bye, |
248 |
janus_sipre_mqueue_event_do_destroy, |
249 |
/* TODO Add other events here */
|
250 |
janus_sipre_mqueue_event_do_exit |
251 |
} janus_sipre_mqueue_event; |
252 |
static const char *janus_sipre_mqueue_event_string(janus_sipre_mqueue_event event) { |
253 |
switch(event) {
|
254 |
case janus_sipre_mqueue_event_do_init:
|
255 |
return "init"; |
256 |
case janus_sipre_mqueue_event_do_register:
|
257 |
return "register"; |
258 |
case janus_sipre_mqueue_event_do_unregister:
|
259 |
return "unregister"; |
260 |
case janus_sipre_mqueue_event_do_call:
|
261 |
return "call"; |
262 |
case janus_sipre_mqueue_event_do_accept:
|
263 |
return "accept"; |
264 |
case janus_sipre_mqueue_event_do_rcode:
|
265 |
return "rcode"; |
266 |
case janus_sipre_mqueue_event_do_update:
|
267 |
return "update"; |
268 |
case janus_sipre_mqueue_event_do_info:
|
269 |
return "info"; |
270 |
case janus_sipre_mqueue_event_do_message:
|
271 |
return "message"; |
272 |
case janus_sipre_mqueue_event_do_bye:
|
273 |
return "bye"; |
274 |
case janus_sipre_mqueue_event_do_destroy:
|
275 |
return "destroy"; |
276 |
case janus_sipre_mqueue_event_do_exit:
|
277 |
return "exit"; |
278 |
default:
|
279 |
return "unknown"; |
280 |
} |
281 |
} |
282 |
typedef struct janus_sipre_mqueue_payload { |
283 |
void *session; /* The session this event refers to */ |
284 |
const struct sip_msg *msg; /* The SIP message this refers to, if any */ |
285 |
int rcode; /* The error code to send back, if any */ |
286 |
void *data; /* Payload specific data */ |
287 |
} janus_sipre_mqueue_payload; |
288 |
static janus_sipre_mqueue_payload *janus_sipre_mqueue_payload_create(void *session, const struct sip_msg *msg, int rcode, void *data) { |
289 |
janus_sipre_mqueue_payload *payload = g_malloc0(sizeof(janus_sipre_mqueue_payload));
|
290 |
payload->session = session; |
291 |
payload->msg = msg; |
292 |
payload->rcode = rcode; |
293 |
payload->data = data; |
294 |
return payload;
|
295 |
} |
296 |
|
297 |
/* Helper to quickly get the reason associated to response codes */
|
298 |
static const char *janus_sipre_error_reason(int rcode) { |
299 |
switch(rcode) {
|
300 |
case 100: return "Trying"; |
301 |
case 180: return "Ringing"; |
302 |
case 181: return "Call is Being Forwarded"; |
303 |
case 182: return "Queued"; |
304 |
case 183: return "Session in Progress"; |
305 |
case 199: return "Early Dialog Terminated"; |
306 |
case 200: return "OK"; |
307 |
case 202: return "Accepted"; |
308 |
case 204: return "No Notification"; |
309 |
case 300: return "Multiple Choices"; |
310 |
case 301: return "Moved Permanently"; |
311 |
case 302: return "Moved Temporarily"; |
312 |
case 305: return "Use Proxy"; |
313 |
case 380: return "Alternative Service"; |
314 |
case 400: return "Bad Request"; |
315 |
case 401: return "Unauthorized"; |
316 |
case 402: return "Payment Required"; |
317 |
case 403: return "Forbidden"; |
318 |
case 404: return "Not Found"; |
319 |
case 405: return "Method Not Allowed"; |
320 |
case 406: return "Not Acceptable"; |
321 |
case 407: return "Proxy Authentication Required"; |
322 |
case 408: return "Request Timeout"; |
323 |
case 409: return "Conflict"; |
324 |
case 410: return "Gone"; |
325 |
case 411: return "Length Required"; |
326 |
case 412: return "Conditional Request Failed"; |
327 |
case 413: return "Request Entity Too Large"; |
328 |
case 414: return "Request-URI Too Long"; |
329 |
case 415: return "Unsupported Media Type"; |
330 |
case 416: return "Unsupported URI Scheme"; |
331 |
case 417: return "Unknown Resource-Priority"; |
332 |
case 420: return "Bad Extension"; |
333 |
case 421: return "Extension Required"; |
334 |
case 422: return "Session Interval Too Small"; |
335 |
case 423: return "Interval Too Brief"; |
336 |
case 424: return "Bad Location Information"; |
337 |
case 428: return "Use Identity Header"; |
338 |
case 429: return "Provide Referrer Identity"; |
339 |
case 430: return "Flow Failed"; |
340 |
case 433: return "Anonymity Disallowed"; |
341 |
case 436: return "Bad Identity-Info"; |
342 |
case 437: return "Unsupported Certificate"; |
343 |
case 438: return "Invalid Identity Header"; |
344 |
case 439: return "First Hop Lacks Outbound Support"; |
345 |
case 470: return "Consent Needed"; |
346 |
case 480: return "Temporarily Unavailable"; |
347 |
case 481: return "Call/Transaction Does Not Exist"; |
348 |
case 482: return "Loop Detected."; |
349 |
case 483: return "Too Many Hops"; |
350 |
case 484: return "Address Incomplete"; |
351 |
case 485: return "Ambiguous"; |
352 |
case 486: return "Busy Here"; |
353 |
case 487: return "Request Terminated"; |
354 |
case 488: return "Not Acceptable Here"; |
355 |
case 489: return "Bad Event"; |
356 |
case 491: return "Request Pending"; |
357 |
case 493: return "Undecipherable"; |
358 |
case 494: return "Security Agreement Required"; |
359 |
case 500: return "Server Internal Error"; |
360 |
case 501: return "Not Implemented"; |
361 |
case 502: return "Bad Gateway"; |
362 |
case 503: return "Service Unavailable"; |
363 |
case 504: return "Server Time-out"; |
364 |
case 505: return "Version Not Supported"; |
365 |
case 513: return "Message Too Large"; |
366 |
case 580: return "Precondition Failure"; |
367 |
case 600: return "Busy Everywhere"; |
368 |
case 603: return "Decline"; |
369 |
case 604: return "Does Not Exist Anywhere"; |
370 |
case 606: return "Not Acceptable"; |
371 |
default: return "Unknown Error"; |
372 |
} |
373 |
} |
374 |
|
375 |
/* Registration info */
|
376 |
typedef enum { |
377 |
janus_sipre_registration_status_disabled = -2,
|
378 |
janus_sipre_registration_status_failed = -1,
|
379 |
janus_sipre_registration_status_unregistered = 0,
|
380 |
janus_sipre_registration_status_registering, |
381 |
janus_sipre_registration_status_registered, |
382 |
janus_sipre_registration_status_unregistering, |
383 |
} janus_sipre_registration_status; |
384 |
|
385 |
static const char *janus_sipre_registration_status_string(janus_sipre_registration_status status) { |
386 |
switch(status) {
|
387 |
case janus_sipre_registration_status_disabled:
|
388 |
return "disabled"; |
389 |
case janus_sipre_registration_status_failed:
|
390 |
return "failed"; |
391 |
case janus_sipre_registration_status_unregistered:
|
392 |
return "unregistered"; |
393 |
case janus_sipre_registration_status_registering:
|
394 |
return "registering"; |
395 |
case janus_sipre_registration_status_registered:
|
396 |
return "registered"; |
397 |
case janus_sipre_registration_status_unregistering:
|
398 |
return "unregistering"; |
399 |
default:
|
400 |
return "unknown"; |
401 |
} |
402 |
} |
403 |
|
404 |
|
405 |
typedef enum { |
406 |
janus_sipre_call_status_idle = 0,
|
407 |
janus_sipre_call_status_inviting, |
408 |
janus_sipre_call_status_invited, |
409 |
janus_sipre_call_status_incall, |
410 |
janus_sipre_call_status_closing, |
411 |
} janus_sipre_call_status; |
412 |
|
413 |
static const char *janus_sipre_call_status_string(janus_sipre_call_status status) { |
414 |
switch(status) {
|
415 |
case janus_sipre_call_status_idle:
|
416 |
return "idle"; |
417 |
case janus_sipre_call_status_inviting:
|
418 |
return "inviting"; |
419 |
case janus_sipre_call_status_invited:
|
420 |
return "invited"; |
421 |
case janus_sipre_call_status_incall:
|
422 |
return "incall"; |
423 |
case janus_sipre_call_status_closing:
|
424 |
return "closing"; |
425 |
default:
|
426 |
return "unknown"; |
427 |
} |
428 |
} |
429 |
|
430 |
|
431 |
typedef enum { |
432 |
janus_sipre_secret_type_plaintext = 1,
|
433 |
janus_sipre_secret_type_hashed = 2,
|
434 |
janus_sipre_secret_type_unknown |
435 |
} janus_sipre_secret_type; |
436 |
|
437 |
typedef struct janus_sipre_account { |
438 |
char *identity;
|
439 |
gboolean sips; |
440 |
char *username;
|
441 |
char *display_name; /* Used for outgoing calls in the From header */ |
442 |
char *authuser; /**< username to use for authentication */ |
443 |
char *secret;
|
444 |
janus_sipre_secret_type secret_type; |
445 |
int sip_port;
|
446 |
char *proxy;
|
447 |
char *outbound_proxy;
|
448 |
janus_sipre_registration_status registration_status; |
449 |
} janus_sipre_account; |
450 |
|
451 |
typedef struct janus_sipre_stack { |
452 |
struct sip *sipstack; /* SIP stack */ |
453 |
struct tls *tls; /* TLS transport, if needed */ |
454 |
struct sipsess *sess; /* SIP session */ |
455 |
struct sipsess_sock *sess_sock; /* SIP session socket */ |
456 |
struct sipreg *reg; /* SIP registration */ |
457 |
struct dnsc *dns_client; /* DNS client */ |
458 |
uint32_t expires; /* Registration interval (seconds) */
|
459 |
const struct sip_msg *invite; /* Current INVITE */ |
460 |
void *session; /* Opaque pointer to the plugin session */ |
461 |
} janus_sipre_stack; |
462 |
|
463 |
typedef struct janus_sipre_media { |
464 |
char *remote_ip;
|
465 |
gboolean earlymedia; |
466 |
gboolean ready; |
467 |
gboolean autoack; |
468 |
gboolean require_srtp, has_srtp_local, has_srtp_remote; |
469 |
gboolean on_hold; |
470 |
gboolean has_audio; |
471 |
int audio_rtp_fd, audio_rtcp_fd;
|
472 |
int local_audio_rtp_port, remote_audio_rtp_port;
|
473 |
int local_audio_rtcp_port, remote_audio_rtcp_port;
|
474 |
guint32 audio_ssrc, audio_ssrc_peer; |
475 |
int audio_pt;
|
476 |
const char *audio_pt_name; |
477 |
srtp_t audio_srtp_in, audio_srtp_out; |
478 |
srtp_policy_t audio_remote_policy, audio_local_policy; |
479 |
int audio_srtp_suite_in, audio_srtp_suite_out;
|
480 |
gboolean audio_send; |
481 |
janus_sdp_mdirection pre_hold_audio_dir; |
482 |
gboolean has_video; |
483 |
int video_rtp_fd, video_rtcp_fd;
|
484 |
int local_video_rtp_port, remote_video_rtp_port;
|
485 |
int local_video_rtcp_port, remote_video_rtcp_port;
|
486 |
guint32 video_ssrc, video_ssrc_peer; |
487 |
int video_pt;
|
488 |
const char *video_pt_name; |
489 |
srtp_t video_srtp_in, video_srtp_out; |
490 |
srtp_policy_t video_remote_policy, video_local_policy; |
491 |
int video_srtp_suite_in, video_srtp_suite_out;
|
492 |
gboolean video_send; |
493 |
janus_sdp_mdirection pre_hold_video_dir; |
494 |
janus_rtp_switching_context context; |
495 |
int pipefd[2]; |
496 |
gboolean updated; |
497 |
} janus_sipre_media; |
498 |
|
499 |
typedef struct janus_sipre_session { |
500 |
janus_plugin_session *handle; |
501 |
janus_sipre_stack stack; |
502 |
janus_sipre_account account; |
503 |
janus_sipre_call_status status; |
504 |
janus_sipre_media media; |
505 |
char *transaction;
|
506 |
char *callee;
|
507 |
char *callid;
|
508 |
char *temp_sdp;
|
509 |
janus_sdp *sdp; /* The SDP this user sent */
|
510 |
janus_recorder *arc; /* The Janus recorder instance for this user's audio, if enabled */
|
511 |
janus_recorder *arc_peer; /* The Janus recorder instance for the peer's audio, if enabled */
|
512 |
janus_recorder *vrc; /* The Janus recorder instance for this user's video, if enabled */
|
513 |
janus_recorder *vrc_peer; /* The Janus recorder instance for the peer's video, if enabled */
|
514 |
janus_mutex rec_mutex; /* Mutex to protect the recorders from race conditions */
|
515 |
volatile gint hangingup;
|
516 |
gint64 destroyed; /* Time at which this session was marked as destroyed */
|
517 |
janus_mutex mutex; |
518 |
} janus_sipre_session; |
519 |
static GHashTable *sessions;
|
520 |
static GList *old_sessions;
|
521 |
static GHashTable *identities;
|
522 |
static GHashTable *callids;
|
523 |
static janus_mutex sessions_mutex;
|
524 |
|
525 |
|
526 |
/* SRTP stuff (in case we need SDES) */
|
527 |
static int janus_sipre_srtp_set_local(janus_sipre_session *session, gboolean video, char **crypto) { |
528 |
if(session == NULL) |
529 |
return -1; |
530 |
/* Generate key/salt */
|
531 |
uint8_t *key = g_malloc0(SRTP_MASTER_LENGTH); |
532 |
srtp_crypto_get_random(key, SRTP_MASTER_LENGTH); |
533 |
/* Set SRTP policies */
|
534 |
srtp_policy_t *policy = video ? &session->media.video_local_policy : &session->media.audio_local_policy; |
535 |
srtp_crypto_policy_set_rtp_default(&(policy->rtp)); |
536 |
srtp_crypto_policy_set_rtcp_default(&(policy->rtcp)); |
537 |
policy->ssrc.type = ssrc_any_inbound; |
538 |
policy->key = key; |
539 |
policy->next = NULL;
|
540 |
/* Create SRTP context */
|
541 |
srtp_err_status_t res = srtp_create(video ? &session->media.video_srtp_out : &session->media.audio_srtp_out, policy); |
542 |
if(res != srtp_err_status_ok) {
|
543 |
/* Something went wrong... */
|
544 |
JANUS_LOG(LOG_ERR, "Oops, error creating outbound SRTP session: %d (%s)\n", res, janus_srtp_error_str(res));
|
545 |
g_free(key); |
546 |
policy->key = NULL;
|
547 |
return -2; |
548 |
} |
549 |
/* Base64 encode the salt */
|
550 |
*crypto = g_base64_encode(key, SRTP_MASTER_LENGTH); |
551 |
if((video && session->media.video_srtp_out) || (!video && session->media.audio_srtp_out)) {
|
552 |
JANUS_LOG(LOG_VERB, "%s outbound SRTP session created\n", video ? "Video" : "Audio"); |
553 |
} |
554 |
return 0; |
555 |
} |
556 |
static int janus_sipre_srtp_set_remote(janus_sipre_session *session, gboolean video, const char *crypto, int suite) { |
557 |
if(session == NULL || crypto == NULL) |
558 |
return -1; |
559 |
/* Base64 decode the crypto string and set it as the remote SRTP context */
|
560 |
gsize len = 0;
|
561 |
guchar *decoded = g_base64_decode(crypto, &len); |
562 |
if(len < SRTP_MASTER_LENGTH) {
|
563 |
/* FIXME Can this happen? */
|
564 |
g_free(decoded); |
565 |
return -2; |
566 |
} |
567 |
/* Set SRTP policies */
|
568 |
srtp_policy_t *policy = video ? &session->media.video_remote_policy : &session->media.audio_remote_policy; |
569 |
srtp_crypto_policy_set_rtp_default(&(policy->rtp)); |
570 |
srtp_crypto_policy_set_rtcp_default(&(policy->rtcp)); |
571 |
if(suite == 32) { |
572 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(policy->rtp)); |
573 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(policy->rtcp)); |
574 |
} else if(suite == 80) { |
575 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(policy->rtp)); |
576 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(policy->rtcp)); |
577 |
} |
578 |
policy->ssrc.type = ssrc_any_inbound; |
579 |
policy->key = decoded; |
580 |
policy->next = NULL;
|
581 |
/* Create SRTP context */
|
582 |
srtp_err_status_t res = srtp_create(video ? &session->media.video_srtp_in : &session->media.audio_srtp_in, policy); |
583 |
if(res != srtp_err_status_ok) {
|
584 |
/* Something went wrong... */
|
585 |
JANUS_LOG(LOG_ERR, "Oops, error creating inbound SRTP session: %d (%s)\n", res, janus_srtp_error_str(res));
|
586 |
g_free(decoded); |
587 |
policy->key = NULL;
|
588 |
return -2; |
589 |
} |
590 |
if((video && session->media.video_srtp_in) || (!video && session->media.audio_srtp_in)) {
|
591 |
JANUS_LOG(LOG_VERB, "%s inbound SRTP session created\n", video ? "Video" : "Audio"); |
592 |
} |
593 |
return 0; |
594 |
} |
595 |
static void janus_sipre_srtp_cleanup(janus_sipre_session *session) { |
596 |
if(session == NULL) |
597 |
return;
|
598 |
session->media.autoack = TRUE; |
599 |
session->media.require_srtp = FALSE; |
600 |
session->media.has_srtp_local = FALSE; |
601 |
session->media.has_srtp_remote = FALSE; |
602 |
/* Audio */
|
603 |
if(session->media.audio_srtp_out)
|
604 |
srtp_dealloc(session->media.audio_srtp_out); |
605 |
session->media.audio_srtp_out = NULL;
|
606 |
g_free(session->media.audio_local_policy.key); |
607 |
session->media.audio_local_policy.key = NULL;
|
608 |
session->media.audio_srtp_suite_out = 0;
|
609 |
if(session->media.audio_srtp_in)
|
610 |
srtp_dealloc(session->media.audio_srtp_in); |
611 |
session->media.audio_srtp_in = NULL;
|
612 |
g_free(session->media.audio_remote_policy.key); |
613 |
session->media.audio_remote_policy.key = NULL;
|
614 |
session->media.audio_srtp_suite_in = 0;
|
615 |
/* Video */
|
616 |
if(session->media.video_srtp_out)
|
617 |
srtp_dealloc(session->media.video_srtp_out); |
618 |
session->media.video_srtp_out = NULL;
|
619 |
g_free(session->media.video_local_policy.key); |
620 |
session->media.video_local_policy.key = NULL;
|
621 |
session->media.video_srtp_suite_out = 0;
|
622 |
if(session->media.video_srtp_in)
|
623 |
srtp_dealloc(session->media.video_srtp_in); |
624 |
session->media.video_srtp_in = NULL;
|
625 |
g_free(session->media.video_remote_policy.key); |
626 |
session->media.video_remote_policy.key = NULL;
|
627 |
session->media.video_srtp_suite_in = 0;
|
628 |
} |
629 |
|
630 |
|
631 |
/* libre event thread */
|
632 |
gpointer janus_sipre_stack_thread(gpointer user_data); |
633 |
/* libre callbacks */
|
634 |
int janus_sipre_cb_auth(char **user, char **pass, const char *realm, void *arg); |
635 |
void janus_sipre_cb_register(int err, const struct sip_msg *msg, void *arg); |
636 |
void janus_sipre_cb_progress(const struct sip_msg *msg, void *arg); |
637 |
void janus_sipre_cb_incoming(const struct sip_msg *msg, void *arg); |
638 |
int janus_sipre_cb_offer(struct mbuf **mbp, const struct sip_msg *msg, void *arg); |
639 |
int janus_sipre_cb_answer(const struct sip_msg *msg, void *arg); |
640 |
void janus_sipre_cb_established(const struct sip_msg *msg, void *arg); |
641 |
void janus_sipre_cb_info(struct sip *sip, const struct sip_msg *msg, void *arg); |
642 |
void janus_sipre_cb_closed(int err, const struct sip_msg *msg, void *arg); |
643 |
void janus_sipre_cb_exit(void *arg); |
644 |
|
645 |
/* URI parsing utilities */
|
646 |
static int janus_sipre_parse_uri(const char *uri) { |
647 |
if(uri == NULL) |
648 |
return -1; |
649 |
struct sip_addr addr;
|
650 |
struct pl pluri;
|
651 |
pl_set_str(&pluri, uri); |
652 |
if(sip_addr_decode(&addr, &pluri) != 0) |
653 |
return -1; |
654 |
return 0; |
655 |
} |
656 |
static char *janus_sipre_get_uri_username(const char *uri) { |
657 |
if(uri == NULL) |
658 |
return NULL; |
659 |
struct sip_addr addr;
|
660 |
struct pl pluri;
|
661 |
pl_set_str(&pluri, uri); |
662 |
if(sip_addr_decode(&addr, &pluri) != 0) |
663 |
return NULL; |
664 |
char *at = strchr(addr.uri.user.p, '@'); |
665 |
if(at != NULL) |
666 |
*(at) = '\0';
|
667 |
char *username = g_strdup(addr.uri.user.p);
|
668 |
if(at != NULL) |
669 |
*(at) = '@';
|
670 |
return username;
|
671 |
} |
672 |
static char *janus_sipre_get_uri_host(const char *uri) { |
673 |
if(uri == NULL) |
674 |
return NULL; |
675 |
struct sip_addr addr;
|
676 |
struct pl pluri;
|
677 |
pl_set_str(&pluri, uri); |
678 |
if(sip_addr_decode(&addr, &pluri) != 0) |
679 |
return NULL; |
680 |
return g_strdup(addr.uri.host.p);
|
681 |
} |
682 |
static uint16_t janus_sipre_get_uri_port(const char *uri) { |
683 |
if(uri == NULL) |
684 |
return 0; |
685 |
struct sip_addr addr;
|
686 |
struct pl pluri;
|
687 |
pl_set_str(&pluri, uri); |
688 |
if(sip_addr_decode(&addr, &pluri) != 0) |
689 |
return 0; |
690 |
return addr.uri.port;
|
691 |
} |
692 |
|
693 |
|
694 |
/* SDP parsing and manipulation */
|
695 |
void janus_sipre_sdp_process(janus_sipre_session *session, janus_sdp *sdp, gboolean answer, gboolean update, gboolean *changed);
|
696 |
char *janus_sipre_sdp_manipulate(janus_sipre_session *session, janus_sdp *sdp, gboolean answer);
|
697 |
/* Media */
|
698 |
static int janus_sipre_allocate_local_ports(janus_sipre_session *session); |
699 |
static void *janus_sipre_relay_thread(void *data); |
700 |
|
701 |
|
702 |
/* Error codes */
|
703 |
#define JANUS_SIPRE_ERROR_UNKNOWN_ERROR 499 |
704 |
#define JANUS_SIPRE_ERROR_NO_MESSAGE 440 |
705 |
#define JANUS_SIPRE_ERROR_INVALID_JSON 441 |
706 |
#define JANUS_SIPRE_ERROR_INVALID_REQUEST 442 |
707 |
#define JANUS_SIPRE_ERROR_MISSING_ELEMENT 443 |
708 |
#define JANUS_SIPRE_ERROR_INVALID_ELEMENT 444 |
709 |
#define JANUS_SIPRE_ERROR_ALREADY_REGISTERED 445 |
710 |
#define JANUS_SIPRE_ERROR_INVALID_ADDRESS 446 |
711 |
#define JANUS_SIPRE_ERROR_WRONG_STATE 447 |
712 |
#define JANUS_SIPRE_ERROR_MISSING_SDP 448 |
713 |
#define JANUS_SIPRE_ERROR_LIBRE_ERROR 449 |
714 |
#define JANUS_SIPRE_ERROR_IO_ERROR 450 |
715 |
#define JANUS_SIPRE_ERROR_RECORDING_ERROR 451 |
716 |
#define JANUS_SIPRE_ERROR_TOO_STRICT 452 |
717 |
|
718 |
|
719 |
/* SIPre watchdog/garbage collector (sort of) */
|
720 |
static void *janus_sipre_watchdog(void *data) { |
721 |
JANUS_LOG(LOG_INFO, "SIPre watchdog started\n");
|
722 |
gint64 now = 0;
|
723 |
while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
|
724 |
janus_mutex_lock(&sessions_mutex); |
725 |
/* Iterate on all the sessions */
|
726 |
now = janus_get_monotonic_time(); |
727 |
if(old_sessions != NULL) { |
728 |
GList *sl = old_sessions; |
729 |
JANUS_LOG(LOG_HUGE, "Checking %d old SIPre sessions...\n", g_list_length(old_sessions));
|
730 |
while(sl) {
|
731 |
janus_sipre_session *session = (janus_sipre_session *)sl->data; |
732 |
if(!session) {
|
733 |
sl = sl->next; |
734 |
continue;
|
735 |
} |
736 |
if(now-session->destroyed >= 5*G_USEC_PER_SEC) { |
737 |
/* We're lazy and actually get rid of the stuff only after a few seconds */
|
738 |
JANUS_LOG(LOG_VERB, "Freeing old SIPre session\n");
|
739 |
GList *rm = sl->next; |
740 |
old_sessions = g_list_delete_link(old_sessions, sl); |
741 |
sl = rm; |
742 |
sipsess_close_all(session->stack.sess_sock); |
743 |
sip_close(session->stack.sipstack, FALSE); |
744 |
session->stack.sipstack = NULL;
|
745 |
if(session->account.identity) {
|
746 |
g_hash_table_remove(identities, session->account.identity); |
747 |
g_free(session->account.identity); |
748 |
session->account.identity = NULL;
|
749 |
} |
750 |
session->account.sips = TRUE; |
751 |
if(session->account.proxy) {
|
752 |
g_free(session->account.proxy); |
753 |
session->account.proxy = NULL;
|
754 |
} |
755 |
if(session->account.outbound_proxy) {
|
756 |
g_free(session->account.outbound_proxy); |
757 |
session->account.outbound_proxy = NULL;
|
758 |
} |
759 |
if(session->account.secret) {
|
760 |
g_free(session->account.secret); |
761 |
session->account.secret = NULL;
|
762 |
} |
763 |
if(session->account.username) {
|
764 |
g_free(session->account.username); |
765 |
session->account.username = NULL;
|
766 |
} |
767 |
if(session->account.display_name) {
|
768 |
g_free(session->account.display_name); |
769 |
session->account.display_name = NULL;
|
770 |
} |
771 |
if(session->account.authuser) {
|
772 |
g_free(session->account.authuser); |
773 |
session->account.authuser = NULL;
|
774 |
} |
775 |
if(session->callee) {
|
776 |
g_free(session->callee); |
777 |
session->callee = NULL;
|
778 |
} |
779 |
if(session->callid) {
|
780 |
g_hash_table_remove(callids, session->callid); |
781 |
g_free(session->callid); |
782 |
session->callid = NULL;
|
783 |
} |
784 |
if(session->sdp) {
|
785 |
janus_sdp_free(session->sdp); |
786 |
session->sdp = NULL;
|
787 |
} |
788 |
if(session->transaction) {
|
789 |
g_free(session->transaction); |
790 |
session->transaction = NULL;
|
791 |
} |
792 |
if(session->media.remote_ip) {
|
793 |
g_free(session->media.remote_ip); |
794 |
session->media.remote_ip = NULL;
|
795 |
} |
796 |
janus_sipre_srtp_cleanup(session); |
797 |
session->handle = NULL;
|
798 |
g_free(session); |
799 |
session = NULL;
|
800 |
continue;
|
801 |
} |
802 |
sl = sl->next; |
803 |
} |
804 |
} |
805 |
janus_mutex_unlock(&sessions_mutex); |
806 |
g_usleep(500000);
|
807 |
} |
808 |
JANUS_LOG(LOG_INFO, "SIPre watchdog stopped\n");
|
809 |
return NULL; |
810 |
} |
811 |
|
812 |
|
813 |
/* Random string helper (for call-ids) */
|
814 |
static char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
815 |
static void janus_sipre_random_string(int length, char *buffer) { |
816 |
if(length > 0 && buffer) { |
817 |
int l = (int)(sizeof(charset)-1); |
818 |
int i=0; |
819 |
for(i=0; i<length; i++) { |
820 |
int key = rand() % l;
|
821 |
buffer[i] = charset[key]; |
822 |
} |
823 |
buffer[length-1] = '\0'; |
824 |
} |
825 |
} |
826 |
|
827 |
|
828 |
#ifdef HAVE_LIBRE_SIPTRACE
|
829 |
/* libre SIP logger function: when the Event Handlers mechanism is enabled,
|
830 |
* we use this to intercept SIP messages sent and received by the stack */
|
831 |
static void janus_sipre_msg_logger(bool tx, enum sip_transp tp, const struct sa *src, const struct sa *dst, |
832 |
const uint8_t *pkt, size_t len, void *arg) { |
833 |
/* Access the session this message refers to */
|
834 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
835 |
/* Print the SIP message */
|
836 |
char sip_msg[2048]; |
837 |
g_snprintf(sip_msg, sizeof(sip_msg), "%.*s", (int)len, (char *)pkt); |
838 |
/* Shoot the event */
|
839 |
json_t *info = json_object(); |
840 |
json_object_set_new(info, "event", json_string(tx ? "sip-out" : "sip-in")); |
841 |
json_object_set_new(info, "sip", json_string(sip_msg));
|
842 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
843 |
} |
844 |
#endif
|
845 |
|
846 |
|
847 |
/* Plugin implementation */
|
848 |
int janus_sipre_init(janus_callbacks *callback, const char *config_path) { |
849 |
if(g_atomic_int_get(&stopping)) {
|
850 |
/* Still stopping from before */
|
851 |
return -1; |
852 |
} |
853 |
if(callback == NULL || config_path == NULL) { |
854 |
/* Invalid arguments */
|
855 |
return -1; |
856 |
} |
857 |
|
858 |
/* Read configuration */
|
859 |
char filename[255]; |
860 |
g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_SIPRE_PACKAGE); |
861 |
JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
|
862 |
janus_config *config = janus_config_parse(filename); |
863 |
if(config != NULL) { |
864 |
janus_config_print(config); |
865 |
|
866 |
janus_config_item *item = janus_config_get_item_drilldown(config, "general", "local_ip"); |
867 |
if(item && item->value) {
|
868 |
/* Verify that the address is valid */
|
869 |
struct ifaddrs *ifas = NULL; |
870 |
janus_network_address iface; |
871 |
janus_network_address_string_buffer ibuf; |
872 |
if(getifaddrs(&ifas) || ifas == NULL) { |
873 |
JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected...\n");
|
874 |
} else {
|
875 |
if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) { |
876 |
JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
|
877 |
} else {
|
878 |
if(janus_network_address_to_string_buffer(&iface, &ibuf) != 0 || janus_network_address_string_buffer_is_null(&ibuf)) { |
879 |
JANUS_LOG(LOG_WARN, "Error getting local IP address from %s, falling back to detecting IP address...\n", item->value);
|
880 |
} else {
|
881 |
local_ip = g_strdup(janus_network_address_string_from_buffer(&ibuf)); |
882 |
} |
883 |
} |
884 |
} |
885 |
} |
886 |
|
887 |
item = janus_config_get_item_drilldown(config, "general", "register_ttl"); |
888 |
if(item && item->value) {
|
889 |
register_ttl = atol(item->value); |
890 |
if(register_ttl <= 0) { |
891 |
JANUS_LOG(LOG_WARN, "Invalid value of register_ttl, using default instead\n");
|
892 |
register_ttl = JANUS_DEFAULT_REGISTER_TTL; |
893 |
} |
894 |
} |
895 |
JANUS_LOG(LOG_VERB, "SIPre registration TTL set to %d seconds\n", register_ttl);
|
896 |
|
897 |
item = janus_config_get_item_drilldown(config, "general", "behind_nat"); |
898 |
if(item && item->value) {
|
899 |
behind_nat = janus_is_true(item->value); |
900 |
} |
901 |
|
902 |
item = janus_config_get_item_drilldown(config, "general", "user_agent"); |
903 |
if(item && item->value) {
|
904 |
user_agent = g_strdup(item->value); |
905 |
} else {
|
906 |
user_agent = g_strdup("Janus WebRTC Gateway SIPre Plugin "JANUS_SIPRE_VERSION_STRING);
|
907 |
} |
908 |
JANUS_LOG(LOG_VERB, "SIPre User-Agent set to %s\n", user_agent);
|
909 |
|
910 |
item = janus_config_get_item_drilldown(config, "general", "events"); |
911 |
if(item != NULL && item->value != NULL) { |
912 |
notify_events = janus_is_true(item->value); |
913 |
} |
914 |
if(!notify_events && callback->events_is_enabled()) {
|
915 |
JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_SIPRE_NAME);
|
916 |
} |
917 |
|
918 |
janus_config_destroy(config); |
919 |
} |
920 |
config = NULL;
|
921 |
|
922 |
if(local_ip == NULL) { |
923 |
local_ip = janus_network_detect_local_ip_as_string(janus_network_query_options_any_ip); |
924 |
if(local_ip == NULL) { |
925 |
JANUS_LOG(LOG_WARN, "Couldn't find any address! using 127.0.0.1 as the local IP... (which is NOT going to work out of your machine)\n");
|
926 |
local_ip = g_strdup("127.0.0.1");
|
927 |
} |
928 |
} |
929 |
JANUS_LOG(LOG_VERB, "Local IP set to %s\n", local_ip);
|
930 |
|
931 |
#ifdef HAVE_SRTP_2
|
932 |
/* Init randomizer (for randum numbers in SRTP) */
|
933 |
RAND_poll(); |
934 |
#endif
|
935 |
|
936 |
sessions = g_hash_table_new(NULL, NULL); |
937 |
callids = g_hash_table_new(g_str_hash, g_str_equal); |
938 |
identities = g_hash_table_new(g_str_hash, g_str_equal); |
939 |
janus_mutex_init(&sessions_mutex); |
940 |
messages = g_async_queue_new_full((GDestroyNotify) janus_sipre_message_free); |
941 |
/* This is the callback we'll need to invoke to contact the gateway */
|
942 |
gateway = callback; |
943 |
|
944 |
g_atomic_int_set(&initialized, 1);
|
945 |
|
946 |
GError *error = NULL;
|
947 |
/* Start the sessions watchdog */
|
948 |
watchdog = g_thread_try_new("sipre watchdog", &janus_sipre_watchdog, NULL, &error); |
949 |
if(error != NULL) { |
950 |
g_atomic_int_set(&initialized, 0);
|
951 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SIPre watchdog thread...\n", error->code, error->message ? error->message : "??"); |
952 |
return -1; |
953 |
} |
954 |
/* Launch the thread that will handle incoming API messages */
|
955 |
handler_thread = g_thread_try_new("sipre handler", janus_sipre_handler, NULL, &error); |
956 |
if(error != NULL) { |
957 |
g_atomic_int_set(&initialized, 0);
|
958 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SIPre handler thread...\n", error->code, error->message ? error->message : "??"); |
959 |
return -1; |
960 |
} |
961 |
/* Launch the thread that will handle the libre initialization and event loop */
|
962 |
g_atomic_int_set(&libre_inited, 0);
|
963 |
sipstack_thread = g_thread_try_new("sipre loop", janus_sipre_stack_thread, NULL, &error); |
964 |
if(error != NULL) { |
965 |
g_atomic_int_set(&initialized, 0);
|
966 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SIPre loop thread...\n", error->code, error->message ? error->message : "??"); |
967 |
return -1; |
968 |
} |
969 |
/* Let's wait for the libre initialization to complete */
|
970 |
while(g_atomic_int_get(&libre_inited) == 0) |
971 |
g_usleep(100000);
|
972 |
if(g_atomic_int_get(&libre_inited) == -1) { |
973 |
g_atomic_int_set(&initialized, 0);
|
974 |
JANUS_LOG(LOG_ERR, "Got error trying to initialize libre...\n");
|
975 |
return -1; |
976 |
} |
977 |
|
978 |
#ifndef HAVE_LIBRE_SIPTRACE
|
979 |
JANUS_LOG(LOG_WARN, "sip_set_trace() was not found in libre... "
|
980 |
"The tracing of SIP incoming/outgoing SIP messages when using the SIPre plugin will not be available to Event Handlers. "
|
981 |
"In case you're interested in that, apply this patch on your libre installation and recompile the plugin: "
|
982 |
"https://raw.githubusercontent.com/alfredh/patches/master/re-sip-trace.patch\n");
|
983 |
#endif
|
984 |
|
985 |
JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_SIPRE_NAME);
|
986 |
return 0; |
987 |
} |
988 |
|
989 |
void janus_sipre_destroy(void) { |
990 |
if(!g_atomic_int_get(&initialized))
|
991 |
return;
|
992 |
g_atomic_int_set(&stopping, 1);
|
993 |
|
994 |
g_async_queue_push(messages, &exit_message); |
995 |
if(handler_thread != NULL) { |
996 |
g_thread_join(handler_thread); |
997 |
handler_thread = NULL;
|
998 |
} |
999 |
|
1000 |
/* Break the libre loop */
|
1001 |
mqueue_push(mq, janus_sipre_mqueue_event_do_exit, NULL);
|
1002 |
if(sipstack_thread != NULL) { |
1003 |
g_thread_join(sipstack_thread); |
1004 |
sipstack_thread = NULL;
|
1005 |
} |
1006 |
if(watchdog != NULL) { |
1007 |
g_thread_join(watchdog); |
1008 |
watchdog = NULL;
|
1009 |
} |
1010 |
/* FIXME We should destroy the sessions cleanly */
|
1011 |
janus_mutex_lock(&sessions_mutex); |
1012 |
g_hash_table_destroy(sessions); |
1013 |
g_hash_table_destroy(callids); |
1014 |
g_hash_table_destroy(identities); |
1015 |
sessions = NULL;
|
1016 |
callids = NULL;
|
1017 |
identities = NULL;
|
1018 |
janus_mutex_unlock(&sessions_mutex); |
1019 |
g_async_queue_unref(messages); |
1020 |
messages = NULL;
|
1021 |
g_atomic_int_set(&initialized, 0);
|
1022 |
g_atomic_int_set(&stopping, 0);
|
1023 |
|
1024 |
g_free(local_ip); |
1025 |
|
1026 |
JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_SIPRE_NAME);
|
1027 |
} |
1028 |
|
1029 |
int janus_sipre_get_api_compatibility(void) { |
1030 |
/* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
|
1031 |
return JANUS_PLUGIN_API_VERSION;
|
1032 |
} |
1033 |
|
1034 |
int janus_sipre_get_version(void) { |
1035 |
return JANUS_SIPRE_VERSION;
|
1036 |
} |
1037 |
|
1038 |
const char *janus_sipre_get_version_string(void) { |
1039 |
return JANUS_SIPRE_VERSION_STRING;
|
1040 |
} |
1041 |
|
1042 |
const char *janus_sipre_get_description(void) { |
1043 |
return JANUS_SIPRE_DESCRIPTION;
|
1044 |
} |
1045 |
|
1046 |
const char *janus_sipre_get_name(void) { |
1047 |
return JANUS_SIPRE_NAME;
|
1048 |
} |
1049 |
|
1050 |
const char *janus_sipre_get_author(void) { |
1051 |
return JANUS_SIPRE_AUTHOR;
|
1052 |
} |
1053 |
|
1054 |
const char *janus_sipre_get_package(void) { |
1055 |
return JANUS_SIPRE_PACKAGE;
|
1056 |
} |
1057 |
|
1058 |
void janus_sipre_create_session(janus_plugin_session *handle, int *error) { |
1059 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
|
1060 |
*error = -1;
|
1061 |
return;
|
1062 |
} |
1063 |
janus_sipre_session *session = g_malloc0(sizeof(janus_sipre_session));
|
1064 |
session->handle = handle; |
1065 |
session->account.identity = NULL;
|
1066 |
session->account.sips = TRUE; |
1067 |
session->account.username = NULL;
|
1068 |
session->account.display_name = NULL;
|
1069 |
session->account.authuser = NULL;
|
1070 |
session->account.secret = NULL;
|
1071 |
session->account.secret_type = janus_sipre_secret_type_unknown; |
1072 |
session->account.sip_port = 0;
|
1073 |
session->account.proxy = NULL;
|
1074 |
session->account.outbound_proxy = NULL;
|
1075 |
session->account.registration_status = janus_sipre_registration_status_unregistered; |
1076 |
session->status = janus_sipre_call_status_idle; |
1077 |
memset(&session->stack, 0, sizeof(janus_sipre_stack)); |
1078 |
session->transaction = NULL;
|
1079 |
session->callee = NULL;
|
1080 |
session->callid = NULL;
|
1081 |
session->sdp = NULL;
|
1082 |
session->media.remote_ip = NULL;
|
1083 |
session->media.earlymedia = FALSE; |
1084 |
session->media.ready = FALSE; |
1085 |
session->media.autoack = TRUE; |
1086 |
session->media.require_srtp = FALSE; |
1087 |
session->media.has_srtp_local = FALSE; |
1088 |
session->media.has_srtp_remote = FALSE; |
1089 |
session->media.on_hold = FALSE; |
1090 |
session->media.has_audio = FALSE; |
1091 |
session->media.audio_rtp_fd = -1;
|
1092 |
session->media.audio_rtcp_fd= -1;
|
1093 |
session->media.local_audio_rtp_port = 0;
|
1094 |
session->media.remote_audio_rtp_port = 0;
|
1095 |
session->media.local_audio_rtcp_port = 0;
|
1096 |
session->media.remote_audio_rtcp_port = 0;
|
1097 |
session->media.audio_ssrc = 0;
|
1098 |
session->media.audio_ssrc_peer = 0;
|
1099 |
session->media.audio_pt = -1;
|
1100 |
session->media.audio_pt_name = NULL;
|
1101 |
session->media.audio_srtp_suite_in = 0;
|
1102 |
session->media.audio_srtp_suite_out = 0;
|
1103 |
session->media.audio_send = TRUE; |
1104 |
session->media.pre_hold_audio_dir = JANUS_SDP_DEFAULT; |
1105 |
session->media.has_video = FALSE; |
1106 |
session->media.video_rtp_fd = -1;
|
1107 |
session->media.video_rtcp_fd= -1;
|
1108 |
session->media.local_video_rtp_port = 0;
|
1109 |
session->media.remote_video_rtp_port = 0;
|
1110 |
session->media.local_video_rtcp_port = 0;
|
1111 |
session->media.remote_video_rtcp_port = 0;
|
1112 |
session->media.video_ssrc = 0;
|
1113 |
session->media.video_ssrc_peer = 0;
|
1114 |
session->media.video_pt = -1;
|
1115 |
session->media.video_pt_name = NULL;
|
1116 |
session->media.video_srtp_suite_in = 0;
|
1117 |
session->media.video_srtp_suite_out = 0;
|
1118 |
session->media.video_send = TRUE; |
1119 |
session->media.pre_hold_video_dir = JANUS_SDP_DEFAULT; |
1120 |
/* Initialize the RTP context */
|
1121 |
janus_rtp_switching_context_reset(&session->media.context); |
1122 |
session->media.pipefd[0] = -1; |
1123 |
session->media.pipefd[1] = -1; |
1124 |
session->media.updated = FALSE; |
1125 |
janus_mutex_init(&session->rec_mutex); |
1126 |
session->destroyed = 0;
|
1127 |
g_atomic_int_set(&session->hangingup, 0);
|
1128 |
janus_mutex_init(&session->mutex); |
1129 |
handle->plugin_handle = session; |
1130 |
|
1131 |
mqueue_push(mq, janus_sipre_mqueue_event_do_init, janus_sipre_mqueue_payload_create(session, NULL, 0, NULL)); |
1132 |
|
1133 |
janus_mutex_lock(&sessions_mutex); |
1134 |
g_hash_table_insert(sessions, handle, session); |
1135 |
janus_mutex_unlock(&sessions_mutex); |
1136 |
|
1137 |
|
1138 |
return;
|
1139 |
} |
1140 |
|
1141 |
void janus_sipre_destroy_session(janus_plugin_session *handle, int *error) { |
1142 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
|
1143 |
*error = -1;
|
1144 |
return;
|
1145 |
} |
1146 |
janus_sipre_session *session = (janus_sipre_session *)handle->plugin_handle; |
1147 |
if(!session) {
|
1148 |
JANUS_LOG(LOG_ERR, "No SIPre session associated with this handle...\n");
|
1149 |
*error = -2;
|
1150 |
return;
|
1151 |
} |
1152 |
janus_mutex_lock(&sessions_mutex); |
1153 |
if(!session->destroyed) {
|
1154 |
g_hash_table_remove(sessions, handle); |
1155 |
janus_sipre_hangup_media(handle); |
1156 |
session->destroyed = janus_get_monotonic_time(); |
1157 |
JANUS_LOG(LOG_VERB, "Destroying SIPre session (%s)...\n", session->account.username ? session->account.username : "unregistered user"); |
1158 |
/* Unregister */
|
1159 |
mqueue_push(mq, janus_sipre_mqueue_event_do_unregister, janus_sipre_mqueue_payload_create(session, NULL, 0, NULL)); |
1160 |
/* Destroy re-related stuff for this SIP session */
|
1161 |
mqueue_push(mq, janus_sipre_mqueue_event_do_destroy, janus_sipre_mqueue_payload_create(session, NULL, 0, NULL)); |
1162 |
/* Cleaning up and removing the session is done in a lazy way */
|
1163 |
old_sessions = g_list_append(old_sessions, session); |
1164 |
} |
1165 |
janus_mutex_unlock(&sessions_mutex); |
1166 |
return;
|
1167 |
} |
1168 |
|
1169 |
json_t *janus_sipre_query_session(janus_plugin_session *handle) { |
1170 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
|
1171 |
return NULL; |
1172 |
} |
1173 |
janus_sipre_session *session = (janus_sipre_session *)handle->plugin_handle; |
1174 |
if(!session) {
|
1175 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
1176 |
return NULL; |
1177 |
} |
1178 |
/* Provide some generic info, e.g., if we're in a call and with whom */
|
1179 |
json_t *info = json_object(); |
1180 |
json_object_set_new(info, "username", session->account.username ? json_string(session->account.username) : NULL); |
1181 |
json_object_set_new(info, "display_name", session->account.display_name ? json_string(session->account.display_name) : NULL); |
1182 |
json_object_set_new(info, "identity", session->account.identity ? json_string(session->account.identity) : NULL); |
1183 |
json_object_set_new(info, "registration_status", json_string(janus_sipre_registration_status_string(session->account.registration_status)));
|
1184 |
json_object_set_new(info, "call_status", json_string(janus_sipre_call_status_string(session->status)));
|
1185 |
if(session->callee) {
|
1186 |
json_object_set_new(info, "callee", json_string(session->callee ? session->callee : "??")); |
1187 |
json_object_set_new(info, "auto-ack", json_string(session->media.autoack ? "yes" : "no")); |
1188 |
json_object_set_new(info, "srtp-required", json_string(session->media.require_srtp ? "yes" : "no")); |
1189 |
json_object_set_new(info, "sdes-local", json_string(session->media.has_srtp_local ? "yes" : "no")); |
1190 |
json_object_set_new(info, "sdes-remote", json_string(session->media.has_srtp_remote ? "yes" : "no")); |
1191 |
} |
1192 |
if(session->arc || session->vrc || session->arc_peer || session->vrc_peer) {
|
1193 |
json_t *recording = json_object(); |
1194 |
if(session->arc && session->arc->filename)
|
1195 |
json_object_set_new(recording, "audio", json_string(session->arc->filename));
|
1196 |
if(session->vrc && session->vrc->filename)
|
1197 |
json_object_set_new(recording, "video", json_string(session->vrc->filename));
|
1198 |
if(session->arc_peer && session->arc_peer->filename)
|
1199 |
json_object_set_new(recording, "audio-peer", json_string(session->arc_peer->filename));
|
1200 |
if(session->vrc_peer && session->vrc_peer->filename)
|
1201 |
json_object_set_new(recording, "video-peer", json_string(session->vrc_peer->filename));
|
1202 |
json_object_set_new(info, "recording", recording);
|
1203 |
} |
1204 |
json_object_set_new(info, "destroyed", json_integer(session->destroyed));
|
1205 |
return info;
|
1206 |
} |
1207 |
|
1208 |
struct janus_plugin_result *janus_sipre_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) { |
1209 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
|
1210 |
return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL); |
1211 |
janus_sipre_message *msg = g_malloc0(sizeof(janus_sipre_message));
|
1212 |
msg->handle = handle; |
1213 |
msg->transaction = transaction; |
1214 |
msg->message = message; |
1215 |
msg->jsep = jsep; |
1216 |
g_async_queue_push(messages, msg); |
1217 |
|
1218 |
/* All the requests to this plugin are handled asynchronously */
|
1219 |
return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL); |
1220 |
} |
1221 |
|
1222 |
void janus_sipre_setup_media(janus_plugin_session *handle) {
|
1223 |
JANUS_LOG(LOG_INFO, "WebRTC media is now available\n");
|
1224 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
|
1225 |
return;
|
1226 |
janus_sipre_session *session = (janus_sipre_session *)handle->plugin_handle; |
1227 |
if(!session) {
|
1228 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
1229 |
return;
|
1230 |
} |
1231 |
if(session->destroyed)
|
1232 |
return;
|
1233 |
g_atomic_int_set(&session->hangingup, 0);
|
1234 |
/* TODO Only relay RTP/RTCP when we get this event */
|
1235 |
} |
1236 |
|
1237 |
void janus_sipre_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len) { |
1238 |
if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) |
1239 |
return;
|
1240 |
if(gateway) {
|
1241 |
/* Honour the audio/video active flags */
|
1242 |
janus_sipre_session *session = (janus_sipre_session *)handle->plugin_handle; |
1243 |
if(!session || session->destroyed) {
|
1244 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
1245 |
return;
|
1246 |
} |
1247 |
/* Forward to our SIPre peer */
|
1248 |
if((video && !session->media.video_send) || (!video && !session->media.audio_send)) {
|
1249 |
/* Dropping packet, peer doesn't want to receive it */
|
1250 |
return;
|
1251 |
} |
1252 |
if((video && session->media.video_ssrc == 0) || (!video && session->media.audio_ssrc == 0)) { |
1253 |
rtp_header *header = (rtp_header *)buf; |
1254 |
if(video) {
|
1255 |
session->media.video_ssrc = ntohl(header->ssrc); |
1256 |
} else {
|
1257 |
session->media.audio_ssrc = ntohl(header->ssrc); |
1258 |
} |
1259 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Got SIPre %s SSRC: %"SCNu32"\n", |
1260 |
session->account.username ? session->account.username : "unknown",
|
1261 |
video ? "video" : "audio", |
1262 |
video ? session->media.video_ssrc : session->media.audio_ssrc); |
1263 |
} |
1264 |
if((video && session->media.has_video && session->media.video_rtp_fd != -1) || |
1265 |
(!video && session->media.has_audio && session->media.audio_rtp_fd != -1)) {
|
1266 |
/* Save the frame if we're recording */
|
1267 |
janus_recorder_save_frame(video ? session->vrc : session->arc, buf, len); |
1268 |
/* Is SRTP involved? */
|
1269 |
if(session->media.has_srtp_local) {
|
1270 |
char sbuf[2048]; |
1271 |
memcpy(&sbuf, buf, len); |
1272 |
int protected = len;
|
1273 |
int res = srtp_protect(
|
1274 |
(video ? session->media.video_srtp_out : session->media.audio_srtp_out), |
1275 |
&sbuf, &protected); |
1276 |
if(res != srtp_err_status_ok) {
|
1277 |
rtp_header *header = (rtp_header *)&sbuf; |
1278 |
guint32 timestamp = ntohl(header->timestamp); |
1279 |
guint16 seq = ntohs(header->seq_number); |
1280 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] %s SRTP protect error... %s (len=%d-->%d, ts=%"SCNu32", seq=%"SCNu16")...\n", |
1281 |
session->account.username ? session->account.username : "unknown",
|
1282 |
video ? "Video" : "Audio", janus_srtp_error_str(res), len, protected, timestamp, seq); |
1283 |
} else {
|
1284 |
/* Forward the frame to the peer */
|
1285 |
send((video ? session->media.video_rtp_fd : session->media.audio_rtp_fd), sbuf, protected, 0);
|
1286 |
} |
1287 |
} else {
|
1288 |
/* Forward the frame to the peer */
|
1289 |
send((video ? session->media.video_rtp_fd : session->media.audio_rtp_fd), buf, len, 0);
|
1290 |
} |
1291 |
} |
1292 |
} |
1293 |
} |
1294 |
|
1295 |
void janus_sipre_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len) { |
1296 |
if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) |
1297 |
return;
|
1298 |
if(gateway) {
|
1299 |
janus_sipre_session *session = (janus_sipre_session *)handle->plugin_handle; |
1300 |
if(!session || session->destroyed) {
|
1301 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
1302 |
return;
|
1303 |
} |
1304 |
/* Forward to our SIPre peer */
|
1305 |
if((video && session->media.has_video && session->media.video_rtcp_fd != -1) || |
1306 |
(!video && session->media.has_audio && session->media.audio_rtcp_fd != -1)) {
|
1307 |
/* Fix SSRCs as the gateway does */
|
1308 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] Fixing %s SSRCs (local %u, peer %u)\n",
|
1309 |
session->account.username ? session->account.username : "unknown",
|
1310 |
video ? "video" : "audio", |
1311 |
(video ? session->media.video_ssrc : session->media.audio_ssrc), |
1312 |
(video ? session->media.video_ssrc_peer : session->media.audio_ssrc_peer)); |
1313 |
janus_rtcp_fix_ssrc(NULL, (char *)buf, len, video, |
1314 |
(video ? session->media.video_ssrc : session->media.audio_ssrc), |
1315 |
(video ? session->media.video_ssrc_peer : session->media.audio_ssrc_peer)); |
1316 |
/* Is SRTP involved? */
|
1317 |
if(session->media.has_srtp_local) {
|
1318 |
char sbuf[2048]; |
1319 |
memcpy(&sbuf, buf, len); |
1320 |
int protected = len;
|
1321 |
int res = srtp_protect_rtcp(
|
1322 |
(video ? session->media.video_srtp_out : session->media.audio_srtp_out), |
1323 |
&sbuf, &protected); |
1324 |
if(res != srtp_err_status_ok) {
|
1325 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] %s SRTCP protect error... %s (len=%d-->%d)...\n",
|
1326 |
session->account.username ? session->account.username : "unknown",
|
1327 |
video ? "Video" : "Audio", |
1328 |
janus_srtp_error_str(res), len, protected); |
1329 |
} else {
|
1330 |
/* Forward the message to the peer */
|
1331 |
send((video ? session->media.video_rtcp_fd : session->media.audio_rtcp_fd), sbuf, protected, 0);
|
1332 |
} |
1333 |
} else {
|
1334 |
/* Forward the message to the peer */
|
1335 |
send((video ? session->media.video_rtcp_fd : session->media.audio_rtcp_fd), buf, len, 0);
|
1336 |
} |
1337 |
} |
1338 |
} |
1339 |
} |
1340 |
|
1341 |
void janus_sipre_hangup_media(janus_plugin_session *handle) {
|
1342 |
JANUS_LOG(LOG_INFO, "No WebRTC media anymore\n");
|
1343 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
|
1344 |
return;
|
1345 |
janus_sipre_session *session = (janus_sipre_session *)handle->plugin_handle; |
1346 |
if(!session) {
|
1347 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
1348 |
return;
|
1349 |
} |
1350 |
if(session->destroyed)
|
1351 |
return;
|
1352 |
if(g_atomic_int_add(&session->hangingup, 1)) |
1353 |
return;
|
1354 |
if(!(session->status == janus_sipre_call_status_inviting ||
|
1355 |
session->status == janus_sipre_call_status_invited || |
1356 |
session->status == janus_sipre_call_status_incall)) |
1357 |
return;
|
1358 |
session->media.ready = FALSE; |
1359 |
session->media.on_hold = FALSE; |
1360 |
session->status = janus_sipre_call_status_closing; |
1361 |
/* Enqueue the BYE */
|
1362 |
mqueue_push(mq, janus_sipre_mqueue_event_do_bye, janus_sipre_mqueue_payload_create(session, NULL, 0, NULL)); |
1363 |
/* Get rid of the recorders, if available */
|
1364 |
janus_mutex_lock(&session->rec_mutex); |
1365 |
if(session->arc) {
|
1366 |
janus_recorder_close(session->arc); |
1367 |
JANUS_LOG(LOG_INFO, "Closed user's audio recording %s\n", session->arc->filename ? session->arc->filename : "??"); |
1368 |
janus_recorder_free(session->arc); |
1369 |
} |
1370 |
session->arc = NULL;
|
1371 |
if(session->arc_peer) {
|
1372 |
janus_recorder_close(session->arc_peer); |
1373 |
JANUS_LOG(LOG_INFO, "Closed peer's audio recording %s\n", session->arc_peer->filename ? session->arc_peer->filename : "??"); |
1374 |
janus_recorder_free(session->arc_peer); |
1375 |
} |
1376 |
session->arc_peer = NULL;
|
1377 |
if(session->vrc) {
|
1378 |
janus_recorder_close(session->vrc); |
1379 |
JANUS_LOG(LOG_INFO, "Closed user's video recording %s\n", session->vrc->filename ? session->vrc->filename : "??"); |
1380 |
janus_recorder_free(session->vrc); |
1381 |
} |
1382 |
session->vrc = NULL;
|
1383 |
if(session->vrc_peer) {
|
1384 |
janus_recorder_close(session->vrc_peer); |
1385 |
JANUS_LOG(LOG_INFO, "Closed peer's video recording %s\n", session->vrc_peer->filename ? session->vrc_peer->filename : "??"); |
1386 |
janus_recorder_free(session->vrc_peer); |
1387 |
} |
1388 |
session->vrc_peer = NULL;
|
1389 |
janus_mutex_unlock(&session->rec_mutex); |
1390 |
} |
1391 |
|
1392 |
/* Thread to handle incoming messages */
|
1393 |
static void *janus_sipre_handler(void *data) { |
1394 |
JANUS_LOG(LOG_VERB, "Joining SIPre handler thread\n");
|
1395 |
janus_sipre_message *msg = NULL;
|
1396 |
int error_code = 0; |
1397 |
char error_cause[512]; |
1398 |
json_t *root = NULL;
|
1399 |
while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
|
1400 |
msg = g_async_queue_pop(messages); |
1401 |
if(msg == NULL) |
1402 |
continue;
|
1403 |
if(msg == &exit_message)
|
1404 |
break;
|
1405 |
if(msg->handle == NULL) { |
1406 |
janus_sipre_message_free(msg); |
1407 |
continue;
|
1408 |
} |
1409 |
janus_sipre_session *session = NULL;
|
1410 |
janus_mutex_lock(&sessions_mutex); |
1411 |
if(g_hash_table_lookup(sessions, msg->handle) != NULL ) { |
1412 |
session = (janus_sipre_session *)msg->handle->plugin_handle; |
1413 |
} |
1414 |
janus_mutex_unlock(&sessions_mutex); |
1415 |
if(!session) {
|
1416 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
1417 |
janus_sipre_message_free(msg); |
1418 |
continue;
|
1419 |
} |
1420 |
if(session->destroyed) {
|
1421 |
janus_sipre_message_free(msg); |
1422 |
continue;
|
1423 |
} |
1424 |
/* Handle request */
|
1425 |
error_code = 0;
|
1426 |
root = msg->message; |
1427 |
if(msg->message == NULL) { |
1428 |
JANUS_LOG(LOG_ERR, "No message??\n");
|
1429 |
error_code = JANUS_SIPRE_ERROR_NO_MESSAGE; |
1430 |
g_snprintf(error_cause, 512, "%s", "No message??"); |
1431 |
goto error;
|
1432 |
} |
1433 |
if(!json_is_object(root)) {
|
1434 |
JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
|
1435 |
error_code = JANUS_SIPRE_ERROR_INVALID_JSON; |
1436 |
g_snprintf(error_cause, 512, "JSON error: not an object"); |
1437 |
goto error;
|
1438 |
} |
1439 |
JANUS_VALIDATE_JSON_OBJECT(root, request_parameters, |
1440 |
error_code, error_cause, TRUE, |
1441 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
1442 |
if(error_code != 0) |
1443 |
goto error;
|
1444 |
json_t *request = json_object_get(root, "request");
|
1445 |
const char *request_text = json_string_value(request); |
1446 |
json_t *result = NULL;
|
1447 |
|
1448 |
if(!strcasecmp(request_text, "register")) { |
1449 |
JANUS_VALIDATE_JSON_OBJECT(root, register_parameters, |
1450 |
error_code, error_cause, TRUE, |
1451 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
1452 |
if(error_code != 0) |
1453 |
goto error;
|
1454 |
gboolean refresh = json_is_true(json_object_get(root, "refresh"));
|
1455 |
if(session->account.registration_status > janus_sipre_registration_status_unregistered && !refresh) {
|
1456 |
JANUS_LOG(LOG_ERR, "Already registered (%s)\n", session->account.username);
|
1457 |
error_code = JANUS_SIPRE_ERROR_ALREADY_REGISTERED; |
1458 |
g_snprintf(error_cause, 512, "Already registered (%s)", session->account.username); |
1459 |
goto error;
|
1460 |
} |
1461 |
/* Parse the request */
|
1462 |
gboolean guest = FALSE; |
1463 |
json_t *type = json_object_get(root, "type");
|
1464 |
if(type != NULL) { |
1465 |
const char *type_text = json_string_value(type); |
1466 |
if(!strcmp(type_text, "guest")) { |
1467 |
JANUS_LOG(LOG_INFO, "Registering as a guest\n");
|
1468 |
guest = TRUE; |
1469 |
} else {
|
1470 |
JANUS_LOG(LOG_WARN, "Unknown type '%s', ignoring...\n", type_text);
|
1471 |
} |
1472 |
} |
1473 |
|
1474 |
gboolean send_register = TRUE; |
1475 |
json_t *do_register = json_object_get(root, "send_register");
|
1476 |
if(do_register != NULL) { |
1477 |
if(guest) {
|
1478 |
JANUS_LOG(LOG_ERR, "Conflicting elements: send_register cannot be true if guest is true\n");
|
1479 |
error_code = JANUS_SIPRE_ERROR_INVALID_ELEMENT; |
1480 |
g_snprintf(error_cause, 512, "Conflicting elements: send_register cannot be true if guest is true"); |
1481 |
goto error;
|
1482 |
} |
1483 |
send_register = json_is_true(do_register); |
1484 |
} |
1485 |
|
1486 |
gboolean sips = TRUE; |
1487 |
json_t *do_sipres = json_object_get(root, "sips");
|
1488 |
if(do_sipres != NULL) { |
1489 |
sips = json_is_true(do_sipres); |
1490 |
} |
1491 |
|
1492 |
/* Parse address */
|
1493 |
json_t *proxy = json_object_get(root, "proxy");
|
1494 |
const char *proxy_text = NULL; |
1495 |
if(proxy && !json_is_null(proxy)) {
|
1496 |
/* Has to be validated separately because it could be null */
|
1497 |
JANUS_VALIDATE_JSON_OBJECT(root, proxy_parameters, |
1498 |
error_code, error_cause, TRUE, |
1499 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
1500 |
if(error_code != 0) |
1501 |
goto error;
|
1502 |
proxy_text = json_string_value(proxy); |
1503 |
if(janus_sipre_parse_uri(proxy_text) < 0) { |
1504 |
JANUS_LOG(LOG_ERR, "Invalid proxy address %s\n", proxy_text);
|
1505 |
error_code = JANUS_SIPRE_ERROR_INVALID_ADDRESS; |
1506 |
g_snprintf(error_cause, 512, "Invalid proxy address %s\n", proxy_text); |
1507 |
goto error;
|
1508 |
} |
1509 |
} |
1510 |
json_t *outbound_proxy = json_object_get(root, "outbound_proxy");
|
1511 |
const char *obproxy_text = NULL; |
1512 |
if(outbound_proxy && !json_is_null(outbound_proxy)) {
|
1513 |
/* Has to be validated separately because it could be null */
|
1514 |
JANUS_VALIDATE_JSON_OBJECT(root, proxy_parameters, |
1515 |
error_code, error_cause, TRUE, |
1516 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
1517 |
if(error_code != 0) |
1518 |
goto error;
|
1519 |
obproxy_text = json_string_value(outbound_proxy); |
1520 |
if(janus_sipre_parse_uri(obproxy_text) < 0) { |
1521 |
JANUS_LOG(LOG_ERR, "Invalid outbound_proxy address %s\n", obproxy_text);
|
1522 |
error_code = JANUS_SIPRE_ERROR_INVALID_ADDRESS; |
1523 |
g_snprintf(error_cause, 512, "Invalid outbound_proxy address %s\n", obproxy_text); |
1524 |
goto error;
|
1525 |
} |
1526 |
} |
1527 |
|
1528 |
/* Parse register TTL */
|
1529 |
int ttl = register_ttl;
|
1530 |
json_t *reg_ttl = json_object_get(root, "register_ttl");
|
1531 |
if(reg_ttl && json_is_integer(reg_ttl))
|
1532 |
ttl = json_integer_value(reg_ttl); |
1533 |
if(ttl <= 0) |
1534 |
ttl = register_ttl; |
1535 |
session->stack.expires = ttl; |
1536 |
|
1537 |
/* Parse display name */
|
1538 |
const char* display_name_text = NULL; |
1539 |
json_t *display_name = json_object_get(root, "display_name");
|
1540 |
if(display_name && json_is_string(display_name))
|
1541 |
display_name_text = json_string_value(display_name); |
1542 |
|
1543 |
/* Now the user part (always needed, even for the guest case) */
|
1544 |
json_t *username = json_object_get(root, "username");
|
1545 |
if(!username) {
|
1546 |
/* The username is mandatory even when registering as guests */
|
1547 |
JANUS_LOG(LOG_ERR, "Missing element (username)\n");
|
1548 |
error_code = JANUS_SIPRE_ERROR_MISSING_ELEMENT; |
1549 |
g_snprintf(error_cause, 512, "Missing element (username)"); |
1550 |
goto error;
|
1551 |
} |
1552 |
const char *username_text = NULL; |
1553 |
const char *secret_text = NULL; |
1554 |
const char *authuser_text = NULL; |
1555 |
janus_sipre_secret_type secret_type = janus_sipre_secret_type_plaintext; |
1556 |
char *user_id = NULL, *user_host = NULL; |
1557 |
guint16 user_port = 0;
|
1558 |
/* Parse address */
|
1559 |
username_text = json_string_value(username); |
1560 |
if(janus_sipre_parse_uri(username_text) < 0) { |
1561 |
JANUS_LOG(LOG_ERR, "Invalid user address %s\n", username_text);
|
1562 |
error_code = JANUS_SIPRE_ERROR_INVALID_ADDRESS; |
1563 |
g_snprintf(error_cause, 512, "Invalid user address %s\n", username_text); |
1564 |
goto error;
|
1565 |
} |
1566 |
user_id = janus_sipre_get_uri_username(username_text); |
1567 |
user_host = janus_sipre_get_uri_host(username_text); |
1568 |
user_port = janus_sipre_get_uri_port(username_text); |
1569 |
if(guest) {
|
1570 |
/* Not needed, we can stop here: just say we're registered */
|
1571 |
JANUS_LOG(LOG_INFO, "Guest will have username %s\n", user_id);
|
1572 |
send_register = FALSE; |
1573 |
} else {
|
1574 |
json_t *secret = json_object_get(root, "secret");
|
1575 |
json_t *authuser = json_object_get(root, "authuser");
|
1576 |
if(!secret) {
|
1577 |
g_free(user_id); |
1578 |
g_free(user_host); |
1579 |
JANUS_LOG(LOG_ERR, "Missing element (secret)\n");
|
1580 |
error_code = JANUS_SIPRE_ERROR_MISSING_ELEMENT; |
1581 |
g_snprintf(error_cause, 512, "Missing element (secret)"); |
1582 |
goto error;
|
1583 |
} |
1584 |
secret_text = json_string_value(secret); |
1585 |
secret_type = janus_sipre_secret_type_plaintext; |
1586 |
if(authuser) {
|
1587 |
authuser_text = json_string_value(authuser); |
1588 |
} |
1589 |
/* Got the values, try registering now */
|
1590 |
JANUS_LOG(LOG_VERB, "Registering user %s (auth=%s, secret %s) @ %s through %s (outbound proxy: %s)\n",
|
1591 |
user_id, secret_text, user_host, |
1592 |
authuser_text != NULL ? authuser_text : user_id,
|
1593 |
proxy_text != NULL ? proxy_text : "(null)", |
1594 |
obproxy_text != NULL ? obproxy_text : "none"); |
1595 |
} |
1596 |
|
1597 |
/* If this is a refresh, get rid of the old values */
|
1598 |
if(refresh) {
|
1599 |
/* Cleanup old values */
|
1600 |
if(session->account.identity != NULL) { |
1601 |
g_hash_table_remove(identities, session->account.identity); |
1602 |
g_free(session->account.identity); |
1603 |
} |
1604 |
session->account.identity = NULL;
|
1605 |
session->account.sips = TRUE; |
1606 |
if(session->account.username != NULL) |
1607 |
g_free(session->account.username); |
1608 |
session->account.username = NULL;
|
1609 |
if(session->account.display_name != NULL) |
1610 |
g_free(session->account.display_name); |
1611 |
session->account.display_name = NULL;
|
1612 |
if(session->account.authuser != NULL) |
1613 |
g_free(session->account.authuser); |
1614 |
session->account.authuser = NULL;
|
1615 |
if(session->account.secret != NULL) |
1616 |
g_free(session->account.secret); |
1617 |
session->account.secret = NULL;
|
1618 |
session->account.secret_type = janus_sipre_secret_type_unknown; |
1619 |
if(session->account.proxy != NULL) |
1620 |
g_free(session->account.proxy); |
1621 |
session->account.proxy = NULL;
|
1622 |
if(session->account.outbound_proxy != NULL) |
1623 |
g_free(session->account.outbound_proxy); |
1624 |
session->account.outbound_proxy = NULL;
|
1625 |
session->account.registration_status = janus_sipre_registration_status_unregistered; |
1626 |
} |
1627 |
session->account.identity = g_strdup(username_text); |
1628 |
g_hash_table_insert(identities, session->account.identity, session); |
1629 |
session->account.sips = sips; |
1630 |
session->account.username = g_strdup(user_id); |
1631 |
session->account.authuser = g_strdup(authuser_text ? authuser_text : user_id); |
1632 |
session->account.secret = secret_text ? g_strdup(secret_text) : NULL;
|
1633 |
session->account.secret_type = secret_type; |
1634 |
if(display_name_text) {
|
1635 |
session->account.display_name = g_strdup(display_name_text); |
1636 |
} |
1637 |
if(proxy_text) {
|
1638 |
session->account.proxy = g_strdup(proxy_text); |
1639 |
} else {
|
1640 |
/* Build one from the user's identity */
|
1641 |
char uri[256]; |
1642 |
g_snprintf(uri, sizeof(uri), "sip:%s:%"SCNu16, user_host, (user_port ? user_port : 5060)); |
1643 |
session->account.proxy = g_strdup(uri); |
1644 |
} |
1645 |
if(obproxy_text) {
|
1646 |
session->account.outbound_proxy = g_strdup(obproxy_text); |
1647 |
} |
1648 |
g_free(user_host); |
1649 |
g_free(user_id); |
1650 |
|
1651 |
session->account.registration_status = janus_sipre_registration_status_registering; |
1652 |
if(send_register) {
|
1653 |
/* Check if the INVITE needs to be enriched with custom headers */
|
1654 |
char custom_headers[2048]; |
1655 |
custom_headers[0] = '\0'; |
1656 |
json_t *headers = json_object_get(root, "headers");
|
1657 |
if(headers) {
|
1658 |
if(json_object_size(headers) > 0) { |
1659 |
/* Parse custom headers */
|
1660 |
const char *key = NULL; |
1661 |
json_t *value = NULL;
|
1662 |
void *iter = json_object_iter(headers);
|
1663 |
while(iter != NULL) { |
1664 |
key = json_object_iter_key(iter); |
1665 |
value = json_object_get(headers, key); |
1666 |
if(value == NULL || !json_is_string(value)) { |
1667 |
JANUS_LOG(LOG_WARN, "Skipping header '%s': value is not a string\n", key);
|
1668 |
iter = json_object_iter_next(headers, iter); |
1669 |
continue;
|
1670 |
} |
1671 |
char h[255]; |
1672 |
g_snprintf(h, 255, "%s: %s\r\n", key, json_string_value(value)); |
1673 |
JANUS_LOG(LOG_VERB, "Adding custom header, %s", h);
|
1674 |
g_strlcat(custom_headers, h, 2048);
|
1675 |
iter = json_object_iter_next(headers, iter); |
1676 |
} |
1677 |
} |
1678 |
} |
1679 |
char *data = NULL; |
1680 |
if(strlen(custom_headers))
|
1681 |
data = g_strdup(custom_headers); |
1682 |
/* We enqueue this REGISTER attempt, to be sure it's done in the re_main loop thread
|
1683 |
* FIXME Maybe passing a key to the session is better than passing the session object
|
1684 |
* itself? it may be gone when it gets handled... won't be an issue with the
|
1685 |
* reference counter branch but needs to be taken into account until then */
|
1686 |
mqueue_push(mq, janus_sipre_mqueue_event_do_register, |
1687 |
janus_sipre_mqueue_payload_create(session, NULL, 0, data)); |
1688 |
result = json_object(); |
1689 |
json_object_set_new(result, "event", json_string("registering")); |
1690 |
} else {
|
1691 |
JANUS_LOG(LOG_VERB, "Not sending a SIPre REGISTER: either send_register was set to false or guest mode was enabled\n");
|
1692 |
session->account.registration_status = janus_sipre_registration_status_disabled; |
1693 |
result = json_object(); |
1694 |
json_object_set_new(result, "event", json_string("registered")); |
1695 |
json_object_set_new(result, "username", json_string(session->account.username));
|
1696 |
json_object_set_new(result, "register_sent", json_false());
|
1697 |
/* Also notify event handlers */
|
1698 |
if(notify_events && gateway->events_is_enabled()) {
|
1699 |
json_t *info = json_object(); |
1700 |
json_object_set_new(info, "event", json_string("registered")); |
1701 |
json_object_set_new(info, "identity", json_string(session->account.identity));
|
1702 |
json_object_set_new(info, "type", json_string("guest")); |
1703 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
1704 |
} |
1705 |
} |
1706 |
} else if(!strcasecmp(request_text, "unregister")) { |
1707 |
if(session->account.registration_status < janus_sipre_registration_status_registered) {
|
1708 |
JANUS_LOG(LOG_ERR, "Wrong state (register first)\n");
|
1709 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
1710 |
g_snprintf(error_cause, 512, "Wrong state (register first)"); |
1711 |
goto error;
|
1712 |
} |
1713 |
/* Unregister now */
|
1714 |
session->account.registration_status = janus_sipre_registration_status_unregistering; |
1715 |
session->stack.expires = 0;
|
1716 |
mqueue_push(mq, janus_sipre_mqueue_event_do_unregister, |
1717 |
janus_sipre_mqueue_payload_create(session, NULL, 0, data)); |
1718 |
result = json_object(); |
1719 |
json_object_set_new(result, "event", json_string("unregistering")); |
1720 |
} else if(!strcasecmp(request_text, "call")) { |
1721 |
/* Call another peer */
|
1722 |
if(session->account.registration_status < janus_sipre_registration_status_registered) {
|
1723 |
JANUS_LOG(LOG_ERR, "Wrong state (register first)\n");
|
1724 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
1725 |
g_snprintf(error_cause, 512, "Wrong state (register first)"); |
1726 |
goto error;
|
1727 |
} |
1728 |
if(session->status >= janus_sipre_call_status_inviting) {
|
1729 |
JANUS_LOG(LOG_ERR, "Wrong state (already in a call? status=%s)\n", janus_sipre_call_status_string(session->status));
|
1730 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
1731 |
g_snprintf(error_cause, 512, "Wrong state (already in a call? status=%s)", janus_sipre_call_status_string(session->status)); |
1732 |
goto error;
|
1733 |
} |
1734 |
JANUS_VALIDATE_JSON_OBJECT(root, call_parameters, |
1735 |
error_code, error_cause, TRUE, |
1736 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
1737 |
if(error_code != 0) |
1738 |
goto error;
|
1739 |
json_t *uri = json_object_get(root, "uri");
|
1740 |
/* Check if we need to ACK manually (e.g., for the Record-Route hack) */
|
1741 |
json_t *autoack = json_object_get(root, "autoack");
|
1742 |
gboolean do_autoack = autoack ? json_is_true(autoack) : TRUE; |
1743 |
/* Check if the INVITE needs to be enriched with custom headers */
|
1744 |
char custom_headers[2048]; |
1745 |
custom_headers[0] = '\0'; |
1746 |
json_t *headers = json_object_get(root, "headers");
|
1747 |
if(headers) {
|
1748 |
if(json_object_size(headers) > 0) { |
1749 |
/* Parse custom headers */
|
1750 |
const char *key = NULL; |
1751 |
json_t *value = NULL;
|
1752 |
void *iter = json_object_iter(headers);
|
1753 |
while(iter != NULL) { |
1754 |
key = json_object_iter_key(iter); |
1755 |
value = json_object_get(headers, key); |
1756 |
if(value == NULL || !json_is_string(value)) { |
1757 |
JANUS_LOG(LOG_WARN, "Skipping header '%s': value is not a string\n", key);
|
1758 |
iter = json_object_iter_next(headers, iter); |
1759 |
continue;
|
1760 |
} |
1761 |
char h[255]; |
1762 |
g_snprintf(h, 255, "%s: %s\r\n", key, json_string_value(value)); |
1763 |
JANUS_LOG(LOG_VERB, "Adding custom header, %s", h);
|
1764 |
g_strlcat(custom_headers, h, 2048);
|
1765 |
iter = json_object_iter_next(headers, iter); |
1766 |
} |
1767 |
} |
1768 |
} |
1769 |
/* SDES-SRTP is disabled by default, let's see if we need to enable it */
|
1770 |
gboolean offer_srtp = FALSE, require_srtp = FALSE; |
1771 |
json_t *srtp = json_object_get(root, "srtp");
|
1772 |
if(srtp) {
|
1773 |
const char *srtp_text = json_string_value(srtp); |
1774 |
if(!strcasecmp(srtp_text, "sdes_optional")) { |
1775 |
/* Negotiate SDES, but make it optional */
|
1776 |
offer_srtp = TRUE; |
1777 |
} else if(!strcasecmp(srtp_text, "sdes_mandatory")) { |
1778 |
/* Negotiate SDES, and require it */
|
1779 |
offer_srtp = TRUE; |
1780 |
require_srtp = TRUE; |
1781 |
} else {
|
1782 |
JANUS_LOG(LOG_ERR, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)\n");
|
1783 |
error_code = JANUS_SIPRE_ERROR_INVALID_ELEMENT; |
1784 |
g_snprintf(error_cause, 512, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)"); |
1785 |
goto error;
|
1786 |
} |
1787 |
} |
1788 |
/* Parse address */
|
1789 |
const char *uri_text = json_string_value(uri); |
1790 |
if(janus_sipre_parse_uri(uri_text) < 0) { |
1791 |
JANUS_LOG(LOG_ERR, "Invalid user address %s\n", uri_text);
|
1792 |
error_code = JANUS_SIPRE_ERROR_INVALID_ADDRESS; |
1793 |
g_snprintf(error_cause, 512, "Invalid user address %s\n", uri_text); |
1794 |
goto error;
|
1795 |
} |
1796 |
/* Any SDP to handle? if not, something's wrong */
|
1797 |
const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type")); |
1798 |
const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp")); |
1799 |
if(!msg_sdp) {
|
1800 |
JANUS_LOG(LOG_ERR, "Missing SDP\n");
|
1801 |
error_code = JANUS_SIPRE_ERROR_MISSING_SDP; |
1802 |
g_snprintf(error_cause, 512, "Missing SDP"); |
1803 |
goto error;
|
1804 |
} |
1805 |
if(strstr(msg_sdp, "m=application")) { |
1806 |
JANUS_LOG(LOG_ERR, "The SIPre plugin does not support DataChannels\n");
|
1807 |
error_code = JANUS_SIPRE_ERROR_MISSING_SDP; |
1808 |
g_snprintf(error_cause, 512, "The SIPre plugin does not support DataChannels"); |
1809 |
goto error;
|
1810 |
} |
1811 |
JANUS_LOG(LOG_VERB, "%s is calling %s\n", session->account.username, uri_text);
|
1812 |
JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
|
1813 |
/* Clean up SRTP stuff from before first, in case it's still needed */
|
1814 |
janus_sipre_srtp_cleanup(session); |
1815 |
session->media.require_srtp = require_srtp; |
1816 |
session->media.has_srtp_local = offer_srtp; |
1817 |
if(offer_srtp) {
|
1818 |
JANUS_LOG(LOG_VERB, "Going to negotiate SDES-SRTP (%s)...\n", require_srtp ? "mandatory" : "optional"); |
1819 |
} |
1820 |
/* Parse the SDP we got, manipulate some things, and generate a new one */
|
1821 |
char sdperror[100]; |
1822 |
janus_sdp *parsed_sdp = janus_sdp_parse(msg_sdp, sdperror, sizeof(sdperror));
|
1823 |
if(!parsed_sdp) {
|
1824 |
JANUS_LOG(LOG_ERR, "Error parsing SDP: %s\n", sdperror);
|
1825 |
error_code = JANUS_SIPRE_ERROR_MISSING_SDP; |
1826 |
g_snprintf(error_cause, 512, "Error parsing SDP: %s", sdperror); |
1827 |
goto error;
|
1828 |
} |
1829 |
/* Allocate RTP ports and merge them with the anonymized SDP */
|
1830 |
if(strstr(msg_sdp, "m=audio") && !strstr(msg_sdp, "m=audio 0")) { |
1831 |
JANUS_LOG(LOG_VERB, "Going to negotiate audio...\n");
|
1832 |
session->media.has_audio = TRUE; /* FIXME Maybe we need a better way to signal this */
|
1833 |
} |
1834 |
if(strstr(msg_sdp, "m=video") && !strstr(msg_sdp, "m=video 0")) { |
1835 |
JANUS_LOG(LOG_VERB, "Going to negotiate video...\n");
|
1836 |
session->media.has_video = TRUE; /* FIXME Maybe we need a better way to signal this */
|
1837 |
} |
1838 |
if(janus_sipre_allocate_local_ports(session) < 0) { |
1839 |
JANUS_LOG(LOG_ERR, "Could not allocate RTP/RTCP ports\n");
|
1840 |
janus_sdp_free(parsed_sdp); |
1841 |
error_code = JANUS_SIPRE_ERROR_IO_ERROR; |
1842 |
g_snprintf(error_cause, 512, "Could not allocate RTP/RTCP ports"); |
1843 |
goto error;
|
1844 |
} |
1845 |
char *sdp = janus_sipre_sdp_manipulate(session, parsed_sdp, FALSE);
|
1846 |
if(sdp == NULL) { |
1847 |
JANUS_LOG(LOG_ERR, "Could not allocate RTP/RTCP ports\n");
|
1848 |
janus_sdp_free(parsed_sdp); |
1849 |
error_code = JANUS_SIPRE_ERROR_IO_ERROR; |
1850 |
g_snprintf(error_cause, 512, "Could not allocate RTP/RTCP ports"); |
1851 |
goto error;
|
1852 |
} |
1853 |
/* Take note of the SDP (may be useful for UPDATEs or re-INVITEs) */
|
1854 |
janus_sdp_free(session->sdp); |
1855 |
session->sdp = parsed_sdp; |
1856 |
JANUS_LOG(LOG_VERB, "Prepared SDP for INVITE:\n%s", sdp);
|
1857 |
/* Prepare the From header */
|
1858 |
char from_hdr[1024]; |
1859 |
if(session->account.display_name) {
|
1860 |
g_snprintf(from_hdr, sizeof(from_hdr), "\"%s\" <%s>", session->account.display_name, session->account.identity); |
1861 |
} else {
|
1862 |
g_snprintf(from_hdr, sizeof(from_hdr), "%s", session->account.identity); |
1863 |
} |
1864 |
g_atomic_int_set(&session->hangingup, 0);
|
1865 |
session->status = janus_sipre_call_status_inviting; |
1866 |
/* Create a random call-id */
|
1867 |
char callid[24]; |
1868 |
janus_sipre_random_string(24, (char *)&callid); |
1869 |
/* Take note of custom headers, if any */
|
1870 |
char *data = NULL; |
1871 |
if(strlen(custom_headers))
|
1872 |
data = g_strdup(custom_headers); |
1873 |
/* Also notify event handlers */
|
1874 |
if(notify_events && gateway->events_is_enabled()) {
|
1875 |
json_t *info = json_object(); |
1876 |
json_object_set_new(info, "event", json_string("calling")); |
1877 |
json_object_set_new(info, "callee", json_string(uri_text));
|
1878 |
json_object_set_new(info, "call-id", json_string(callid));
|
1879 |
json_object_set_new(info, "sdp", json_string(sdp));
|
1880 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
1881 |
} |
1882 |
/* Enqueue the INVITE */
|
1883 |
session->callee = g_strdup(uri_text); |
1884 |
session->callid = g_strdup(callid); |
1885 |
g_hash_table_insert(callids, session->callid, session); |
1886 |
session->media.autoack = do_autoack; |
1887 |
session->temp_sdp = sdp; |
1888 |
mqueue_push(mq, janus_sipre_mqueue_event_do_call, janus_sipre_mqueue_payload_create(session, NULL, 0, data)); |
1889 |
/* Done for now */
|
1890 |
if(session->transaction)
|
1891 |
g_free(session->transaction); |
1892 |
session->transaction = msg->transaction ? g_strdup(msg->transaction) : NULL;
|
1893 |
/* Send an ack back */
|
1894 |
result = json_object(); |
1895 |
json_object_set_new(result, "event", json_string("calling")); |
1896 |
} else if(!strcasecmp(request_text, "accept")) { |
1897 |
if(session->status != janus_sipre_call_status_invited) {
|
1898 |
JANUS_LOG(LOG_ERR, "Wrong state (not invited? status=%s)\n", janus_sipre_call_status_string(session->status));
|
1899 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
1900 |
g_snprintf(error_cause, 512, "Wrong state (not invited? status=%s)", janus_sipre_call_status_string(session->status)); |
1901 |
goto error;
|
1902 |
} |
1903 |
if(session->callee == NULL) { |
1904 |
JANUS_LOG(LOG_ERR, "Wrong state (no caller?)\n");
|
1905 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
1906 |
g_snprintf(error_cause, 512, "Wrong state (no caller?)"); |
1907 |
goto error;
|
1908 |
} |
1909 |
JANUS_VALIDATE_JSON_OBJECT(root, accept_parameters, |
1910 |
error_code, error_cause, TRUE, |
1911 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
1912 |
if(error_code != 0) |
1913 |
goto error;
|
1914 |
json_t *srtp = json_object_get(root, "srtp");
|
1915 |
gboolean answer_srtp = FALSE; |
1916 |
if(srtp) {
|
1917 |
const char *srtp_text = json_string_value(srtp); |
1918 |
if(!strcasecmp(srtp_text, "sdes_optional")) { |
1919 |
/* Negotiate SDES, but make it optional */
|
1920 |
answer_srtp = TRUE; |
1921 |
} else if(!strcasecmp(srtp_text, "sdes_mandatory")) { |
1922 |
/* Negotiate SDES, and require it */
|
1923 |
answer_srtp = TRUE; |
1924 |
session->media.require_srtp = TRUE; |
1925 |
} else {
|
1926 |
JANUS_LOG(LOG_ERR, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)\n");
|
1927 |
error_code = JANUS_SIPRE_ERROR_INVALID_ELEMENT; |
1928 |
g_snprintf(error_cause, 512, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)"); |
1929 |
goto error;
|
1930 |
} |
1931 |
} |
1932 |
if(session->media.require_srtp && !session->media.has_srtp_remote) {
|
1933 |
JANUS_LOG(LOG_ERR, "Can't accept the call: SDES-SRTP required, but caller didn't offer it\n");
|
1934 |
error_code = JANUS_SIPRE_ERROR_TOO_STRICT; |
1935 |
g_snprintf(error_cause, 512, "Can't accept the call: SDES-SRTP required, but caller didn't offer it"); |
1936 |
goto error;
|
1937 |
} |
1938 |
answer_srtp = answer_srtp || session->media.has_srtp_remote; |
1939 |
/* Any SDP to handle? if not, something's wrong */
|
1940 |
const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type")); |
1941 |
const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp")); |
1942 |
if(!msg_sdp) {
|
1943 |
JANUS_LOG(LOG_ERR, "Missing SDP\n");
|
1944 |
error_code = JANUS_SIPRE_ERROR_MISSING_SDP; |
1945 |
g_snprintf(error_cause, 512, "Missing SDP"); |
1946 |
goto error;
|
1947 |
} |
1948 |
/* Accept a call from another peer */
|
1949 |
JANUS_LOG(LOG_VERB, "We're accepting the call from %s\n", session->callee);
|
1950 |
gboolean answer = !strcasecmp(msg_sdp_type, "answer");
|
1951 |
if(!answer) {
|
1952 |
JANUS_LOG(LOG_VERB, "This is a response to an offerless INVITE\n");
|
1953 |
} |
1954 |
JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
|
1955 |
session->media.has_srtp_local = answer_srtp; |
1956 |
if(answer_srtp) {
|
1957 |
JANUS_LOG(LOG_VERB, "Going to negotiate SDES-SRTP (%s)...\n", session->media.require_srtp ? "mandatory" : "optional"); |
1958 |
} |
1959 |
/* Parse the SDP we got, manipulate some things, and generate a new one */
|
1960 |
char sdperror[100]; |
1961 |
janus_sdp *parsed_sdp = janus_sdp_parse(msg_sdp, sdperror, sizeof(sdperror));
|
1962 |
if(!parsed_sdp) {
|
1963 |
JANUS_LOG(LOG_ERR, "Error parsing SDP: %s\n", sdperror);
|
1964 |
error_code = JANUS_SIPRE_ERROR_MISSING_SDP; |
1965 |
g_snprintf(error_cause, 512, "Error parsing SDP: %s", sdperror); |
1966 |
goto error;
|
1967 |
} |
1968 |
/* Allocate RTP ports and merge them with the anonymized SDP */
|
1969 |
if(strstr(msg_sdp, "m=audio") && !strstr(msg_sdp, "m=audio 0")) { |
1970 |
JANUS_LOG(LOG_VERB, "Going to negotiate audio...\n");
|
1971 |
session->media.has_audio = TRUE; /* FIXME Maybe we need a better way to signal this */
|
1972 |
} |
1973 |
if(strstr(msg_sdp, "m=video") && !strstr(msg_sdp, "m=video 0")) { |
1974 |
JANUS_LOG(LOG_VERB, "Going to negotiate video...\n");
|
1975 |
session->media.has_video = TRUE; /* FIXME Maybe we need a better way to signal this */
|
1976 |
} |
1977 |
if(janus_sipre_allocate_local_ports(session) < 0) { |
1978 |
JANUS_LOG(LOG_ERR, "Could not allocate RTP/RTCP ports\n");
|
1979 |
janus_sdp_free(parsed_sdp); |
1980 |
error_code = JANUS_SIPRE_ERROR_IO_ERROR; |
1981 |
g_snprintf(error_cause, 512, "Could not allocate RTP/RTCP ports"); |
1982 |
goto error;
|
1983 |
} |
1984 |
char *sdp = janus_sipre_sdp_manipulate(session, parsed_sdp, TRUE);
|
1985 |
if(sdp == NULL) { |
1986 |
JANUS_LOG(LOG_ERR, "Could not allocate RTP/RTCP ports\n");
|
1987 |
janus_sdp_free(parsed_sdp); |
1988 |
error_code = JANUS_SIPRE_ERROR_IO_ERROR; |
1989 |
g_snprintf(error_cause, 512, "Could not allocate RTP/RTCP ports"); |
1990 |
goto error;
|
1991 |
} |
1992 |
if(session->media.audio_pt > -1) { |
1993 |
session->media.audio_pt_name = janus_get_codec_from_pt(sdp, session->media.audio_pt); |
1994 |
JANUS_LOG(LOG_VERB, "Detected audio codec: %d (%s)\n", session->media.audio_pt, session->media.audio_pt_name);
|
1995 |
} |
1996 |
if(session->media.video_pt > -1) { |
1997 |
session->media.video_pt_name = janus_get_codec_from_pt(sdp, session->media.video_pt); |
1998 |
JANUS_LOG(LOG_VERB, "Detected video codec: %d (%s)\n", session->media.video_pt, session->media.video_pt_name);
|
1999 |
} |
2000 |
/* Take note of the SDP (may be useful for UPDATEs or re-INVITEs) */
|
2001 |
janus_sdp_free(session->sdp); |
2002 |
session->sdp = parsed_sdp; |
2003 |
JANUS_LOG(LOG_VERB, "Prepared SDP for 200 OK:\n%s", sdp);
|
2004 |
/* Also notify event handlers */
|
2005 |
if(notify_events && gateway->events_is_enabled()) {
|
2006 |
json_t *info = json_object(); |
2007 |
json_object_set_new(info, "event", json_string(answer ? "accepted" : "accepting")); |
2008 |
if(session->callid)
|
2009 |
json_object_set_new(info, "call-id", json_string(session->callid));
|
2010 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
2011 |
} |
2012 |
/* Enqueue the 200 OK */
|
2013 |
if(!answer) {
|
2014 |
if(session->transaction)
|
2015 |
g_free(session->transaction); |
2016 |
session->transaction = msg->transaction ? g_strdup(msg->transaction) : NULL;
|
2017 |
} |
2018 |
g_atomic_int_set(&session->hangingup, 0);
|
2019 |
session->status = janus_sipre_call_status_incall; |
2020 |
session->temp_sdp = sdp; |
2021 |
mqueue_push(mq, janus_sipre_mqueue_event_do_accept, janus_sipre_mqueue_payload_create(session, NULL, 0, NULL)); |
2022 |
/* Send an ack back */
|
2023 |
result = json_object(); |
2024 |
json_object_set_new(result, "event", json_string(answer ? "accepted" : "accepting")); |
2025 |
if(answer) {
|
2026 |
/* Start the media */
|
2027 |
session->media.ready = TRUE; /* FIXME Maybe we need a better way to signal this */
|
2028 |
GError *error = NULL;
|
2029 |
char tname[16]; |
2030 |
g_snprintf(tname, sizeof(tname), "siprertp %s", session->account.username); |
2031 |
g_thread_try_new(tname, janus_sipre_relay_thread, session, &error); |
2032 |
if(error != NULL) { |
2033 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the RTP/RTCP thread...\n", error->code, error->message ? error->message : "??"); |
2034 |
} |
2035 |
} |
2036 |
} else if(!strcasecmp(request_text, "decline")) { |
2037 |
/* Reject an incoming call */
|
2038 |
if(session->status != janus_sipre_call_status_invited) {
|
2039 |
JANUS_LOG(LOG_ERR, "Wrong state (not invited? status=%s)\n", janus_sipre_call_status_string(session->status));
|
2040 |
/* Ignore */
|
2041 |
janus_sipre_message_free(msg); |
2042 |
continue;
|
2043 |
//~ g_snprintf(error_cause, 512, "Wrong state (not in a call?)");
|
2044 |
//~ goto error;
|
2045 |
} |
2046 |
if(session->callee == NULL) { |
2047 |
JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n");
|
2048 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2049 |
g_snprintf(error_cause, 512, "Wrong state (no callee?)"); |
2050 |
goto error;
|
2051 |
} |
2052 |
session->media.earlymedia = FALSE; |
2053 |
session->media.ready = FALSE; |
2054 |
session->media.on_hold = FALSE; |
2055 |
session->status = janus_sipre_call_status_closing; |
2056 |
/* Prepare response */
|
2057 |
int response_code = 486; |
2058 |
json_t *code_json = json_object_get(root, "code");
|
2059 |
if(code_json && json_is_integer(code_json))
|
2060 |
response_code = json_integer_value(code_json); |
2061 |
if(response_code <= 399) { |
2062 |
JANUS_LOG(LOG_WARN, "Invalid SIPre response code specified, using 486 to decline call\n");
|
2063 |
response_code = 486;
|
2064 |
} |
2065 |
/* Enqueue the response */
|
2066 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, |
2067 |
janus_sipre_mqueue_payload_create(session, session->stack.invite, response_code, NULL));
|
2068 |
/* Also notify event handlers */
|
2069 |
if(notify_events && gateway->events_is_enabled()) {
|
2070 |
json_t *info = json_object(); |
2071 |
json_object_set_new(info, "event", json_string("declined")); |
2072 |
json_object_set_new(info, "callee", json_string(session->callee));
|
2073 |
if(session->callid)
|
2074 |
json_object_set_new(info, "call-id", json_string(session->callid));
|
2075 |
json_object_set_new(info, "code", json_integer(response_code));
|
2076 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
2077 |
} |
2078 |
/* Notify the operation */
|
2079 |
result = json_object(); |
2080 |
json_object_set_new(result, "event", json_string("declining")); |
2081 |
json_object_set_new(result, "code", json_integer(response_code));
|
2082 |
} else if(!strcasecmp(request_text, "hold") || !strcasecmp(request_text, "unhold")) { |
2083 |
/* We either need to put the call on-hold, or resume it */
|
2084 |
if(!(session->status == janus_sipre_call_status_inviting || session->status == janus_sipre_call_status_incall)) {
|
2085 |
JANUS_LOG(LOG_ERR, "Wrong state (not in a call? status=%s)\n", janus_sipre_call_status_string(session->status));
|
2086 |
/* Ignore */
|
2087 |
janus_sipre_message_free(msg); |
2088 |
continue;
|
2089 |
//~ g_snprintf(error_cause, 512, "Wrong state (not in a call?)");
|
2090 |
//~ goto error;
|
2091 |
} |
2092 |
if(session->callee == NULL) { |
2093 |
JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n");
|
2094 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2095 |
g_snprintf(error_cause, 512, "Wrong state (no callee?)"); |
2096 |
goto error;
|
2097 |
} |
2098 |
if(session->sdp == NULL) { |
2099 |
JANUS_LOG(LOG_ERR, "Wrong state (no SDP?)\n");
|
2100 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2101 |
g_snprintf(error_cause, 512, "Wrong state (no SDP?)"); |
2102 |
goto error;
|
2103 |
} |
2104 |
gboolean hold = !strcasecmp(request_text, "hold");
|
2105 |
if(hold != session->media.on_hold) {
|
2106 |
/* To put the call on-hold, we need to set the direction to recvonly:
|
2107 |
* resuming it means resuming the direction we had before */
|
2108 |
session->media.on_hold = hold; |
2109 |
janus_sdp_mline *m = janus_sdp_mline_find(session->sdp, JANUS_SDP_AUDIO); |
2110 |
if(m) {
|
2111 |
if(hold) {
|
2112 |
/* Take note of the original media direction */
|
2113 |
session->media.pre_hold_audio_dir = m->direction; |
2114 |
/* Update the media direction */
|
2115 |
switch(m->direction) {
|
2116 |
case JANUS_SDP_DEFAULT:
|
2117 |
case JANUS_SDP_SENDRECV:
|
2118 |
m->direction = JANUS_SDP_SENDONLY; |
2119 |
break;
|
2120 |
default:
|
2121 |
m->direction = JANUS_SDP_INACTIVE; |
2122 |
break;
|
2123 |
} |
2124 |
} else {
|
2125 |
m->direction = session->media.pre_hold_audio_dir; |
2126 |
} |
2127 |
} |
2128 |
m = janus_sdp_mline_find(session->sdp, JANUS_SDP_VIDEO); |
2129 |
if(m) {
|
2130 |
if(hold) {
|
2131 |
/* Take note of the original media direction */
|
2132 |
session->media.pre_hold_video_dir = m->direction; |
2133 |
/* Update the media direction */
|
2134 |
switch(m->direction) {
|
2135 |
case JANUS_SDP_DEFAULT:
|
2136 |
case JANUS_SDP_SENDRECV:
|
2137 |
m->direction = JANUS_SDP_SENDONLY; |
2138 |
break;
|
2139 |
default:
|
2140 |
m->direction = JANUS_SDP_INACTIVE; |
2141 |
break;
|
2142 |
} |
2143 |
} else {
|
2144 |
m->direction = session->media.pre_hold_video_dir; |
2145 |
} |
2146 |
} |
2147 |
/* Send the re-INVITE */
|
2148 |
char *sdp = janus_sdp_write(session->sdp);
|
2149 |
session->temp_sdp = sdp; |
2150 |
mqueue_push(mq, janus_sipre_mqueue_event_do_update, janus_sipre_mqueue_payload_create(session, NULL, 0, data)); |
2151 |
} |
2152 |
/* Send an ack back */
|
2153 |
result = json_object(); |
2154 |
json_object_set_new(result, "event", json_string(hold ? "holding" : "resuming")); |
2155 |
} else if(!strcasecmp(request_text, "hangup")) { |
2156 |
/* Hangup an ongoing call */
|
2157 |
if(!(session->status == janus_sipre_call_status_inviting || session->status == janus_sipre_call_status_incall)) {
|
2158 |
JANUS_LOG(LOG_ERR, "Wrong state (not in a call? status=%s)\n", janus_sipre_call_status_string(session->status));
|
2159 |
/* Ignore */
|
2160 |
janus_sipre_message_free(msg); |
2161 |
continue;
|
2162 |
//~ g_snprintf(error_cause, 512, "Wrong state (not in a call?)");
|
2163 |
//~ goto error;
|
2164 |
} |
2165 |
if(session->callee == NULL) { |
2166 |
JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n");
|
2167 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2168 |
g_snprintf(error_cause, 512, "Wrong state (no callee?)"); |
2169 |
goto error;
|
2170 |
} |
2171 |
session->media.earlymedia = FALSE; |
2172 |
session->media.ready = FALSE; |
2173 |
session->media.on_hold = FALSE; |
2174 |
session->status = janus_sipre_call_status_closing; |
2175 |
/* Enqueue the BYE */
|
2176 |
mqueue_push(mq, janus_sipre_mqueue_event_do_bye, janus_sipre_mqueue_payload_create(session, NULL, 0, NULL)); |
2177 |
/* Notify the operation */
|
2178 |
result = json_object(); |
2179 |
json_object_set_new(result, "event", json_string("hangingup")); |
2180 |
} else if(!strcasecmp(request_text, "recording")) { |
2181 |
/* Start or stop recording */
|
2182 |
if(!(session->status == janus_sipre_call_status_inviting || session->status == janus_sipre_call_status_incall)) {
|
2183 |
JANUS_LOG(LOG_ERR, "Wrong state (not in a call? status=%s)\n", janus_sipre_call_status_string(session->status));
|
2184 |
g_snprintf(error_cause, 512, "Wrong state (not in a call?)"); |
2185 |
goto error;
|
2186 |
} |
2187 |
if(session->callee == NULL) { |
2188 |
JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n");
|
2189 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2190 |
g_snprintf(error_cause, 512, "Wrong state (no callee?)"); |
2191 |
goto error;
|
2192 |
} |
2193 |
JANUS_VALIDATE_JSON_OBJECT(root, recording_parameters, |
2194 |
error_code, error_cause, TRUE, |
2195 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
2196 |
if(error_code != 0) |
2197 |
goto error;
|
2198 |
json_t *action = json_object_get(root, "action");
|
2199 |
const char *action_text = json_string_value(action); |
2200 |
if(strcasecmp(action_text, "start") && strcasecmp(action_text, "stop")) { |
2201 |
JANUS_LOG(LOG_ERR, "Invalid action (should be start|stop)\n");
|
2202 |
error_code = JANUS_SIPRE_ERROR_INVALID_ELEMENT; |
2203 |
g_snprintf(error_cause, 512, "Invalid action (should be start|stop)"); |
2204 |
goto error;
|
2205 |
} |
2206 |
gboolean record_audio = FALSE, record_video = FALSE, /* No media is recorded by default */
|
2207 |
record_peer_audio = FALSE, record_peer_video = FALSE; |
2208 |
json_t *audio = json_object_get(root, "audio");
|
2209 |
record_audio = audio ? json_is_true(audio) : FALSE; |
2210 |
json_t *video = json_object_get(root, "video");
|
2211 |
record_video = video ? json_is_true(video) : FALSE; |
2212 |
json_t *peer_audio = json_object_get(root, "peer_audio");
|
2213 |
record_peer_audio = peer_audio ? json_is_true(peer_audio) : FALSE; |
2214 |
json_t *peer_video = json_object_get(root, "peer_video");
|
2215 |
record_peer_video = peer_video ? json_is_true(peer_video) : FALSE; |
2216 |
if(!record_audio && !record_video && !record_peer_audio && !record_peer_video) {
|
2217 |
JANUS_LOG(LOG_ERR, "Invalid request (at least one of audio, video, peer_audio and peer_video should be true)\n");
|
2218 |
error_code = JANUS_SIPRE_ERROR_RECORDING_ERROR; |
2219 |
g_snprintf(error_cause, 512, "Invalid request (at least one of audio, video, peer_audio and peer_video should be true)"); |
2220 |
goto error;
|
2221 |
} |
2222 |
json_t *recfile = json_object_get(root, "filename");
|
2223 |
const char *recording_base = json_string_value(recfile); |
2224 |
janus_mutex_lock(&session->rec_mutex); |
2225 |
if(!strcasecmp(action_text, "start")) { |
2226 |
/* Start recording something */
|
2227 |
char filename[255]; |
2228 |
gint64 now = janus_get_real_time(); |
2229 |
if(record_peer_audio || record_peer_video) {
|
2230 |
JANUS_LOG(LOG_INFO, "Starting recording of peer's %s (user %s, call %s)\n",
|
2231 |
(record_peer_audio && record_peer_video ? "audio and video" : (record_peer_audio ? "audio" : "video")), |
2232 |
session->account.username, session->transaction); |
2233 |
/* Start recording this peer's audio and/or video */
|
2234 |
if(record_peer_audio) {
|
2235 |
memset(filename, 0, 255); |
2236 |
if(recording_base) {
|
2237 |
/* Use the filename and path we have been provided */
|
2238 |
g_snprintf(filename, 255, "%s-peer-audio", recording_base); |
2239 |
/* FIXME This only works if offer/answer happened */
|
2240 |
session->arc_peer = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
2241 |
if(session->arc_peer == NULL) { |
2242 |
/* FIXME We should notify the fact the recorder could not be created */
|
2243 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
2244 |
} |
2245 |
} else {
|
2246 |
/* Build a filename */
|
2247 |
g_snprintf(filename, 255, "sip-%s-%s-%"SCNi64"-peer-audio", |
2248 |
session->account.username ? session->account.username : "unknown",
|
2249 |
session->transaction ? session->transaction : "unknown",
|
2250 |
now); |
2251 |
/* FIXME This only works if offer/answer happened */
|
2252 |
session->arc_peer = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
2253 |
if(session->arc_peer == NULL) { |
2254 |
/* FIXME We should notify the fact the recorder could not be created */
|
2255 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
2256 |
} |
2257 |
} |
2258 |
} |
2259 |
if(record_peer_video) {
|
2260 |
memset(filename, 0, 255); |
2261 |
if(recording_base) {
|
2262 |
/* Use the filename and path we have been provided */
|
2263 |
g_snprintf(filename, 255, "%s-peer-video", recording_base); |
2264 |
/* FIXME This only works if offer/answer happened */
|
2265 |
session->vrc_peer = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
2266 |
if(session->vrc_peer == NULL) { |
2267 |
/* FIXME We should notify the fact the recorder could not be created */
|
2268 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this peer!\n");
|
2269 |
} |
2270 |
} else {
|
2271 |
/* Build a filename */
|
2272 |
g_snprintf(filename, 255, "sip-%s-%s-%"SCNi64"-peer-video", |
2273 |
session->account.username ? session->account.username : "unknown",
|
2274 |
session->transaction ? session->transaction : "unknown",
|
2275 |
now); |
2276 |
/* FIXME This only works if offer/answer happened */
|
2277 |
session->vrc_peer = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
2278 |
if(session->vrc_peer == NULL) { |
2279 |
/* FIXME We should notify the fact the recorder could not be created */
|
2280 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this peer!\n");
|
2281 |
} |
2282 |
} |
2283 |
/* TODO We should send a FIR/PLI to this peer... */
|
2284 |
} |
2285 |
} |
2286 |
if(record_audio || record_video) {
|
2287 |
/* Start recording the user's audio and/or video */
|
2288 |
JANUS_LOG(LOG_INFO, "Starting recording of user's %s (user %s, call %s)\n",
|
2289 |
(record_audio && record_video ? "audio and video" : (record_audio ? "audio" : "video")), |
2290 |
session->account.username, session->transaction); |
2291 |
if(record_audio) {
|
2292 |
memset(filename, 0, 255); |
2293 |
if(recording_base) {
|
2294 |
/* Use the filename and path we have been provided */
|
2295 |
g_snprintf(filename, 255, "%s-user-audio", recording_base); |
2296 |
/* FIXME This only works if offer/answer happened */
|
2297 |
session->arc = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
2298 |
if(session->arc == NULL) { |
2299 |
/* FIXME We should notify the fact the recorder could not be created */
|
2300 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
2301 |
} |
2302 |
} else {
|
2303 |
/* Build a filename */
|
2304 |
g_snprintf(filename, 255, "sip-%s-%s-%"SCNi64"-own-audio", |
2305 |
session->account.username ? session->account.username : "unknown",
|
2306 |
session->transaction ? session->transaction : "unknown",
|
2307 |
now); |
2308 |
/* FIXME This only works if offer/answer happened */
|
2309 |
session->arc = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
2310 |
if(session->arc == NULL) { |
2311 |
/* FIXME We should notify the fact the recorder could not be created */
|
2312 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
2313 |
} |
2314 |
} |
2315 |
} |
2316 |
if(record_video) {
|
2317 |
memset(filename, 0, 255); |
2318 |
if(recording_base) {
|
2319 |
/* Use the filename and path we have been provided */
|
2320 |
g_snprintf(filename, 255, "%s-user-video", recording_base); |
2321 |
/* FIXME This only works if offer/answer happened */
|
2322 |
session->vrc = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
2323 |
if(session->vrc == NULL) { |
2324 |
/* FIXME We should notify the fact the recorder could not be created */
|
2325 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this user!\n");
|
2326 |
} |
2327 |
} else {
|
2328 |
/* Build a filename */
|
2329 |
g_snprintf(filename, 255, "sip-%s-%s-%"SCNi64"-own-video", |
2330 |
session->account.username ? session->account.username : "unknown",
|
2331 |
session->transaction ? session->transaction : "unknown",
|
2332 |
now); |
2333 |
/* FIXME This only works if offer/answer happened */
|
2334 |
session->vrc = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
2335 |
if(session->vrc == NULL) { |
2336 |
/* FIXME We should notify the fact the recorder could not be created */
|
2337 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this user!\n");
|
2338 |
} |
2339 |
} |
2340 |
/* Send a PLI */
|
2341 |
JANUS_LOG(LOG_VERB, "Recording video, sending a PLI to kickstart it\n");
|
2342 |
char buf[12]; |
2343 |
memset(buf, 0, 12); |
2344 |
janus_rtcp_pli((char *)&buf, 12); |
2345 |
gateway->relay_rtcp(session->handle, 1, buf, 12); |
2346 |
} |
2347 |
} |
2348 |
} else {
|
2349 |
/* Stop recording something: notice that this never returns an error, even when we were not recording anything */
|
2350 |
if(record_audio) {
|
2351 |
if(session->arc) {
|
2352 |
janus_recorder_close(session->arc); |
2353 |
JANUS_LOG(LOG_INFO, "Closed user's audio recording %s\n", session->arc->filename ? session->arc->filename : "??"); |
2354 |
janus_recorder_free(session->arc); |
2355 |
} |
2356 |
session->arc = NULL;
|
2357 |
} |
2358 |
if(record_video) {
|
2359 |
if(session->vrc) {
|
2360 |
janus_recorder_close(session->vrc); |
2361 |
JANUS_LOG(LOG_INFO, "Closed user's video recording %s\n", session->vrc->filename ? session->vrc->filename : "??"); |
2362 |
janus_recorder_free(session->vrc); |
2363 |
} |
2364 |
session->vrc = NULL;
|
2365 |
} |
2366 |
if(record_peer_audio) {
|
2367 |
if(session->arc_peer) {
|
2368 |
janus_recorder_close(session->arc_peer); |
2369 |
JANUS_LOG(LOG_INFO, "Closed peer's audio recording %s\n", session->arc_peer->filename ? session->arc_peer->filename : "??"); |
2370 |
janus_recorder_free(session->arc_peer); |
2371 |
} |
2372 |
session->arc_peer = NULL;
|
2373 |
} |
2374 |
if(record_peer_video) {
|
2375 |
if(session->vrc_peer) {
|
2376 |
janus_recorder_close(session->vrc_peer); |
2377 |
JANUS_LOG(LOG_INFO, "Closed peer's video recording %s\n", session->vrc_peer->filename ? session->vrc_peer->filename : "??"); |
2378 |
janus_recorder_free(session->vrc_peer); |
2379 |
} |
2380 |
session->vrc_peer = NULL;
|
2381 |
} |
2382 |
} |
2383 |
janus_mutex_unlock(&session->rec_mutex); |
2384 |
/* Notify the result */
|
2385 |
result = json_object(); |
2386 |
json_object_set_new(result, "event", json_string("recordingupdated")); |
2387 |
} else if(!strcasecmp(request_text, "info")) { |
2388 |
/* Send a SIP INFO request: we'll need the payload type and content */
|
2389 |
if(!(session->status == janus_sipre_call_status_inviting || session->status == janus_sipre_call_status_incall)) {
|
2390 |
JANUS_LOG(LOG_ERR, "Wrong state (not in a call? status=%s)\n", janus_sipre_call_status_string(session->status));
|
2391 |
g_snprintf(error_cause, 512, "Wrong state (not in a call?)"); |
2392 |
goto error;
|
2393 |
} |
2394 |
if(session->callee == NULL) { |
2395 |
JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n");
|
2396 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2397 |
g_snprintf(error_cause, 512, "Wrong state (no callee?)"); |
2398 |
goto error;
|
2399 |
} |
2400 |
JANUS_VALIDATE_JSON_OBJECT(root, info_parameters, |
2401 |
error_code, error_cause, TRUE, |
2402 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
2403 |
if(error_code != 0) |
2404 |
goto error;
|
2405 |
const char *info_type = json_string_value(json_object_get(root, "type")); |
2406 |
const char *info_content = json_string_value(json_object_get(root, "content")); |
2407 |
json_t *info = json_object(); |
2408 |
json_object_set_new(info, "type", json_string(info_type));
|
2409 |
json_object_set_new(info, "content", json_string(info_content));
|
2410 |
/* Send SIP INFO */
|
2411 |
mqueue_push(mq, janus_sipre_mqueue_event_do_info, |
2412 |
janus_sipre_mqueue_payload_create(session, NULL, 0, info)); |
2413 |
/* Notify the operation */
|
2414 |
result = json_object(); |
2415 |
json_object_set_new(result, "event", json_string("infosent")); |
2416 |
} else if(!strcasecmp(request_text, "message")) { |
2417 |
/* Send a SIP MESSAGE request: we'll only need the content */
|
2418 |
if(!(session->status == janus_sipre_call_status_inviting || session->status == janus_sipre_call_status_incall)) {
|
2419 |
JANUS_LOG(LOG_ERR, "Wrong state (not in a call? status=%s)\n", janus_sipre_call_status_string(session->status));
|
2420 |
g_snprintf(error_cause, 512, "Wrong state (not in a call?)"); |
2421 |
goto error;
|
2422 |
} |
2423 |
if(session->callee == NULL) { |
2424 |
JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n");
|
2425 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2426 |
g_snprintf(error_cause, 512, "Wrong state (no callee?)"); |
2427 |
goto error;
|
2428 |
} |
2429 |
JANUS_VALIDATE_JSON_OBJECT(root, sipmessage_parameters, |
2430 |
error_code, error_cause, TRUE, |
2431 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
2432 |
if(error_code != 0) |
2433 |
goto error;
|
2434 |
const char *msg_content = json_string_value(json_object_get(root, "content")); |
2435 |
/* Send SIP MESSAGE */
|
2436 |
mqueue_push(mq, janus_sipre_mqueue_event_do_message, |
2437 |
janus_sipre_mqueue_payload_create(session, NULL, 0, g_strdup(msg_content))); |
2438 |
/* Notify the operation */
|
2439 |
result = json_object(); |
2440 |
json_object_set_new(result, "event", json_string("messagesent")); |
2441 |
} else if(!strcasecmp(request_text, "dtmf_info")) { |
2442 |
/* Send DMTF tones using SIPre INFO
|
2443 |
* (https://tools.ietf.org/html/draft-kaplan-dispatch-info-dtmf-package-00)
|
2444 |
*/
|
2445 |
if(!(session->status == janus_sipre_call_status_inviting || session->status == janus_sipre_call_status_incall)) {
|
2446 |
JANUS_LOG(LOG_ERR, "Wrong state (not in a call? status=%s)\n", janus_sipre_call_status_string(session->status));
|
2447 |
g_snprintf(error_cause, 512, "Wrong state (not in a call?)"); |
2448 |
goto error;
|
2449 |
} |
2450 |
if(session->callee == NULL) { |
2451 |
JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n");
|
2452 |
error_code = JANUS_SIPRE_ERROR_WRONG_STATE; |
2453 |
g_snprintf(error_cause, 512, "Wrong state (no callee?)"); |
2454 |
goto error;
|
2455 |
} |
2456 |
JANUS_VALIDATE_JSON_OBJECT(root, dtmf_info_parameters, |
2457 |
error_code, error_cause, TRUE, |
2458 |
JANUS_SIPRE_ERROR_MISSING_ELEMENT, JANUS_SIPRE_ERROR_INVALID_ELEMENT); |
2459 |
if(error_code != 0) |
2460 |
goto error;
|
2461 |
json_t *digit = json_object_get(root, "digit");
|
2462 |
const char *digit_text = json_string_value(digit); |
2463 |
if(strlen(digit_text) != 1) { |
2464 |
JANUS_LOG(LOG_ERR, "Invalid element (digit should be one character))\n");
|
2465 |
error_code = JANUS_SIPRE_ERROR_INVALID_ELEMENT; |
2466 |
g_snprintf(error_cause, 512, "Invalid element (digit should be one character)"); |
2467 |
goto error;
|
2468 |
} |
2469 |
int duration_ms = 0; |
2470 |
json_t *duration = json_object_get(root, "duration");
|
2471 |
duration_ms = duration ? json_integer_value(duration) : 0;
|
2472 |
if(duration_ms <= 0 || duration_ms > 5000) { |
2473 |
duration_ms = 160; /* default value */ |
2474 |
} |
2475 |
char payload[64]; |
2476 |
g_snprintf(payload, sizeof(payload), "Signal=%s\r\nDuration=%d", digit_text, duration_ms); |
2477 |
json_t *info = json_object(); |
2478 |
json_object_set_new(info, "type", json_string("application/dtmf-relay")); |
2479 |
json_object_set_new(info, "content", json_string(payload));
|
2480 |
/* Send "application/dtmf-relay" SIP INFO */
|
2481 |
mqueue_push(mq, janus_sipre_mqueue_event_do_info, |
2482 |
janus_sipre_mqueue_payload_create(session, NULL, 0, info)); |
2483 |
/* Notify the result */
|
2484 |
result = json_object(); |
2485 |
json_object_set_new(result, "event", json_string("dtmfsent")); |
2486 |
} else {
|
2487 |
JANUS_LOG(LOG_ERR, "Unknown request (%s)\n", request_text);
|
2488 |
error_code = JANUS_SIPRE_ERROR_INVALID_REQUEST; |
2489 |
g_snprintf(error_cause, 512, "Unknown request (%s)", request_text); |
2490 |
goto error;
|
2491 |
} |
2492 |
|
2493 |
/* Prepare JSON event */
|
2494 |
json_t *event = json_object(); |
2495 |
json_object_set_new(event, "sip", json_string("event")); |
2496 |
if(result != NULL) |
2497 |
json_object_set_new(event, "result", result);
|
2498 |
int ret = gateway->push_event(msg->handle, &janus_sipre_plugin, msg->transaction, event, NULL); |
2499 |
JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
|
2500 |
json_decref(event); |
2501 |
janus_sipre_message_free(msg); |
2502 |
continue;
|
2503 |
|
2504 |
error:
|
2505 |
{ |
2506 |
/* Prepare JSON error event */
|
2507 |
json_t *event = json_object(); |
2508 |
json_object_set_new(event, "sip", json_string("event")); |
2509 |
json_object_set_new(event, "error_code", json_integer(error_code));
|
2510 |
json_object_set_new(event, "error", json_string(error_cause));
|
2511 |
int ret = gateway->push_event(msg->handle, &janus_sipre_plugin, msg->transaction, event, NULL); |
2512 |
JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
|
2513 |
json_decref(event); |
2514 |
janus_sipre_message_free(msg); |
2515 |
} |
2516 |
} |
2517 |
JANUS_LOG(LOG_VERB, "Leaving SIPre handler thread\n");
|
2518 |
return NULL; |
2519 |
} |
2520 |
|
2521 |
|
2522 |
/* Process an incoming SDP */
|
2523 |
void janus_sipre_sdp_process(janus_sipre_session *session, janus_sdp *sdp, gboolean answer, gboolean update, gboolean *changed) {
|
2524 |
if(!session || !sdp)
|
2525 |
return;
|
2526 |
/* c= */
|
2527 |
if(sdp->c_addr) {
|
2528 |
if(update && strcmp(sdp->c_addr, session->media.remote_ip)) {
|
2529 |
/* This is an update and an address changed */
|
2530 |
if(changed)
|
2531 |
*changed = TRUE; |
2532 |
} |
2533 |
g_free(session->media.remote_ip); |
2534 |
session->media.remote_ip = g_strdup(sdp->c_addr); |
2535 |
} |
2536 |
GList *temp = sdp->m_lines; |
2537 |
while(temp) {
|
2538 |
janus_sdp_mline *m = (janus_sdp_mline *)temp->data; |
2539 |
session->media.require_srtp = session->media.require_srtp || (m->proto && !strcasecmp(m->proto, "RTP/SAVP"));
|
2540 |
if(m->type == JANUS_SDP_AUDIO) {
|
2541 |
if(m->port) {
|
2542 |
if(m->port != session->media.remote_audio_rtp_port) {
|
2543 |
/* This is an update and an address changed */
|
2544 |
if(changed)
|
2545 |
*changed = TRUE; |
2546 |
} |
2547 |
session->media.has_audio = TRUE; |
2548 |
session->media.remote_audio_rtp_port = m->port; |
2549 |
session->media.remote_audio_rtcp_port = m->port+1; /* FIXME We're assuming RTCP is on the next port */ |
2550 |
if(m->direction == JANUS_SDP_SENDONLY || m->direction == JANUS_SDP_INACTIVE)
|
2551 |
session->media.audio_send = FALSE; |
2552 |
else
|
2553 |
session->media.audio_send = TRUE; |
2554 |
} else {
|
2555 |
session->media.audio_send = FALSE; |
2556 |
} |
2557 |
} else if(m->type == JANUS_SDP_VIDEO) { |
2558 |
if(m->port) {
|
2559 |
if(m->port != session->media.remote_video_rtp_port) {
|
2560 |
/* This is an update and an address changed */
|
2561 |
if(changed)
|
2562 |
*changed = TRUE; |
2563 |
} |
2564 |
session->media.has_video = TRUE; |
2565 |
session->media.remote_video_rtp_port = m->port; |
2566 |
session->media.remote_video_rtcp_port = m->port+1; /* FIXME We're assuming RTCP is on the next port */ |
2567 |
if(m->direction == JANUS_SDP_SENDONLY || m->direction == JANUS_SDP_INACTIVE)
|
2568 |
session->media.video_send = FALSE; |
2569 |
else
|
2570 |
session->media.video_send = TRUE; |
2571 |
} else {
|
2572 |
session->media.video_send = FALSE; |
2573 |
} |
2574 |
} else {
|
2575 |
JANUS_LOG(LOG_WARN, "Unsupported media line (not audio/video)\n");
|
2576 |
temp = temp->next; |
2577 |
continue;
|
2578 |
} |
2579 |
if(m->c_addr) {
|
2580 |
if(update && strcmp(m->c_addr, session->media.remote_ip)) {
|
2581 |
/* This is an update and an address changed */
|
2582 |
if(changed)
|
2583 |
*changed = TRUE; |
2584 |
} |
2585 |
g_free(session->media.remote_ip); |
2586 |
session->media.remote_ip = g_strdup(m->c_addr); |
2587 |
} |
2588 |
if(update) {
|
2589 |
/* FIXME This is a session update, we only accept changes in IP/ports */
|
2590 |
temp = temp->next; |
2591 |
continue;
|
2592 |
} |
2593 |
GList *tempA = m->attributes; |
2594 |
while(tempA) {
|
2595 |
janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data; |
2596 |
if(a->name) {
|
2597 |
if(!strcasecmp(a->name, "crypto")) { |
2598 |
if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
|
2599 |
gint32 tag = 0;
|
2600 |
int suite;
|
2601 |
char crypto[81]; |
2602 |
/* FIXME inline can be more complex than that, and we're currently only offering SHA1_80 */
|
2603 |
int res = sscanf(a->value, "%"SCNi32" AES_CM_128_HMAC_SHA1_%2d inline:%80s", |
2604 |
&tag, &suite, crypto); |
2605 |
if(res != 3) { |
2606 |
JANUS_LOG(LOG_WARN, "Failed to parse crypto line, ignoring... %s\n", a->value);
|
2607 |
} else {
|
2608 |
gboolean video = (m->type == JANUS_SDP_VIDEO); |
2609 |
int current_suite = video ? session->media.video_srtp_suite_in : session->media.audio_srtp_suite_in;
|
2610 |
if(current_suite == 0) { |
2611 |
if(video)
|
2612 |
session->media.video_srtp_suite_in = suite; |
2613 |
else
|
2614 |
session->media.audio_srtp_suite_in = suite; |
2615 |
janus_sipre_srtp_set_remote(session, video, crypto, suite); |
2616 |
session->media.has_srtp_remote = TRUE; |
2617 |
} else {
|
2618 |
JANUS_LOG(LOG_WARN, "We already configured a %s crypto context (AES_CM_128_HMAC_SHA1_%d), skipping additional crypto line\n",
|
2619 |
video ? "video" : "audio", current_suite); |
2620 |
} |
2621 |
} |
2622 |
} |
2623 |
} |
2624 |
} |
2625 |
tempA = tempA->next; |
2626 |
} |
2627 |
if(answer && (m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO)) {
|
2628 |
/* Check which codec was negotiated eventually */
|
2629 |
int pt = -1; |
2630 |
if(m->ptypes)
|
2631 |
pt = GPOINTER_TO_INT(m->ptypes->data); |
2632 |
if(pt > -1) { |
2633 |
if(m->type == JANUS_SDP_AUDIO) {
|
2634 |
session->media.audio_pt = pt; |
2635 |
} else {
|
2636 |
session->media.video_pt = pt; |
2637 |
} |
2638 |
} |
2639 |
} |
2640 |
temp = temp->next; |
2641 |
} |
2642 |
if(update && changed && *changed) {
|
2643 |
/* Something changed: mark this on the session, so that the thread can update the sockets */
|
2644 |
session->media.updated = TRUE; |
2645 |
if(session->media.pipefd[1] > 0) { |
2646 |
int code = 1; |
2647 |
ssize_t res = 0;
|
2648 |
do {
|
2649 |
res = write(session->media.pipefd[1], &code, sizeof(int)); |
2650 |
} while(res == -1 && errno == EINTR); |
2651 |
} |
2652 |
} |
2653 |
} |
2654 |
|
2655 |
char *janus_sipre_sdp_manipulate(janus_sipre_session *session, janus_sdp *sdp, gboolean answer) {
|
2656 |
if(!session || !sdp)
|
2657 |
return NULL; |
2658 |
/* Start replacing stuff */
|
2659 |
JANUS_LOG(LOG_VERB, "Setting protocol to %s\n", session->media.require_srtp ? "RTP/SAVP" : "RTP/AVP"); |
2660 |
GList *temp = sdp->m_lines; |
2661 |
while(temp) {
|
2662 |
janus_sdp_mline *m = (janus_sdp_mline *)temp->data; |
2663 |
g_free(m->proto); |
2664 |
m->proto = g_strdup(session->media.require_srtp ? "RTP/SAVP" : "RTP/AVP"); |
2665 |
if(m->type == JANUS_SDP_AUDIO) {
|
2666 |
m->port = session->media.local_audio_rtp_port; |
2667 |
if(session->media.has_srtp_local) {
|
2668 |
char *crypto = NULL; |
2669 |
session->media.audio_srtp_suite_out = 80;
|
2670 |
janus_sipre_srtp_set_local(session, FALSE, &crypto); |
2671 |
/* FIXME 32? 80? Both? */
|
2672 |
janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 AES_CM_128_HMAC_SHA1_80 inline:%s", crypto); |
2673 |
g_free(crypto); |
2674 |
m->attributes = g_list_append(m->attributes, a); |
2675 |
} |
2676 |
} else if(m->type == JANUS_SDP_VIDEO) { |
2677 |
m->port = session->media.local_video_rtp_port; |
2678 |
if(session->media.has_srtp_local) {
|
2679 |
char *crypto = NULL; |
2680 |
session->media.audio_srtp_suite_out = 80;
|
2681 |
janus_sipre_srtp_set_local(session, TRUE, &crypto); |
2682 |
/* FIXME 32? 80? Both? */
|
2683 |
janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 AES_CM_128_HMAC_SHA1_80 inline:%s", crypto); |
2684 |
g_free(crypto); |
2685 |
m->attributes = g_list_append(m->attributes, a); |
2686 |
} |
2687 |
} |
2688 |
g_free(m->c_addr); |
2689 |
m->c_addr = g_strdup(local_ip); |
2690 |
if(answer && (m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO)) {
|
2691 |
/* Check which codec was negotiated eventually */
|
2692 |
int pt = -1; |
2693 |
if(m->ptypes)
|
2694 |
pt = GPOINTER_TO_INT(m->ptypes->data); |
2695 |
if(pt > -1) { |
2696 |
if(m->type == JANUS_SDP_AUDIO) {
|
2697 |
session->media.audio_pt = pt; |
2698 |
} else {
|
2699 |
session->media.video_pt = pt; |
2700 |
} |
2701 |
} |
2702 |
} |
2703 |
temp = temp->next; |
2704 |
} |
2705 |
/* Generate a SDP string out of our changes */
|
2706 |
return janus_sdp_write(sdp);
|
2707 |
} |
2708 |
|
2709 |
/* Bind local RTP/RTCP sockets */
|
2710 |
static int janus_sipre_allocate_local_ports(janus_sipre_session *session) { |
2711 |
if(session == NULL) { |
2712 |
JANUS_LOG(LOG_ERR, "Invalid session\n");
|
2713 |
return -1; |
2714 |
} |
2715 |
/* Reset status */
|
2716 |
if(session->media.audio_rtp_fd != -1) { |
2717 |
close(session->media.audio_rtp_fd); |
2718 |
session->media.audio_rtp_fd = -1;
|
2719 |
} |
2720 |
if(session->media.audio_rtcp_fd != -1) { |
2721 |
close(session->media.audio_rtcp_fd); |
2722 |
session->media.audio_rtcp_fd = -1;
|
2723 |
} |
2724 |
session->media.local_audio_rtp_port = 0;
|
2725 |
session->media.local_audio_rtcp_port = 0;
|
2726 |
session->media.audio_ssrc = 0;
|
2727 |
if(session->media.video_rtp_fd != -1) { |
2728 |
close(session->media.video_rtp_fd); |
2729 |
session->media.video_rtp_fd = -1;
|
2730 |
} |
2731 |
if(session->media.video_rtcp_fd != -1) { |
2732 |
close(session->media.video_rtcp_fd); |
2733 |
session->media.video_rtcp_fd = -1;
|
2734 |
} |
2735 |
session->media.local_video_rtp_port = 0;
|
2736 |
session->media.local_video_rtcp_port = 0;
|
2737 |
session->media.video_ssrc = 0;
|
2738 |
if(session->media.pipefd[0] > 0) { |
2739 |
close(session->media.pipefd[0]);
|
2740 |
session->media.pipefd[0] = -1; |
2741 |
} |
2742 |
if(session->media.pipefd[1] > 0) { |
2743 |
close(session->media.pipefd[1]);
|
2744 |
session->media.pipefd[1] = -1; |
2745 |
} |
2746 |
/* Start */
|
2747 |
int attempts = 100; /* FIXME Don't retry forever */ |
2748 |
if(session->media.has_audio) {
|
2749 |
JANUS_LOG(LOG_VERB, "Allocating audio ports:\n");
|
2750 |
struct sockaddr_in audio_rtp_address, audio_rtcp_address;
|
2751 |
while(session->media.local_audio_rtp_port == 0 || session->media.local_audio_rtcp_port == 0) { |
2752 |
if(attempts == 0) /* Too many failures */ |
2753 |
return -1; |
2754 |
if(session->media.audio_rtp_fd == -1) { |
2755 |
session->media.audio_rtp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
2756 |
} |
2757 |
if(session->media.audio_rtcp_fd == -1) { |
2758 |
session->media.audio_rtcp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
2759 |
} |
2760 |
int rtp_port = g_random_int_range(10000, 60000); /* FIXME Should this be configurable? */ |
2761 |
if(rtp_port % 2) |
2762 |
rtp_port++; /* Pick an even port for RTP */
|
2763 |
audio_rtp_address.sin_family = AF_INET; |
2764 |
audio_rtp_address.sin_port = htons(rtp_port); |
2765 |
inet_pton(AF_INET, local_ip, &audio_rtp_address.sin_addr.s_addr); |
2766 |
if(bind(session->media.audio_rtp_fd, (struct sockaddr *)(&audio_rtp_address), sizeof(struct sockaddr)) < 0) { |
2767 |
JANUS_LOG(LOG_ERR, "Bind failed for audio RTP (port %d), trying a different one...\n", rtp_port);
|
2768 |
attempts--; |
2769 |
continue;
|
2770 |
} |
2771 |
JANUS_LOG(LOG_VERB, "Audio RTP listener bound to port %d\n", rtp_port);
|
2772 |
int rtcp_port = rtp_port+1; |
2773 |
audio_rtcp_address.sin_family = AF_INET; |
2774 |
audio_rtcp_address.sin_port = htons(rtcp_port); |
2775 |
inet_pton(AF_INET, local_ip, &audio_rtcp_address.sin_addr.s_addr); |
2776 |
if(bind(session->media.audio_rtcp_fd, (struct sockaddr *)(&audio_rtcp_address), sizeof(struct sockaddr)) < 0) { |
2777 |
JANUS_LOG(LOG_ERR, "Bind failed for audio RTCP (port %d), trying a different one...\n", rtcp_port);
|
2778 |
/* RTP socket is not valid anymore, reset it */
|
2779 |
close(session->media.audio_rtp_fd); |
2780 |
session->media.audio_rtp_fd = -1;
|
2781 |
attempts--; |
2782 |
continue;
|
2783 |
} |
2784 |
JANUS_LOG(LOG_VERB, "Audio RTCP listener bound to port %d\n", rtcp_port);
|
2785 |
session->media.local_audio_rtp_port = rtp_port; |
2786 |
session->media.local_audio_rtcp_port = rtcp_port; |
2787 |
} |
2788 |
} |
2789 |
if(session->media.has_video) {
|
2790 |
JANUS_LOG(LOG_VERB, "Allocating video ports:\n");
|
2791 |
struct sockaddr_in video_rtp_address, video_rtcp_address;
|
2792 |
while(session->media.local_video_rtp_port == 0 || session->media.local_video_rtcp_port == 0) { |
2793 |
if(attempts == 0) /* Too many failures */ |
2794 |
return -1; |
2795 |
if(session->media.video_rtp_fd == -1) { |
2796 |
session->media.video_rtp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
2797 |
} |
2798 |
if(session->media.video_rtcp_fd == -1) { |
2799 |
session->media.video_rtcp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
2800 |
} |
2801 |
int rtp_port = g_random_int_range(10000, 60000); /* FIXME Should this be configurable? */ |
2802 |
if(rtp_port % 2) |
2803 |
rtp_port++; /* Pick an even port for RTP */
|
2804 |
video_rtp_address.sin_family = AF_INET; |
2805 |
video_rtp_address.sin_port = htons(rtp_port); |
2806 |
inet_pton(AF_INET, local_ip, &video_rtp_address.sin_addr.s_addr); |
2807 |
if(bind(session->media.video_rtp_fd, (struct sockaddr *)(&video_rtp_address), sizeof(struct sockaddr)) < 0) { |
2808 |
JANUS_LOG(LOG_ERR, "Bind failed for video RTP (port %d), trying a different one...\n", rtp_port);
|
2809 |
attempts--; |
2810 |
continue;
|
2811 |
} |
2812 |
JANUS_LOG(LOG_VERB, "Video RTP listener bound to port %d\n", rtp_port);
|
2813 |
int rtcp_port = rtp_port+1; |
2814 |
video_rtcp_address.sin_family = AF_INET; |
2815 |
video_rtcp_address.sin_port = htons(rtcp_port); |
2816 |
inet_pton(AF_INET, local_ip, &video_rtcp_address.sin_addr.s_addr); |
2817 |
if(bind(session->media.video_rtcp_fd, (struct sockaddr *)(&video_rtcp_address), sizeof(struct sockaddr)) < 0) { |
2818 |
JANUS_LOG(LOG_ERR, "Bind failed for video RTCP (port %d), trying a different one...\n", rtcp_port);
|
2819 |
/* RTP socket is not valid anymore, reset it */
|
2820 |
close(session->media.video_rtp_fd); |
2821 |
session->media.video_rtp_fd = -1;
|
2822 |
attempts--; |
2823 |
continue;
|
2824 |
} |
2825 |
JANUS_LOG(LOG_VERB, "Video RTCP listener bound to port %d\n", rtcp_port);
|
2826 |
session->media.local_video_rtp_port = rtp_port; |
2827 |
session->media.local_video_rtcp_port = rtcp_port; |
2828 |
} |
2829 |
} |
2830 |
/* We need this to quickly interrupt the poll when it's time to update a session or wrap up */
|
2831 |
pipe(session->media.pipefd); |
2832 |
return 0; |
2833 |
} |
2834 |
|
2835 |
/* Helper method to (re)connect RTP/RTCP sockets */
|
2836 |
static void janus_sipre_connect_sockets(janus_sipre_session *session, struct sockaddr_in *server_addr) { |
2837 |
if(!session || !server_addr)
|
2838 |
return;
|
2839 |
|
2840 |
if(session->media.updated) {
|
2841 |
JANUS_LOG(LOG_VERB, "Updating session sockets\n");
|
2842 |
} |
2843 |
|
2844 |
/* Connect peers (FIXME This pretty much sucks right now) */
|
2845 |
if(session->media.remote_audio_rtp_port) {
|
2846 |
server_addr->sin_port = htons(session->media.remote_audio_rtp_port); |
2847 |
if(connect(session->media.audio_rtp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
2848 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Couldn't connect audio RTP? (%s:%d)\n", session->account.username, session->media.remote_ip, session->media.remote_audio_rtp_port);
|
2849 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno));
|
2850 |
} |
2851 |
} |
2852 |
if(session->media.remote_audio_rtcp_port) {
|
2853 |
server_addr->sin_port = htons(session->media.remote_audio_rtcp_port); |
2854 |
if(connect(session->media.audio_rtcp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
2855 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Couldn't connect audio RTCP? (%s:%d)\n", session->account.username, session->media.remote_ip, session->media.remote_audio_rtcp_port);
|
2856 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno));
|
2857 |
} |
2858 |
} |
2859 |
if(session->media.remote_video_rtp_port) {
|
2860 |
server_addr->sin_port = htons(session->media.remote_video_rtp_port); |
2861 |
if(connect(session->media.video_rtp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
2862 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Couldn't connect video RTP? (%s:%d)\n", session->account.username, session->media.remote_ip, session->media.remote_video_rtp_port);
|
2863 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno));
|
2864 |
} |
2865 |
} |
2866 |
if(session->media.remote_video_rtcp_port) {
|
2867 |
server_addr->sin_port = htons(session->media.remote_video_rtcp_port); |
2868 |
if(connect(session->media.video_rtcp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
2869 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Couldn't connect video RTCP? (%s:%d)\n", session->account.username, session->media.remote_ip, session->media.remote_video_rtcp_port);
|
2870 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno));
|
2871 |
} |
2872 |
} |
2873 |
|
2874 |
} |
2875 |
|
2876 |
/* Thread to relay RTP/RTCP frames coming from the SIPre peer */
|
2877 |
static void *janus_sipre_relay_thread(void *data) { |
2878 |
janus_sipre_session *session = (janus_sipre_session *)data; |
2879 |
if(!session || !session->account.username || !session->callee) {
|
2880 |
g_thread_unref(g_thread_self()); |
2881 |
return NULL; |
2882 |
} |
2883 |
JANUS_LOG(LOG_VERB, "Starting relay thread (%s <--> %s)\n", session->account.username, session->callee);
|
2884 |
|
2885 |
gboolean have_server_ip = TRUE; |
2886 |
struct sockaddr_in server_addr;
|
2887 |
memset(&server_addr, 0, sizeof(server_addr)); |
2888 |
server_addr.sin_family = AF_INET; |
2889 |
if(inet_aton(session->media.remote_ip, &server_addr.sin_addr) == 0) { /* Not a numeric IP... */ |
2890 |
struct hostent *host = gethostbyname(session->media.remote_ip); /* ...resolve name */ |
2891 |
if(!host) {
|
2892 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Couldn't get host (%s)\n", session->account.username, session->media.remote_ip);
|
2893 |
have_server_ip = FALSE; |
2894 |
} else {
|
2895 |
server_addr.sin_addr = *(struct in_addr *)host->h_addr_list;
|
2896 |
} |
2897 |
} |
2898 |
if(have_server_ip)
|
2899 |
janus_sipre_connect_sockets(session, &server_addr); |
2900 |
|
2901 |
if(!session->callee) {
|
2902 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Leaving thread, no callee...\n", session->account.username);
|
2903 |
g_thread_unref(g_thread_self()); |
2904 |
return NULL; |
2905 |
} |
2906 |
/* File descriptors */
|
2907 |
socklen_t addrlen; |
2908 |
struct sockaddr_in remote;
|
2909 |
int resfd = 0, bytes = 0; |
2910 |
struct pollfd fds[5]; |
2911 |
int pipe_fd = session->media.pipefd[0]; |
2912 |
char buffer[1500]; |
2913 |
memset(buffer, 0, 1500); |
2914 |
/* Loop */
|
2915 |
int num = 0; |
2916 |
gboolean goon = TRUE; |
2917 |
int astep = 0, vstep = 0; |
2918 |
guint32 ats = 0, vts = 0; |
2919 |
while(goon && session != NULL && !session->destroyed && |
2920 |
session->status > janus_sipre_call_status_idle && |
2921 |
session->status < janus_sipre_call_status_closing) { /* FIXME We need a per-call watchdog as well */
|
2922 |
|
2923 |
if(session->media.updated) {
|
2924 |
/* Apparently there was a session update */
|
2925 |
if(session->media.remote_ip != NULL && (inet_aton(session->media.remote_ip, &server_addr.sin_addr) != 0)) { |
2926 |
janus_sipre_connect_sockets(session, &server_addr); |
2927 |
} else {
|
2928 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Couldn't update session details (missing or invalid remote IP address)\n", session->account.username);
|
2929 |
} |
2930 |
session->media.updated = FALSE; |
2931 |
} |
2932 |
|
2933 |
/* Prepare poll */
|
2934 |
num = 0;
|
2935 |
if(session->media.audio_rtp_fd != -1) { |
2936 |
fds[num].fd = session->media.audio_rtp_fd; |
2937 |
fds[num].events = POLLIN; |
2938 |
fds[num].revents = 0;
|
2939 |
num++; |
2940 |
} |
2941 |
if(session->media.audio_rtcp_fd != -1) { |
2942 |
fds[num].fd = session->media.audio_rtcp_fd; |
2943 |
fds[num].events = POLLIN; |
2944 |
fds[num].revents = 0;
|
2945 |
num++; |
2946 |
} |
2947 |
if(session->media.video_rtp_fd != -1) { |
2948 |
fds[num].fd = session->media.video_rtp_fd; |
2949 |
fds[num].events = POLLIN; |
2950 |
fds[num].revents = 0;
|
2951 |
num++; |
2952 |
} |
2953 |
if(session->media.video_rtcp_fd != -1) { |
2954 |
fds[num].fd = session->media.video_rtcp_fd; |
2955 |
fds[num].events = POLLIN; |
2956 |
fds[num].revents = 0;
|
2957 |
num++; |
2958 |
} |
2959 |
if(pipe_fd != -1) { |
2960 |
fds[num].fd = pipe_fd; |
2961 |
fds[num].events = POLLIN; |
2962 |
fds[num].revents = 0;
|
2963 |
num++; |
2964 |
} |
2965 |
/* Wait for some data */
|
2966 |
resfd = poll(fds, num, 1000);
|
2967 |
if(resfd < 0) { |
2968 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Error polling...\n", session->account.username);
|
2969 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno));
|
2970 |
break;
|
2971 |
} else if(resfd == 0) { |
2972 |
/* No data, keep going */
|
2973 |
continue;
|
2974 |
} |
2975 |
if(session == NULL || session->destroyed || |
2976 |
session->status <= janus_sipre_call_status_idle || |
2977 |
session->status >= janus_sipre_call_status_closing) |
2978 |
break;
|
2979 |
int i = 0; |
2980 |
for(i=0; i<num; i++) { |
2981 |
if(fds[i].revents & (POLLERR | POLLHUP)) {
|
2982 |
/* If we just updated the session, let's wait until things have calmed down */
|
2983 |
if(session->media.updated)
|
2984 |
break;
|
2985 |
/* Check the socket error */
|
2986 |
int error = 0; |
2987 |
socklen_t errlen = sizeof(error);
|
2988 |
getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen);
|
2989 |
if(error == 0) { |
2990 |
/* Maybe not a breaking error after all? */
|
2991 |
continue;
|
2992 |
} else if(error == 111) { |
2993 |
/* ICMP error? If it's related to RTCP, let's just close the RTCP socket and move on */
|
2994 |
if(fds[i].fd == session->media.audio_rtcp_fd) {
|
2995 |
JANUS_LOG(LOG_WARN, "[SIPre-%s] Got a '%s' on the audio RTCP socket, closing it\n",
|
2996 |
session->account.username, strerror(error)); |
2997 |
close(session->media.audio_rtcp_fd); |
2998 |
session->media.audio_rtcp_fd = -1;
|
2999 |
} else if(fds[i].fd == session->media.video_rtcp_fd) { |
3000 |
JANUS_LOG(LOG_WARN, "[SIPre-%s] Got a '%s' on the video RTCP socket, closing it\n",
|
3001 |
session->account.username, strerror(error)); |
3002 |
close(session->media.video_rtcp_fd); |
3003 |
session->media.video_rtcp_fd = -1;
|
3004 |
} |
3005 |
/* FIXME Should we do the same with the RTP sockets as well? We may risk overreacting, there... */
|
3006 |
continue;
|
3007 |
} |
3008 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] Error polling %d (socket #%d): %s...\n", session->account.username,
|
3009 |
fds[i].fd, i, fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP"); |
3010 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] -- %d (%s)\n", session->account.username, error, strerror(error));
|
3011 |
/* Can we assume it's pretty much over, after a POLLERR? */
|
3012 |
goon = FALSE; |
3013 |
/* FIXME Simulate a "hangup" coming from the browser */
|
3014 |
janus_sipre_message *msg = g_malloc0(sizeof(janus_sipre_message));
|
3015 |
msg->handle = session->handle; |
3016 |
msg->message = json_pack("{ss}", "request", "hangup"); |
3017 |
msg->transaction = NULL;
|
3018 |
msg->jsep = NULL;
|
3019 |
g_async_queue_push(messages, msg); |
3020 |
break;
|
3021 |
} else if(fds[i].revents & POLLIN) { |
3022 |
if(pipe_fd != -1 && fds[i].fd == pipe_fd) { |
3023 |
/* Poll interrupted for a reason, go on */
|
3024 |
int code = 0; |
3025 |
bytes = read(pipe_fd, &code, sizeof(int)); |
3026 |
break;
|
3027 |
} |
3028 |
/* Got an RTP/RTCP packet */
|
3029 |
addrlen = sizeof(remote);
|
3030 |
bytes = recvfrom(fds[i].fd, buffer, 1500, 0, (struct sockaddr*)&remote, &addrlen); |
3031 |
/* Let's check what this is */
|
3032 |
gboolean video = fds[i].fd == session->media.video_rtp_fd || fds[i].fd == session->media.video_rtcp_fd; |
3033 |
gboolean rtcp = fds[i].fd == session->media.audio_rtcp_fd || fds[i].fd == session->media.video_rtcp_fd; |
3034 |
if(!rtcp) {
|
3035 |
/* Audio or Video RTP */
|
3036 |
rtp_header *header = (rtp_header *)buffer; |
3037 |
if((video && session->media.video_ssrc_peer != ntohl(header->ssrc)) ||
|
3038 |
(!video && session->media.audio_ssrc_peer != ntohl(header->ssrc))) { |
3039 |
if(video) {
|
3040 |
session->media.video_ssrc_peer = ntohl(header->ssrc); |
3041 |
} else {
|
3042 |
session->media.audio_ssrc_peer = ntohl(header->ssrc); |
3043 |
} |
3044 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Got SIP peer %s SSRC: %"SCNu32"\n", |
3045 |
session->account.username ? session->account.username : "unknown",
|
3046 |
video ? "video" : "audio", session->media.audio_ssrc_peer); |
3047 |
} |
3048 |
/* Is this SRTP? */
|
3049 |
if(session->media.has_srtp_remote) {
|
3050 |
int buflen = bytes;
|
3051 |
srtp_err_status_t res = srtp_unprotect( |
3052 |
(video ? session->media.video_srtp_in : session->media.audio_srtp_in), |
3053 |
buffer, &buflen); |
3054 |
if(res != srtp_err_status_ok && res != srtp_err_status_replay_fail && res != srtp_err_status_replay_old) {
|
3055 |
guint32 timestamp = ntohl(header->timestamp); |
3056 |
guint16 seq = ntohs(header->seq_number); |
3057 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] %s SRTP unprotect error: %s (len=%d-->%d, ts=%"SCNu32", seq=%"SCNu16")\n", |
3058 |
session->account.username ? session->account.username : "unknown",
|
3059 |
video ? "Video" : "Audio", janus_srtp_error_str(res), bytes, buflen, timestamp, seq); |
3060 |
continue;
|
3061 |
} |
3062 |
bytes = buflen; |
3063 |
} |
3064 |
/* Check if the SSRC changed (e.g., after a re-INVITE or UPDATE) */
|
3065 |
guint32 timestamp = ntohl(header->timestamp); |
3066 |
janus_rtp_header_update(header, &session->media.context, video, |
3067 |
(video ? (vstep ? vstep : 4500) : (astep ? astep : 960))); |
3068 |
if(video) {
|
3069 |
if(vts == 0) { |
3070 |
vts = timestamp; |
3071 |
} else if(vstep == 0) { |
3072 |
vstep = timestamp-vts; |
3073 |
if(vstep < 0) { |
3074 |
vstep = 0;
|
3075 |
} |
3076 |
} |
3077 |
} else {
|
3078 |
if(ats == 0) { |
3079 |
ats = timestamp; |
3080 |
} else if(astep == 0) { |
3081 |
astep = timestamp-ats; |
3082 |
if(astep < 0) { |
3083 |
astep = 0;
|
3084 |
} |
3085 |
} |
3086 |
} |
3087 |
/* Save the frame if we're recording */
|
3088 |
janus_recorder_save_frame(video ? session->vrc_peer : session->arc_peer, buffer, bytes); |
3089 |
/* Relay to browser */
|
3090 |
gateway->relay_rtp(session->handle, video, buffer, bytes); |
3091 |
continue;
|
3092 |
} else {
|
3093 |
/* Audio or Video RTCP */
|
3094 |
if(session->media.has_srtp_remote) {
|
3095 |
int buflen = bytes;
|
3096 |
srtp_err_status_t res = srtp_unprotect_rtcp( |
3097 |
(video ? session->media.video_srtp_in : session->media.audio_srtp_in), |
3098 |
buffer, &buflen); |
3099 |
if(res != srtp_err_status_ok && res != srtp_err_status_replay_fail && res != srtp_err_status_replay_old) {
|
3100 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] %s SRTCP unprotect error: %s (len=%d-->%d)\n",
|
3101 |
session->account.username ? session->account.username : "unknown",
|
3102 |
video ? "Video" : "Audio", janus_srtp_error_str(res), bytes, buflen); |
3103 |
continue;
|
3104 |
} |
3105 |
bytes = buflen; |
3106 |
} |
3107 |
/* Relay to browser */
|
3108 |
gateway->relay_rtcp(session->handle, video, buffer, bytes); |
3109 |
continue;
|
3110 |
} |
3111 |
} |
3112 |
} |
3113 |
} |
3114 |
if(session->media.audio_rtp_fd != -1) { |
3115 |
close(session->media.audio_rtp_fd); |
3116 |
session->media.audio_rtp_fd = -1;
|
3117 |
} |
3118 |
if(session->media.audio_rtcp_fd != -1) { |
3119 |
close(session->media.audio_rtcp_fd); |
3120 |
session->media.audio_rtcp_fd = -1;
|
3121 |
} |
3122 |
session->media.local_audio_rtp_port = 0;
|
3123 |
session->media.local_audio_rtcp_port = 0;
|
3124 |
session->media.audio_ssrc = 0;
|
3125 |
if(session->media.video_rtp_fd != -1) { |
3126 |
close(session->media.video_rtp_fd); |
3127 |
session->media.video_rtp_fd = -1;
|
3128 |
} |
3129 |
if(session->media.video_rtcp_fd != -1) { |
3130 |
close(session->media.video_rtcp_fd); |
3131 |
session->media.video_rtcp_fd = -1;
|
3132 |
} |
3133 |
session->media.local_video_rtp_port = 0;
|
3134 |
session->media.local_video_rtcp_port = 0;
|
3135 |
session->media.video_ssrc = 0;
|
3136 |
if(session->media.pipefd[0] > 0) { |
3137 |
close(session->media.pipefd[0]);
|
3138 |
session->media.pipefd[0] = -1; |
3139 |
} |
3140 |
if(session->media.pipefd[1] > 0) { |
3141 |
close(session->media.pipefd[1]);
|
3142 |
session->media.pipefd[1] = -1; |
3143 |
} |
3144 |
/* Clean up SRTP stuff, if needed */
|
3145 |
janus_sipre_srtp_cleanup(session); |
3146 |
/* Done */
|
3147 |
JANUS_LOG(LOG_VERB, "Leaving SIPre relay thread\n");
|
3148 |
g_thread_unref(g_thread_self()); |
3149 |
return NULL; |
3150 |
} |
3151 |
|
3152 |
|
3153 |
/* libre loop thread */
|
3154 |
gpointer janus_sipre_stack_thread(gpointer user_data) { |
3155 |
JANUS_LOG(LOG_INFO, "Joining libre loop thread...\n");
|
3156 |
|
3157 |
/* Setup libre */
|
3158 |
int err = libre_init();
|
3159 |
if(err) {
|
3160 |
JANUS_LOG(LOG_ERR, "libre_init() failed: %d (%s)\n", err, strerror(err));
|
3161 |
goto done;
|
3162 |
} |
3163 |
/* Initialize this thread as a worker */
|
3164 |
err = re_thread_init(); |
3165 |
if(err != 0) { |
3166 |
printf("re_thread_init failed: %d (%s)\n", err, strerror(err));
|
3167 |
goto done;
|
3168 |
} |
3169 |
err = mqueue_alloc(&mq, janus_sipre_mqueue_handler, NULL);
|
3170 |
if(err) {
|
3171 |
JANUS_LOG(LOG_ERR, "Failed to initialize message queue: %d (%s)\n", err, strerror(err));
|
3172 |
goto done;
|
3173 |
} |
3174 |
g_atomic_int_set(&libre_inited, 1);
|
3175 |
|
3176 |
/* Enter loop */
|
3177 |
err = re_main(NULL);
|
3178 |
if(err != 0) { |
3179 |
JANUS_LOG(LOG_ERR, "re_main() failed: %d (%s)\n", err, strerror(err));
|
3180 |
} |
3181 |
|
3182 |
/* Done here */
|
3183 |
JANUS_LOG(LOG_INFO, "Leaving libre loop thread...\n");
|
3184 |
re_thread_close(); |
3185 |
/* Deinitialize libre */
|
3186 |
libre_close(); |
3187 |
|
3188 |
done:
|
3189 |
g_atomic_int_set(&libre_inited, -1);
|
3190 |
return NULL; |
3191 |
} |
3192 |
|
3193 |
/* Called when challenged for credentials */
|
3194 |
int janus_sipre_cb_auth(char **user, char **pass, const char *realm, void *arg) { |
3195 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3196 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] janus_sipre_cb_auth (realm=%s)\n", session->account.username, realm);
|
3197 |
/* TODO How do we handle hashed secrets? */
|
3198 |
int err = 0; |
3199 |
err |= str_dup(user, session->account.authuser); |
3200 |
err |= str_dup(pass, session->account.secret); |
3201 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- %s / %s\n", session->account.username, *user, *pass);
|
3202 |
return err;
|
3203 |
} |
3204 |
|
3205 |
/* Called when REGISTER responses are received */
|
3206 |
void janus_sipre_cb_register(int err, const struct sip_msg *msg, void *arg) { |
3207 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3208 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] janus_sipre_cb_register\n", session->account.username);
|
3209 |
if(err) {
|
3210 |
JANUS_LOG(LOG_ERR, "[SIPre-%s] REGISTER error: %s\n", session->account.username, strerror(err));
|
3211 |
/* FIXME Should we send an event here? */
|
3212 |
} else {
|
3213 |
const char *event_name = (session->stack.expires > 0 ? "registered" : "unregistered"); |
3214 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] REGISTER reply: %u\n", session->account.username, msg->scode);
|
3215 |
if(msg->scode == 200) { |
3216 |
if(session->stack.expires > 0) { |
3217 |
if(session->account.registration_status < janus_sipre_registration_status_registered)
|
3218 |
session->account.registration_status = janus_sipre_registration_status_registered; |
3219 |
} else {
|
3220 |
session->account.registration_status = janus_sipre_registration_status_unregistered; |
3221 |
} |
3222 |
/* Notify the browser */
|
3223 |
json_t *call = json_object(); |
3224 |
json_object_set_new(call, "sip", json_string("event")); |
3225 |
json_t *calling = json_object(); |
3226 |
json_object_set_new(calling, "event", json_string(event_name));
|
3227 |
json_object_set_new(calling, "username", json_string(session->account.username));
|
3228 |
json_object_set_new(calling, "register_sent", json_true());
|
3229 |
json_object_set_new(call, "result", calling);
|
3230 |
if(!session->destroyed) {
|
3231 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, call, NULL); |
3232 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3233 |
} |
3234 |
json_decref(call); |
3235 |
/* Also notify event handlers */
|
3236 |
if(notify_events && gateway->events_is_enabled()) {
|
3237 |
json_t *info = json_object(); |
3238 |
json_object_set_new(info, "event", json_string(event_name));
|
3239 |
json_object_set_new(info, "identity", json_string(session->account.identity));
|
3240 |
if(session->account.proxy)
|
3241 |
json_object_set_new(info, "proxy", json_string(session->account.proxy));
|
3242 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3243 |
} |
3244 |
} else {
|
3245 |
/* Authentication failed? */
|
3246 |
session->account.registration_status = janus_sipre_registration_status_failed; |
3247 |
mem_deref(session->stack.reg); |
3248 |
session->stack.reg = NULL;
|
3249 |
/* Tell the browser... */
|
3250 |
json_t *event = json_object(); |
3251 |
json_object_set_new(event, "sip", json_string("event")); |
3252 |
json_t *result = json_object(); |
3253 |
json_object_set_new(result, "event", json_string("registration_failed")); |
3254 |
json_object_set_new(result, "code", json_integer(msg->scode));
|
3255 |
char reason[256]; |
3256 |
reason[0] = '\0'; |
3257 |
if(msg->reason.l > 0) { |
3258 |
g_snprintf(reason, (msg->reason.l < 255 ? msg->reason.l+1 : 255), "%s", msg->reason.p); |
3259 |
json_object_set_new(result, "reason", json_string(reason));
|
3260 |
} |
3261 |
json_object_set_new(event, "result", result);
|
3262 |
if(!session->destroyed) {
|
3263 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, event, NULL); |
3264 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3265 |
} |
3266 |
json_decref(event); |
3267 |
/* Also notify event handlers */
|
3268 |
if(notify_events && gateway->events_is_enabled()) {
|
3269 |
json_t *info = json_object(); |
3270 |
json_object_set_new(info, "event", json_string("registration_failed")); |
3271 |
json_object_set_new(info, "code", json_integer(msg->scode));
|
3272 |
if(msg->reason.l > 0) { |
3273 |
json_object_set_new(info, "reason", json_string(reason));
|
3274 |
} |
3275 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3276 |
} |
3277 |
} |
3278 |
} |
3279 |
} |
3280 |
|
3281 |
/* Called when SIP progress (e.g., 180 Ringing or 183 Session Progress) responses are received */
|
3282 |
void janus_sipre_cb_progress(const struct sip_msg *msg, void *arg) { |
3283 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3284 |
char reason[256]; |
3285 |
reason[0] = '\0'; |
3286 |
if(msg->reason.l > 0) { |
3287 |
g_snprintf(reason, (msg->reason.l < 255 ? msg->reason.l+1 : 255), "%s", msg->reason.p); |
3288 |
} |
3289 |
/* Not ready yet, either notify the user (e.g., "ringing") or handle early media (if it's a 183) */
|
3290 |
JANUS_LOG(LOG_WARN, "[SIPre-%s] Session progress: %u %s\n", session->account.username, msg->scode, reason);
|
3291 |
if(msg->scode == 180) { |
3292 |
/* Ringing, notify the application */
|
3293 |
json_t *ringing = json_object(); |
3294 |
json_object_set_new(ringing, "sip", json_string("event")); |
3295 |
json_t *result = json_object(); |
3296 |
json_object_set_new(result, "event", json_string("ringing")); |
3297 |
json_object_set_new(ringing, "result", result);
|
3298 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, ringing, NULL); |
3299 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3300 |
json_decref(ringing); |
3301 |
} else if(msg->scode == 183) { |
3302 |
/* If's a Session Progress: check if there's an SDP, and if so, treat it like a 200 */
|
3303 |
(void)janus_sipre_cb_answer(msg, arg);
|
3304 |
} |
3305 |
} |
3306 |
|
3307 |
/* Called upon incoming INVITEs */
|
3308 |
void janus_sipre_cb_incoming(const struct sip_msg *msg, void *arg) { |
3309 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3310 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] janus_sipre_cb_incoming (%p)\n", session->account.username, session);
|
3311 |
/* Increase the reference to the msg instance, as we'll need it either
|
3312 |
* to reply with an error right away, or for a success/error later */
|
3313 |
mem_ref((struct sip_msg *)msg);
|
3314 |
/* Parse a few relevant identifiers */
|
3315 |
char *from = NULL; |
3316 |
re_sdprintf(&from, "%H", uri_encode, &msg->from.uri);
|
3317 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- Caller: %s\n", session->account.username, from);
|
3318 |
char dname[256]; |
3319 |
dname[0] = '\0'; |
3320 |
if(msg->from.dname.l > 0) { |
3321 |
g_snprintf(dname, sizeof(dname), "%.*s", (int)msg->from.dname.l, msg->from.dname.p); |
3322 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- Display: %s\n", session->account.username, dname);
|
3323 |
} |
3324 |
char callid[256]; |
3325 |
g_snprintf(callid, sizeof(callid), "%.*s", (int)msg->callid.l, msg->callid.p); |
3326 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- Call-ID: %s\n", session->account.username, callid);
|
3327 |
/* Make sure we're not in a call already */
|
3328 |
if(session->stack.sess != NULL) { |
3329 |
/* Already in a call */
|
3330 |
JANUS_LOG(LOG_VERB, "Already in a call (busy, status=%s)\n", janus_sipre_call_status_string(session->status));
|
3331 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 486, session));
|
3332 |
/* Notify the web app about the missed invite */
|
3333 |
json_t *missed = json_object(); |
3334 |
json_object_set_new(missed, "sip", json_string("event")); |
3335 |
json_t *result = json_object(); |
3336 |
json_object_set_new(result, "event", json_string("missed_call")); |
3337 |
json_object_set_new(result, "caller", json_string(from));
|
3338 |
if(strlen(dname)) {
|
3339 |
json_object_set_new(result, "displayname", json_string(dname));
|
3340 |
} |
3341 |
json_object_set_new(missed, "result", result);
|
3342 |
if(!session->destroyed) {
|
3343 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, missed, NULL); |
3344 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3345 |
} |
3346 |
json_decref(missed); |
3347 |
/* Also notify event handlers */
|
3348 |
if(notify_events && gateway->events_is_enabled()) {
|
3349 |
json_t *info = json_object(); |
3350 |
json_object_set_new(info, "event", json_string("missed_call")); |
3351 |
json_object_set_new(info, "caller", json_string(from));
|
3352 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3353 |
} |
3354 |
return;
|
3355 |
} |
3356 |
/* New incoming call, check if there's an SDP to process */
|
3357 |
char sdp_offer[1024]; |
3358 |
janus_sdp *sdp = NULL;
|
3359 |
const char *offer = (const char *)mbuf_buf(msg->mb); |
3360 |
if(offer == NULL || mbuf_get_left(msg->mb) == 0) { |
3361 |
JANUS_LOG(LOG_WARN, "[SIPre-%s] Received offerless INVITE\n", session->account.username);
|
3362 |
} else {
|
3363 |
g_snprintf(sdp_offer, sizeof(sdp_offer), "%.*s", (int)mbuf_get_left(msg->mb), offer); |
3364 |
JANUS_LOG(LOG_WARN, "[SIPre-%s] -- Offer: %s\n", session->account.username, sdp_offer);
|
3365 |
/* Parse the remote SDP */
|
3366 |
char sdperror[100]; |
3367 |
sdp = janus_sdp_parse(sdp_offer, sdperror, sizeof(sdperror));
|
3368 |
if(!sdp) {
|
3369 |
JANUS_LOG(LOG_ERR, "Error parsing SDP! %s\n", sdperror);
|
3370 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 488, NULL)); |
3371 |
return;
|
3372 |
} |
3373 |
} |
3374 |
session->callee = g_strdup(from); |
3375 |
session->callid = g_strdup(callid); |
3376 |
g_hash_table_insert(callids, session->callid, session); |
3377 |
session->status = janus_sipre_call_status_invited; |
3378 |
/* Clean up SRTP stuff from before first, in case it's still needed */
|
3379 |
janus_sipre_srtp_cleanup(session); |
3380 |
/* Parse SDP */
|
3381 |
JANUS_LOG(LOG_VERB, "Someone is inviting us a call\n");
|
3382 |
if(sdp) {
|
3383 |
gboolean changed = FALSE; |
3384 |
janus_sipre_sdp_process(session, sdp, FALSE, FALSE, &changed); |
3385 |
/* Check if offer has neither audio nor video, fail with 488 */
|
3386 |
if(!session->media.has_audio && !session->media.has_video) {
|
3387 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 488, NULL)); |
3388 |
janus_sdp_free(sdp); |
3389 |
return;
|
3390 |
} |
3391 |
/* Also fail with 488 if there's no remote IP address that can be used for RTP */
|
3392 |
if(!session->media.remote_ip) {
|
3393 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 488, NULL)); |
3394 |
janus_sdp_free(sdp); |
3395 |
return;
|
3396 |
} |
3397 |
} |
3398 |
session->stack.invite = msg; |
3399 |
/* Notify the browser about the call */
|
3400 |
json_t *jsep = NULL;
|
3401 |
if(sdp)
|
3402 |
jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp_offer); |
3403 |
json_t *call = json_object(); |
3404 |
json_object_set_new(call, "sip", json_string("event")); |
3405 |
json_t *calling = json_object(); |
3406 |
json_object_set_new(calling, "event", json_string("incomingcall")); |
3407 |
json_object_set_new(calling, "username", json_string(session->callee));
|
3408 |
if(strlen(dname)) {
|
3409 |
json_object_set_new(calling, "displayname", json_string(dname));
|
3410 |
} |
3411 |
if(sdp && session->media.has_srtp_remote) {
|
3412 |
/* FIXME Maybe a true/false instead? */
|
3413 |
json_object_set_new(calling, "srtp", json_string(session->media.require_srtp ? "sdes_mandatory" : "sdes_optional")); |
3414 |
} |
3415 |
json_object_set_new(call, "result", calling);
|
3416 |
if(!session->destroyed) {
|
3417 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, call, jsep);
|
3418 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3419 |
} |
3420 |
json_decref(call); |
3421 |
if(jsep)
|
3422 |
json_decref(jsep); |
3423 |
janus_sdp_free(sdp); |
3424 |
/* Also notify event handlers */
|
3425 |
if(notify_events && gateway->events_is_enabled()) {
|
3426 |
json_t *info = json_object(); |
3427 |
json_object_set_new(info, "event", json_string("incomingcall")); |
3428 |
if(session->callid)
|
3429 |
json_object_set_new(info, "call-id", json_string(session->callid));
|
3430 |
json_object_set_new(info, "username", json_string(session->callee));
|
3431 |
if(strlen(dname))
|
3432 |
json_object_set_new(info, "displayname", json_string(dname));
|
3433 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3434 |
} |
3435 |
/* Send a Ringing back */
|
3436 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 180, NULL)); |
3437 |
} |
3438 |
|
3439 |
/* Called when an SDP offer is received (re-INVITE) */
|
3440 |
int janus_sipre_cb_offer(struct mbuf **mbp, const struct sip_msg *msg, void *arg) { |
3441 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3442 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] janus_sipre_cb_offer\n", session->account.username);
|
3443 |
/* Get the SDP */
|
3444 |
const char *offer = (const char *)mbuf_buf(msg->mb); |
3445 |
if(offer == NULL) { |
3446 |
/* No SDP? */
|
3447 |
JANUS_LOG(LOG_WARN, "[SIPre-%s] No SDP in the re-INVITE?\n", session->account.username);
|
3448 |
return EINVAL;
|
3449 |
} |
3450 |
char sdp_offer[1024]; |
3451 |
g_snprintf(sdp_offer, sizeof(sdp_offer), "%.*s", (int)mbuf_get_left(msg->mb), offer); |
3452 |
JANUS_LOG(LOG_VERB, "Someone is updating a call:\n%s", sdp_offer);
|
3453 |
/* Parse the remote SDP */
|
3454 |
char sdperror[100]; |
3455 |
janus_sdp *sdp = janus_sdp_parse(sdp_offer, sdperror, sizeof(sdperror));
|
3456 |
if(!sdp) {
|
3457 |
JANUS_LOG(LOG_ERR, "Error parsing SDP! %s\n", sdperror);
|
3458 |
return EINVAL;
|
3459 |
} |
3460 |
gboolean changed = FALSE; |
3461 |
janus_sipre_sdp_process(session, sdp, FALSE, TRUE, &changed); |
3462 |
janus_sdp_free(sdp); |
3463 |
/* Check if offer has neither audio nor video, fail with 488 */
|
3464 |
if (!session->media.has_audio && !session->media.has_video) {
|
3465 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 488, NULL)); |
3466 |
return EINVAL;
|
3467 |
} |
3468 |
/* Also fail with 488 if there's no remote IP address that can be used for RTP */
|
3469 |
if (!session->media.remote_ip) {
|
3470 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 488, NULL)); |
3471 |
return EINVAL;
|
3472 |
} |
3473 |
char *answer = janus_sdp_write(session->sdp);
|
3474 |
JANUS_LOG(LOG_VERB, "Answering re-INVITE:\n%s", answer);
|
3475 |
*mbp = mbuf_alloc(strlen(answer)+1);
|
3476 |
mbuf_printf(*mbp, "%s", answer);
|
3477 |
mbuf_set_pos(*mbp, 0);
|
3478 |
return 0; |
3479 |
} |
3480 |
|
3481 |
|
3482 |
/* Called when an SDP answer is received */
|
3483 |
int janus_sipre_cb_answer(const struct sip_msg *msg, void *arg) { |
3484 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3485 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] janus_sipre_cb_answer\n", session->account.username);
|
3486 |
gboolean in_progress = FALSE; |
3487 |
if(msg->scode == 183) |
3488 |
in_progress = TRUE; |
3489 |
/* Get the SDP */
|
3490 |
const char *answer = (const char *)mbuf_buf(msg->mb); |
3491 |
if(answer == NULL) { |
3492 |
/* No SDP? */
|
3493 |
if(in_progress) {
|
3494 |
/* This was a 183, so we don't care */
|
3495 |
return 0; |
3496 |
} |
3497 |
JANUS_LOG(LOG_WARN, "[SIPre-%s] No SDP in the answer?\n", session->account.username);
|
3498 |
return EINVAL;
|
3499 |
} |
3500 |
char sdp_answer[1024]; |
3501 |
g_snprintf(sdp_answer, sizeof(sdp_answer), "%.*s", (int)mbuf_get_left(msg->mb), answer); |
3502 |
/* Parse the SDP */
|
3503 |
char sdperror[100]; |
3504 |
janus_sdp *sdp = janus_sdp_parse(sdp_answer, sdperror, sizeof(sdperror));
|
3505 |
if(!sdp) {
|
3506 |
JANUS_LOG(LOG_ERR, "Error parsing SDP! %s\n", sdperror);
|
3507 |
return EINVAL;
|
3508 |
} |
3509 |
/* Parse SDP */
|
3510 |
JANUS_LOG(LOG_VERB, "Peer accepted our call:\n%s", sdp_answer);
|
3511 |
session->status = janus_sipre_call_status_incall; |
3512 |
gboolean changed = FALSE; |
3513 |
gboolean update = session->media.ready; |
3514 |
janus_sipre_sdp_process(session, sdp, TRUE, update, &changed); |
3515 |
/* If we asked for SRTP and are not getting it, fail */
|
3516 |
if(session->media.require_srtp && !session->media.has_srtp_remote) {
|
3517 |
JANUS_LOG(LOG_ERR, "We asked for mandatory SRTP but didn't get any in the reply!\n");
|
3518 |
janus_sdp_free(sdp); |
3519 |
/* Hangup immediately */
|
3520 |
session->media.earlymedia = FALSE; |
3521 |
session->media.ready = FALSE; |
3522 |
session->media.on_hold = FALSE; |
3523 |
session->status = janus_sipre_call_status_closing; |
3524 |
mqueue_push(mq, janus_sipre_mqueue_event_do_bye, janus_sipre_mqueue_payload_create(session, msg, 0, NULL)); |
3525 |
g_free(session->callee); |
3526 |
session->callee = NULL;
|
3527 |
return EINVAL;
|
3528 |
} |
3529 |
if(!session->media.remote_ip) {
|
3530 |
/* No remote address parsed? Give up */
|
3531 |
JANUS_LOG(LOG_ERR, "No remote IP address found for RTP, something's wrong with the SDP!\n");
|
3532 |
janus_sdp_free(sdp); |
3533 |
/* Hangup immediately */
|
3534 |
session->media.earlymedia = FALSE; |
3535 |
session->media.ready = FALSE; |
3536 |
session->media.on_hold = FALSE; |
3537 |
session->status = janus_sipre_call_status_closing; |
3538 |
mqueue_push(mq, janus_sipre_mqueue_event_do_bye, janus_sipre_mqueue_payload_create(session, msg, 0, NULL)); |
3539 |
g_free(session->callee); |
3540 |
session->callee = NULL;
|
3541 |
return EINVAL;
|
3542 |
} |
3543 |
if(session->media.audio_pt > -1) { |
3544 |
session->media.audio_pt_name = janus_get_codec_from_pt(sdp_answer, session->media.audio_pt); |
3545 |
JANUS_LOG(LOG_VERB, "Detected audio codec: %d (%s)\n", session->media.audio_pt, session->media.audio_pt_name);
|
3546 |
} |
3547 |
if(session->media.video_pt > -1) { |
3548 |
session->media.video_pt_name = janus_get_codec_from_pt(sdp_answer, session->media.video_pt); |
3549 |
JANUS_LOG(LOG_VERB, "Detected video codec: %d (%s)\n", session->media.video_pt, session->media.video_pt_name);
|
3550 |
} |
3551 |
session->media.ready = TRUE; /* FIXME Maybe we need a better way to signal this */
|
3552 |
if(update && !session->media.earlymedia) {
|
3553 |
/* Don't push to the browser if this is in response to a hold/unhold we sent ourselves */
|
3554 |
JANUS_LOG(LOG_WARN, "This is an update to an existing call (possibly in response to hold/unhold)\n");
|
3555 |
return 0; |
3556 |
} |
3557 |
if(!session->media.earlymedia) {
|
3558 |
GError *error = NULL;
|
3559 |
char tname[16]; |
3560 |
g_snprintf(tname, sizeof(tname), "siprertp %s", session->account.username); |
3561 |
g_thread_try_new(tname, janus_sipre_relay_thread, session, &error); |
3562 |
if(error != NULL) { |
3563 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the RTP/RTCP thread...\n", error->code, error->message ? error->message : "??"); |
3564 |
} |
3565 |
} |
3566 |
/* Send event back to the browser */
|
3567 |
json_t *jsep = NULL;
|
3568 |
if(!session->media.earlymedia) {
|
3569 |
jsep = json_pack("{ssss}", "type", "answer", "sdp", sdp_answer); |
3570 |
} else {
|
3571 |
/* We've received the 200 OK after the 183, we can remove the flag now */
|
3572 |
session->media.earlymedia = FALSE; |
3573 |
} |
3574 |
if(in_progress) {
|
3575 |
/* If we just received the 183, set the flag instead so that we can handle the 200 OK differently */
|
3576 |
session->media.earlymedia = TRUE; |
3577 |
} |
3578 |
json_t *call = json_object(); |
3579 |
json_object_set_new(call, "sip", json_string("event")); |
3580 |
json_t *calling = json_object(); |
3581 |
json_object_set_new(calling, "event", json_string(in_progress ? "progress" : "accepted")); |
3582 |
json_object_set_new(calling, "username", json_string(session->callee));
|
3583 |
json_object_set_new(call, "result", calling);
|
3584 |
if(!session->destroyed) {
|
3585 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, call, jsep);
|
3586 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3587 |
} |
3588 |
json_decref(call); |
3589 |
json_decref(jsep); |
3590 |
janus_sdp_free(sdp); |
3591 |
/* Also notify event handlers */
|
3592 |
if(notify_events && gateway->events_is_enabled()) {
|
3593 |
json_t *info = json_object(); |
3594 |
json_object_set_new(info, "event", json_string(in_progress ? "progress" : "accepted")); |
3595 |
if(session->callid)
|
3596 |
json_object_set_new(info, "call-id", json_string(session->callid));
|
3597 |
json_object_set_new(info, "username", json_string(session->callee));
|
3598 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3599 |
} |
3600 |
|
3601 |
return 0; |
3602 |
} |
3603 |
|
3604 |
/* Called when the session is established */
|
3605 |
void janus_sipre_cb_established(const struct sip_msg *msg, void *arg) { |
3606 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3607 |
if(session == NULL) { |
3608 |
JANUS_LOG(LOG_WARN, "[SIPre-??] janus_sipre_cb_established\n");
|
3609 |
return;
|
3610 |
} |
3611 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] janus_sipre_cb_established\n", session->account.username);
|
3612 |
/* FIXME Anything to do here? */
|
3613 |
} |
3614 |
|
3615 |
/* Called when an incoming SIP INFO arrives */
|
3616 |
void janus_sipre_cb_info(struct sip *sip, const struct sip_msg *msg, void *arg) { |
3617 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3618 |
if(session == NULL) { |
3619 |
JANUS_LOG(LOG_WARN, "[SIPre-??] janus_sipre_cb_info\n");
|
3620 |
return;
|
3621 |
} |
3622 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] janus_sipre_cb_info\n", session->account.username);
|
3623 |
char *from = NULL; |
3624 |
re_sdprintf(&from, "%H", uri_encode, &msg->from.uri);
|
3625 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- Sender: %s\n", session->account.username, from);
|
3626 |
char dname[256]; |
3627 |
dname[0] = '\0'; |
3628 |
if(msg->from.dname.l > 0) { |
3629 |
g_snprintf(dname, sizeof(dname), "%.*s", (int)msg->from.dname.l, msg->from.dname.p); |
3630 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- Display: %s\n", session->account.username, dname);
|
3631 |
} |
3632 |
char type[200]; |
3633 |
type[0] = '\0'; |
3634 |
if(msg->ctyp.type.l > 0) { |
3635 |
g_snprintf(type, sizeof(type), "%.*s", (int)msg->ctyp.type.l, msg->ctyp.type.p); |
3636 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- Type: %s\n", session->account.username, type);
|
3637 |
} |
3638 |
const char *payload = (const char *)mbuf_buf(msg->mb); |
3639 |
char content[1024]; |
3640 |
g_snprintf(content, sizeof(content), "%.*s", (int)mbuf_get_left(msg->mb), payload); |
3641 |
JANUS_LOG(LOG_HUGE, "[SIPre-%s] -- Content: %s\n", session->account.username, content);
|
3642 |
/* Notify the application */
|
3643 |
json_t *info = json_object(); |
3644 |
json_object_set_new(info, "sip", json_string("event")); |
3645 |
json_t *result = json_object(); |
3646 |
json_object_set_new(result, "event", json_string("info")); |
3647 |
json_object_set_new(result, "sender", json_string(from));
|
3648 |
if(strlen(dname)) {
|
3649 |
json_object_set_new(result, "displayname", json_string(dname));
|
3650 |
} |
3651 |
json_object_set_new(result, "type", json_string(type));
|
3652 |
json_object_set_new(result, "content", json_string(content));
|
3653 |
json_object_set_new(info, "result", result);
|
3654 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, info, NULL); |
3655 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3656 |
json_decref(info); |
3657 |
/* Send a 200 back */
|
3658 |
mqueue_push(mq, janus_sipre_mqueue_event_do_rcode, janus_sipre_mqueue_payload_create(session, msg, 200, session));
|
3659 |
} |
3660 |
|
3661 |
/* Called when the session fails to connect or is terminated by the peer */
|
3662 |
void janus_sipre_cb_closed(int err, const struct sip_msg *msg, void *arg) { |
3663 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3664 |
if(session == NULL) { |
3665 |
JANUS_LOG(LOG_HUGE, "[SIPre-??] janus_sipre_cb_closed\n");
|
3666 |
return;
|
3667 |
} |
3668 |
if(err) {
|
3669 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Session closed: %d %s\n", session->account.username, err, strerror(err));
|
3670 |
} else {
|
3671 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Session closed: %u %s\n", session->account.username, msg->scode, (char *)&msg->reason.p); |
3672 |
} |
3673 |
|
3674 |
/* Tell the browser... */
|
3675 |
json_t *event = json_object(); |
3676 |
json_object_set_new(event, "sip", json_string("event")); |
3677 |
json_t *result = json_object(); |
3678 |
json_object_set_new(result, "event", json_string("hangup")); |
3679 |
json_object_set_new(result, "code", json_integer(err ? err : msg->scode));
|
3680 |
char reason[256]; |
3681 |
reason[0] = '\0'; |
3682 |
if(!err && msg->reason.l > 0) { |
3683 |
g_snprintf(reason, (msg->reason.l < 255 ? msg->reason.l+1 : 255), "%s", msg->reason.p); |
3684 |
} |
3685 |
json_object_set_new(result, "reason", json_string(err ? strerror(err) : reason));
|
3686 |
json_object_set_new(event, "result", result);
|
3687 |
if(!session->destroyed) {
|
3688 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, event, NULL); |
3689 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3690 |
} |
3691 |
json_decref(event); |
3692 |
/* Also notify event handlers */
|
3693 |
if(notify_events && gateway->events_is_enabled()) {
|
3694 |
json_t *info = json_object(); |
3695 |
json_object_set_new(info, "event", json_string("hangup")); |
3696 |
json_object_set_new(info, "code", json_integer(err ? err : msg->scode));
|
3697 |
json_object_set_new(info, "reason", json_string(err ? strerror(err) : reason));
|
3698 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3699 |
} |
3700 |
|
3701 |
/* Cleanup */
|
3702 |
mem_deref(session->stack.sess); |
3703 |
session->stack.sess = NULL;
|
3704 |
session->media.earlymedia = FALSE; |
3705 |
session->media.ready = FALSE; |
3706 |
session->media.on_hold = FALSE; |
3707 |
session->status = janus_sipre_call_status_idle; |
3708 |
} |
3709 |
|
3710 |
/* Called when all SIP transactions are completed */
|
3711 |
void janus_sipre_cb_exit(void *arg) { |
3712 |
janus_sipre_session *session = (janus_sipre_session *)arg; |
3713 |
if(session == NULL) { |
3714 |
JANUS_LOG(LOG_HUGE, "[SIPre-??] janus_sipre_cb_exit\n");
|
3715 |
return;
|
3716 |
} |
3717 |
JANUS_LOG(LOG_INFO, "[SIPre-%s] Cleaning SIP stack\n", session->account.username);
|
3718 |
} |
3719 |
|
3720 |
/* Callback to implement SIP requests in the re_main loop thread */
|
3721 |
void janus_sipre_mqueue_handler(int id, void *data, void *arg) { |
3722 |
JANUS_LOG(LOG_HUGE, "janus_sipre_mqueue_handler: %d (%s)\n", id, janus_sipre_mqueue_event_string((janus_sipre_mqueue_event)id));
|
3723 |
switch((janus_sipre_mqueue_event)id) {
|
3724 |
case janus_sipre_mqueue_event_do_init: {
|
3725 |
janus_sipre_mqueue_payload *payload = (janus_sipre_mqueue_payload *)data; |
3726 |
janus_sipre_session *session = (janus_sipre_session *)payload->session; |
3727 |
/* We need a DNS client */
|
3728 |
struct sa nsv[8]; |
3729 |
uint32_t nsn = ARRAY_SIZE(nsv); |
3730 |
int err = dns_srv_get(NULL, 0, nsv, &nsn); |
3731 |
if(err) {
|
3732 |
JANUS_LOG(LOG_ERR, "Failed to get the DNS servers list for the SIP stack: %d (%s)\n", err, strerror(err));
|
3733 |
g_free(payload); |
3734 |
return;
|
3735 |
} |
3736 |
err = dnsc_alloc(&session->stack.dns_client, NULL, nsv, nsn);
|
3737 |
if(err) {
|
3738 |
JANUS_LOG(LOG_ERR, "Failed to initialize the DNS client for the SIP stack: %d (%s)\n", err, strerror(err));
|
3739 |
g_free(payload); |
3740 |
return;
|
3741 |
} |
3742 |
/* Let's allocate the stack now */
|
3743 |
err = sip_alloc(&session->stack.sipstack, session->stack.dns_client, 32, 32, 32, |
3744 |
(user_agent ? user_agent : JANUS_SIPRE_NAME), janus_sipre_cb_exit, session); |
3745 |
if(err) {
|
3746 |
JANUS_LOG(LOG_ERR, "Failed to initialize libre SIP stack: %d (%s)\n", err, strerror(err));
|
3747 |
mem_deref(session->stack.dns_client); |
3748 |
session->stack.dns_client = NULL;
|
3749 |
g_free(payload); |
3750 |
return;
|
3751 |
} |
3752 |
JANUS_LOG(LOG_INFO, "Initializing SIP transports\n");
|
3753 |
struct sa laddr, laddrs;
|
3754 |
sa_set_str(&laddr, local_ip, 0);
|
3755 |
sa_set_str(&laddrs, local_ip, 0);
|
3756 |
err = 0;
|
3757 |
err |= sip_transp_add(session->stack.sipstack, SIP_TRANSP_UDP, &laddr); |
3758 |
err |= sip_transp_add(session->stack.sipstack, SIP_TRANSP_TCP, &laddr); |
3759 |
if(err) {
|
3760 |
JANUS_LOG(LOG_ERR, "Failed to initialize libre SIP transports: %d (%s)\n", err, strerror(err));
|
3761 |
g_free(payload); |
3762 |
return;
|
3763 |
} |
3764 |
err = tls_alloc(&session->stack.tls, TLS_METHOD_SSLV23, NULL, NULL); |
3765 |
err |= sip_transp_add(session->stack.sipstack, SIP_TRANSP_TLS, &laddrs, session->stack.tls); |
3766 |
err |= sipsess_listen(&session->stack.sess_sock, session->stack.sipstack, 32, janus_sipre_cb_incoming, session);
|
3767 |
if(err) {
|
3768 |
sip_close(session->stack.sipstack, TRUE); |
3769 |
session->stack.sipstack = NULL;
|
3770 |
mem_deref(session->stack.tls); |
3771 |
session->stack.tls = NULL;
|
3772 |
JANUS_LOG(LOG_ERR, "Failed to initialize libre SIPS transports: %d (%s)\n", err, strerror(err));
|
3773 |
g_free(payload); |
3774 |
return;
|
3775 |
} |
3776 |
#ifdef HAVE_LIBRE_SIPTRACE
|
3777 |
if(gateway->events_is_enabled()) {
|
3778 |
/* Trace incoming/outgoing SIP messages */
|
3779 |
sip_set_trace(session->stack.sipstack, janus_sipre_msg_logger); |
3780 |
} |
3781 |
#endif
|
3782 |
mem_deref(session->stack.tls); |
3783 |
session->stack.tls = NULL;
|
3784 |
g_free(payload); |
3785 |
break;
|
3786 |
} |
3787 |
case janus_sipre_mqueue_event_do_register:
|
3788 |
case janus_sipre_mqueue_event_do_unregister: {
|
3789 |
janus_sipre_mqueue_payload *payload = (janus_sipre_mqueue_payload *)data; |
3790 |
janus_sipre_session *session = (janus_sipre_session *)payload->session; |
3791 |
/* Whether it's a REGISTER or an unregister, get rid of the previous instance */
|
3792 |
mem_deref(session->stack.reg); |
3793 |
session->stack.reg = NULL;
|
3794 |
/* If it's an unregister, we're done */
|
3795 |
if(session->stack.expires == 0) { |
3796 |
/* Notify the browser */
|
3797 |
json_t *event = json_object(); |
3798 |
json_object_set_new(event, "sip", json_string("event")); |
3799 |
json_t *unreging = json_object(); |
3800 |
json_object_set_new(unreging, "event", json_string("unregistered")); |
3801 |
json_object_set_new(unreging, "username", json_string(session->account.username));
|
3802 |
json_object_set_new(unreging, "register_sent", json_true());
|
3803 |
json_object_set_new(event, "result", unreging);
|
3804 |
if(!session->destroyed) {
|
3805 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, event, NULL); |
3806 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3807 |
} |
3808 |
json_decref(event); |
3809 |
/* Also notify event handlers */
|
3810 |
if(notify_events && gateway->events_is_enabled()) {
|
3811 |
json_t *info = json_object(); |
3812 |
json_object_set_new(info, "event", json_string("unregistered")); |
3813 |
json_object_set_new(info, "identity", json_string(session->account.identity));
|
3814 |
if(session->account.proxy)
|
3815 |
json_object_set_new(info, "proxy", json_string(session->account.proxy));
|
3816 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3817 |
} |
3818 |
break;
|
3819 |
} |
3820 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Sending REGISTER\n", session->account.username);
|
3821 |
/* Check if there is an outbound proxy to take into account */
|
3822 |
const char *outbound_proxy[1]; |
3823 |
outbound_proxy[0] = session->account.outbound_proxy;
|
3824 |
/* Check if there are custom headers to add */
|
3825 |
char *headers = (char *)payload->data; |
3826 |
/* Send the REGISTER */
|
3827 |
int err = sipreg_register(&session->stack.reg, session->stack.sipstack,
|
3828 |
session->account.proxy, |
3829 |
session->account.identity, session->account.identity, session->stack.expires, |
3830 |
session->account.username, |
3831 |
outbound_proxy, (outbound_proxy[0] ? 1 : 0), 0, |
3832 |
janus_sipre_cb_auth, session, FALSE, |
3833 |
janus_sipre_cb_register, session, NULL, (headers ? headers : ""), NULL); |
3834 |
g_free(headers); |
3835 |
if(err != 0) { |
3836 |
JANUS_LOG(LOG_ERR, "Error attempting to REGISTER: %d (%s)\n", err, strerror(err));
|
3837 |
/* Tell the browser... */
|
3838 |
json_t *event = json_object(); |
3839 |
json_object_set_new(event, "sip", json_string("event")); |
3840 |
json_t *result = json_object(); |
3841 |
json_object_set_new(result, "event", json_string("registration_failed")); |
3842 |
json_object_set_new(result, "code", json_integer(err));
|
3843 |
json_object_set_new(result, "reason", json_string(strerror(err)));
|
3844 |
json_object_set_new(event, "result", result);
|
3845 |
if(!session->destroyed) {
|
3846 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, event, NULL); |
3847 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3848 |
} |
3849 |
json_decref(event); |
3850 |
/* Also notify event handlers */
|
3851 |
if(notify_events && gateway->events_is_enabled()) {
|
3852 |
json_t *info = json_object(); |
3853 |
json_object_set_new(info, "event", json_string("registration_failed")); |
3854 |
json_object_set_new(info, "code", json_integer(err));
|
3855 |
json_object_set_new(info, "reason", json_string(strerror(err)));
|
3856 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3857 |
} |
3858 |
} |
3859 |
g_free(payload); |
3860 |
break;
|
3861 |
} |
3862 |
case janus_sipre_mqueue_event_do_call: {
|
3863 |
janus_sipre_mqueue_payload *payload = (janus_sipre_mqueue_payload *)data; |
3864 |
janus_sipre_session *session = (janus_sipre_session *)payload->session; |
3865 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Sending INVITE\n%s", session->account.username, session->temp_sdp);
|
3866 |
/* Check if there is an outbound proxy to take into account */
|
3867 |
const char *outbound_proxy[1]; |
3868 |
outbound_proxy[0] = session->account.outbound_proxy;
|
3869 |
/* Check if there are custom headers to add */
|
3870 |
char *headers = (char *)payload->data; |
3871 |
/* Convert the SDP into a struct mbuf */
|
3872 |
struct mbuf *mb = mbuf_alloc(strlen(session->temp_sdp)+1); |
3873 |
mbuf_printf(mb, "%s", session->temp_sdp);
|
3874 |
mbuf_set_pos(mb, 0);
|
3875 |
/* Send the INVITE */
|
3876 |
int err = sipsess_connect(&session->stack.sess, session->stack.sess_sock,
|
3877 |
session->callee, |
3878 |
session->account.display_name, session->account.identity, |
3879 |
session->account.username, |
3880 |
outbound_proxy, (outbound_proxy[0] ? 1 : 0), |
3881 |
"application/sdp", mb,
|
3882 |
janus_sipre_cb_auth, session, FALSE, |
3883 |
janus_sipre_cb_offer, janus_sipre_cb_answer, |
3884 |
janus_sipre_cb_progress, janus_sipre_cb_established, |
3885 |
NULL, NULL, janus_sipre_cb_closed, session, |
3886 |
"%s", (headers ? headers : "")); |
3887 |
g_free(headers); |
3888 |
if(err != 0) { |
3889 |
JANUS_LOG(LOG_ERR, "Error attempting to INVITE: %d (%s)\n", err, strerror(err));
|
3890 |
/* Tell the browser... */
|
3891 |
json_t *event = json_object(); |
3892 |
json_object_set_new(event, "sip", json_string("event")); |
3893 |
json_t *result = json_object(); |
3894 |
json_object_set_new(result, "event", json_string("hangup")); |
3895 |
json_object_set_new(result, "code", json_integer(err));
|
3896 |
json_object_set_new(result, "reason", json_string(strerror(err)));
|
3897 |
json_object_set_new(event, "result", result);
|
3898 |
if(!session->destroyed) {
|
3899 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, event, NULL); |
3900 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3901 |
} |
3902 |
json_decref(event); |
3903 |
/* Also notify event handlers */
|
3904 |
if(notify_events && gateway->events_is_enabled()) {
|
3905 |
json_t *info = json_object(); |
3906 |
json_object_set_new(info, "event", json_string("hangup")); |
3907 |
json_object_set_new(info, "code", json_integer(err));
|
3908 |
json_object_set_new(info, "reason", json_string(strerror(err)));
|
3909 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3910 |
} |
3911 |
} |
3912 |
mem_deref(mb); |
3913 |
g_free(session->temp_sdp); |
3914 |
session->temp_sdp = NULL;
|
3915 |
g_free(payload); |
3916 |
break;
|
3917 |
} |
3918 |
case janus_sipre_mqueue_event_do_accept: {
|
3919 |
janus_sipre_mqueue_payload *payload = (janus_sipre_mqueue_payload *)data; |
3920 |
janus_sipre_session *session = (janus_sipre_session *)payload->session; |
3921 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Sending 200 OK\n%s", session->account.username, session->temp_sdp);
|
3922 |
/* Convert the SDP into a struct mbuf */
|
3923 |
struct mbuf *mb = mbuf_alloc(strlen(session->temp_sdp)+1); |
3924 |
mbuf_printf(mb, "%s", session->temp_sdp);
|
3925 |
mbuf_set_pos(mb, 0);
|
3926 |
/* Send the 200 OK */
|
3927 |
int err = sipsess_answer(session->stack.sess, 200, "OK", mb, NULL); |
3928 |
if(err != 0) { |
3929 |
JANUS_LOG(LOG_ERR, "Error attempting to send the 200 OK: %d (%s)\n", err, strerror(err));
|
3930 |
/* Tell the browser... */
|
3931 |
json_t *event = json_object(); |
3932 |
json_object_set_new(event, "sip", json_string("event")); |
3933 |
json_t *result = json_object(); |
3934 |
json_object_set_new(result, "event", json_string("hangup")); |
3935 |
json_object_set_new(result, "code", json_integer(err));
|
3936 |
json_object_set_new(result, "reason", json_string(strerror(err)));
|
3937 |
json_object_set_new(event, "result", result);
|
3938 |
if(!session->destroyed) {
|
3939 |
int ret = gateway->push_event(session->handle, &janus_sipre_plugin, session->transaction, event, NULL); |
3940 |
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
|
3941 |
} |
3942 |
json_decref(event); |
3943 |
/* Also notify event handlers */
|
3944 |
if(notify_events && gateway->events_is_enabled()) {
|
3945 |
json_t *info = json_object(); |
3946 |
json_object_set_new(info, "event", json_string("hangup")); |
3947 |
json_object_set_new(info, "code", json_integer(err));
|
3948 |
json_object_set_new(info, "reason", json_string(strerror(err)));
|
3949 |
gateway->notify_event(&janus_sipre_plugin, session->handle, info); |
3950 |
} |
3951 |
} |
3952 |
mem_deref(mb); |
3953 |
g_free(session->temp_sdp); |
3954 |
session->temp_sdp = NULL;
|
3955 |
g_free(payload); |
3956 |
break;
|
3957 |
} |
3958 |
case janus_sipre_mqueue_event_do_rcode: {
|
3959 |
janus_sipre_mqueue_payload *payload = (janus_sipre_mqueue_payload *)data; |
3960 |
janus_sipre_session *session = (janus_sipre_session *)payload->session; |
3961 |
JANUS_LOG(LOG_VERB, "[SIPre-%s] Sending response code %d\n", session->account.username, payload->rcode);
|
3962 |
/* Send the response code */
|
3963 |
int err = 0; |
3964 |
if(session->stack.sess == NULL) { |
3965 |
/* We still need to accept the connection */
|
3966 |
err = sipsess_accept(&session->stack.sess, session->stack.sess_sock, |
3967 |
session->stack.invite, payload->rcode, janus_sipre_error_reason(payload->rcode), |
3968 |
session->account.display_name ? session->account.display_name : session->account.username, |
3969 |
"application/sdp", NULL, |
3970 |
janus_sipre_cb_auth, session, FALSE, |
3971 |
janus_sipre_cb_offer, janus_sipre_cb_answer, |
3972 |
janus_sipre_cb_established, janus_sipre_cb_info, NULL,
|
3973 |
janus_sipre_cb_closed, session, NULL);
|
3974 |
} else {
|
3975 |
/* Connection already accepted */
|
3976 |
if(payload->rcode < 200) { |
3977 |
/* 1xx */
|
3978 |
err = sipsess_progress(session->stack.sess, payload->rcode, janus_sipre_error_reason(payload->rcode), N |