static pj_status_t apply_msg_options(pj_stun_session *sess, pj_pool_t *pool, const pj_stun_req_cred_info *auth_info, pj_stun_msg *msg) { pj_status_t status = 0; pj_str_t realm, username, nonce, auth_key; /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute * to the request. The server SHOULD include a SOFTWARE attribute in all * responses. * * If magic value is not PJ_STUN_MAGIC, only apply the attribute for * responses. */ if (sess->srv_name.slen && pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL && (PJ_STUN_IS_RESPONSE(msg->hdr.type) || (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) { pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, &sess->srv_name); } if (pj_stun_auth_valid_for_msg(msg) && auth_info) { realm = auth_info->realm; username = auth_info->username; nonce = auth_info->nonce; auth_key = auth_info->auth_key; } else { realm.slen = username.slen = nonce.slen = auth_key.slen = 0; } /* Create and add USERNAME attribute if needed */ if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, &username); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add REALM only when long term credential is used */ if (realm.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_REALM, &realm); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add NONCE when desired */ if (nonce.slen && (PJ_STUN_IS_REQUEST(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_NONCE, &nonce); } /* Add MESSAGE-INTEGRITY attribute */ if (username.slen && auth_key.slen) { status = pj_stun_msg_add_msgint_attr(pool, msg); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add FINGERPRINT attribute if necessary */ if (sess->use_fingerprint) { status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } return PJ_SUCCESS; }
static int fingerprint_test_vector() { pj_pool_t *pool; pj_status_t status; unsigned i; int rc = 0; /* To avoid function not referenced warnings */ (void)create_msgint2; (void)create_msgint3; PJ_LOG(3,(THIS_FILE, " draft-denis-behave-rfc3489bis-test-vectors-02")); pool = pj_pool_create(mem, "fingerprint", 1024, 1024, NULL); for (i=0; i<PJ_ARRAY_SIZE(test_vectors); ++i) { struct test_vector *v; pj_stun_msg *ref_msg, *msg; pj_size_t parsed_len; pj_size_t len; unsigned pos; pj_uint8_t buf[1500]; char print[1500]; pj_str_t key; PJ_LOG(3,(THIS_FILE, " Running test %d/%d", i, PJ_ARRAY_SIZE(test_vectors))); v = &test_vectors[i]; /* Print reference message */ PJ_LOG(4,(THIS_FILE, "Reference message PDU:\n%s", print_binary((pj_uint8_t*)v->pdu, v->pdu_len))); /* Try to parse the reference message first */ status = pj_stun_msg_decode(pool, (pj_uint8_t*)v->pdu, v->pdu_len, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, &ref_msg, &parsed_len, NULL); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, " Error decoding reference message")); rc = -1010; goto on_return; } if (parsed_len != v->pdu_len) { PJ_LOG(1,(THIS_FILE, " Parsed len error")); rc = -1020; goto on_return; } /* Print the reference message */ pj_stun_msg_dump(ref_msg, print, sizeof(print), NULL); PJ_LOG(4,(THIS_FILE, "Reference message:\n%s", print)); /* Create our message */ msg = v->create(pool, v); if (msg == NULL) { PJ_LOG(1,(THIS_FILE, " Error creating stun message")); rc = -1030; goto on_return; } /* Encode message */ if (v->options & USE_MESSAGE_INTEGRITY) { pj_str_t s1, s2, r; pj_stun_create_key(pool, &key, pj_cstr(&r, v->realm), pj_cstr(&s1, v->username), PJ_STUN_PASSWD_PLAIN, pj_cstr(&s2, v->password)); pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len); } else { pj_stun_msg_encode(msg, buf, sizeof(buf), 0, NULL, &len); } /* Print our raw message */ PJ_LOG(4,(THIS_FILE, "Message PDU:\n%s", print_binary((pj_uint8_t*)buf, len))); /* Print our message */ pj_stun_msg_dump(msg, print, sizeof(print), NULL); PJ_LOG(4,(THIS_FILE, "Message is:\n%s", print)); /* Compare message length */ if (len != v->pdu_len) { PJ_LOG(1,(THIS_FILE, " Message length mismatch")); rc = -1050; goto on_return; } pos = cmp_buf(buf, (const pj_uint8_t*)v->pdu, len); if (pos != (unsigned)-1) { PJ_LOG(1,(THIS_FILE, " Message mismatch at byte %d", pos)); rc = -1060; goto on_return; } /* Authenticate the request/response */ if (v->options & USE_MESSAGE_INTEGRITY) { if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { pj_stun_auth_cred cred; pj_status_t status; pj_bzero(&cred, sizeof(cred)); cred.type = PJ_STUN_AUTH_CRED_STATIC; cred.data.static_cred.realm = pj_str(v->realm); cred.data.static_cred.username = pj_str(v->username); cred.data.static_cred.data = pj_str(v->password); cred.data.static_cred.nonce = pj_str(v->nonce); status = pj_stun_authenticate_request(buf, len, msg, &cred, pool, NULL, NULL); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, " Request authentication failed: %s", errmsg)); rc = -1070; goto on_return; } } else if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) { pj_status_t status; status = pj_stun_authenticate_response(buf, len, msg, &key); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, " Response authentication failed: %s", errmsg)); rc = -1080; goto on_return; } } } } on_return: pj_pool_release(pool); return rc; }
/* Callback from active socket when incoming packet is received */ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status) { pj_stun_sock *stun_sock; pj_stun_msg_hdr *hdr; pj_uint16_t type; stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); /* Log socket error */ if (status != PJ_SUCCESS) { PJ_PERROR(2,(stun_sock->obj_name, status, "recvfrom() error")); return PJ_TRUE; } /* Check that this is STUN message */ status = pj_stun_msg_check((const pj_uint8_t*)data, size, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); if (status != PJ_SUCCESS) { /* Not STUN -- give it to application */ goto process_app_data; } /* Treat packet as STUN header and copy the STUN message type. * We don't want to access the type directly from the header * since it may not be properly aligned. */ hdr = (pj_stun_msg_hdr*) data; pj_memcpy(&type, &hdr->type, 2); type = pj_ntohs(type); /* If the packet is a STUN Binding response and part of the * transaction ID matches our internal ID, then this is * our internal STUN message (Binding request or keep alive). * Give it to our STUN session. */ if (!PJ_STUN_IS_RESPONSE(type) || PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) { /* Not STUN Binding response, or STUN transaction ID mismatch. * This is not our message too -- give it to application. */ goto process_app_data; } /* This is our STUN Binding response. Give it to the STUN session */ status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, PJ_STUN_IS_DATAGRAM, NULL, NULL, src_addr, addr_len); return status!=PJNATH_ESTUNDESTROYED ? PJ_TRUE : PJ_FALSE; process_app_data: if (stun_sock->cb.on_rx_data) { pj_bool_t ret; ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size, src_addr, addr_len); return ret; } return PJ_TRUE; }