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 stun_bind_error (StunAgent *agent, StunMessage *msg, uint8_t *buf, size_t *plen, const StunMessage *req, StunError code) { size_t len = *plen; int val; *plen = 0; stun_debug ("STUN Error Reply (buffer size: %u)...\n", (unsigned)len); val = stun_agent_init_error (agent, msg, buf, len, req, code); if (!val) return val; len = stun_agent_finish_message (agent, msg, NULL, 0); if (len == 0) return 0; *plen = len; stun_debug (" Error response (%u) of %u bytes\n", (unsigned)code, (unsigned)*plen); return 1; }
int main (void) { uint8_t buf[100]; size_t len; union { struct sockaddr_storage storage; struct sockaddr addr; } addr; StunAgent agent; StunMessage msg; uint16_t known_attributes[] = {STUN_ATTRIBUTE_USERNAME, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, STUN_ATTRIBUTE_ERROR_CODE, 0}; stun_agent_init (&agent, known_attributes, STUN_COMPATIBILITY_RFC5389, STUN_AGENT_USAGE_USE_FINGERPRINT); /* Request formatting test */ stun_agent_init_request (&agent, &msg, buf, sizeof(buf), STUN_BINDING); finish_check (&agent, &msg); if (memcmp (buf, "\x00\x01", 2)) fatal ("Request formatting test failed"); /* Response formatting test */ stun_agent_init_response (&agent, &msg, buf, sizeof (buf), &msg); finish_check (&agent, &msg); if (memcmp (buf, "\x01\x01", 2)) fatal ("Response formatting test failed"); /* Error formatting test */ stun_agent_init_request (&agent, &msg, buf, sizeof(buf), STUN_BINDING); finish_check (&agent, &msg); if (!stun_agent_init_error (&agent, &msg, buf, sizeof (buf), &msg, 400)) fatal ("Error initialization test failed"); finish_check (&agent, &msg); if (memcmp (buf, "\x01\x11", 2)) fatal ("Error formatting test failed"); /* Unknown error formatting test */ stun_agent_init_request (&agent, &msg, buf, sizeof(buf), STUN_BINDING); finish_check (&agent, &msg); if (!stun_agent_init_error (&agent, &msg, buf, sizeof (buf), &msg, 666)) fatal ("Unknown error initialization test failed"); finish_check (&agent, &msg); if (memcmp (buf, "\x01\x11", 2)) fatal ("Unknown error formatting test failed"); /* Overflow tests */ stun_agent_init_request (&agent, &msg, buf, sizeof(buf), STUN_BINDING); for (len = 0; stun_message_append_flag (&msg, 0xffff) != STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE; len += 4) { if (len > 0xffff) fatal ("Overflow protection test failed"); } if (stun_message_append32 (&msg, 0xffff, 0x12345678) != STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE) fatal ("Double-word overflow test failed"); if (stun_message_append64 (&msg, 0xffff, 0x123456789abcdef0) != STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE) fatal ("Quad-word overflow test failed"); if (stun_message_append_string (&msg, 0xffff, "foobar") != STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE) fatal ("String overflow test failed"); memset (&addr, 0, sizeof (addr)); addr.addr.sa_family = AF_INET; #ifdef HAVE_SS_LEN addr.addr.ss_len = sizeof (addr); #endif if (stun_message_append_xor_addr (&msg, 0xffff, &addr.storage, sizeof (addr)) != STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE) fatal ("Address overflow test failed"); len = sizeof (msg); if (stun_agent_finish_message (&agent, &msg, NULL, 0) != 0) fatal ("Fingerprint overflow test failed"); if (stun_agent_finish_message (&agent, &msg, pwd, strlen ((char *) pwd)) != 0) fatal ("Message integrity overflow test failed"); /* Address attributes tests */ check_af ("IPv4", AF_INET, sizeof (struct sockaddr_in)); #ifdef AF_INET6 check_af ("IPv6", AF_INET6, sizeof (struct sockaddr_in6)); #endif 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; }
/** Various responses test */ static void responses (void) { struct sockaddr_storage addr; socklen_t addrlen = sizeof (addr); ssize_t val; size_t len; int servfd, fd; uint8_t buf[STUN_MAX_MESSAGE_SIZE]; uint8_t req[STUN_MAX_MESSAGE_SIZE]; size_t req_len; StunAgent agent; StunMessage msg; StunMessage req_msg; uint16_t known_attributes[] = { STUN_ATTRIBUTE_MAPPED_ADDRESS, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, STUN_ATTRIBUTE_PRIORITY, STUN_ATTRIBUTE_USERNAME, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, STUN_ATTRIBUTE_ERROR_CODE, 0}; stun_agent_init (&agent, known_attributes, STUN_COMPATIBILITY_RFC5389, 0); /* Allocate a local UDP port for server */ servfd = listen_dgram (); assert (servfd != -1); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); /* Allocate a client socket and connect to server */ fd = socket (addr.ss_family, SOCK_DGRAM, 0); assert (fd != -1); /* Send error response */ req_len = stun_usage_bind_create (&agent, &req_msg, req, sizeof(req)); assert (req_len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = sendto (fd, req, req_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&addr, addrlen); assert (val >= 0); val = recvfrom (servfd, buf, 1000, MSG_DONTWAIT, NULL, 0); assert (val >= 0); assert (stun_agent_validate (&agent, &msg, buf, val, NULL, NULL) == STUN_VALIDATION_SUCCESS); stun_agent_init_error (&agent, &msg, buf, sizeof (buf), &msg, STUN_ERROR_SERVER_ERROR); len = stun_agent_finish_message (&agent, &msg, NULL, 0); assert (len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_ERROR); /* Send response with a no mapped address at all */ req_len = stun_usage_bind_create (&agent, &req_msg, req, sizeof(req)); assert (req_len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = sendto (fd, req, req_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&addr, addrlen); assert (val >= 0); val = recvfrom (servfd, buf, 1000, MSG_DONTWAIT, NULL, 0); assert (val >= 0); assert (stun_agent_validate (&agent, &msg, buf, val, NULL, NULL) == STUN_VALIDATION_SUCCESS); stun_agent_init_response (&agent, &msg, buf, sizeof (buf), &msg); len = stun_agent_finish_message (&agent, &msg, NULL, 0); assert (len > 0); assert (stun_agent_validate (&agent, &msg, buf, len, NULL, NULL) == STUN_VALIDATION_SUCCESS); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_ERROR); /* Send old-style response */ req_len = stun_usage_bind_create (&agent, &req_msg, req, sizeof(req)); assert (req_len > 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = sendto (fd, req, req_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&addr, addrlen); assert (val >= 0); val = recvfrom (servfd, buf, 1000, MSG_DONTWAIT, NULL, 0); assert (val >= 0); assert (stun_agent_validate (&agent, &msg, buf, val, NULL, NULL) == STUN_VALIDATION_SUCCESS); stun_agent_init_response (&agent, &msg, buf, sizeof (buf), &msg); assert (stun_message_append_addr (&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, (struct sockaddr *) &addr, addrlen) == STUN_MESSAGE_RETURN_SUCCESS); len = stun_agent_finish_message (&agent, &msg, NULL, 0); assert (len > 0); assert (stun_agent_validate (&agent, &msg, buf, len, NULL, NULL) == STUN_VALIDATION_SUCCESS); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); assert (val == 0); val = stun_usage_bind_process (&msg, (struct sockaddr *) &addr, &addrlen, (struct sockaddr *) &addr, &addrlen); assert (val == STUN_USAGE_BIND_RETURN_SUCCESS); /* End */ close (servfd); val = close (fd); assert (val == 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; 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); }