Esempio n. 1
0
static void
check_af (const char *name, int family, socklen_t addrlen)
{
  struct sockaddr_storage addr;
  uint8_t buf[100];
  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);

  assert (addrlen <= sizeof (addr));

  memset (&addr, 0, sizeof (addr));
  stun_agent_init_request (&agent, &msg, buf, sizeof(buf), STUN_BINDING);

  if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS,
          (struct sockaddr *) &addr, addrlen) !=
      STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS)
    fatal ("Unknown address family test failed");
  if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
          &addr, addrlen) !=
      STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS)
    fatal ("Unknown address family xor test failed");

  addr.ss_family = family;
  if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS,
          (struct sockaddr *) &addr, addrlen - 1) !=
      STUN_MESSAGE_RETURN_INVALID)
    fatal ("Too small %s sockaddr test failed", name);

  if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
          &addr, addrlen - 1) != STUN_MESSAGE_RETURN_INVALID)
    fatal ("Too small %s sockaddr xor test failed", name);

  if (stun_message_append_addr (&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS,
          (struct sockaddr *) &addr, addrlen) != STUN_MESSAGE_RETURN_SUCCESS)
    fatal ("%s sockaddr test failed", name);

  if (stun_message_append_xor_addr (&msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
          &addr, addrlen) != STUN_MESSAGE_RETURN_SUCCESS)
    fatal ("%s sockaddr xor test failed", name);
}
Esempio n. 2
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);
}
Esempio n. 4
0
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;
    }
}
Esempio n. 5
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;
}
Esempio n. 6
0
File: stund.c Progetto: zsx/ossbuild
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);
}
Esempio n. 7
0
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;
  }
}