/* * pj_thread_create(...) */ PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **ptr_thread) { #if PJ_HAS_THREADS pj_thread_t *rec; pthread_attr_t thread_attr; void *stack_addr; int rc; PJ_UNUSED_ARG(stack_addr); PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); /* Create thread record and assign name for the thread */ rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t)); PJ_ASSERT_RETURN(rec, PJ_ENOMEM); /* Set name. */ if (!thread_name) thread_name = "thr%p"; if (strchr(thread_name, '%')) { pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); } else { strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } /* Set default stack size */ if (stack_size == 0) stack_size = PJ_THREAD_DEFAULT_STACK_SIZE; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 rec->stk_size = stack_size; rec->stk_max_usage = 0; #endif /* Emulate suspended thread with mutex. */ if (flags & PJ_THREAD_SUSPENDED) { rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex); if (rc != PJ_SUCCESS) { return rc; } pj_mutex_lock(rec->suspended_mutex); } else { pj_assert(rec->suspended_mutex == NULL); } /* Init thread attributes */ pthread_attr_init(&thread_attr); #if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0 /* Set thread's stack size */ rc = pthread_attr_setstacksize(&thread_attr, stack_size); if (rc != 0) return PJ_RETURN_OS_ERROR(rc); #endif /* PJ_THREAD_SET_STACK_SIZE */ #if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0 /* Allocate memory for the stack */ stack_addr = pj_pool_alloc(pool, stack_size); PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM); rc = pthread_attr_setstackaddr(&thread_attr, stack_addr); if (rc != 0) return PJ_RETURN_OS_ERROR(rc); #endif /* PJ_THREAD_ALLOCATE_STACK */ /* Create the thread. */ rec->proc = proc; rec->arg = arg; rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec); if (rc != 0) { return PJ_RETURN_OS_ERROR(rc); } *ptr_thread = rec; PJ_LOG(6, (rec->obj_name, "Thread created")); return PJ_SUCCESS; #else pj_assert(!"Threading is disabled!"); return PJ_EINVALIDOP; #endif }
/* Update local media session (offer) to create active local session * after receiving remote answer. */ static pj_status_t process_answer(pj_pool_t *pool, pjmedia_sdp_session *offer, pjmedia_sdp_session *answer, pj_bool_t allow_asym, pjmedia_sdp_session **p_active) { unsigned omi = 0; /* Offer media index */ unsigned ami = 0; /* Answer media index */ pj_bool_t has_active = PJ_FALSE; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(pool && offer && answer && p_active, PJ_EINVAL); /* Check that media count match between offer and answer */ // Ticket #527, different media count is allowed for more interoperability, // however, the media order must be same between offer and answer. // if (offer->media_count != answer->media_count) // return PJMEDIA_SDPNEG_EMISMEDIA; /* Now update each media line in the offer with the answer. */ for (; omi<offer->media_count; ++omi) { if (ami == answer->media_count) { /* The answer has less media than the offer */ pjmedia_sdp_media *am; /* Generate matching-but-disabled-media for the answer */ am = pjmedia_sdp_media_clone_deactivate(pool, offer->media[omi]); answer->media[answer->media_count++] = am; ++ami; /* Deactivate our media offer too */ pjmedia_sdp_media_deactivate(pool, offer->media[omi]); /* No answer media to be negotiated */ continue; } status = process_m_answer(pool, offer->media[omi], answer->media[ami], allow_asym); /* If media type is mismatched, just disable the media. */ if (status == PJMEDIA_SDPNEG_EINVANSMEDIA) { pjmedia_sdp_media_deactivate(pool, offer->media[omi]); continue; } /* No common format in the answer media. */ else if (status == PJMEDIA_SDPNEG_EANSNOMEDIA) { pjmedia_sdp_media_deactivate(pool, offer->media[omi]); pjmedia_sdp_media_deactivate(pool, answer->media[ami]); } /* Return the error code, for other errors. */ else if (status != PJ_SUCCESS) { return status; } if (offer->media[omi]->desc.port != 0) has_active = PJ_TRUE; ++ami; } *p_active = offer; return has_active ? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA; }
/* * Parse address */ PJ_DEF(pj_status_t) pj_sockaddr_parse2(int af, unsigned options, const pj_str_t *str, pj_str_t *p_hostpart, pj_uint16_t *p_port, int *raf) { const char *end = str->ptr + str->slen; const char *last_colon_pos = NULL; unsigned colon_cnt = 0; const char *p; PJ_ASSERT_RETURN((af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && options==0 && str!=NULL, PJ_EINVAL); /* Special handling for empty input */ if (str->slen==0 || str->ptr==NULL) { if (p_hostpart) p_hostpart->slen = 0; if (p_port) *p_port = 0; if (raf) *raf = PJ_AF_INET; return PJ_SUCCESS; } /* Count the colon and get the last colon */ for (p=str->ptr; p!=end; ++p) { if (*p == ':') { ++colon_cnt; last_colon_pos = p; } } /* Deduce address family if it's not given */ if (af == PJ_AF_UNSPEC) { if (colon_cnt > 1) af = PJ_AF_INET6; else af = PJ_AF_INET; } else if (af == PJ_AF_INET && colon_cnt > 1) return PJ_EINVAL; if (raf) *raf = af; if (af == PJ_AF_INET) { /* Parse as IPv4. Supported formats: * - "10.0.0.1:80" * - "10.0.0.1" * - "10.0.0.1:" * - ":80" * - ":" */ pj_str_t hostpart; unsigned long port; hostpart.ptr = (char*)str->ptr; if (last_colon_pos) { pj_str_t port_part; int i; hostpart.slen = last_colon_pos - str->ptr; port_part.ptr = (char*)last_colon_pos + 1; port_part.slen = end - port_part.ptr; /* Make sure port number is valid */ for (i=0; i<port_part.slen; ++i) { if (!pj_isdigit(port_part.ptr[i])) return PJ_EINVAL; } port = pj_strtoul(&port_part); if (port > 65535) return PJ_EINVAL; } else { hostpart.slen = str->slen; port = 0; } if (p_hostpart) *p_hostpart = hostpart; if (p_port) *p_port = (pj_uint16_t)port; return PJ_SUCCESS; } else if (af == PJ_AF_INET6) { /* Parse as IPv6. Supported formats: * - "fe::01:80" ==> note: port number is zero in this case, not 80! * - "[fe::01]:80" * - "fe::01" * - "fe::01:" * - "[fe::01]" * - "[fe::01]:" * - "[::]:80" * - ":::80" * - "[::]" * - "[::]:" * - ":::" * - "::" */ pj_str_t hostpart, port_part; if (*str->ptr == '[') { char *end_bracket; int i; unsigned long port; if (last_colon_pos == NULL) return PJ_EINVAL; end_bracket = pj_strchr(str, ']'); if (end_bracket == NULL) return PJ_EINVAL; hostpart.ptr = (char*)str->ptr + 1; hostpart.slen = end_bracket - hostpart.ptr; if (last_colon_pos < end_bracket) { port_part.ptr = NULL; port_part.slen = 0; } else { port_part.ptr = (char*)last_colon_pos + 1; port_part.slen = end - port_part.ptr; } /* Make sure port number is valid */ for (i=0; i<port_part.slen; ++i) { if (!pj_isdigit(port_part.ptr[i])) return PJ_EINVAL; } port = pj_strtoul(&port_part); if (port > 65535) return PJ_EINVAL; if (p_hostpart) *p_hostpart = hostpart; if (p_port) *p_port = (pj_uint16_t)port; return PJ_SUCCESS; } else { /* Treat everything as part of the IPv6 IP address */ if (p_hostpart) *p_hostpart = *str; if (p_port) *p_port = 0; return PJ_SUCCESS; } } else { return PJ_EAFNOTSUP; } }
/* Create complete answer for remote's offer. */ static pj_status_t create_answer( pj_pool_t *pool, pj_bool_t prefer_remote_codec_order, const pjmedia_sdp_session *initial, const pjmedia_sdp_session *offer, pjmedia_sdp_session **p_answer) { pj_status_t status = PJMEDIA_SDPNEG_ENOMEDIA; pj_bool_t has_active = PJ_FALSE; pjmedia_sdp_session *answer; char media_used[PJMEDIA_MAX_SDP_MEDIA]; unsigned i; /* Validate remote offer. * This should have been validated before. */ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(offer))==PJ_SUCCESS, status); /* Create initial answer by duplicating initial SDP, * but clear all media lines. The media lines will be filled up later. */ answer = pjmedia_sdp_session_clone(pool, initial); PJ_ASSERT_RETURN(answer != NULL, PJ_ENOMEM); answer->media_count = 0; pj_bzero(media_used, sizeof(media_used)); /* For each media line, create our answer based on our initial * capability. */ for (i=0; i<offer->media_count; ++i) { const pjmedia_sdp_media *om; /* offer */ const pjmedia_sdp_media *im; /* initial media */ pjmedia_sdp_media *am = NULL; /* answer/result */ unsigned j; om = offer->media[i]; /* Find media description in our initial capability that matches * the media type and transport type of offer's media, has * matching codec, and has not been used to answer other offer. */ for (im=NULL, j=0; j<initial->media_count; ++j) { im = initial->media[j]; if (pj_strcmp(&om->desc.media, &im->desc.media)==0 && pj_strcmp(&om->desc.transport, &im->desc.transport)==0 && media_used[j] == 0) { /* See if it has matching codec. */ status = match_offer(pool, prefer_remote_codec_order, om, im, &am); if (status == PJ_SUCCESS) { /* Mark media as used. */ media_used[j] = 1; break; } } } if (j==initial->media_count) { /* No matching media. * Reject the offer by setting the port to zero in the answer. */ /* For simplicity in the construction of the answer, we'll * just clone the media from the offer. Anyway receiver will * ignore anything in the media once it sees that the port * number is zero. */ am = pjmedia_sdp_media_clone_deactivate(pool, om); } else { /* The answer is in am */ pj_assert(am != NULL); } /* Add the media answer */ answer->media[answer->media_count++] = am; /* Check if this media is active.*/ if (am->desc.port != 0) has_active = PJ_TRUE; } *p_answer = answer; return has_active ? PJ_SUCCESS : status; }
PJ_DEF(pj_bool_t) pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg) { PJ_ASSERT_RETURN(neg, PJ_FALSE); return neg->answer_was_remote; }
/* This callback is called by transport manager for the TLS factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_tx_data *tdata, pjsip_transport **p_transport) { struct tls_listener *listener; struct tls_transport *tls; pj_pool_t *pool; pj_ssl_sock_t *ssock; pj_ssl_sock_param ssock_param; pj_sockaddr local_addr; pj_str_t remote_name; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in or sockaddr_in6*/ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in)) || (rem_addr->addr.sa_family == pj_AF_INET6() && addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tls_listener*)factory; pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); /* Get remote host name from tdata */ if (tdata) remote_name = tdata->dest_info.name; else pj_bzero(&remote_name, sizeof(remote_name)); /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.sock_af = (factory->type & PJSIP_TRANSPORT_IPV6) ? pj_AF_INET6() : pj_AF_INET(); ssock_param.cb.on_connect_complete = &on_connect_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; ssock_param.async_cnt = 1; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); ssock_param.server_name = remote_name; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = NULL; /* pending, must be set later */ ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket * due to verification error */ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; ssock_param.ciphers_num = listener->tls_setting.ciphers_num; ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, sizeof(ssock_param.qos_params)); switch(listener->tls_setting.method) { case PJSIP_TLSV1_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1; break; case PJSIP_SSLV2_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2; break; case PJSIP_SSLV3_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3; break; case PJSIP_SSLV23_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23; break; default: ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT; break; } status = pj_ssl_sock_create(pool, &ssock_param, &ssock); if (status != PJ_SUCCESS) return status; /* Apply SSL certificate */ if (listener->cert) { status = pj_ssl_sock_set_certificate(ssock, pool, listener->cert); if (status != PJ_SUCCESS) return status; } /* Initially set bind address to listener's bind address */ pj_sockaddr_init(listener->bound_addr.addr.sa_family, &local_addr, NULL, 0); pj_sockaddr_copy_addr(&local_addr, &listener->bound_addr); /* Create the transport descriptor */ status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr, rem_addr, &remote_name, &tls); if (status != PJ_SUCCESS) return status; /* Set the "pending" SSL socket user data */ pj_ssl_sock_set_user_data(tls->ssock, tls); /* Start asynchronous connect() operation */ tls->has_pending_connect = PJ_TRUE; status = pj_ssl_sock_start_connect(tls->ssock, tls->base.pool, (pj_sockaddr_t*)&local_addr, (pj_sockaddr_t*)rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tls->ssock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tls_destroy(&tls->base, status); return status; } if (tls->has_pending_connect) { pj_ssl_sock_info info; /* Update local address, just in case local address currently set is * different now that asynchronous connect() is started. */ /* Retrieve the bound address */ status = pj_ssl_sock_get_info(tls->ssock, &info); if (status == PJ_SUCCESS) { pj_uint16_t new_port; new_port = pj_sockaddr_get_port((pj_sockaddr_t*)&info.local_addr); if (pj_sockaddr_has_addr((pj_sockaddr_t*)&info.local_addr)) { /* Update sockaddr */ pj_sockaddr_cp((pj_sockaddr_t*)&tls->base.local_addr, (pj_sockaddr_t*)&info.local_addr); } else if (new_port && new_port != pj_sockaddr_get_port( (pj_sockaddr_t*)&tls->base.local_addr)) { /* Update port only */ pj_sockaddr_set_port(&tls->base.local_addr, new_port); } sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, &tls->base.local_addr); } PJ_LOG(4,(tls->base.obj_name, "TLS transport %.*s:%d is connecting to %.*s:%d...", (int)tls->base.local_name.host.slen, tls->base.local_name.host.ptr, tls->base.local_name.port, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); } /* Done */ *p_transport = &tls->base; return PJ_SUCCESS; }
static int multipart_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size) { const struct multipart_data *m_data; pj_str_t clen_hdr = { "Content-Length: ", 16}; pjsip_multipart_part *part; char *p = buf, *end = buf+size; #define SIZE_LEFT() (end-p) m_data = (const struct multipart_data*)msg_body->data; PJ_ASSERT_RETURN(m_data && !pj_list_empty(&m_data->part_head), PJ_EINVAL); part = m_data->part_head.next; while (part != &m_data->part_head) { enum { CLEN_SPACE = 5 }; char *clen_pos; const pjsip_hdr *hdr; clen_pos = NULL; /* Print delimiter */ if (SIZE_LEFT() <= (m_data->boundary.slen+8) << 1) return -1; *p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-'; pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen); p += m_data->boundary.slen; *p++ = 13; *p++ = 10; /* Print optional headers */ hdr = part->hdr.next; while (hdr != &part->hdr) { int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, SIZE_LEFT()-2); if (printed < 0) return -1; p += printed; *p++ = '\r'; *p++ = '\n'; hdr = hdr->next; } /* Automaticly adds Content-Type and Content-Length headers, only * if content_type is set in the message body. */ if (part->body && part->body->content_type.type.slen) { pj_str_t ctype_hdr = { "Content-Type: ", 14}; const pjsip_media_type *media = &part->body->content_type; if (pjsip_use_compact_form) { ctype_hdr.ptr = "c: "; ctype_hdr.slen = 3; } /* Add Content-Type header. */ if ( (end-p) < 24 + media->type.slen + media->subtype.slen) { return -1; } pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); p += ctype_hdr.slen; p += pjsip_media_type_print(p, end-p, media); *p++ = '\r'; *p++ = '\n'; /* Add Content-Length header. */ if ((end-p) < clen_hdr.slen + 12 + 2) { return -1; } pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); p += clen_hdr.slen; /* Print blanks after "Content-Length:", this is where we'll put * the content length value after we know the length of the * body. */ pj_memset(p, ' ', CLEN_SPACE); clen_pos = p; p += CLEN_SPACE; *p++ = '\r'; *p++ = '\n'; } /* Empty newline */ *p++ = 13; *p++ = 10; /* Print the body */ pj_assert(part->body != NULL); if (part->body) { int printed = part->body->print_body(part->body, p, SIZE_LEFT()); if (printed < 0) return -1; p += printed; /* Now that we have the length of the body, print this to the * Content-Length header. */ if (clen_pos) { char tmp[16]; int len; len = pj_utoa(printed, tmp); if (len > CLEN_SPACE) len = CLEN_SPACE; pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len); } } part = part->next; } /* Print closing delimiter */ if (SIZE_LEFT() < m_data->boundary.slen+8) return -1; *p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-'; pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen); p += m_data->boundary.slen; *p++ = '-'; *p++ = '-'; *p++ = 13; *p++ = 10; #undef SIZE_LEFT return p - buf; }
/* Set client credentials. */ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, int cred_cnt, const pjsip_cred_info *c) { PJ_ASSERT_RETURN(sess && c, PJ_EINVAL); if (cred_cnt == 0) { sess->cred_cnt = 0; } else { int i; sess->cred_info = (pjsip_cred_info*) pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c)); for (i=0; i<cred_cnt; ++i) { sess->cred_info[i].data_type = c[i].data_type; /* When data_type is PJSIP_CRED_DATA_EXT_AKA, * callback must be specified. */ if ((c[i].data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { #if !PJSIP_HAS_DIGEST_AKA_AUTH if (!PJSIP_HAS_DIGEST_AKA_AUTH) { pj_assert(!"PJSIP_HAS_DIGEST_AKA_AUTH is not enabled"); return PJSIP_EAUTHINAKACRED; } #endif /* Callback must be specified */ PJ_ASSERT_RETURN(c[i].ext.aka.cb != NULL, PJ_EINVAL); /* Verify K len */ PJ_ASSERT_RETURN(c[i].ext.aka.k.slen <= PJSIP_AKA_KLEN, PJSIP_EAUTHINAKACRED); /* Verify OP len */ PJ_ASSERT_RETURN(c[i].ext.aka.op.slen <= PJSIP_AKA_OPLEN, PJSIP_EAUTHINAKACRED); /* Verify AMF len */ PJ_ASSERT_RETURN(c[i].ext.aka.amf.slen <= PJSIP_AKA_AMFLEN, PJSIP_EAUTHINAKACRED); sess->cred_info[i].ext.aka.cb = c[i].ext.aka.cb; pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.k, &c[i].ext.aka.k); pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.op, &c[i].ext.aka.op); pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.amf, &c[i].ext.aka.amf); } pj_strdup(sess->pool, &sess->cred_info[i].scheme, &c[i].scheme); pj_strdup(sess->pool, &sess->cred_info[i].realm, &c[i].realm); pj_strdup(sess->pool, &sess->cred_info[i].username, &c[i].username); pj_strdup(sess->pool, &sess->cred_info[i].data, &c[i].data); } sess->cred_cnt = cred_cnt; } return PJ_SUCCESS; }
/* * Create Authorization/Proxy-Authorization response header based on the challege * in WWW-Authenticate/Proxy-Authenticate header. */ static pj_status_t auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **p_h_auth) { pjsip_authorization_hdr *hauth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(req_pool && hdr && uri && cred_info && method && sess_pool && cached_auth && p_h_auth, PJ_EINVAL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); return PJSIP_EURITOOLONG; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) hauth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) hauth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(!"Invalid response header!"); return PJSIP_EINVALIDHDR; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (cached_auth) { update_digest_session( sess_pool, cached_auth, hdr ); cnonce = &cached_auth->cnonce; nc = cached_auth->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ hauth->scheme = pjsip_DIGEST_STR; status = respond_digest( pool, &hauth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (status != PJ_SUCCESS) return status; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && cached_auth) { if (cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &hauth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { cached_auth->qop_value = PJSIP_AUTH_QOP_AUTH; } else { cached_auth->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { return PJSIP_EINVALIDAUTHSCHEME; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (hauth && cached_auth && cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = cached_auth->cached_hdr.next; while (cached_hdr != &cached_auth->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &cached_auth->cached_hdr) { cached_hdr->hdr = hauth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = hauth; pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr ); } } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (hdr != cached_auth->last_chal) { cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr); } # endif } # endif *p_h_auth = hauth; return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool, pj_bool_t high_quality, pj_bool_t large_filter, unsigned channel_count, unsigned rate_in, unsigned rate_out, unsigned samples_per_frame, pjmedia_resample **p_resample) { pjmedia_resample *resample; PJ_ASSERT_RETURN(pool && p_resample && rate_in && rate_out && samples_per_frame, PJ_EINVAL); resample = PJ_POOL_ZALLOC_T(pool, pjmedia_resample); PJ_ASSERT_RETURN(resample, PJ_ENOMEM); /* * If we're downsampling, always use the fast algorithm since it seems * to yield the same quality. */ if (rate_out < rate_in) { //no this is not a good idea. It sounds pretty good with speech, //but very poor with background noise etc. //high_quality = 0; } resample->factor = rate_out * 1.0 / rate_in; resample->large_filter = large_filter; resample->high_quality = high_quality; resample->channel_cnt = channel_count; resample->frame_size = samples_per_frame; if (high_quality) { /* This is a bug in xoff calculation, thanks Stephane Lussier * of Macadamian dot com. * resample->xoff = large_filter ? 32 : 6; */ resample->xoff = res_GetXOFF(resample->factor, (char)large_filter); } else { resample->xoff = 1; } if (channel_count == 1) { unsigned size; /* Allocate input buffer */ size = (samples_per_frame + 2*resample->xoff) * sizeof(pj_int16_t); resample->buffer = (pj_int16_t*) pj_pool_alloc(pool, size); PJ_ASSERT_RETURN(resample->buffer, PJ_ENOMEM); pjmedia_zero_samples(resample->buffer, resample->xoff*2); } else if (channel_count > 1) { unsigned i, size; /* Allocate input buffer table */ size = channel_count * sizeof(pj_int16_t*); resample->in_buffer = (pj_int16_t**)pj_pool_alloc(pool, size); /* Allocate input buffer */ size = (samples_per_frame/channel_count + 2*resample->xoff) * sizeof(pj_int16_t); for (i = 0; i < channel_count; ++i) { resample->in_buffer[i] = (pj_int16_t*)pj_pool_alloc(pool, size); PJ_ASSERT_RETURN(resample->in_buffer, PJ_ENOMEM); pjmedia_zero_samples(resample->in_buffer[i], resample->xoff*2); } /* Allocate temporary output buffer */ size = (unsigned) (resample->frame_size * sizeof(pj_int16_t) * resample->factor / channel_count + 0.5); resample->tmp_buffer = (pj_int16_t*) pj_pool_alloc(pool, size); PJ_ASSERT_RETURN(resample->tmp_buffer, PJ_ENOMEM); } *p_resample = resample; PJ_LOG(5,(THIS_FILE, "resample created: %s qualiy, %s filter, in/out " "rate=%d/%d", (high_quality?"high":"low"), (large_filter?"large":"small"), rate_in, rate_out)); return PJ_SUCCESS; }
/* Reinitialize outgoing request after 401/407 response is received. * The purpose of this function is: * - to add a Authorization/Proxy-Authorization header. * - to put the newly created Authorization/Proxy-Authorization header * in cached_list. */ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, const pjsip_rx_data *rdata, pjsip_tx_data *old_request, pjsip_tx_data **new_request ) { pjsip_tx_data *tdata; const pjsip_hdr *hdr; unsigned chal_cnt; pjsip_via_hdr *via; pj_status_t status; PJ_ASSERT_RETURN(sess && rdata && old_request && new_request, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); PJ_ASSERT_RETURN(old_request->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_ASSERT_RETURN(rdata->msg_info.msg->line.status.code == 401 || rdata->msg_info.msg->line.status.code == 407, PJSIP_EINVALIDSTATUS); tdata = old_request; tdata->auth_retry = PJ_FALSE; /* * Respond to each authentication challenge. */ hdr = rdata->msg_info.msg->hdr.next; chal_cnt = 0; while (hdr != &rdata->msg_info.msg->hdr) { pjsip_cached_auth *cached_auth; const pjsip_www_authenticate_hdr *hchal; pjsip_authorization_hdr *hauth; /* Find WWW-Authenticate or Proxy-Authenticate header. */ while (hdr != &rdata->msg_info.msg->hdr && hdr->type != PJSIP_H_WWW_AUTHENTICATE && hdr->type != PJSIP_H_PROXY_AUTHENTICATE) { hdr = hdr->next; } if (hdr == &rdata->msg_info.msg->hdr) break; hchal = (const pjsip_www_authenticate_hdr*) hdr; ++chal_cnt; /* Find authentication session for this realm, create a new one * if not present. */ cached_auth = find_cached_auth(sess, &hchal->challenge.common.realm ); if (!cached_auth) { cached_auth = PJ_POOL_ZALLOC_T( sess->pool, pjsip_cached_auth); pj_strdup( sess->pool, &cached_auth->realm, &hchal->challenge.common.realm); cached_auth->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); # if (PJSIP_AUTH_HEADER_CACHING) { pj_list_init(&cached_auth->cached_hdr); } # endif pj_list_insert_before( &sess->cached_auth, cached_auth ); } /* Create authorization header for this challenge, and update * authorization session. */ status = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, tdata, sess, cached_auth, &hauth); if (status != PJ_SUCCESS) return status; /* Add to the message. */ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); /* Process next header. */ hdr = hdr->next; } /* Check if challenge is present */ if (chal_cnt == 0) return PJSIP_EAUTHNOCHAL; /* Remove branch param in Via header. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.slen = 0; /* Restore strict route set. * See http://trac.pjsip.org/repos/ticket/492 */ pjsip_restore_strict_route_set(tdata); /* Must invalidate the message! */ pjsip_tx_data_invalidate_msg(tdata); /* Retrying.. */ tdata->auth_retry = PJ_TRUE; /* Increment reference counter. */ pjsip_tx_data_add_ref(tdata); /* Done. */ *new_request = tdata; return PJ_SUCCESS; }
/* Get info */ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, pj_stun_sock_info *info) { int addr_len; pj_status_t status; PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL); /* Copy STUN server address and mapped address */ pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, sizeof(pj_sockaddr)); pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, sizeof(pj_sockaddr)); /* Retrieve bound address */ addr_len = sizeof(info->bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr, &addr_len); if (status != PJ_SUCCESS) return status; /* If socket is bound to a specific interface, then only put that * interface in the alias list. Otherwise query all the interfaces * in the host. */ if (pj_sockaddr_has_addr(&info->bound_addr)) { info->alias_cnt = 1; pj_sockaddr_cp(&info->aliases[0], &info->bound_addr); } else { pj_sockaddr def_addr; pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); unsigned i; /* Get the default address */ status = pj_gethostip(stun_sock->af, &def_addr); if (status != PJ_SUCCESS) return status; pj_sockaddr_set_port(&def_addr, port); /* Enum all IP interfaces in the host */ info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, info->aliases); if (status != PJ_SUCCESS) return status; /* Set the port number for each address. */ for (i=0; i<info->alias_cnt; ++i) { pj_sockaddr_set_port(&info->aliases[i], port); } /* Put the default IP in the first slot */ for (i=0; i<info->alias_cnt; ++i) { if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) { if (i!=0) { pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]); pj_sockaddr_cp(&info->aliases[0], &def_addr); } break; } } } return PJ_SUCCESS; }
/* Get user data */ PJ_DEF(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock) { PJ_ASSERT_RETURN(stun_sock, NULL); return stun_sock->user_data; }
/* * Create the STUN transport using the specified configuration. */ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, const char *name, int af, const pj_stun_sock_cb *cb, const pj_stun_sock_cfg *cfg, void *user_data, pj_stun_sock **p_stun_sock) { pj_pool_t *pool; pj_stun_sock *stun_sock; pj_stun_sock_cfg default_cfg; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); status = pj_stun_config_check_valid(stun_cfg); if (status != PJ_SUCCESS) return status; if (name == NULL) name = "stuntp%p"; if (cfg == NULL) { pj_stun_sock_cfg_default(&default_cfg); cfg = &default_cfg; } /* Create structure */ pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); stun_sock->pool = pool; stun_sock->obj_name = pool->obj_name; stun_sock->user_data = user_data; stun_sock->af = af; stun_sock->sock_fd = PJ_INVALID_SOCKET; pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); stun_sock->ka_interval = cfg->ka_interval; if (stun_sock->ka_interval == 0) stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; /* Create socket and bind socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, &cfg->qos_params, 2, stun_sock->obj_name, NULL); if (status != PJ_SUCCESS && !cfg->qos_ignore_error) goto on_error; /* Bind socket */ if (pj_sockaddr_has_addr(&cfg->bound_addr)) { status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr, pj_sockaddr_get_len(&cfg->bound_addr)); } else { pj_sockaddr bound_addr; pj_sockaddr_init(af, &bound_addr, NULL, 0); status = pj_sock_bind(stun_sock->sock_fd, &bound_addr, pj_sockaddr_get_len(&bound_addr)); } if (status != PJ_SUCCESS) goto on_error; /* Create more useful information string about this transport */ #if 0 { pj_sockaddr bound_addr; int addr_len = sizeof(bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); pj_sockaddr_print(&bound_addr, stun_sock->info, PJ_INET6_ADDRSTRLEN, 3); } #endif /* Init active socket configuration */ { pj_activesock_cfg activesock_cfg; pj_activesock_cb activesock_cb; pj_activesock_cfg_default(&activesock_cfg); activesock_cfg.async_cnt = cfg->async_cnt; activesock_cfg.concurrency = 0; /* Create the active socket */ pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &on_data_recvfrom; activesock_cb.on_data_sent = &on_data_sent; status = pj_activesock_create(pool, stun_sock->sock_fd, pj_SOCK_DGRAM(), &activesock_cfg, stun_cfg->ioqueue, &activesock_cb, stun_sock, &stun_sock->active_sock); if (status != PJ_SUCCESS) goto on_error; /* Start asynchronous read operations */ status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, cfg->max_pkt_size, 0); if (status != PJ_SUCCESS) goto on_error; /* Init send keys */ pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); pj_ioqueue_op_key_init(&stun_sock->int_send_key, sizeof(stun_sock->int_send_key)); } /* Create STUN session */ { pj_stun_session_cb sess_cb; pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &sess_on_request_complete; sess_cb.on_send_msg = &sess_on_send_msg; status = pj_stun_session_create(&stun_sock->stun_cfg, stun_sock->obj_name, &sess_cb, PJ_FALSE, &stun_sock->stun_sess); if (status != PJ_SUCCESS) goto on_error; } /* Associate us with the STUN session */ pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); /* Initialize random numbers to be used as STUN transaction ID for * outgoing Binding request. We use the 80bit number to distinguish * STUN messages we sent with STUN messages that the application sends. * The last 16bit value in the array is a counter. */ for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) { stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); } stun_sock->tsx_id[5] = 0; /* Init timer entry */ stun_sock->ka_timer.cb = &ka_timer_cb; stun_sock->ka_timer.user_data = stun_sock; /* Done */ *p_stun_sock = stun_sock; return PJ_SUCCESS; on_error: pj_stun_sock_destroy(stun_sock); return status; }
PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_pool_t *pool; pj_bool_t is_ipv6; int af; struct tls_listener *listener; pj_ssl_sock_param ssock_param; pj_sockaddr *listener_addr; pj_bool_t has_listener; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6()); af = is_ipv6 ? pj_AF_INET6() : pj_AF_INET(); /* Verify that address given in a_name (if any) is valid */ if (a_name && a_name->host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(af, &tmp, &a_name->host, (pj_uint16_t)a_name->port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (!is_ipv6 && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tlslis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener); listener->factory.pool = pool; if (is_ipv6) listener->factory.type = PJSIP_TRANSPORT_TLS6; else listener->factory.type = PJSIP_TRANSPORT_TLS; listener->factory.type_name = (char*) pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = pjsip_transport_get_flag_from_type(listener->factory.type); pj_ansi_strcpy(listener->factory.obj_name, "tlslis"); if (is_ipv6) pj_ansi_strcat(listener->factory.obj_name, "6"); if (opt) pjsip_tls_setting_copy(pool, &listener->tls_setting, opt); else pjsip_tls_setting_default(&listener->tls_setting); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.sock_af = af; ssock_param.cb.on_accept_complete = &on_accept_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; ssock_param.async_cnt = async_cnt; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt); ssock_param.require_client_cert = listener->tls_setting.require_client_cert; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = listener; ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket * due to verification error */ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; ssock_param.ciphers_num = listener->tls_setting.ciphers_num; ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.reuse_addr = listener->tls_setting.reuse_addr; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, sizeof(ssock_param.qos_params)); has_listener = PJ_FALSE; switch(listener->tls_setting.method) { case PJSIP_TLSV1_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1; break; case PJSIP_SSLV2_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2; break; case PJSIP_SSLV3_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3; break; case PJSIP_SSLV23_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23; break; default: ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT; break; } /* Create SSL socket */ status = pj_ssl_sock_create(pool, &ssock_param, &listener->ssock); if (status != PJ_SUCCESS) goto on_error; /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ listener_addr = &listener->factory.local_addr; if (local) { pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, (const pj_sockaddr_t*)local); pj_sockaddr_cp(&listener->bound_addr, local); } else { pj_sockaddr_init(af, listener_addr, NULL, 0); pj_sockaddr_init(af, &listener->bound_addr, NULL, 0); } /* Check if certificate/CA list for SSL socket is set */ if (listener->tls_setting.cert_file.slen || listener->tls_setting.ca_list_file.slen) { status = pj_ssl_cert_load_from_files(pool, &listener->tls_setting.ca_list_file, &listener->tls_setting.cert_file, &listener->tls_setting.privkey_file, &listener->tls_setting.password, &listener->cert); if (status != PJ_SUCCESS) goto on_error; status = pj_ssl_sock_set_certificate(listener->ssock, pool, listener->cert); if (status != PJ_SUCCESS) goto on_error; } /* Start accepting incoming connections. Note that some TLS/SSL backends * may not support for SSL socket server. */ has_listener = PJ_FALSE; status = pj_ssl_sock_start_accept(listener->ssock, pool, (pj_sockaddr_t*)listener_addr, pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr)); if (status == PJ_SUCCESS || status == PJ_EPENDING) { pj_ssl_sock_info info; has_listener = PJ_TRUE; /* Retrieve the bound address */ status = pj_ssl_sock_get_info(listener->ssock, &info); if (status == PJ_SUCCESS) pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr); } else if (status != PJ_ENOTSUP) { goto on_error; } /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (a_name && a_name->host.slen) { /* Copy the address */ listener->factory.addr_name = *a_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &a_name->host); listener->factory.addr_name.port = a_name->port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(af, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tlslis:%d", listener->factory.addr_name.port); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport2 = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } if (has_listener) { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener is ready for incoming connections " "at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); } else { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS is ready " "(client only)")); } /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: lis_destroy(&listener->factory); return status; }
/* Initialize outgoing request. */ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata ) { const pjsip_method *method; pjsip_cached_auth *auth; pjsip_hdr added; PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Init list */ pj_list_init(&added); /* Get the method. */ method = &tdata->msg->line.req.method; auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { /* Reset stale counter */ auth->stale_cnt = 0; if (auth->qop_value == PJSIP_AUTH_QOP_NONE) { # if defined(PJSIP_AUTH_HEADER_CACHING) && \ PJSIP_AUTH_HEADER_CACHING!=0 { pjsip_cached_auth_hdr *entry = auth->cached_hdr.next; while (entry != &auth->cached_hdr) { if (pjsip_method_cmp(&entry->method, method)==0) { pjsip_authorization_hdr *hauth; hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); break; } entry = entry->next; } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { if (entry == &auth->cached_hdr) new_auth_for_req( tdata, sess, auth, NULL); } # endif } # elif defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { new_auth_for_req( tdata, sess, auth, NULL); } # endif } # if defined(PJSIP_AUTH_QOP_SUPPORT) && \ defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) else if (auth->qop_value == PJSIP_AUTH_QOP_AUTH) { /* For qop="auth", we have to re-create the authorization header. */ const pjsip_cred_info *cred; pjsip_authorization_hdr *hauth; pj_status_t status; cred = auth_find_cred(sess, &auth->realm, &auth->last_chal->scheme); if (!cred) { auth = auth->next; continue; } status = auth_respond( tdata->pool, auth->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, sess->pool, auth, &hauth); if (status != PJ_SUCCESS) return status; //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); } # endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ auth = auth->next; } if (sess->pref.initial_auth == PJ_FALSE) { pjsip_hdr *h; /* Don't want to send initial empty Authorization header, so * just send whatever available in the list (maybe empty). */ h = added.next; while (h != &added) { pjsip_hdr *next = h->next; pjsip_msg_add_hdr(tdata->msg, h); h = next; } } else { /* For each realm, add either the cached authorization header * or add an empty authorization header. */ unsigned i; char *uri_str; int len; uri_str = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri, uri_str, PJSIP_MAX_URL_SIZE); if (len < 1 || len >= PJSIP_MAX_URL_SIZE) return PJSIP_EURITOOLONG; for (i=0; i<sess->cred_cnt; ++i) { pjsip_cred_info *c = &sess->cred_info[i]; pjsip_authorization_hdr *h; h = get_header_for_realm(&added, &c->realm); if (h) { pj_list_erase(h); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h); } else { pjsip_authorization_hdr *hs; hs = pjsip_authorization_hdr_create(tdata->pool); pj_strdup(tdata->pool, &hs->scheme, &c->scheme); pj_strdup(tdata->pool, &hs->credential.digest.username, &c->username); pj_strdup(tdata->pool, &hs->credential.digest.realm, &c->realm); pj_strdup2(tdata->pool, &hs->credential.digest.uri, uri_str); pj_strdup(tdata->pool, &hs->credential.digest.algorithm, &sess->pref.algorithm); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs); } } } return PJ_SUCCESS; }
/* * Common function to create TLS transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tls_create( struct tls_listener *listener, pj_pool_t *pool, pj_ssl_sock_t *ssock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, const pj_str_t *remote_name, struct tls_transport **p_tls) { struct tls_transport *tls; const pj_str_t ka_pkt = PJSIP_TLS_KEEP_ALIVE_DATA; char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; PJ_ASSERT_RETURN(listener && ssock && local && remote && p_tls, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport); tls->is_server = is_server; tls->verify_server = listener->tls_setting.verify_server; pj_list_init(&tls->delayed_list); tls->base.pool = pool; pj_ansi_snprintf(tls->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tlss%p" :"tlsc%p"), tls); status = pj_atomic_create(pool, 0, &tls->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tls", &tls->base.lock); if (status != PJ_SUCCESS) { goto on_error; } if (remote_name) pj_strdup(pool, &tls->remote_name, remote_name); tls->base.key.type = listener->factory.type; pj_sockaddr_cp(&tls->base.key.rem_addr, remote); tls->base.type_name = (char*)pjsip_transport_get_type_name( (pjsip_transport_type_e)tls->base.key.type); tls->base.flag = pjsip_transport_get_flag_from_type( (pjsip_transport_type_e)tls->base.key.type); tls->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tls->base.info, 64, "%s to %s", tls->base.type_name, pj_sockaddr_print(remote, print_addr, sizeof(print_addr), 3)); tls->base.addr_len = pj_sockaddr_get_len(remote); tls->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; /* Set initial local address */ if (!pj_sockaddr_has_addr(local)) { pj_sockaddr_cp(&tls->base.local_addr, &listener->factory.local_addr); } else { pj_sockaddr_cp(&tls->base.local_addr, local); } sockaddr_to_host_port(pool, &tls->base.local_name, &tls->base.local_addr); if (tls->remote_name.slen) { tls->base.remote_name.host = tls->remote_name; tls->base.remote_name.port = pj_sockaddr_get_port(remote); } else { sockaddr_to_host_port(pool, &tls->base.remote_name, remote); } tls->base.endpt = listener->endpt; tls->base.tpmgr = listener->tpmgr; tls->base.send_msg = &tls_send_msg; tls->base.do_shutdown = &tls_shutdown; tls->base.destroy = &tls_destroy_transport; tls->ssock = ssock; /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tls->base); if (status != PJ_SUCCESS) { goto on_error; } tls->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tls->ka_timer.user_data = (void*)tls; tls->ka_timer.cb = &tls_keep_alive_timer; pj_ioqueue_op_key_init(&tls->ka_op_key.key, sizeof(pj_ioqueue_op_key_t)); pj_strdup(tls->base.pool, &tls->ka_pkt, &ka_pkt); /* Done setting up basic transport. */ *p_tls = tls; PJ_LOG(4,(tls->base.obj_name, "TLS %s transport created", (tls->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tls_destroy(&tls->base, status); return status; }
/* * Handler to receive incoming MESSAGE */ static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata) { pj_str_t from, to; pjsip_accept_hdr *accept_hdr; pjsip_msg *msg; pj_status_t status; pjsua_inst_id inst_id = rdata->tp_info.pool->factory->inst_id; msg = rdata->msg_info.msg; /* Only want to handle MESSAGE requests. */ if (pjsip_method_cmp(&msg->line.req.method, &pjsip_message_method) != 0) { return PJ_FALSE; } /* Should not have any transaction attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_FALSE); /* Should not have any dialog attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_dlg(rdata)==NULL, PJ_FALSE); /* Check if we can accept the message. */ if (!pjsua_im_accept_pager(rdata, &accept_hdr)) { pjsip_hdr hdr_list; pj_list_init(&hdr_list); pj_list_push_back(&hdr_list, accept_hdr); pjsip_endpt_respond_stateless(pjsua_var[inst_id].endpt, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, &hdr_list, NULL); return PJ_TRUE; } /* Respond with 183 first, so that remote doesn't retransmit in case * the UI takes too long to process the message. */ //status = pjsip_endpt_respond( pjsua_var[inst_id].endpt, NULL, rdata, 200, NULL, // NULL, NULL, NULL); // DEAN. Use stateless function to avoid status = pjsip_endpt_respond_stateless(pjsua_var[inst_id].endpt, rdata, 183, NULL, NULL, NULL); /* For the source URI, we use Contact header if present, since * Contact header contains the port number information. If this is * not available, then use From header. */ from.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from.ptr, PJSIP_MAX_URL_SIZE); if (from.slen < 1) from = pj_str("<--URI is too long-->"); /* Build the To text. */ to.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); to.slen = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.to->uri, to.ptr, PJSIP_MAX_URL_SIZE); if (to.slen < 1) to = pj_str("<--URI is too long-->"); /* Process pager. */ pjsua_im_process_pager(-1, &from, &to, rdata); /* Done. */ return PJ_TRUE; }
/* * Create server subscription. */ PJ_DEF(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_accept_hdr *accept; pjsip_event_hdr *event; pjsip_evsub *sub; pjsip_mwi *mwi; char obj_name[PJ_MAX_OBJ_NAME]; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is SUBSCRIBE */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_subscribe_method)==0, PJSIP_SIMPLE_ENOTSUBSCRIBE); /* Check that Event header contains "mwi" */ event = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); if (!event) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } if (pj_stricmp(&event->event_type, &STR_MWI) != 0) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); } /* Check that request contains compatible Accept header. */ accept = (pjsip_accept_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; for (i=0; i<accept->count; ++i) { if (pj_stricmp(&accept->values[i], &STR_APP_SIMPLE_SMS)==0) { break; } } if (i==accept->count) { /* Nothing is acceptable */ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); } } else { /* No Accept header. * Assume client supports "application/simple-message-summary" */ } /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &mwi_user, rdata, 0, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server mwi subscription */ mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi); mwi->dlg = dlg; mwi->sub = sub; if (user_cb) pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user)); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "mwibd%p", dlg->pool); mwi->body_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; }
/* * Send instant messaging outside dialog, using the specified account for * route set and authentication. */ PJ_DEF(pj_status_t) pjsua_im_send( pjsua_inst_id inst_id, pjsua_acc_id acc_id, const pj_str_t *to, const pj_str_t *mime_type, const pj_str_t *content, const pjsua_msg_data *msg_data, char *s_rport, char *s_timeout, void *user_data) { pjsip_tx_data *tdata; const pj_str_t mime_text_plain = pj_str("text/plain"); const pj_str_t STR_CONTACT = { "Contact", 7 }; pjsip_media_type media_type; pjsua_im_data *im_data; pjsua_acc *acc; pj_str_t contact; pj_status_t status; pj_str_t rport = (s_rport == NULL ? pj_str("8889") : pj_str(s_rport)); pj_str_t timeout = (s_rport == NULL ? pj_str("30") : pj_str(s_timeout)); /* To and message body must be specified. */ PJ_ASSERT_RETURN(to && content, PJ_EINVAL); acc = &pjsua_var[inst_id].acc[acc_id]; /* Create request. */ status = pjsip_endpt_create_request(pjsua_var[inst_id].endpt, &pjsip_message_method, to, &acc->cfg.id, to, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; } /* If account is locked to specific transport, then set transport to * the request. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(inst_id, acc->cfg.transport_id, &tp_sel); pjsip_tx_data_set_transport(tdata, &tp_sel); } /* Add accept header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); /* Add rport header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_rport(tdata->pool, &rport)); /* Add timeout header. */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)pjsua_im_create_timtout(tdata->pool, &timeout)); /* Create suitable Contact header unless a Contact header has been * set in the account. */ if (acc->contact.slen) { contact = acc->contact; } else { status = pjsua_acc_create_uac_contact(inst_id, tdata->pool, &contact, acc_id, to); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pjsip_tx_data_dec_ref(tdata); return status; } } pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_CONTACT, &contact)); /* Create IM data to keep message details and give it back to * application on the callback */ im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data); im_data->inst_id = inst_id; im_data->acc_id = acc_id; im_data->call_id = PJSUA_INVALID_ID; pj_strdup_with_null(tdata->pool, &im_data->to, to); pj_strdup_with_null(tdata->pool, &im_data->body, content); im_data->user_data = user_data; /* Set default media type if none is specified */ if (mime_type == NULL) { mime_type = &mime_text_plain; } /* Parse MIME type */ pjsua_parse_media_type(tdata->pool, mime_type, &media_type); /* Add message body */ tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type, &media_type.subtype, &im_data->body); if (tdata->msg->body == NULL) { pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); pjsip_tx_data_dec_ref(tdata); return PJ_ENOMEM; } /* Add additional headers etc. */ pjsua_process_msg_data(inst_id, tdata, msg_data); /* Add route set */ pjsua_set_msg_route_set(tdata, &acc->route_set); /* Send request (statefully) */ status = pjsip_endpt_send_request( pjsua_var[inst_id].endpt, tdata, -1, im_data, &im_callback); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send request", status); return status; } return PJ_SUCCESS; }
/* Public function to parse multipart message bodies into its parts */ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool, char *buf, pj_size_t len, const pjsip_media_type *ctype, unsigned options) { pj_str_t boundary, delim; char *curptr, *endptr; const pjsip_param *ctype_param; const pj_str_t STR_BOUNDARY = { "boundary", 8 }; pjsip_msg_body *body = NULL; PJ_ASSERT_RETURN(pool && buf && len && ctype && !options, NULL); TRACE_((THIS_FILE, "Started parsing multipart body")); /* Get the boundary value in the ctype */ boundary.ptr = NULL; boundary.slen = 0; ctype_param = pjsip_param_find(&ctype->param, &STR_BOUNDARY); if (ctype_param) { boundary = ctype_param->value; if (boundary.slen>2 && *boundary.ptr=='"') { /* Remove quote */ boundary.ptr++; boundary.slen -= 2; } TRACE_((THIS_FILE, "Boundary is specified: '%.*s'", (int)boundary.slen, boundary.ptr)); } if (!boundary.slen) { /* Boundary not found or not specified. Try to be clever, get * the boundary from the body. */ char *p=buf, *end=buf+len; PJ_LOG(4,(THIS_FILE, "Warning: boundary parameter not found or " "not specified when parsing multipart body")); /* Find the first "--". This "--" must be right after a CRLF, unless * it really appears at the start of the buffer. */ for (;;) { while (p!=end && *p!='-') ++p; if (p!=end && *(p+1)=='-' && ((p>buf && *(p-1)=='\n') || (p==buf))) { p+=2; break; } else { ++p; } } if (p==end) { /* Unable to determine boundary. Maybe this is not a multipart * message? */ PJ_LOG(4,(THIS_FILE, "Error: multipart boundary not specified and" " unable to calculate from the body")); return NULL; } boundary.ptr = p; while (p!=end && !pj_isspace(*p)) ++p; boundary.slen = p - boundary.ptr; TRACE_((THIS_FILE, "Boundary is calculated: '%.*s'", (int)boundary.slen, boundary.ptr)); } /* Build the delimiter: * delimiter = "--" boundary */ delim.slen = boundary.slen+2; delim.ptr = (char*)pj_pool_alloc(pool, (int)delim.slen); delim.ptr[0] = '-'; delim.ptr[1] = '-'; pj_memcpy(delim.ptr+2, boundary.ptr, boundary.slen); /* Start parsing the body, skip until the first delimiter. */ curptr = buf; endptr = buf + len; { pj_str_t body; body.ptr = buf; body.slen = len; curptr = pj_strstr(&body, &delim); if (!curptr) return NULL; } body = pjsip_multipart_create(pool, ctype, &boundary); for (;;) { char *start_body, *end_body; pjsip_multipart_part *part; /* Eat the boundary */ curptr += delim.slen; if (*curptr=='-' && curptr<endptr-1 && *(curptr+1)=='-') { /* Found the closing delimiter */ curptr += 2; break; } /* Optional whitespace after delimiter */ while (curptr!=endptr && IS_SPACE(*curptr)) ++curptr; /* Mandatory CRLF */ if (*curptr=='\r') ++curptr; if (*curptr!='\n') { /* Expecting a newline here */ return NULL; } ++curptr; /* We now in the start of the body */ start_body = curptr; /* Find the next delimiter */ { pj_str_t subbody; subbody.ptr = curptr; subbody.slen = endptr - curptr; curptr = pj_strstr(&subbody, &delim); if (!curptr) { /* We're really expecting end delimiter to be found. */ return NULL; } } end_body = curptr; /* The newline preceeding the delimiter is conceptually part of * the delimiter, so trim it from the body. */ if (*(end_body-1) == '\n') --end_body; if (*(end_body-1) == '\r') --end_body; /* Now that we have determined the part's boundary, parse it * to get the header and body part of the part. */ part = parse_multipart_part(pool, start_body, end_body - start_body, ctype); if (part) { pjsip_multipart_add_part(pool, body, part); } } return body; }
/* * Convert text to IPv4/IPv6 address. */ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) { char tempaddr[PJ_INET6_ADDRSTRLEN]; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be * compatible with pj_inet_aton() */ if (af==PJ_AF_INET) { ((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE; } /* Caution: * this function might be called with cp->slen >= 46 * (i.e. when called with hostname to check if it's an IP addr). */ if (src->slen >= PJ_INET6_ADDRSTRLEN) { return PJ_ENAMETOOLONG; } pj_memcpy(tempaddr, src->ptr, src->slen); tempaddr[src->slen] = '\0'; #if defined(PJ_SOCK_HAS_INET_PTON) && PJ_SOCK_HAS_INET_PTON != 0 /* * Implementation using inet_pton() */ if (inet_pton(af, tempaddr, dst) != 1) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; #elif defined(PJ_WIN32) || defined(PJ_WIN32_WINCE) /* * Implementation on Windows, using WSAStringToAddress(). * Should also work on Unicode systems. */ { PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) pj_sockaddr sock_addr; int addr_len = sizeof(sock_addr); int rc; sock_addr.addr.sa_family = (pj_uint16_t)af; rc = WSAStringToAddress( PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), af, NULL, (LPSOCKADDR)&sock_addr, &addr_len); if (rc != 0) { /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6, * check that you have IPv6 enabled (install it in the network * adapter). */ pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } if (sock_addr.addr.sa_family == PJ_AF_INET) { pj_memcpy(dst, &sock_addr.ipv4.sin_addr, 4); return PJ_SUCCESS; } else if (sock_addr.addr.sa_family == PJ_AF_INET6) { pj_memcpy(dst, &sock_addr.ipv6.sin6_addr, 16); return PJ_SUCCESS; } else { pj_assert(!"Shouldn't happen"); return PJ_EBUG; } } #elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 /* IPv6 support is disabled, just return error without raising assertion */ return PJ_EIPV6NOTSUP; #else pj_assert(!"Not supported"); return PJ_EIPV6NOTSUP; #endif }
/* * Get SDP negotiator state. */ PJ_DEF(pjmedia_sdp_neg_state) pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg ) { /* Check arguments are valid. */ PJ_ASSERT_RETURN(neg != NULL, PJMEDIA_SDP_NEG_STATE_NULL); return neg->state; }
/* * Convert IPv4/IPv6 address to text. */ PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src, char *dst, int size) { PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL); *dst = '\0'; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); #if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0 /* * Implementation using inet_ntop() */ if (inet_ntop(af, src, dst, size) == NULL) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; #elif defined(PJ_WIN32) || defined(PJ_WIN32_WINCE) /* * Implementation on Windows, using WSAAddressToString(). * Should also work on Unicode systems. */ { PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) pj_sockaddr sock_addr; DWORD addr_len, addr_str_len; int rc; pj_bzero(&sock_addr, sizeof(sock_addr)); sock_addr.addr.sa_family = (pj_uint16_t)af; if (af == PJ_AF_INET) { if (size < PJ_INET_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv4.sin_addr, src, 4); addr_len = sizeof(pj_sockaddr_in); addr_str_len = PJ_INET_ADDRSTRLEN; } else if (af == PJ_AF_INET6) { if (size < PJ_INET6_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv6.sin6_addr, src, 16); addr_len = sizeof(pj_sockaddr_in6); addr_str_len = PJ_INET6_ADDRSTRLEN; } else { pj_assert(!"Unsupported address family"); return PJ_EAFNOTSUP; } #if PJ_NATIVE_STRING_IS_UNICODE rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, wtempaddr, &addr_str_len); if (rc == 0) { pj_unicode_to_ansi(wtempaddr, wcslen(wtempaddr), dst, size); } #else rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, dst, &addr_str_len); #endif if (rc != 0) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; } #elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 /* IPv6 support is disabled, just return error without raising assertion */ return PJ_EIPV6NOTSUP; #else pj_assert(!"Not supported"); return PJ_EIPV6NOTSUP; #endif }
/* * Modify local SDP and wait for remote answer. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *local) { pjmedia_sdp_session *new_offer; pjmedia_sdp_session *old_offer; char media_used[PJMEDIA_MAX_SDP_MEDIA]; unsigned oi; /* old offer media index */ pj_status_t status; /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL); /* Can only do this in STATE_DONE. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, PJMEDIA_SDPNEG_EINSTATE); /* Validate the new offer */ status = pjmedia_sdp_validate(local); if (status != PJ_SUCCESS) return status; /* Change state to STATE_LOCAL_OFFER */ neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER; /* Init vars */ pj_bzero(media_used, sizeof(media_used)); old_offer = neg->active_local_sdp; new_offer = pjmedia_sdp_session_clone(pool, local); /* RFC 3264 Section 8: When issuing an offer that modifies the session, * the "o=" line of the new SDP MUST be identical to that in the * previous SDP, except that the version in the origin field MUST * increment by one from the previous SDP. */ pj_strdup(pool, &new_offer->origin.user, &old_offer->origin.user); new_offer->origin.id = old_offer->origin.id; new_offer->origin.version = old_offer->origin.version + 1; pj_strdup(pool, &new_offer->origin.net_type, &old_offer->origin.net_type); pj_strdup(pool, &new_offer->origin.addr_type,&old_offer->origin.addr_type); pj_strdup(pool, &new_offer->origin.addr, &old_offer->origin.addr); /* Generating the new offer, in the case media lines doesn't match the * active SDP (e.g. current/active SDP's have m=audio and m=video lines, * and the new offer only has m=audio line), the negotiator will fix * the new offer by reordering and adding the missing media line with * port number set to zero. */ for (oi = 0; oi < old_offer->media_count; ++oi) { pjmedia_sdp_media *om; pjmedia_sdp_media *nm; unsigned ni; /* new offer media index */ pj_bool_t found = PJ_FALSE; om = old_offer->media[oi]; for (ni = oi; ni < new_offer->media_count; ++ni) { nm = new_offer->media[ni]; if (pj_strcmp(&nm->desc.media, &om->desc.media) == 0) { if (ni != oi) { /* The same media found but the position unmatched to the * old offer, so let's put this media in the right place, * and keep the order of the rest. */ pj_array_insert(new_offer->media, /* array */ sizeof(new_offer->media[0]), /* elmt size*/ ni, /* count */ oi, /* pos */ &nm); /* new elmt */ } found = PJ_TRUE; break; } } if (!found) { pjmedia_sdp_media *m; m = pjmedia_sdp_media_clone_deactivate(pool, om); pj_array_insert(new_offer->media, sizeof(new_offer->media[0]), new_offer->media_count++, oi, &m); } } /* New_offer fixed */ neg->initial_sdp = new_offer; neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, new_offer); return PJ_SUCCESS; }
/* * This callback is called by SSL socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, pj_ssl_sock_t *new_ssock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tls_listener *listener; struct tls_transport *tls; pj_ssl_sock_info ssl_info; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_sockaddr tmp_src_addr; pj_bool_t is_shutdown; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tls_listener*) pj_ssl_sock_get_user_data(ssock); PJ_ASSERT_RETURN(new_ssock, PJ_TRUE); PJ_LOG(4,(listener->factory.obj_name, "TLS listener %.*s:%d: got incoming TLS connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), new_ssock)); /* Retrieve SSL socket info, close the socket if this is failed * as the SSL socket info availability is rather critical here. */ status = pj_ssl_sock_get_info(new_ssock, &ssl_info); if (status != PJ_SUCCESS) { pj_ssl_sock_close(new_ssock); return PJ_TRUE; } /* Copy to larger buffer, just in case */ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); pj_sockaddr_cp(&tmp_src_addr, src_addr); /* * Incoming connection! * Create TLS transport for the new socket. */ status = tls_create( listener, NULL, new_ssock, PJ_TRUE, &listener->factory.local_addr, &tmp_src_addr, NULL, &tls); if (status != PJ_SUCCESS) return PJ_TRUE; /* Set the "pending" SSL socket user data */ pj_ssl_sock_set_user_data(new_ssock, tls); /* Prevent immediate transport destroy as application may access it * (getting info, etc) in transport state notification callback. */ pjsip_transport_add_ref(&tls->base); /* If there is verification error and verification is mandatory, shutdown * and destroy the transport. */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { if (tls->close_reason == PJ_SUCCESS) tls->close_reason = PJSIP_TLS_ECERTVERIF; pjsip_transport_shutdown(&tls->base); } /* Notify transport state to application */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pjsip_transport_state tp_state; /* Init transport state info */ pj_bzero(&tls_info, sizeof(tls_info)); pj_bzero(&state_info, sizeof(state_info)); tls_info.ssl_sock_info = &ssl_info; state_info.ext_info = &tls_info; /* Set transport state based on verification status */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { tp_state = PJSIP_TP_STATE_DISCONNECTED; state_info.status = PJSIP_TLS_ECERTVERIF; } else { tp_state = PJSIP_TP_STATE_CONNECTED; state_info.status = PJ_SUCCESS; } (*state_cb)(&tls->base, tp_state, &state_info); } /* Release transport reference. If transport is shutting down, it may * get destroyed here. */ is_shutdown = tls->base.is_shutdown; pjsip_transport_dec_ref(&tls->base); if (is_shutdown) return PJ_TRUE; status = tls_start_read(tls); if (status != PJ_SUCCESS) { PJ_LOG(3,(tls->base.obj_name, "New transport cancelled")); tls_init_shutdown(tls, status); tls_destroy(&tls->base, status); } else { /* Start keep-alive timer */ if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) { pj_time_val delay = {PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tls->last_activity); } } return PJ_TRUE; }
/* * Create server subscription. */ PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_accept_hdr *accept; pjsip_event_hdr *event; pjsip_expires_hdr *expires_hdr; unsigned expires; content_type content_type = CONTENT_TYPE_NONE; pjsip_evsub *sub; pjsip_pres *pres; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is SUBSCRIBE */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_subscribe_method)==0, PJSIP_SIMPLE_ENOTSUBSCRIBE); /* Check that Event header contains "presence" */ event = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); if (!event) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); } /* Check that request contains compatible Accept header. */ accept = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; for (i=0; i<accept->count; ++i) { if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) { content_type = CONTENT_TYPE_PIDF; break; } else if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) { content_type = CONTENT_TYPE_XPIDF; break; } } if (i==accept->count) { /* Nothing is acceptable */ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); } } else { /* No Accept header. * Treat as "application/pidf+xml" */ content_type = CONTENT_TYPE_PIDF; } /* Check that expires is not too short. */ expires_hdr=pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); if (expires_hdr) { if (expires_hdr->ivalue < 5) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_INTERVAL_TOO_BRIEF); } expires = expires_hdr->ivalue; if (expires > PRES_DEFAULT_EXPIRES) expires = PRES_DEFAULT_EXPIRES; } else { expires = PRES_DEFAULT_EXPIRES; } /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server presence subscription */ pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres)); pres->dlg = dlg; pres->sub = sub; pres->content_type = content_type; if (user_cb) pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; }
/* * This callback is called by transport manager to send SIP message */ static pj_status_t tls_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback) { struct tls_transport *tls = (struct tls_transport*)transport; pj_ssize_t size; pj_bool_t delayed = PJ_FALSE; pj_status_t status = PJ_SUCCESS; /* Sanity check */ PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL); /* Check that there's no pending operation associated with the tdata */ PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Check the address is supported */ PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) || addr_len==sizeof(pj_sockaddr_in6)), PJ_EINVAL); /* Init op key. */ tdata->op_key.tdata = tdata; tdata->op_key.token = token; tdata->op_key.callback = callback; /* If asynchronous connect() has not completed yet, just put the * transmit data in the pending transmission list since we can not * use the socket yet. */ if (tls->has_pending_connect) { /* * Looks like connect() is still in progress. Check again (this time * with holding the lock) to be sure. */ pj_lock_acquire(tls->base.lock); if (tls->has_pending_connect) { struct delayed_tdata *delayed_tdata; /* * connect() is still in progress. Put the transmit data to * the delayed list. * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583), * we also add timeout value for the transmit data. When the * connect() is completed, the timeout value will be checked to * determine whether the transmit data needs to be sent. */ delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, struct delayed_tdata); delayed_tdata->tdata_op_key = &tdata->op_key; if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) { pj_gettickcount(&delayed_tdata->timeout); delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td; pj_time_val_normalize(&delayed_tdata->timeout); } pj_list_push_back(&tls->delayed_list, delayed_tdata); status = PJ_EPENDING; /* Prevent pj_ioqueue_send() to be called below */ delayed = PJ_TRUE; } pj_lock_release(tls->base.lock); }
/* * Parse address */ PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, const pj_str_t *str, pj_sockaddr *addr) { pj_str_t hostpart; pj_uint16_t port; pj_status_t status; PJ_ASSERT_RETURN(addr, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af); if (status != PJ_SUCCESS) return status; #if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6 if (af==PJ_AF_INET6) return PJ_EIPV6NOTSUP; #endif status = pj_sockaddr_init(af, addr, &hostpart, port); #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 if (status != PJ_SUCCESS && af == PJ_AF_INET6) { /* Parsing does not yield valid address. Try to treat the last * portion after the colon as port number. */ const char *last_colon_pos=NULL, *p; const char *end = str->ptr + str->slen; unsigned long long_port; pj_str_t port_part; int i; /* Parse as IPv6:port */ for (p=str->ptr; p!=end; ++p) { if (*p == ':') last_colon_pos = p; } if (last_colon_pos == NULL) return status; hostpart.ptr = (char*)str->ptr; hostpart.slen = last_colon_pos - str->ptr; port_part.ptr = (char*)last_colon_pos + 1; port_part.slen = end - port_part.ptr; /* Make sure port number is valid */ for (i=0; i<port_part.slen; ++i) { if (!pj_isdigit(port_part.ptr[i])) return status; } long_port = pj_strtoul(&port_part); if (long_port > 65535) return status; port = (pj_uint16_t)long_port; status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port); } #endif return status; }
/* * pj_sem_create() */ PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **ptr_sem) { #if PJ_HAS_THREADS pj_sem_t *sem; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL); sem = PJ_POOL_ALLOC_T(pool, pj_sem_t); PJ_ASSERT_RETURN(sem, PJ_ENOMEM); #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 /* MacOS X doesn't support anonymous semaphore */ { char sem_name[PJ_GUID_MAX_LENGTH+1]; pj_str_t nam; /* We should use SEM_NAME_LEN, but this doesn't seem to be * declared anywhere? The value here is just from trial and error * to get the longest name supported. */ # define MAX_SEM_NAME_LEN 23 /* Create a unique name for the semaphore. */ if (PJ_GUID_STRING_LENGTH <= MAX_SEM_NAME_LEN) { nam.ptr = sem_name; pj_generate_unique_string(&nam); sem_name[nam.slen] = '\0'; } else { pj_create_random_string(sem_name, MAX_SEM_NAME_LEN); sem_name[MAX_SEM_NAME_LEN] = '\0'; } /* Create semaphore */ sem->sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, initial); if (sem->sem == SEM_FAILED) return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); /* And immediately release the name as we don't need it */ sem_unlink(sem_name); } #else sem->sem = PJ_POOL_ALLOC_T(pool, sem_t); if (sem_init( sem->sem, 0, initial) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); #endif /* Set name. */ if (!name) { name = "sem%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); } else { strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (sem->obj_name, "Semaphore created")); *ptr_sem = sem; return PJ_SUCCESS; #else *ptr_sem = (pj_sem_t*)1; return PJ_SUCCESS; #endif }