SIP: Register, STUN and authentication support...
- Register works in both ways - STUN works as client - Authentication to remote endpoints only - Early audio (183) works in both directions - Caller ID works in both directions Note: The implementation is only a small subset of many SIP features.
This commit is contained in:
parent
8ed162789e
commit
00675eb48b
|
@ -1278,7 +1278,7 @@ int cfnr_call_timeout(struct lcr_timer *timer, void *instance, int index)
|
|||
{
|
||||
class EndpointAppPBX *ea = (class EndpointAppPBX *)instance;
|
||||
|
||||
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) call-forward-busy time has expired, calling the forwarded number: %s.\n", ea->ea_endpoint->ep_serial, ea->e_ext.cfnr);
|
||||
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) call-forward-no-response time has expired, calling the forwarded number: %s.\n", ea->ea_endpoint->ep_serial, ea->e_ext.cfnr);
|
||||
ea->out_setup(1);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -208,6 +208,48 @@
|
|||
#earlyb no
|
||||
#tones no
|
||||
|
||||
# Use Sofia-SIP as SIP client to register to a SIP gateway/proxy
|
||||
#[sip]
|
||||
## define source and destination IP to make a call
|
||||
#sip 192.168.0.55 sipgate.de
|
||||
## define <user> <host> [<options-interval>] to register to a SIP gateway
|
||||
#register <user> sipgate.de 300
|
||||
##define RTP port range or use default
|
||||
#rtp-ports 30000 39999
|
||||
## use authentication credentials, use realm to authenticate remote
|
||||
#authenticate <user> <password> [<realm>]
|
||||
## define keepalive timer to keep INVITE/REGISTER alive
|
||||
## this is also required to keep the NAT router's table alive
|
||||
#options-interval 15
|
||||
## define asserted ID (real caller ID) to use no screening CLIP
|
||||
##asserted-id <my real phone number>
|
||||
## define public IP (if behind NAT firewall)
|
||||
#public 123.45.67.89
|
||||
## OR define stun server and resolving interval
|
||||
#stun stun.sipgate.net 300
|
||||
## screen caller ID to SIP caller ID
|
||||
#screen-out % <callerID>
|
||||
#tones yes
|
||||
#earlyb yes
|
||||
|
||||
# Use Sofia-SIP as SIP gateway/proxy to allow SIP clients to register
|
||||
#[sip]
|
||||
## define source
|
||||
#sip 192.168.0.55
|
||||
##define RTP port range or use default
|
||||
#rtp-ports 30000 39999
|
||||
## use authentication credentials and realm to authenticate remote
|
||||
#authenticate <user> <password> <realm>
|
||||
## define keepalive timer to keep INVITE/REGISTER alive
|
||||
## this is also required to keep the NAT router's table alive
|
||||
#options-interval 15
|
||||
## define public IP (if behind NAT firewall)
|
||||
#public 123.45.67.89
|
||||
## OR define stun server and resolving interval
|
||||
#stun stun.sipgate.net 300
|
||||
#tones yes
|
||||
#earlyb yes
|
||||
|
||||
|
||||
# Hint: Enter "lcr interface" for quick help on interface options.
|
||||
|
||||
|
|
238
interface.c
238
interface.c
|
@ -974,16 +974,204 @@ static int inter_sip(struct interface *interface, char *filename, int line, char
|
|||
|
||||
/* copy values */
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing SIP local IP.\n", filename, line);
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing SIP local peer.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
p = get_seperated(value);
|
||||
SCPY(interface->sip_local_peer, value);
|
||||
SCPY(interface->sip_remote_peer, p);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
static int inter_authenticate(struct interface *interface, char *filename, int line, char *parameter, char *value)
|
||||
{
|
||||
#ifndef WITH_SIP
|
||||
SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line);
|
||||
return(-1);
|
||||
#else
|
||||
char *p, *q;
|
||||
|
||||
if (!interface->sip) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* copy values */
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing SIP user.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
p = get_seperated(value);
|
||||
if (!p[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing SIP remote IP.\n", filename, line);
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing SIP password.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
SCPY(interface->sip_local_peer, value);
|
||||
SCPY(interface->sip_remote_peer, p);
|
||||
q = get_seperated(p);
|
||||
SCPY(interface->sip_auth_user, value);
|
||||
SCPY(interface->sip_auth_password, p);
|
||||
if (q[0])
|
||||
SCPY(interface->sip_auth_realm, q);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
static int options_interval(struct interface *interface, char *filename, int line, char *parameter, char *value)
|
||||
{
|
||||
#ifndef WITH_SIP
|
||||
SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line);
|
||||
return(-1);
|
||||
#else
|
||||
if (!interface->sip) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing options interval.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
interface->sip_options_interval = atoi(value);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
static int options_asserted_id(struct interface *interface, char *filename, int line, char *parameter, char *value)
|
||||
{
|
||||
#ifndef WITH_SIP
|
||||
SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line);
|
||||
return(-1);
|
||||
#else
|
||||
if (!interface->sip) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing asserted caller ID.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
SCPY(interface->sip_asserted_id, value);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
static int options_public(struct interface *interface, char *filename, int line, char *parameter, char *value)
|
||||
{
|
||||
#ifndef WITH_SIP
|
||||
SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line);
|
||||
return(-1);
|
||||
#else
|
||||
if (!interface->sip) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
if (interface->sip_stun_server[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Don't specify STUN, if you want to define public IP.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing public IP.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
SCPY(interface->sip_public_ip, value);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
static int options_stun(struct interface *interface, char *filename, int line, char *parameter, char *value)
|
||||
{
|
||||
#ifndef WITH_SIP
|
||||
SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line);
|
||||
return(-1);
|
||||
#else
|
||||
char *p;
|
||||
|
||||
if (!interface->sip) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
if (interface->sip_public_ip[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Don't specify public IP, if you want to define STUN.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing STUN server.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
p = get_seperated(value);
|
||||
if (!p[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing STUN timer.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
SCPY(interface->sip_stun_server, value);
|
||||
interface->sip_stun_interval = atoi(p);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
static int inter_rtp_ports(struct interface *interface, char *filename, int line, char *parameter, char *value)
|
||||
{
|
||||
#ifndef WITH_SIP
|
||||
SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line);
|
||||
return(-1);
|
||||
#else
|
||||
char *p;
|
||||
|
||||
if (!interface->sip) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* copy values */
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing 'from' port.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
p = get_seperated(value);
|
||||
if (!p[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing 'to' port.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
interface->rtp_port_from = atoi(value);
|
||||
interface->rtp_port_to = atoi(p);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
static int inter_register(struct interface *interface, char *filename, int line, char *parameter, char *value)
|
||||
{
|
||||
#ifndef WITH_SIP
|
||||
SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line);
|
||||
return(-1);
|
||||
#else
|
||||
char *p, *q;
|
||||
|
||||
if (!interface->sip) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* copy values */
|
||||
if (!value || !value[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing SIP user.\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
p = get_seperated(value);
|
||||
if (!p[0]) {
|
||||
SPRINT(interface_error, "Error in %s (line %d): Missing SIP host\n", filename, line);
|
||||
return(-1);
|
||||
}
|
||||
q = get_seperated(p);
|
||||
if (!q[0])
|
||||
interface->sip_register_interval = 0;
|
||||
else
|
||||
interface->sip_register_interval = atoi(q);
|
||||
interface->sip_register = 1;
|
||||
SCPY(interface->sip_register_user, value);
|
||||
SCPY(interface->sip_register_host, p);
|
||||
|
||||
return(0);
|
||||
#endif
|
||||
|
@ -1307,7 +1495,7 @@ struct interface_param interface_param[] = {
|
|||
{"timeouts", &inter_timeouts, "<setup> <dialing> <proceeding> <alerting> <disconnect>",
|
||||
"Timeout values for call states. They are both for incoming and outgoing states.\n"
|
||||
"The default is 120 seconds for all states. Use 0 to disable.\n"
|
||||
"This parameter must follow a 'port' parameter.\n"},
|
||||
"This parameter must follow a 'port' parameter."},
|
||||
|
||||
{"msn", &inter_msn, "<default MSN>,[<additional MSN>[,...]]",
|
||||
"Incoming caller ID is checked against given MSN numbers.\n"
|
||||
|
@ -1345,12 +1533,11 @@ struct interface_param interface_param[] = {
|
|||
{"dialmax", &inter_dialmax, "<digits>",
|
||||
"Limits the number of digits in setup/information message."},
|
||||
|
||||
{"tones_dir", &inter_tones_dir, "<path>",
|
||||
"Overrides the given tone_dir in options.conf.\n"
|
||||
"To used kernel tones in mISDN_dsp.ko, say 'american', 'german', or 'oldgerman'."},
|
||||
{"tones-dir", &inter_tones_dir, "<path>",
|
||||
"Overrides the given tone_dir in options.conf.\n"
|
||||
"To used kernel tones in mISDN_dsp.ko, say 'american', 'german', or 'oldgerman'."},
|
||||
{"tones_dir", &inter_tones_dir, "<path>",
|
||||
"Same as tones-dir"},
|
||||
|
||||
{"gsm", &inter_gsm, "",
|
||||
""},
|
||||
|
@ -1363,24 +1550,45 @@ struct interface_param interface_param[] = {
|
|||
{"gsm-ms", &inter_gsm_ms, "<socket name>",
|
||||
"Sets up GSM mobile station interface for using Osmocom-BB.\n"
|
||||
"The socket will be /tmp/ms_mncc_<socket name>."},
|
||||
{"sip", &inter_sip, "<local IP> <remote IP>",
|
||||
|
||||
{"sip", &inter_sip, "<local IP/host>[:port] [<remote IP/host>[port]]",
|
||||
"Sets up SIP interface that represents one SIP endpoint.\n"
|
||||
"Give SIP configuration file."},
|
||||
"If the remote IP/host is omitted, a client must register first to us."},
|
||||
{"register", &inter_register, "<user> <host> [options-interval]",
|
||||
"Registers to given SIP registrar.\n"
|
||||
"Optionally give SIP timer to send OPTIONS messages to keepalive REGISTER sessions."},
|
||||
{"authenticate", &inter_authenticate, "<user> <password> [realm]",
|
||||
"Defines SIP user and password for authentication.\n"
|
||||
"If no remote IP was give, we are SIP gateway, so realm must be given also."},
|
||||
{"options-interval", &options_interval, "<interval> | 0",
|
||||
"Defines SIP timer to send OPTIONS messages to keepalive INVITE sessions."},
|
||||
{"asserted-id", &options_asserted_id, "<caller-id>",
|
||||
"Defines SIP's asserted-id in INVITE message."},
|
||||
{"public", &options_public, "<server> <interval>",
|
||||
"Defines public IP, if this endpoint is behind NAT firewall."},
|
||||
{"stun", &options_stun, "<server> <interval>",
|
||||
"Defines STUN server to resolv real local IP.\n"
|
||||
"The interval is used to check if IP has changed. (use 300)"},
|
||||
{"rtp-ports", &inter_rtp_ports, "<port from> <port to>",
|
||||
"Defines the range of ports to be used for RTP. This overrides the default."},
|
||||
|
||||
{"rtp-bridge", &inter_rtp_bridge, "",
|
||||
"Enables RTP bridging directly from this interface.\n"
|
||||
"This only works if both bridged interfaces use RTP, e.g. between gsm-bs and sip.\n"
|
||||
"This parameter must follow a 'bridge' parameter.\n"},
|
||||
"This parameter must follow a 'bridge' parameter."},
|
||||
#if 0
|
||||
not needed, since ms defines what is supports and remote (sip) tells what is selected
|
||||
{"rtp-payload", &inter_rtp_payload, "<codec>",
|
||||
"Define RTP payload to use. Only valid in conjuntion with gsm-bs!\n"
|
||||
"If multiple payloads are defined, the first has highest priority.\n"
|
||||
"If none are defined, GSM fullrate V1 (type 3) is assumed.\n"},
|
||||
"If none are defined, GSM fullrate V1 (type 3) is assumed."},
|
||||
#endif
|
||||
|
||||
{"nonotify", &inter_nonotify, "",
|
||||
"Prevents sending notify messages to this interface. A call placed on hold will\n"
|
||||
"Not affect the remote end (phone or telcom switch).\n"
|
||||
"This parameter must follow a 'port' parameter."},
|
||||
|
||||
{"bridge", &inter_bridge, "<destination interface>",
|
||||
"Define bridge application for this interface. All calls received on this\n"
|
||||
"interface will be directly bridged to the given destination interface.\n"
|
||||
|
@ -1412,17 +1620,17 @@ struct interface_param interface_param[] = {
|
|||
{"pots-flash", &inter_pots_flash, "",
|
||||
"Allow flash button to hold an active call and setup a new call.\n"
|
||||
"Ihis parameter only appies to POTS type of interfaces\n"
|
||||
"This parameter must follow a 'port' parameter.\n"},
|
||||
"This parameter must follow a 'port' parameter."},
|
||||
{"pots-ring-after-hangup", &inter_pots_ring, "",
|
||||
"Allow ringing of last hold call after hangup. Other calls on hold will not be\n"
|
||||
"released.\n"
|
||||
"Ihis parameter only appies to POTS type of interfaces\n"
|
||||
"This parameter must follow a 'port' parameter.\n"},
|
||||
"This parameter must follow a 'port' parameter."},
|
||||
{"pots-transfer-after-hangup", &inter_pots_transfer, "",
|
||||
"If two calls on hold, both are connected after hangup.\n"
|
||||
"If one call is on hold and another one alerting, call on hold is tranfered.\n"
|
||||
"Ihis parameter only appies to POTS type of interfaces\n"
|
||||
"This parameter must follow a 'port' parameter.\n"},
|
||||
"This parameter must follow a 'port' parameter."},
|
||||
|
||||
{"shutdown", &inter_shutdown, "",
|
||||
"Interface will not be loaded when processing interface.conf"},
|
||||
|
|
18
interface.h
18
interface.h
|
@ -126,9 +126,23 @@ struct interface {
|
|||
#endif
|
||||
#ifdef WITH_SIP
|
||||
int sip; /* interface is a SIP interface */
|
||||
char sip_local_peer[32];
|
||||
char sip_remote_peer[32];
|
||||
char sip_local_peer[128];
|
||||
char sip_remote_peer[128];
|
||||
char sip_asserted_id[128];
|
||||
char sip_auth_user[128];
|
||||
char sip_auth_password[128];
|
||||
char sip_auth_realm[128];
|
||||
int sip_register;
|
||||
char sip_register_user[128];
|
||||
char sip_register_host[128];
|
||||
int sip_register_interval; /* interval to register */
|
||||
int sip_options_interval; /* timer to keepalive invite/register transactions */
|
||||
char sip_public_ip[128];
|
||||
char sip_stun_server[128];
|
||||
int sip_stun_interval; /* timer to check own IP address */
|
||||
void *sip_inst; /* sip instance */
|
||||
unsigned short rtp_port_from;
|
||||
unsigned short rtp_port_to;
|
||||
#endif
|
||||
int rtp_bridge; /* bridge RTP directly (for calls comming from interface) */
|
||||
};
|
||||
|
|
9
sip.h
9
sip.h
|
@ -19,6 +19,7 @@ class Psip : public Port
|
|||
public:
|
||||
Psip(int type, char *portname, struct port_settings *settings, struct interface *interface);
|
||||
~Psip();
|
||||
unsigned int get_local_ip(unsigned int ip);
|
||||
int message_epoint(unsigned int epoint_id, int message, union parameter *param);
|
||||
int message_connect(unsigned int epoint_id, int message, union parameter *param);
|
||||
int message_release(unsigned int epoint_id, int message, union parameter *param);
|
||||
|
@ -32,10 +33,15 @@ class Psip : public Port
|
|||
void r_bye(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[]);
|
||||
void r_cancel(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[]);
|
||||
void r_invite(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[]);
|
||||
void i_options(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[]);
|
||||
void r_options(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[]);
|
||||
void i_state(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[]);
|
||||
void *p_s_sip_inst;
|
||||
struct lcr_work p_s_delete;
|
||||
nua_handle_t *p_s_handle;
|
||||
nua_magic_t *p_s_magic;
|
||||
struct lcr_timer p_s_invite_option_timer; /* time to send OPTION to invite transaction */
|
||||
int p_s_invite_direction; /* DIRECTION_* of invite */
|
||||
int p_s_rtp_bridge; /* bridge RTP instead of having a local RTP peer */
|
||||
unsigned short p_s_rtp_port_local;
|
||||
unsigned short p_s_rtp_port_remote;
|
||||
|
@ -64,11 +70,12 @@ class Psip : public Port
|
|||
unsigned char p_s_rxdata[160]; /* receive audio buffer */
|
||||
int p_s_rxpos; /* position in audio buffer 0..159 */
|
||||
int bridge_rx(unsigned char *data, int len);
|
||||
const char *generate_sdp(unsigned int rtp_ip_local, unsigned short rtp_port_local, int payloads, unsigned char *payload_types, int *media_types);
|
||||
int parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port, uint8_t *payload_types, int *media_types, int *payloads, int max_payloads);
|
||||
void rtp_shutdown(void);
|
||||
|
||||
/* audio */
|
||||
struct lcr_timer p_s_loadtimer; /* timer for audio transmission */
|
||||
struct lcr_timer p_s_load_timer; /* timer for audio transmission */
|
||||
virtual void update_load(void);
|
||||
void load_tx(void);
|
||||
unsigned int p_s_next_tv_sec; /* time stamp of next expected tx_load call, (to sync audio data) */
|
||||
|
|
6
todo.txt
6
todo.txt
|
@ -25,11 +25,7 @@ doku:
|
|||
|
||||
sip:
|
||||
|
||||
- blockiert der prozess beim lookup? (also invite oder register bei dns-unerreichbarkeit)
|
||||
- feste ip statt stun
|
||||
- ankommende registrierung
|
||||
- earlyb testen (siehe interface.conf)
|
||||
- callerid als display-info
|
||||
- ankommende authentisierung
|
||||
|
||||
routing:
|
||||
- ersetzten-operator
|
||||
|
|
4
trace.c
4
trace.c
|
@ -202,6 +202,10 @@ static char *print_trace(int detail, int port, char *interface, char *caller, ch
|
|||
/* elements */
|
||||
switch(detail) {
|
||||
case 1: /* brief */
|
||||
if (trace.interface[0]) {
|
||||
SPRINT(buffer, " iface %s", trace.interface);
|
||||
SCAT(trace_string, buffer);
|
||||
}
|
||||
if (trace.port >= 0) {
|
||||
SPRINT(buffer, " port %d", trace.port);
|
||||
SCAT(trace_string, buffer);
|
||||
|
|
Loading…
Reference in New Issue