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:
Andreas Eversberg 2017-11-01 20:43:13 +01:00
parent 8ed162789e
commit 00675eb48b
8 changed files with 1376 additions and 216 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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"},

View File

@ -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) */
};

1273
sip.cpp

File diff suppressed because it is too large Load Diff

9
sip.h
View File

@ -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) */

View File

@ -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

View File

@ -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);