Asterisk - The Open Source Telephony Project GIT-master-a358458
sip_to_pjsip.py
Go to the documentation of this file.
1#!/usr/bin/env python
2
3from __future__ import print_function
4
5import sys
6import optparse
7import socket
8try:
9 from urllib.parse import urlparse
10except ImportError:
11 from urlparse import urlparse # Python 2.7 required for Literal IPv6 Addresses
12import astdicts
13import astconfigparser
14
15PREFIX = 'pjsip_'
16QUIET = False
17
18###############################################################################
19### some utility functions
20###############################################################################
21
22
23def section_by_type(section, pjsip, type):
24 """Finds a section based upon the given type, adding it if not found."""
25 def __find_dict(mdicts, key, val):
26 """Given a list of multi-dicts, return the multi-dict that contains
27 the given key/value pair."""
28
29 def found(d):
30 return key in d and val in d[key]
31
32 try:
33 return [d for d in mdicts if found(d)][0]
34 except IndexError:
35 raise LookupError("Dictionary not located for key = %s, value = %s"
36 % (key, val))
37
38 try:
39 return __find_dict(pjsip.section(section), 'type', type)
40 except LookupError:
41 # section for type doesn't exist, so add
42 sect = pjsip.add_section(section)
43 sect['type'] = type
44 return sect
45
46
47def ignore(key=None, val=None, section=None, pjsip=None,
48 nmapped=None, type='endpoint'):
49 """Ignore a key and mark it as mapped"""
50
51
52def set_value(key=None, val=None, section=None, pjsip=None,
53 nmapped=None, type='endpoint'):
54 """Sets the key to the value within the section in pjsip.conf"""
55 def _set_value(k, v, s, r, n):
56 set_value(key if key else k, v, s, r, n, type)
57
58 # if no value or section return the set_value
59 # function with the enclosed key and type
60 if not val and not section:
61 return _set_value
62
63 # otherwise try to set the value
64 section_by_type(section, pjsip, type)[key] = \
65 val[0] if isinstance(val, list) else val
66
67
68def merge_value(key=None, val=None, section=None, pjsip=None,
69 nmapped=None, type='endpoint', section_to=None,
70 key_to=None):
71 """Merge values from the given section with those from the default."""
72 def _merge_value(k, v, s, r, n):
73 merge_value(key if key else k, v, s, r, n, type, section_to, key_to)
74
75 # if no value or section return the merge_value
76 # function with the enclosed key and type
77 if not val and not section:
78 return _merge_value
79
80 # should return a single value section list
81 try:
82 sect = sip.section(section)[0]
83 except LookupError:
84 sect = sip.default(section)[0]
85 # for each merged value add it to pjsip.conf
86 for i in sect.get_merged(key):
87 set_value(key_to if key_to else key, i,
88 section_to if section_to else section,
89 pjsip, nmapped, type)
90
91def merge_codec_value(key=None, val=None, section=None, pjsip=None,
92 nmapped=None, type='endpoint', section_to=None,
93 key_to=None):
94 """Merge values from allow/deny with those from the default. Special treatment for all"""
95 def _merge_codec_value(k, v, s, r, n):
96 merge_codec_value(key if key else k, v, s, r, n, type, section_to, key_to)
97
98 # if no value or section return the merge_codec_value
99 # function with the enclosed key and type
100 if not val and not section:
101 return _merge_codec_value
102
103 if key == 'allow':
104 try:
105 disallow = sip.get(section, 'disallow')[0]
106 if disallow == 'all':
107 #don't inherit
108 for i in sip.get(section, 'allow'):
109 set_value(key, i, section, pjsip, nmapped, type)
110 else:
111 merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
112 except LookupError:
113 print("lookup error", file=sys.stderr)
114 merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
115 return
116 elif key == 'disallow':
117 try:
118 allow = sip.get(section, 'allow')[0]
119 if allow == 'all':
120 #don't inherit
121 for i in sip.get(section, 'disallow'):
122 set_value(key, i, section, pjsip, nmapped, type)
123 else:
124 merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
125 except LookupError:
126 merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
127 return
128 else:
129 merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
130
131
132def non_mapped(nmapped):
133 """Write non-mapped sip.conf values to the non-mapped object"""
134 def _non_mapped(section, key, val):
135 """Writes a non-mapped value from sip.conf to the non-mapped object."""
136 if section not in nmapped:
137 nmapped[section] = astconfigparser.Section()
138 if isinstance(val, list):
139 for v in val:
140 # since coming from sip.conf we can assume
141 # single section lists
142 nmapped[section][0][key] = v
143 else:
144 nmapped[section][0][key] = val
145 return _non_mapped
146
147###############################################################################
148### mapping functions -
149### define f(key, val, section) where key/val are the key/value pair to
150### write to given section in pjsip.conf
151###############################################################################
152
153
154def set_dtmfmode(key, val, section, pjsip, nmapped):
155 """
156 Sets the dtmfmode value. If value matches allowable option in pjsip
157 then map it, otherwise set it to none.
158 """
159 key = 'dtmf_mode'
160 # available pjsip.conf values: rfc4733, inband, info, none
161 if val == 'inband' or val == 'info' or val == 'auto':
162 set_value(key, val, section, pjsip, nmapped)
163 elif val == 'rfc2833':
164 set_value(key, 'rfc4733', section, pjsip, nmapped)
165 else:
166 nmapped(section, key, val + " ; did not fully map - set to none")
167 set_value(key, 'none', section, pjsip, nmapped)
168
169
170def setup_udptl(section, pjsip, nmapped):
171 """Sets values from udptl into the appropriate pjsip.conf options."""
172 try:
173 val = sip.get(section, 't38pt_udptl')[0]
174 except LookupError:
175 try:
176 val = sip.get('general', 't38pt_udptl')[0]
177 except LookupError:
178 return
179
180 ec = 'none'
181 if 'yes' in val:
182 set_value('t38_udptl', 'yes', section, pjsip, nmapped)
183 if 'no' in val:
184 set_value('t38_udptl', 'no', section, pjsip, nmapped)
185 if 'redundancy' in val:
186 ec = 'redundancy'
187 if 'fec' in val:
188 ec = 'fec'
189 set_value('t38_udptl_ec', ec, section, pjsip, nmapped)
190
191def from_nat(key, val, section, pjsip, nmapped):
192 """Sets values from nat into the appropriate pjsip.conf options."""
193 # nat from sip.conf can be comma separated list of values:
194 # yes/no, [auto_]force_rport, [auto_]comedia
195 if 'yes' in val:
196 set_value('rtp_symmetric', 'yes', section, pjsip, nmapped)
197 set_value('rewrite_contact', 'yes', section, pjsip, nmapped)
198 if 'comedia' in val:
199 set_value('rtp_symmetric', 'yes', section, pjsip, nmapped)
200 if 'force_rport' in val:
201 set_value('force_rport', 'yes', section, pjsip, nmapped)
202 set_value('rewrite_contact', 'yes', section, pjsip, nmapped)
203
204
205def set_timers(key, val, section, pjsip, nmapped):
206 """
207 Sets the timers in pjsip.conf from the session-timers option
208 found in sip.conf.
209 """
210 # pjsip.conf values can be yes/no, required, always
211 # 'required' is a new feature of chan_pjsip, which rejects
212 # all SIP clients not supporting Session Timers
213 # 'Accept' is the default value of chan_sip and maps to 'yes'
214 # chan_sip ignores the case, for example 'session-timers=Refuse'
215 val = val.lower()
216 if val == 'originate':
217 set_value('timers', 'always', section, pjsip, nmapped)
218 elif val == 'refuse':
219 set_value('timers', 'no', section, pjsip, nmapped)
220 else:
221 set_value('timers', 'yes', section, pjsip, nmapped)
222
223
224def set_direct_media(key, val, section, pjsip, nmapped):
225 """
226 Maps values from the sip.conf comma separated direct_media option
227 into pjsip.conf direct_media options.
228 """
229 if 'yes' in val:
230 set_value('direct_media', 'yes', section, pjsip, nmapped)
231 if 'update' in val:
232 set_value('direct_media_method', 'update', section, pjsip, nmapped)
233 if 'outgoing' in val:
234 set_value('directed_media_glare_mitigation', 'outgoing', section,
235 pjsip, nmapped)
236 if 'nonat' in val:
237 set_value('disable_directed_media_on_nat', 'yes', section, pjsip,
238 nmapped)
239 if 'no' in val:
240 set_value('direct_media', 'no', section, pjsip, nmapped)
241
242
243def from_sendrpid(key, val, section, pjsip, nmapped):
244 """Sets the send_rpid/pai values in pjsip.conf."""
245 if val == 'yes' or val == 'rpid':
246 set_value('send_rpid', 'yes', section, pjsip, nmapped)
247 elif val == 'pai':
248 set_value('send_pai', 'yes', section, pjsip, nmapped)
249
250
251def set_media_encryption(key, val, section, pjsip, nmapped):
252 """Sets the media_encryption value in pjsip.conf"""
253 try:
254 dtls = sip.get(section, 'dtlsenable')[0]
255 if dtls == 'yes':
256 # If DTLS is enabled, then that overrides SDES encryption.
257 return
258 except LookupError:
259 pass
260
261 if val == 'yes':
262 set_value('media_encryption', 'sdes', section, pjsip, nmapped)
263
264
265def from_recordfeature(key, val, section, pjsip, nmapped):
266 """
267 If record on/off feature is set to automixmon then set
268 one_touch_recording, otherwise it can't be mapped.
269 """
270 set_value('one_touch_recording', 'yes', section, pjsip, nmapped)
271 set_value(key, val, section, pjsip, nmapped)
272
273def set_record_on_feature(key, val, section, pjsip, nmapped):
274 """Sets the record_on_feature in pjsip.conf"""
275 from_recordfeature('record_on_feature', val, section, pjsip, nmapped)
276
277def set_record_off_feature(key, val, section, pjsip, nmapped):
278 """Sets the record_off_feature in pjsip.conf"""
279 from_recordfeature('record_off_feature', val, section, pjsip, nmapped)
280
281def from_progressinband(key, val, section, pjsip, nmapped):
282 """Sets the inband_progress value in pjsip.conf"""
283 # progressinband can = yes/no/never
284 if val == 'never':
285 val = 'no'
286 set_value('inband_progress', val, section, pjsip, nmapped)
287
288
289def build_host(config, host, section='general', port_key=None):
290 """
291 Returns a string composed of a host:port. This assumes that the host
292 may have a port as part of the initial value. The port_key overrides
293 a port in host, see parameter 'bindport' in chan_sip.
294 """
295 try:
296 socket.inet_pton(socket.AF_INET6, host)
297 if not host.startswith('['):
298 # SIP URI will need brackets.
299 host = '[' + host + ']'
300 except socket.error:
301 pass
302
303 # Literal IPv6 (like [::]), IPv4, or hostname
304 # does not work for IPv6 without brackets; case catched above
305 url = urlparse('sip://' + host)
306
307 if port_key:
308 try:
309 port = config.get(section, port_key)[0]
310 host = url.hostname # no port, but perhaps no brackets
311 try:
312 socket.inet_pton(socket.AF_INET6, host)
313 if not host.startswith('['):
314 # SIP URI will need brackets.
315 host = '[' + host + ']'
316 except socket.error:
317 pass
318 return host + ':' + port
319 except LookupError:
320 pass
321
322 # Returns host:port, in brackets if required
323 # TODO Does not compress IPv6, for example 0:0:0:0:0:0:0:0 should get [::]
324 return url.netloc
325
326
327def from_host(key, val, section, pjsip, nmapped):
328 """
329 Sets contact info in an AOR section in pjsip.conf using 'host'
330 and 'port' data from sip.conf
331 """
332 # all aors have the same name as the endpoint so makes
333 # it easy to set endpoint's 'aors' value
334 set_value('aors', section, section, pjsip, nmapped)
335 if val == 'dynamic':
336 # Easy case. Just set the max_contacts on the aor and we're done
337 set_value('max_contacts', 1, section, pjsip, nmapped, 'aor')
338 return
339
340 result = 'sip:'
341
342 # More difficult case. The host will be either a hostname or
343 # IP address and may or may not have a port specified. pjsip.conf
344 # expects the contact to be a SIP URI.
345
346 user = None
347
348 try:
349 user = sip.multi_get(section, ['defaultuser', 'username'])[0]
350 result += user + '@'
351 except LookupError:
352 # It's fine if there's no user name
353 pass
354
355 result += build_host(sip, val, section, 'port')
356
357 set_value('contact', result, section, pjsip, nmapped, 'aor')
358
359
360def from_mailbox(key, val, section, pjsip, nmapped):
361 """
362 Determines whether a mailbox configured in sip.conf should map to
363 an endpoint or aor in pjsip.conf. If subscribemwi is true, then the
364 mailboxes are set on an aor. Otherwise the mailboxes are set on the
365 endpoint.
366 """
367
368 try:
369 subscribemwi = sip.get(section, 'subscribemwi')[0]
370 except LookupError:
371 # No subscribemwi option means default it to 'no'
372 subscribemwi = 'no'
373
374 set_value('mailboxes', val, section, pjsip, nmapped, 'aor'
375 if subscribemwi == 'yes' else 'endpoint')
376
377
378def setup_auth(key, val, section, pjsip, nmapped):
379 """
380 Sets up authentication information for a specific endpoint based on the
381 'secret' setting on a peer in sip.conf
382 """
383 set_value('username', section, section, pjsip, nmapped, 'auth')
384 # In chan_sip, if a secret and an md5secret are both specified on a peer,
385 # then in practice, only the md5secret is used. If both are encountered
386 # then we build an auth section that has both an md5_cred and password.
387 # However, the auth_type will indicate to authenticators to use the
388 # md5_cred, so like with sip.conf, the password will be there but have
389 # no purpose.
390 if key == 'secret':
391 set_value('password', val, section, pjsip, nmapped, 'auth')
392 else:
393 set_value('md5_cred', val, section, pjsip, nmapped, 'auth')
394 set_value('auth_type', 'md5', section, pjsip, nmapped, 'auth')
395
396 realms = [section]
397 try:
398 auths = sip.get('authentication', 'auth')
399 for i in auths:
400 user, at, realm = i.partition('@')
401 realms.append(realm)
402 except LookupError:
403 pass
404
405 realm_str = ','.join(realms)
406
407 set_value('auth', section, section, pjsip, nmapped)
408 set_value('outbound_auth', realm_str, section, pjsip, nmapped)
409
410
411def setup_ident(key, val, section, pjsip, nmapped):
412 """
413 Examines the 'type' field for a sip.conf peer and creates an identify
414 section if the type is either 'peer' or 'friend'. The identify section uses
415 either the host or defaultip field of the sip.conf peer.
416 """
417 if val != 'peer' and val != 'friend':
418 return
419
420 try:
421 ip = sip.get(section, 'host')[0]
422 except LookupError:
423 return
424
425 if ip == 'dynamic':
426 try:
427 ip = sip.get(section, 'defaultip')[0]
428 except LookupError:
429 return
430
431 set_value('endpoint', section, section, pjsip, nmapped, 'identify')
432 set_value('match', ip, section, pjsip, nmapped, 'identify')
433
434
435def from_encryption_taglen(key, val, section, pjsip, nmapped):
436 """Sets the srtp_tag32 option based on sip.conf encryption_taglen"""
437 if val == '32':
438 set_value('srtp_tag_32', 'yes', section, pjsip, nmapped)
439
440
441def from_dtlsenable(key, val, section, pjsip, nmapped):
442 """Optionally sets media_encryption=dtls based on sip.conf dtlsenable"""
443 if val == 'yes':
444 set_value('media_encryption', 'dtls', section, pjsip, nmapped)
445
446###############################################################################
447
448# options in pjsip.conf on an endpoint that have no sip.conf equivalent:
449# type, 100rel, trust_id_outbound, aggregate_mwi, connected_line_method
450
451# known sip.conf peer keys that can be mapped to a pjsip.conf section/key
452peer_map = [
453 # sip.conf option mapping function pjsip.conf option(s)
454 ###########################################################################
455 ['context', set_value],
456 ['dtmfmode', set_dtmfmode],
457 ['disallow', merge_codec_value],
458 ['allow', merge_codec_value],
459 ['nat', from_nat], # rtp_symmetric, force_rport,
460 # rewrite_contact
461 ['rtptimeout', set_value('rtp_timeout')],
462 ['icesupport', set_value('ice_support')],
463 ['autoframing', set_value('use_ptime')],
464 ['outboundproxy', set_value('outbound_proxy')],
465 ['mohsuggest', set_value('moh_suggest')],
466 ['session-timers', set_timers], # timers
467 ['session-minse', set_value('timers_min_se')],
468 ['session-expires', set_value('timers_sess_expires')],
469 # identify_by ?
470 ['canreinvite', set_direct_media], # direct_media alias
471 ['directmedia', set_direct_media], # direct_media
472 # direct_media_method
473 # directed_media_glare_mitigation
474 # disable_directed_media_on_nat
475 ['callerid', set_value], # callerid
476 ['callingpres', set_value('callerid_privacy')],
477 ['cid_tag', set_value('callerid_tag')],
478 ['trustrpid', set_value('trust_id_inbound')],
479 ['sendrpid', from_sendrpid], # send_pai, send_rpid
480 ['send_diversion', set_value],
481 ['encryption', set_media_encryption],
482 ['avpf', set_value('use_avpf')],
483 ['recordonfeature', set_record_on_feature], # automixon
484 ['recordofffeature', set_record_off_feature], # automixon
485 ['progressinband', from_progressinband], # in_band_progress
486 ['callgroup', set_value('call_group')],
487 ['pickupgroup', set_value('pickup_group')],
488 ['namedcallgroup', set_value('named_call_group')],
489 ['namedpickupgroup', set_value('named_pickup_group')],
490 ['allowtransfer', set_value('allow_transfer')],
491 ['fromuser', set_value('from_user')],
492 ['fromdomain', set_value('from_domain')],
493 ['mwifrom', set_value('mwi_from_user')],
494 ['tos_audio', set_value],
495 ['tos_video', set_value],
496 ['cos_audio', set_value],
497 ['cos_video', set_value],
498 ['sdpowner', set_value('sdp_owner')],
499 ['sdpsession', set_value('sdp_session')],
500 ['tonezone', set_value('tone_zone')],
501 ['language', set_value],
502 ['allowsubscribe', set_value('allow_subscribe')],
503 ['subscribecontext', set_value('subscribe_context')],
504 ['subminexpiry', set_value('sub_min_expiry')],
505 ['rtp_engine', set_value],
506 ['mailbox', from_mailbox],
507 ['busylevel', set_value('device_state_busy_at')],
508 ['secret', setup_auth],
509 ['md5secret', setup_auth],
510 ['type', setup_ident],
511 ['dtlsenable', from_dtlsenable],
512 ['dtlsverify', set_value('dtls_verify')],
513 ['dtlsrekey', set_value('dtls_rekey')],
514 ['dtlscertfile', set_value('dtls_cert_file')],
515 ['dtlsprivatekey', set_value('dtls_private_key')],
516 ['dtlscipher', set_value('dtls_cipher')],
517 ['dtlscafile', set_value('dtls_ca_file')],
518 ['dtlscapath', set_value('dtls_ca_path')],
519 ['dtlssetup', set_value('dtls_setup')],
520 ['encryption_taglen', from_encryption_taglen],
521 ['setvar', ignore],
522
523############################ maps to an aor ###################################
524
525 ['host', from_host], # contact, max_contacts
526 ['qualifyfreq', set_value('qualify_frequency', type='aor')],
527 ['maxexpiry', set_value('maximum_expiration', type='aor')],
528 ['minexpiry', set_value('minimum_expiration', type='aor')],
529 ['defaultexpiry', set_value('default_expiration', type='aor')],
530
531############################# maps to auth#####################################
532# type = auth
533# username
534# password
535# md5_cred
536# realm
537# nonce_lifetime
538# auth_type
539######################### maps to acl/security ################################
540
541 ['permit', merge_value(type='acl', section_to='acl')],
542 ['deny', merge_value(type='acl', section_to='acl')],
543 ['acl', merge_value(type='acl', section_to='acl')],
544 ['contactpermit', merge_value(type='acl', section_to='acl', key_to='contact_permit')],
545 ['contactdeny', merge_value(type='acl', section_to='acl', key_to='contact_deny')],
546 ['contactacl', merge_value(type='acl', section_to='acl', key_to='contact_acl')],
547
548########################### maps to transport #################################
549# type = transport
550# protocol
551# bind
552# async_operations
553# ca_list_file
554# ca_list_path
555# cert_file
556# privkey_file
557# password
558# external_signaling_address - externip & externhost
559# external_signaling_port
560# external_media_address
561# domain
562# verify_server
563# verify_client
564# require_client_cert
565# method
566# cipher
567# localnet
568######################### maps to domain_alias ################################
569# type = domain_alias
570# domain
571######################### maps to registration ################################
572# type = registration
573# server_uri
574# client_uri
575# contact_user
576# transport
577# outbound_proxy
578# expiration
579# retry_interval
580# max_retries
581# auth_rejection_permanent
582# outbound_auth
583########################### maps to identify ##################################
584# type = identify
585# endpoint
586# match
587]
588
589
591 """
592 Given an address in the form 'host:port' separate the host and port
593 components.
594 Returns a two-tuple of strings, (host, port). If no port is present in the
595 string, then the port section of the tuple is None.
596 """
597 try:
598 socket.inet_pton(socket.AF_INET6, addr)
599 if not addr.startswith('['):
600 return (addr, None)
601 except socket.error:
602 pass
603
604 # Literal IPv6 (like [::]), IPv4, or hostname
605 # does not work for IPv6 without brackets; case catched above
606 url = urlparse('sip://' + addr)
607 # TODO Does not compress IPv6, for example 0:0:0:0:0:0:0:0 should get [::]
608 return (url.hostname, url.port)
609
610
611def set_transport_common(section, sip, pjsip, protocol, nmapped):
612 """
613 sip.conf has several global settings that in pjsip.conf apply to individual
614 transports. This function adds these global settings to each individual
615 transport.
616
617 The settings included are:
618 externaddr (or externip)
619 externhost
620 externtcpport for TCP
621 externtlsport for TLS
622 localnet
623 tos_sip
624 cos_sip
625 """
626 try:
627 extern_addr = sip.multi_get('general', ['externaddr', 'externip',
628 'externhost'])[0]
629 host, port = split_hostport(extern_addr)
630 try:
631 port = sip.get('general', 'extern' + protocol + 'port')[0]
632 except LookupError:
633 pass
634 set_value('external_media_address', host, section, pjsip,
635 nmapped, 'transport')
636 set_value('external_signaling_address', host, section, pjsip,
637 nmapped, 'transport')
638 if port:
639 set_value('external_signaling_port', port, section, pjsip,
640 nmapped, 'transport')
641 except LookupError:
642 pass
643
644 try:
645 merge_value('localnet', sip.get('general', 'localnet')[0], 'general',
646 pjsip, nmapped, 'transport', section, "local_net")
647 except LookupError:
648 # No localnet options configured. Move on.
649 pass
650
651 try:
652 set_value('tos', sip.get('general', 'tos_sip')[0], section, pjsip,
653 nmapped, 'transport')
654 except LookupError:
655 pass
656
657 try:
658 set_value('cos', sip.get('general', 'cos_sip')[0], section, pjsip,
659 nmapped, 'transport')
660 except LookupError:
661 pass
662
663
664def get_bind(sip, pjsip, protocol):
665 """
666 Given the protocol (udp, tcp, or tls), return
667 - the bind address, like [::] or 0.0.0.0
668 - name of the section to be created
669 """
670 section = 'transport-' + protocol
671
672 # UDP cannot be disabled in chan_sip
673 if protocol != 'udp':
674 try:
675 enabled = sip.get('general', protocol + 'enable')[0]
676 except LookupError:
677 # No value means disabled by default. Don't create this transport
678 return (None, section)
679 if enabled != 'yes':
680 return (None, section)
681
682 try:
683 bind = pjsip.get(section, 'bind')[0]
684 # The first run created an transport already but this
685 # server was not configured for IPv4/IPv6 Dual Stack
686 return (None, section)
687 except LookupError:
688 pass
689
690 try:
691 bind = pjsip.get(section + '6', 'bind')[0]
692 # The first run created an IPv6 transport, because
693 # the server was configured with :: as bindaddr.
694 # Now, re-use its port and create the IPv4 transport
695 host, port = split_hostport(bind)
696 bind = '0.0.0.0'
697 if port:
698 bind += ':' + str(port)
699 except LookupError:
700 # This is the first run, no transport in pjsip exists.
701 try:
702 bind = sip.get('general', protocol + 'bindaddr')[0]
703 except LookupError:
704 if protocol == 'udp':
705 try:
706 bind = sip.get('general', 'bindaddr')[0]
707 except LookupError:
708 bind = '0.0.0.0'
709 else:
710 try:
711 bind = pjsip.get('transport-udp6', 'bind')[0]
712 except LookupError:
713 bind = pjsip.get('transport-udp', 'bind')[0]
714 # Only TCP reuses host:port of UDP, others reuse just host
715 if protocol == 'tls':
716 bind, port = split_hostport(bind)
717 host, port = split_hostport(bind)
718 if host == '::':
719 section += '6'
720
721 if protocol == 'udp':
722 host = build_host(sip, bind, 'general', 'bindport')
723 else:
724 host = build_host(sip, bind)
725
726 return (host, section)
727
728
729def create_udp(sip, pjsip, nmapped):
730 """
731 Creates a 'transport-udp' section in the pjsip.conf file based
732 on the following settings from sip.conf:
733
734 bindaddr (or udpbindaddr)
735 bindport
736 """
737 protocol = 'udp'
738 bind, section = get_bind(sip, pjsip, protocol)
739
740 set_value('protocol', protocol, section, pjsip, nmapped, 'transport')
741 set_value('bind', bind, section, pjsip, nmapped, 'transport')
742 set_transport_common(section, sip, pjsip, protocol, nmapped)
743
744
745def create_tcp(sip, pjsip, nmapped):
746 """
747 Creates a 'transport-tcp' section in the pjsip.conf file based
748 on the following settings from sip.conf:
749
750 tcpenable
751 tcpbindaddr (or bindaddr)
752 """
753 protocol = 'tcp'
754 bind, section = get_bind(sip, pjsip, protocol)
755 if not bind:
756 return
757
758 set_value('protocol', protocol, section, pjsip, nmapped, 'transport')
759 set_value('bind', bind, section, pjsip, nmapped, 'transport')
760 set_transport_common(section, sip, pjsip, protocol, nmapped)
761
762
763def set_tls_cert_file(val, pjsip, section, nmapped):
764 """Sets cert_file based on sip.conf tlscertfile"""
765 set_value('cert_file', val, section, pjsip, nmapped,
766 'transport')
767
768
769def set_tls_private_key(val, pjsip, section, nmapped):
770 """Sets privkey_file based on sip.conf tlsprivatekey or sslprivatekey"""
771 set_value('priv_key_file', val, section, pjsip, nmapped,
772 'transport')
773
774
775def set_tls_cipher(val, pjsip, section, nmapped):
776 """Sets cipher based on sip.conf tlscipher or sslcipher"""
777 if val == 'ALL':
778 return
779 print('chan_sip ciphers do not match 1:1 with PJSIP ciphers.' \
780 ' You should manually review and adjust this.', file=sys.stderr)
781
782 set_value('cipher', val, section, pjsip, nmapped, 'transport')
783
784
785def set_tls_cafile(val, pjsip, section, nmapped):
786 """Sets ca_list_file based on sip.conf tlscafile"""
787 set_value('ca_list_file', val, section, pjsip, nmapped,
788 'transport')
789
790
791def set_tls_capath(val, pjsip, section, nmapped):
792 """Sets ca_list_path based on sip.conf tlscapath"""
793 set_value('ca_list_path', val, section, pjsip, nmapped,
794 'transport')
795
796
797def set_tls_verifyclient(val, pjsip, section, nmapped):
798 """Sets verify_client based on sip.conf tlsverifyclient"""
799 set_value('verify_client', val, section, pjsip, nmapped,
800 'transport')
801
802
803def set_tls_verifyserver(val, pjsip, section, nmapped):
804 """Sets verify_server based on sip.conf tlsdontverifyserver"""
805
806 if val == 'no':
807 set_value('verify_server', 'yes', section, pjsip, nmapped,
808 'transport')
809 else:
810 set_value('verify_server', 'no', section, pjsip, nmapped,
811 'transport')
812
813
814def create_tls(sip, pjsip, nmapped):
815 """
816 Creates a 'transport-tls' section in pjsip.conf based on the following
817 settings from sip.conf:
818
819 tlsenable (or sslenable)
820 tlsbindaddr (or sslbindaddr or bindaddr)
821 tlsprivatekey (or sslprivatekey)
822 tlscipher (or sslcipher)
823 tlscafile
824 tlscapath (or tlscadir)
825 tlscertfile (or sslcert or tlscert)
826 tlsverifyclient
827 tlsdontverifyserver
828 tlsclientmethod (or sslclientmethod)
829 """
830 protocol = 'tls'
831 bind, section = get_bind(sip, pjsip, protocol)
832 if not bind:
833 return
834
835 set_value('protocol', protocol, section, pjsip, nmapped, 'transport')
836 set_value('bind', bind, section, pjsip, nmapped, 'transport')
837 set_transport_common(section, sip, pjsip, protocol, nmapped)
838
839 tls_map = [
840 (['tlscertfile', 'sslcert', 'tlscert'], set_tls_cert_file),
841 (['tlsprivatekey', 'sslprivatekey'], set_tls_private_key),
842 (['tlscipher', 'sslcipher'], set_tls_cipher),
843 (['tlscafile'], set_tls_cafile),
844 (['tlscapath', 'tlscadir'], set_tls_capath),
845 (['tlsverifyclient'], set_tls_verifyclient),
846 (['tlsdontverifyserver'], set_tls_verifyserver)
847 ]
848
849 for i in tls_map:
850 try:
851 i[1](sip.multi_get('general', i[0])[0], pjsip, section, nmapped)
852 except LookupError:
853 pass
854
855 try:
856 method = sip.multi_get('general', ['tlsclientmethod',
857 'sslclientmethod'])[0]
858 if section != 'transport-' + protocol + '6': # print only once
859 print('In chan_sip, you specified the TLS version. With chan_sip,' \
860 ' this was just for outbound client connections. In' \
861 ' chan_pjsip, this value is for client and server. Instead,' \
862 ' consider not to specify \'tlsclientmethod\' for chan_sip' \
863 ' and \'method = sslv23\' for chan_pjsip.', file=sys.stderr)
864 except LookupError:
865 """
866 OpenSSL emerged during the 90s. SSLv2 and SSLv3 were the only
867 existing methods at that time. The OpenSSL project continued. And as
868 of today (OpenSSL 1.0.2) this does not start SSLv2 and SSLv3 anymore
869 but TLSv1.0 and v1.2. Or stated differently: This method should
870 have been called 'method = secure' or 'method = automatic' back in
871 the 90s. The PJProject did not realize this and uses 'tlsv1' as
872 default when unspecified, which disables TLSv1.2. chan_sip used
873 'sslv23' as default when unspecified, which gives TLSv1.0 and v1.2.
874 """
875 method = 'sslv23'
876 set_value('method', method, section, pjsip, nmapped, 'transport')
877
878
879def map_transports(sip, pjsip, nmapped):
880 """
881 Finds options in sip.conf general section pertaining to
882 transport configuration and creates appropriate transport
883 configuration sections in pjsip.conf.
884
885 sip.conf only allows a single UDP transport, TCP transport,
886 and TLS transport for each IP version. As such, the mapping
887 into PJSIP can be made consistent by defining six sections:
888
889 transport-udp6
890 transport-udp
891 transport-tcp6
892 transport-tcp
893 transport-tls6
894 transport-tls
895
896 To accommodate the default behaviors in sip.conf, we'll need to
897 create the UDP transports first, followed by the TCP and TLS transports.
898 """
899
900 # First create a UDP transport. Even if no bind parameters were provided
901 # in sip.conf, chan_sip would always bind to UDP 0.0.0.0:5060
902 create_udp(sip, pjsip, nmapped)
903 create_udp(sip, pjsip, nmapped)
904
905 # TCP settings may be dependent on UDP settings, so do it second.
906 create_tcp(sip, pjsip, nmapped)
907 create_tcp(sip, pjsip, nmapped)
908 create_tls(sip, pjsip, nmapped)
909 create_tls(sip, pjsip, nmapped)
910
911
912def map_auth(sip, pjsip, nmapped):
913 """
914 Creates auth sections based on entries in the authentication section of
915 sip.conf. pjsip.conf section names consist of "auth_" followed by the name
916 of the realm.
917 """
918 try:
919 auths = sip.get('authentication', 'auth')
920 except LookupError:
921 return
922
923 for i in auths:
924 creds, at, realm = i.partition('@')
925 if not at and not realm:
926 # Invalid. Move on
927 continue
928 user, colon, secret = creds.partition(':')
929 if not secret:
930 user, sharp, md5 = creds.partition('#')
931 if not md5:
932 #Invalid. move on
933 continue
934 section = "auth_" + realm
935
936 set_value('realm', realm, section, pjsip, nmapped, 'auth')
937 set_value('username', user, section, pjsip, nmapped, 'auth')
938 if secret:
939 set_value('password', secret, section, pjsip, nmapped, 'auth')
940 else:
941 set_value('md5_cred', md5, section, pjsip, nmapped, 'auth')
942 set_value('auth_type', 'md5', section, pjsip, nmapped, 'auth')
943
944
946 """
947 Class for parsing and storing information in a register line in sip.conf.
948 """
949 def __init__(self, line, retry_interval, max_attempts, outbound_proxy):
950 self.retry_interval = retry_interval
951 self.max_attempts = max_attempts
952 self.outbound_proxy = outbound_proxy
953 self.parse(line)
954
955 def parse(self, line):
956 """
957 Initial parsing routine for register lines in sip.conf.
958
959 This splits the line into the part before the host, and the part
960 after the '@' symbol. These two parts are then passed to their
961 own parsing routines
962 """
963
964 # register =>
965 # [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry]
966
967 prehost, at, host_part = line.rpartition('@')
968 if not prehost:
969 raise
970
971 self.parse_host_part(host_part)
972 self.parse_user_part(prehost)
973
974 def parse_host_part(self, host_part):
975 """
976 Parsing routine for the part after the final '@' in a register line.
977 The strategy is to use partition calls to peel away the data starting
978 from the right and working to the left.
979 """
980 pre_expiry, sep, expiry = host_part.partition('~')
981 pre_extension, sep, self.extension = pre_expiry.partition('/')
982 self.host, sep, self.port = pre_extension.partition(':')
983
984 self.expiry = expiry if expiry else '120'
985
986 def parse_user_part(self, user_part):
987 """
988 Parsing routine for the part before the final '@' in a register line.
989 The only mandatory part of this line is the user portion. The strategy
990 here is to start by using partition calls to remove everything to
991 the right of the user, then finish by using rpartition calls to remove
992 everything to the left of the user.
993 """
994 self.peer = ''
995 self.protocol = 'udp'
996 protocols = ['udp', 'tcp', 'tls']
997 for protocol in protocols:
998 position = user_part.find(protocol + '://')
999 if -1 < position:
1000 post_transport = user_part[position + 6:]
1001 self.peer, sep, self.protocol = user_part[:position + 3].rpartition('?')
1002 user_part = post_transport
1003 break
1004
1005 colons = user_part.count(':')
1006 if (colons == 3):
1007 # :domainport:secret:authuser
1008 pre_auth, sep, port_auth = user_part.partition(':')
1009 self.domainport, sep, auth = port_auth.partition(':')
1010 self.secret, sep, self.authuser = auth.partition(':')
1011 elif (colons == 2):
1012 # :secret:authuser
1013 pre_auth, sep, auth = user_part.partition(':')
1014 self.secret, sep, self.authuser = auth.partition(':')
1015 elif (colons == 1):
1016 # :secret
1017 pre_auth, sep, self.secret = user_part.partition(':')
1018 elif (colons == 0):
1019 # No port, secret, or authuser
1020 pre_auth = user_part
1021 else:
1022 # Invalid setting
1023 raise
1024
1025 self.user, sep, self.domain = pre_auth.partition('@')
1026
1027 def write(self, pjsip, nmapped):
1028 """
1029 Write parsed registration data into a section in pjsip.conf
1030
1031 Most of the data in self will get written to a registration section.
1032 However, there will also need to be an auth section created if a
1033 secret or authuser is present.
1034
1035 General mapping of values:
1036 A combination of self.host and self.port is server_uri
1037 A combination of self.user, self.domain, and self.domainport is
1038 client_uri
1039 self.expiry is expiration
1040 self.extension is contact_user
1041 self.protocol will map to one of the mapped transports
1042 self.secret and self.authuser will result in a new auth section, and
1043 outbound_auth will point to that section.
1044 XXX self.peer really doesn't map to anything :(
1045 """
1046
1047 section = 'reg_' + self.host
1048
1049 set_value('retry_interval', self.retry_interval, section, pjsip,
1050 nmapped, 'registration')
1051 set_value('max_retries', self.max_attempts, section, pjsip, nmapped,
1052 'registration')
1053 if self.extension:
1054 set_value('contact_user', self.extension, section, pjsip, nmapped,
1055 'registration')
1056
1057 set_value('expiration', self.expiry, section, pjsip, nmapped,
1058 'registration')
1059
1060 if self.protocol == 'udp':
1061 set_value('transport', 'transport-udp', section, pjsip, nmapped,
1062 'registration')
1063 elif self.protocol == 'tcp':
1064 set_value('transport', 'transport-tcp', section, pjsip, nmapped,
1065 'registration')
1066 elif self.protocol == 'tls':
1067 set_value('transport', 'transport-tls', section, pjsip, nmapped,
1068 'registration')
1069
1070 auth_section = 'auth_reg_' + self.host
1071
1072 if hasattr(self, 'secret') and self.secret:
1073 set_value('password', self.secret, auth_section, pjsip, nmapped,
1074 'auth')
1075 set_value('username', self.authuser if hasattr(self, 'authuser')
1076 else self.user, auth_section, pjsip, nmapped, 'auth')
1077 set_value('outbound_auth', auth_section, section, pjsip, nmapped,
1078 'registration')
1079
1080 client_uri = "sip:%s@" % self.user
1081 if self.domain:
1082 client_uri += self.domain
1083 else:
1084 client_uri += self.host
1085
1086 if hasattr(self, 'domainport') and self.domainport:
1087 client_uri += ":" + self.domainport
1088 elif self.port:
1089 client_uri += ":" + self.port
1090
1091 set_value('client_uri', client_uri, section, pjsip, nmapped,
1092 'registration')
1093
1094 server_uri = "sip:%s" % self.host
1095 if self.port:
1096 server_uri += ":" + self.port
1097
1098 set_value('server_uri', server_uri, section, pjsip, nmapped,
1099 'registration')
1100
1101 if self.outbound_proxy:
1102 set_value('outboundproxy', self.outbound_proxy, section, pjsip,
1103 nmapped, 'registration')
1104
1105
1106def map_registrations(sip, pjsip, nmapped):
1107 """
1108 Gathers all necessary outbound registration data in sip.conf and creates
1109 corresponding registration sections in pjsip.conf
1110 """
1111 try:
1112 regs = sip.get('general', 'register')
1113 except LookupError:
1114 return
1115
1116 try:
1117 retry_interval = sip.get('general', 'registertimeout')[0]
1118 except LookupError:
1119 retry_interval = '20'
1120
1121 try:
1122 max_attempts = sip.get('general', 'registerattempts')[0]
1123 except LookupError:
1124 max_attempts = '10'
1125
1126 try:
1127 outbound_proxy = sip.get('general', 'outboundproxy')[0]
1128 except LookupError:
1129 outbound_proxy = ''
1130
1131 for i in regs:
1132 reg = Registration(i, retry_interval, max_attempts, outbound_proxy)
1133 reg.write(pjsip, nmapped)
1134
1135
1136def map_setvars(sip, section, pjsip, nmapped):
1137 """
1138 Map all setvar in peer section to the appropriate endpoint set_var
1139 """
1140 try:
1141 setvars = sip.section(section)[0].get('setvar')
1142 except LookupError:
1143 return
1144
1145 for setvar in setvars:
1146 set_value('set_var', setvar, section, pjsip, nmapped)
1147
1148
1149def map_peer(sip, section, pjsip, nmapped):
1150 """
1151 Map the options from a peer section in sip.conf into the appropriate
1152 sections in pjsip.conf
1153 """
1154 for i in peer_map:
1155 try:
1156 # coming from sip.conf the values should mostly be a list with a
1157 # single value. In the few cases that they are not a specialized
1158 # function (see merge_value) is used to retrieve the values.
1159 i[1](i[0], sip.get(section, i[0])[0], section, pjsip, nmapped)
1160 except LookupError:
1161 pass # key not found in sip.conf
1162
1163 setup_udptl(section, pjsip, nmapped)
1164
1165def find_non_mapped(sections, nmapped):
1166 """
1167 Determine sip.conf options that were not properly mapped to pjsip.conf
1168 options.
1169 """
1170 for section, sect in sections.iteritems():
1171 try:
1172 # since we are pulling from sip.conf this should always
1173 # be a single value list
1174 sect = sect[0]
1175 # loop through the section and store any values that were not
1176 # mapped
1177 for key in sect.keys(True):
1178 for i in peer_map:
1179 if i[0] == key:
1180 break
1181 else:
1182 nmapped(section, key, sect[key])
1183 except LookupError:
1184 pass
1185
1186
1187def map_system(sip, pjsip, nmapped):
1188 section = 'system' # Just a label; you as user can change that
1189 type = 'system' # Not a label, therefore not the same as section
1190
1191 try:
1192 user_agent = sip.get('general', 'useragent')[0]
1193 set_value('user_agent', user_agent, 'global', pjsip, nmapped, 'global')
1194 except LookupError:
1195 pass
1196
1197
1198 try:
1199 sipdebug = sip.get('general', 'sipdebug')[0]
1200 set_value('debug', sipdebug, 'global', pjsip, nmapped, 'global')
1201 except LookupError:
1202 pass
1203
1204 try:
1205 useroption_parsing = sip.get('general', 'legacy_useroption_parsing')[0]
1206 set_value('ignore_uri_user_options', useroption_parsing, 'global', pjsip, nmapped, 'global')
1207 except LookupError:
1208 pass
1209
1210 try:
1211 timer_t1 = sip.get('general', 'timert1')[0]
1212 set_value('timer_t1', timer_t1, section, pjsip, nmapped, type)
1213 except LookupError:
1214 pass
1215
1216 try:
1217 timer_b = sip.get('general', 'timerb')[0]
1218 set_value('timer_b', timer_b, section, pjsip, nmapped, type)
1219 except LookupError:
1220 pass
1221
1222 try:
1223 compact_headers = sip.get('general', 'compactheaders')[0]
1224 set_value('compact_headers', compact_headers, section, pjsip, nmapped, type)
1225 except LookupError:
1226 pass
1227
1228
1229def convert(sip, filename, non_mappings, include):
1230 """
1231 Entry point for configuration file conversion. This
1232 function will create a pjsip.conf object and begin to
1233 map specific sections from sip.conf into it.
1234 Returns the new pjsip.conf object once completed
1235 """
1236 pjsip = sip.__class__()
1237 non_mappings[filename] = astdicts.MultiOrderedDict()
1238 nmapped = non_mapped(non_mappings[filename])
1239 if not include:
1240 # Don't duplicate transport and registration configs
1241 map_system(sip, pjsip, nmapped)
1242 map_transports(sip, pjsip, nmapped)
1243 map_registrations(sip, pjsip, nmapped)
1244 map_auth(sip, pjsip, nmapped)
1245 for section in sip.sections():
1246 if section == 'authentication':
1247 pass
1248 else:
1249 map_peer(sip, section, pjsip, nmapped)
1250 map_setvars(sip, section, pjsip, nmapped)
1251
1252 find_non_mapped(sip.defaults(), nmapped)
1253 find_non_mapped(sip.sections(), nmapped)
1254
1255 for key, val in sip.includes().iteritems():
1256 pjsip.add_include(PREFIX + key, convert(val, PREFIX + key,
1257 non_mappings, True)[0])
1258 return pjsip, non_mappings
1259
1260
1261def write_pjsip(filename, pjsip, non_mappings):
1262 """
1263 Write pjsip.conf file to disk
1264 """
1265 try:
1266 with open(filename, 'wt') as fp:
1267 fp.write(';--\n')
1268 fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n')
1269 fp.write('Non mapped elements start\n')
1270 fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n')
1271 astconfigparser.write_dicts(fp, non_mappings[filename])
1272 fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n')
1273 fp.write('Non mapped elements end\n')
1274 fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n')
1275 fp.write('--;\n\n')
1276 # write out include file(s)
1277 pjsip.write(fp)
1278
1279 except IOError:
1280 print("Could not open file " + filename + " for writing", file=sys.stderr)
1281
1282###############################################################################
1283
1284
1286 """
1287 Parse command line options and apply them. If invalid input is given,
1288 print usage information
1289 """
1290 global PREFIX
1291 global QUIET
1292 usage = "usage: %prog [options] [input-file [output-file]]\n\n" \
1293 "Converts the chan_sip configuration input-file to the chan_pjsip output-file.\n" \
1294 "The input-file defaults to 'sip.conf'.\n" \
1295 "The output-file defaults to 'pjsip.conf'."
1296 parser = optparse.OptionParser(usage=usage)
1297 parser.add_option('-p', '--prefix', dest='prefix', default=PREFIX,
1298 help='output prefix for include files')
1299 parser.add_option('-q', '--quiet', dest='quiet', default=False, action='store_true',
1300 help="don't print messages to stdout")
1301
1302 options, args = parser.parse_args()
1303 PREFIX = options.prefix
1304 if options.quiet:
1305 QUIET = True
1306
1307 sip_filename = args[0] if len(args) else 'sip.conf'
1308 pjsip_filename = args[1] if len(args) == 2 else 'pjsip.conf'
1309
1310 return sip_filename, pjsip_filename
1311
1312
1313def info(msg):
1314 if QUIET:
1315 return
1316 print(msg)
1317
1318
1319if __name__ == "__main__":
1320 sip_filename, pjsip_filename = cli_options()
1321 # configuration parser for sip.conf
1323 info('Please, report any issue at:')
1324 info(' https://github.com/asterisk/asterisk/issues/')
1325 info('Reading ' + sip_filename)
1326 sip.read(sip_filename)
1327 info('Converting to PJSIP...')
1328 pjsip, non_mappings = convert(sip, pjsip_filename, dict(), False)
1329 info('Writing ' + pjsip_filename)
1330 write_pjsip(pjsip_filename, pjsip, non_mappings)
const char * str
Definition: app_jack.c:147
def parse_host_part(self, host_part)
def write(self, pjsip, nmapped)
def __init__(self, line, retry_interval, max_attempts, outbound_proxy)
def parse_user_part(self, user_part)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
def write_dicts(config_file, mdicts)
def setup_ident(key, val, section, pjsip, nmapped)
def convert(sip, filename, non_mappings, include)
def map_auth(sip, pjsip, nmapped)
def ignore(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint')
Definition: sip_to_pjsip.py:48
def build_host(config, host, section='general', port_key=None)
def map_transports(sip, pjsip, nmapped)
def set_tls_verifyserver(val, pjsip, section, nmapped)
def setup_udptl(section, pjsip, nmapped)
def set_tls_cert_file(val, pjsip, section, nmapped)
def map_peer(sip, section, pjsip, nmapped)
def set_tls_cafile(val, pjsip, section, nmapped)
def set_tls_cipher(val, pjsip, section, nmapped)
def set_tls_private_key(val, pjsip, section, nmapped)
def info(msg)
def set_dtmfmode(key, val, section, pjsip, nmapped)
mapping functions - define f(key, val, section) where key/val are the key/value pair to write to give...
def set_record_off_feature(key, val, section, pjsip, nmapped)
def set_value(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint')
Definition: sip_to_pjsip.py:53
def setup_auth(key, val, section, pjsip, nmapped)
def from_mailbox(key, val, section, pjsip, nmapped)
def set_timers(key, val, section, pjsip, nmapped)
def create_tcp(sip, pjsip, nmapped)
def from_host(key, val, section, pjsip, nmapped)
def merge_value(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint', section_to=None, key_to=None)
Definition: sip_to_pjsip.py:70
def from_dtlsenable(key, val, section, pjsip, nmapped)
def from_encryption_taglen(key, val, section, pjsip, nmapped)
def create_tls(sip, pjsip, nmapped)
def from_nat(key, val, section, pjsip, nmapped)
def set_record_on_feature(key, val, section, pjsip, nmapped)
def map_registrations(sip, pjsip, nmapped)
def write_pjsip(filename, pjsip, non_mappings)
def set_direct_media(key, val, section, pjsip, nmapped)
def set_transport_common(section, sip, pjsip, protocol, nmapped)
def map_setvars(sip, section, pjsip, nmapped)
def section_by_type(section, pjsip, type)
some utility functions
Definition: sip_to_pjsip.py:23
def merge_codec_value(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint', section_to=None, key_to=None)
Definition: sip_to_pjsip.py:93
def set_tls_capath(val, pjsip, section, nmapped)
def from_progressinband(key, val, section, pjsip, nmapped)
def set_media_encryption(key, val, section, pjsip, nmapped)
def from_sendrpid(key, val, section, pjsip, nmapped)
def map_system(sip, pjsip, nmapped)
def split_hostport(addr)
def get_bind(sip, pjsip, protocol)
def set_tls_verifyclient(val, pjsip, section, nmapped)
def create_udp(sip, pjsip, nmapped)
def from_recordfeature(key, val, section, pjsip, nmapped)
def find_non_mapped(sections, nmapped)
def non_mapped(nmapped)