size_t stun_agent_build_unknown_attributes_error (StunAgent *agent, StunMessage *msg, uint8_t *buffer, size_t buffer_len, const StunMessage *request) { unsigned counter; uint16_t ids[STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES]; counter = stun_agent_find_unknowns (agent, request, ids, STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES); if (stun_agent_init_error (agent, msg, buffer, buffer_len, request, STUN_ERROR_UNKNOWN_ATTRIBUTE) == FALSE) { return 0; } /* NOTE: Old RFC3489 compatibility: * When counter is odd, duplicate one value for 32-bits padding. */ if (!stun_message_has_cookie (request) && (counter & 1)) ids[counter++] = ids[0]; if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES, ids, counter * 2) == STUN_MESSAGE_RETURN_SUCCESS) { return stun_agent_finish_message (agent, msg, request->key, request->key_len); } return 0; }
static int dgram_process (int sock, StunAgent *oldagent, StunAgent *newagent) { union { struct sockaddr_storage storage; struct sockaddr addr; } addr; socklen_t addr_len; uint8_t buf[STUN_MAX_MESSAGE_SIZE]; size_t buf_len = 0; size_t len = 0; StunMessage request; StunMessage response; StunValidationStatus validation; StunAgent *agent = NULL; gint ret; addr_len = sizeof (struct sockaddr_in); recv_packet: len = recvfrom (sock, buf, sizeof(buf), 0, &addr.addr, &addr_len); if (drop_stun_packets) { g_debug ("Dropping STUN packet as requested"); return -1; } if (len == (size_t)-1) { return -1; } validation = stun_agent_validate (newagent, &request, buf, len, NULL, 0); if (validation == STUN_VALIDATION_SUCCESS) { agent = newagent; } else { validation = stun_agent_validate (oldagent, &request, buf, len, NULL, 0); agent = oldagent; } /* Unknown attributes */ if (validation == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) { buf_len = stun_agent_build_unknown_attributes_error (agent, &response, buf, sizeof (buf), &request); goto send_buf; } /* Mal-formatted packets */ if (validation != STUN_VALIDATION_SUCCESS || stun_message_get_class (&request) != STUN_REQUEST) { goto recv_packet; } switch (stun_message_get_method (&request)) { case STUN_BINDING: stun_agent_init_response (agent, &response, buf, sizeof (buf), &request); if (stun_message_has_cookie (&request)) stun_message_append_xor_addr (&response, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr.storage, addr_len); else stun_message_append_addr (&response, STUN_ATTRIBUTE_MAPPED_ADDRESS, &addr.addr, addr_len); break; case STUN_SHARED_SECRET: case STUN_ALLOCATE: case STUN_SET_ACTIVE_DST: case STUN_CONNECT: case STUN_OLD_SET_ACTIVE_DST: case STUN_IND_DATA: case STUN_IND_CONNECT_STATUS: case STUN_CHANNELBIND: default: if (!stun_agent_init_error (agent, &response, buf, sizeof (buf), &request, STUN_ERROR_BAD_REQUEST)) { g_debug ("STUN error message not initialized properly"); g_assert_not_reached(); } } buf_len = stun_agent_finish_message (agent, &response, NULL, 0); send_buf: g_cancellable_cancel (global_cancellable); g_debug ("Ready to send a STUN response"); g_assert (g_mutex_trylock (stun_mutex_ptr)); got_stun_packet = TRUE; while (send_stun) { g_debug ("Waiting for signal. State is %d", global_lagent_state); g_cond_wait (stun_signal_ptr, stun_mutex_ptr); } g_mutex_unlock (stun_mutex_ptr); len = sendto (sock, buf, buf_len, 0, &addr.addr, addr_len); g_debug ("STUN response sent"); drop_stun_packets = TRUE; ret = (len < buf_len) ? -1 : 0; return ret; }
StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg, const uint8_t *buffer, size_t buffer_len, StunMessageIntegrityValidate validater, void * validater_data) { StunTransactionId msg_id; uint32_t fpr; uint32_t crc32; int len; uint8_t *username = NULL; uint16_t username_len; uint8_t *key = NULL; size_t key_len; uint8_t *hash; uint8_t sha[20]; uint16_t hlen; int sent_id_idx = -1; uint16_t unknown; int error_code; int ignore_credentials = 0; uint8_t long_term_key[16]; bool long_term_key_valid = FALSE; len = stun_message_validate_buffer_length (buffer, buffer_len); if (len == STUN_MESSAGE_BUFFER_INVALID) { return STUN_VALIDATION_NOT_STUN; } else if (len == STUN_MESSAGE_BUFFER_INCOMPLETE) { return STUN_VALIDATION_INCOMPLETE_STUN; } else if (len != (int) buffer_len) { return STUN_VALIDATION_NOT_STUN; } msg->buffer = (uint8_t *) buffer; msg->buffer_len = buffer_len; msg->agent = agent; msg->key = NULL; msg->key_len = 0; msg->long_term_valid = FALSE; /* TODO: reject it or not ? */ if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 || agent->compatibility == STUN_COMPATIBILITY_WLM2009) && !stun_message_has_cookie (msg)) { stun_debug ("STUN demux error: no cookie!\n"); return STUN_VALIDATION_BAD_REQUEST; } if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 || agent->compatibility == STUN_COMPATIBILITY_WLM2009) && agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) { /* Looks for FINGERPRINT */ if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("STUN demux error: no FINGERPRINT attribute!\n"); return STUN_VALIDATION_BAD_REQUEST; } /* Checks FINGERPRINT */ crc32 = stun_fingerprint (msg->buffer, stun_message_length (msg), agent->compatibility == STUN_COMPATIBILITY_WLM2009); fpr = ntohl (fpr); if (fpr != crc32) { stun_debug ("STUN demux error: bad fingerprint: 0x%08x," " expected: 0x%08x!\n", fpr, crc32); return STUN_VALIDATION_BAD_REQUEST; } stun_debug ("STUN demux: OK!\n"); } if (stun_message_get_class (msg) == STUN_RESPONSE || stun_message_get_class (msg) == STUN_ERROR) { stun_message_id (msg, msg_id); for (sent_id_idx = 0; sent_id_idx < STUN_AGENT_MAX_SAVED_IDS; sent_id_idx++) { if (agent->sent_ids[sent_id_idx].valid == TRUE && agent->sent_ids[sent_id_idx].method == stun_message_get_method (msg) && memcmp (msg_id, agent->sent_ids[sent_id_idx].id, sizeof(StunTransactionId)) == 0) { key = agent->sent_ids[sent_id_idx].key; key_len = agent->sent_ids[sent_id_idx].key_len; memcpy (long_term_key, agent->sent_ids[sent_id_idx].long_term_key, sizeof(long_term_key)); long_term_key_valid = agent->sent_ids[sent_id_idx].long_term_valid; break; } } if (sent_id_idx == STUN_AGENT_MAX_SAVED_IDS) { return STUN_VALIDATION_UNMATCHED_RESPONSE; } } ignore_credentials = (agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) || (stun_message_get_class (msg) == STUN_ERROR && stun_message_find_error (msg, &error_code) == STUN_MESSAGE_RETURN_SUCCESS && (error_code == 400 || error_code == 401)) || (stun_message_get_class (msg) == STUN_INDICATION && (agent->usage_flags & STUN_AGENT_USAGE_NO_INDICATION_AUTH)); if (key == NULL && ignore_credentials == 0 && (stun_message_get_class (msg) == STUN_REQUEST || stun_message_get_class (msg) == STUN_INDICATION) && (((agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS) && (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY))) || ((agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) && stun_message_get_class (msg) == STUN_REQUEST && (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_NONCE) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_REALM))) || ((agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) == 0 && stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) && !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY)))) { return STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST; } if (stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) && ((key == NULL && ignore_credentials == 0) || (agent->usage_flags & STUN_AGENT_USAGE_FORCE_VALIDATER))) { username_len = 0; username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME, &username_len); if (validater == NULL || validater (agent, msg, username, username_len, &key, &key_len, validater_data) == FALSE) { return STUN_VALIDATION_UNAUTHORIZED; } } if (ignore_credentials == 0 && key != NULL && key_len > 0) { hash = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, &hlen); if (hash) { /* We must give the size from start to the end of the attribute because you might have a FINGERPRINT attribute after it... */ if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) { uint8_t *realm = NULL; uint8_t *username = NULL; uint16_t realm_len; uint16_t username_len; uint8_t md5[16]; if (long_term_key_valid) { memcpy (md5, long_term_key, sizeof (md5)); } else { realm = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_REALM, &realm_len); username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME, &username_len); if (username == NULL || realm == NULL) { return STUN_VALIDATION_UNAUTHORIZED; } stun_hash_creds (realm, realm_len, username, username_len, key, key_len, md5); } memcpy (msg->long_term_key, md5, sizeof(md5)); msg->long_term_valid = TRUE; if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, md5, sizeof(md5), TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, stun_message_length (msg) - 20, sha, md5, sizeof(md5), TRUE); } else { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, md5, sizeof(md5), FALSE); } } else { if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, key, key_len, TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, stun_message_length (msg) - 20, sha, key, key_len, TRUE); } else { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, key, key_len, FALSE); } } stun_debug (" Message HMAC-SHA1 fingerprint:"); stun_debug ("\nkey : "); stun_debug_bytes (key, key_len); stun_debug ("\n expected: "); stun_debug_bytes (sha, sizeof (sha)); stun_debug ("\n received: "); stun_debug_bytes (hash, sizeof (sha)); stun_debug ("\n"); if (memcmp (sha, hash, sizeof (sha))) { stun_debug ("STUN auth error: SHA1 fingerprint mismatch!\n"); return STUN_VALIDATION_UNAUTHORIZED; } stun_debug ("STUN auth: OK!\n"); msg->key = key; msg->key_len = key_len; } else if (!(stun_message_get_class (msg) == STUN_ERROR && stun_message_find_error (msg, &error_code) == STUN_MESSAGE_RETURN_SUCCESS && (error_code == 400 || error_code == 401))) { stun_debug ("STUN auth error: No message integrity attribute!\n"); return STUN_VALIDATION_UNAUTHORIZED; } } if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) { agent->sent_ids[sent_id_idx].valid = FALSE; } if (stun_agent_find_unknowns (agent, msg, &unknown, 1) > 0) { if (stun_message_get_class (msg) == STUN_REQUEST) return STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE; else return STUN_VALIDATION_UNKNOWN_ATTRIBUTE; } return STUN_VALIDATION_SUCCESS; }
StunUsageIceReturn stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, StunMessage *msg, uint8_t *buf, size_t *plen, const struct sockaddr *src, socklen_t srclen, bool *control, uint64_t tie, StunUsageIceCompatibility compatibility) { const char *username = NULL; uint16_t username_len; size_t len = *plen; uint64_t q; StunMessageReturn val = STUN_MESSAGE_RETURN_SUCCESS; StunUsageIceReturn ret = STUN_USAGE_ICE_RETURN_SUCCESS; #define err( code ) \ stun_bind_error (agent, msg, buf, &len, req, code); \ *plen = len *plen = 0; stun_debug ("STUN Reply (buffer size = %u)...\n", (unsigned)len); if (stun_message_get_class (req) != STUN_REQUEST) { stun_debug (" Unhandled non-request (class %u) message.\n", stun_message_get_class (req)); return STUN_USAGE_ICE_RETURN_INVALID_REQUEST; } if (stun_message_get_method (req) != STUN_BINDING) { stun_debug (" Bad request (method %u) message.\n", stun_message_get_method (req)); err (STUN_ERROR_BAD_REQUEST); return STUN_USAGE_ICE_RETURN_INVALID_METHOD; } /* Role conflict handling */ assert (control != NULL); if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("STUN Role Conflict detected:\n"); if (tie < q) { stun_debug (" switching role from \"controll%s\" to \"controll%s\"\n", *control ? "ing" : "ed", *control ? "ed" : "ing"); *control = !*control; ret = STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; } else { stun_debug (" staying \"controll%s\" (sending error)\n", *control ? "ing" : "ed"); err (STUN_ERROR_ROLE_CONFLICT); return STUN_USAGE_ICE_RETURN_SUCCESS; } } else { stun_debug ("STUN Role not specified by peer!\n"); } if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) { stun_debug ("Unable to create response\n"); goto failure; } if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) { StunTransactionId transid; uint32_t magic_cookie; stun_message_id (msg, transid); magic_cookie = *((uint32_t *) transid); val = stun_message_append_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen, htonl (magic_cookie)); } else if (stun_message_has_cookie (msg)) { val = stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen); } else { val = stun_message_append_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, src, srclen); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" Mapped address problem: %d\n", val); goto failure; } username = (const char *)stun_message_find (req, STUN_ATTRIBUTE_USERNAME, &username_len); if (username) { val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, username, username_len); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("Error appending username: %d\n", val); goto failure; } /* the stun agent will automatically use the password of the request */ len = stun_agent_finish_message (agent, msg, NULL, 0); if (len == 0) goto failure; *plen = len; stun_debug (" All done (response size: %u)\n", (unsigned)len); return ret; failure: assert (*plen == 0); stun_debug (" Fatal error formatting Response: %d\n", val); switch (val) { case STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE: return STUN_USAGE_ICE_RETURN_MEMORY_ERROR; case STUN_MESSAGE_RETURN_INVALID: case STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS: return STUN_USAGE_ICE_RETURN_INVALID_ADDRESS; default: return STUN_USAGE_ICE_RETURN_ERROR; } }
static int dgram_process (int sock, StunAgent *oldagent, StunAgent *newagent) { union { struct sockaddr_storage storage; struct sockaddr addr; } addr; socklen_t addr_len; uint8_t buf[STUN_MAX_MESSAGE_SIZE]; size_t buf_len = 0; size_t len = 0; StunMessage request; StunMessage response; StunValidationStatus validation; StunAgent *agent = NULL; addr_len = sizeof (struct sockaddr_in); len = recvfrom (sock, buf, sizeof(buf), 0, &addr.addr, &addr_len); if (len == (size_t)-1) return -1; validation = stun_agent_validate (newagent, &request, buf, len, NULL, 0); if (validation == STUN_VALIDATION_SUCCESS) { agent = newagent; } else { validation = stun_agent_validate (oldagent, &request, buf, len, NULL, 0); agent = oldagent; } /* Unknown attributes */ if (validation == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) { buf_len = stun_agent_build_unknown_attributes_error (agent, &response, buf, sizeof (buf), &request); goto send_buf; } /* Mal-formatted packets */ if (validation != STUN_VALIDATION_SUCCESS || stun_message_get_class (&request) != STUN_REQUEST) { return -1; } switch (stun_message_get_method (&request)) { case STUN_BINDING: stun_agent_init_response (agent, &response, buf, sizeof (buf), &request); if (stun_message_has_cookie (&request)) stun_message_append_xor_addr (&response, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr.addr, addr_len); else stun_message_append_addr (&response, STUN_ATTRIBUTE_MAPPED_ADDRESS, &addr.addr, addr_len); break; case STUN_SHARED_SECRET: case STUN_ALLOCATE: case STUN_SEND: case STUN_CONNECT: case STUN_IND_SEND: case STUN_IND_DATA: case STUN_CREATEPERMISSION: case STUN_CHANNELBIND: default: if (!stun_agent_init_error (agent, &response, buf, sizeof (buf), &request, STUN_ERROR_BAD_REQUEST)) return -1; } buf_len = stun_agent_finish_message (agent, &response, NULL, 0); send_buf: len = sendto (sock, buf, buf_len, 0, &addr.addr, addr_len); return (len < buf_len) ? -1 : 0; }
static int dgram_process (int sock, StunAgent *oldagent, StunAgent *newagent) { struct sockaddr_storage addr; uint8_t buf[STUN_MAX_MESSAGE_SIZE]; char ctlbuf[CMSG_SPACE (sizeof (struct in6_pktinfo))]; struct iovec iov = { buf, sizeof (buf) }; StunMessage request; StunMessage response; StunValidationStatus validation; StunAgent *agent = NULL; struct msghdr mh = { .msg_name = (struct sockaddr *)&addr, .msg_namelen = sizeof (addr), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = ctlbuf, .msg_controllen = sizeof (ctlbuf) }; size_t len = recv_safe (sock, &mh); if (len == (size_t)-1) return -1; validation = stun_agent_validate (newagent, &request, buf, len, NULL, 0); if (validation == STUN_VALIDATION_SUCCESS) { agent = newagent; } else { validation = stun_agent_validate (oldagent, &request, buf, len, NULL, 0); agent = oldagent; } /* Unknown attributes */ if (validation == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) { stun_agent_build_unknown_attributes_error (agent, &response, buf, sizeof (buf), &request); goto send_buf; } /* Mal-formatted packets */ if (validation != STUN_VALIDATION_SUCCESS || stun_message_get_class (&request) != STUN_REQUEST) { return -1; } switch (stun_message_get_method (&request)) { case STUN_BINDING: stun_agent_init_response (agent, &response, buf, sizeof (buf), &request); if (stun_message_has_cookie (&request)) stun_message_append_xor_addr (&response, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, mh.msg_name, mh.msg_namelen); else stun_message_append_addr (&response, STUN_ATTRIBUTE_MAPPED_ADDRESS, mh.msg_name, mh.msg_namelen); break; default: stun_agent_init_error (agent, &response, buf, sizeof (buf), &request, STUN_ERROR_BAD_REQUEST); } iov.iov_len = stun_agent_finish_message (agent, &response, NULL, 0); send_buf: len = send_safe (sock, &mh); return (len < iov.iov_len) ? -1 : 0; } static int run (int family, int protocol, unsigned port) { StunAgent oldagent; StunAgent newagent; int sock = listen_socket (family, SOCK_DGRAM, protocol, port); if (sock == -1) return -1; stun_agent_init (&oldagent, known_attributes, STUN_COMPATIBILITY_RFC3489, 0); stun_agent_init (&newagent, known_attributes, STUN_COMPATIBILITY_RFC5389, STUN_AGENT_USAGE_USE_FINGERPRINT); for (;;) dgram_process (sock, &oldagent, &newagent); }
StunUsageIceReturn stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, StunMessage *msg, uint8_t *buf, size_t *plen, const struct sockaddr_storage *src, socklen_t srclen, bool *control, uint64_t tie, StunUsageIceCompatibility compatibility) { const char *username = NULL; uint16_t username_len; size_t len = *plen; uint64_t q; StunMessageReturn val = STUN_MESSAGE_RETURN_SUCCESS; StunUsageIceReturn ret = STUN_USAGE_ICE_RETURN_SUCCESS; #define err( code ) \ stun_bind_error (agent, msg, buf, &len, req, code); \ *plen = len *plen = 0; stun_debug ("STUN Reply (buffer size = %u)...", (unsigned)len); if (stun_message_get_class (req) != STUN_REQUEST) { stun_debug (" Unhandled non-request (class %u) message.", stun_message_get_class (req)); return STUN_USAGE_ICE_RETURN_INVALID_REQUEST; } if (stun_message_get_method (req) != STUN_BINDING) { stun_debug (" Bad request (method %u) message.", stun_message_get_method (req)); err (STUN_ERROR_BAD_REQUEST); return STUN_USAGE_ICE_RETURN_INVALID_METHOD; } /* Role conflict handling */ assert (control != NULL); if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS) { /* we have the ice-controlling/controlled attribute, * and there's a role conflict */ stun_debug ("STUN Role Conflict detected:"); /* According to ICE RFC 5245, section 7.2.1.1, we consider the four * possible cases when a role conflict is detected: two cases are * resolved by switching role locally, and the two other cases are * handled by responding with a STUN error. */ if ((tie < q && *control) || (tie >= q && !*control)) { stun_debug (" switching role from \"controll%s\" to \"controll%s\"", *control ? "ing" : "ed", *control ? "ed" : "ing"); *control = !*control; ret = STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; } else { stun_debug (" staying \"controll%s\" (sending error)", *control ? "ing" : "ed"); err (STUN_ERROR_ROLE_CONFLICT); return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; } } else { if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLED : STUN_ATTRIBUTE_ICE_CONTROLLING, &q) != STUN_MESSAGE_RETURN_SUCCESS) { /* we don't have the expected ice-controlling/controlled * attribute */ if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 || compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2) { stun_debug ("STUN Role not specified by peer!"); } } } if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) { stun_debug ("Unable to create response"); goto failure; } if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) { union { StunTransactionId transid; uint32_t magic_cookie; } conv; stun_message_id (msg, conv.transid); val = stun_message_append_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen, htonl (conv.magic_cookie)); } else if (stun_message_has_cookie (msg) && compatibility != STUN_USAGE_ICE_COMPATIBILITY_GOOGLE) { val = stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen); } else { val = stun_message_append_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, (struct sockaddr *) src, srclen); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" Mapped address problem: %d", val); goto failure; } username = (const char *)stun_message_find (req, STUN_ATTRIBUTE_USERNAME, &username_len); if (username) { val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, username, username_len); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("Error appending username: %d", val); goto failure; } if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2) { val = stun_message_append32 (msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION, 2); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("Error appending implementation version: %d", val); goto failure; } } /* the stun agent will automatically use the password of the request */ len = stun_agent_finish_message (agent, msg, NULL, 0); if (len == 0) goto failure; *plen = len; stun_debug (" All done (response size: %u)", (unsigned)len); return ret; failure: assert (*plen == 0); stun_debug (" Fatal error formatting Response: %d", val); switch (val) { case STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE: return STUN_USAGE_ICE_RETURN_MEMORY_ERROR; case STUN_MESSAGE_RETURN_INVALID: case STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS: return STUN_USAGE_ICE_RETURN_INVALID_ADDRESS; case STUN_MESSAGE_RETURN_SUCCESS: assert (0); /* shouldn’t be reached */ case STUN_MESSAGE_RETURN_NOT_FOUND: default: return STUN_USAGE_ICE_RETURN_ERROR; } }