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