bool STUNUDPSocketFilter::filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
                                                 const uint8_t *data, uint32_t len) {
  // Check white list
  if (white_list_.find(*remote_addr) != white_list_.end()) {
    return true;
  }

  // Check if we had sent any stun request to this destination. If we had sent a request
  // to this host, we check the transaction id, and we can add this address to whitelist.
  std::set<PendingSTUNRequest>::iterator it =
    pending_requests_.find(PendingSTUNRequest(*remote_addr));
  if (it != pending_requests_.end()) {
    if (nr_is_stun_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
      const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
      // If it is a STUN response message and we can match its id with one of the pending
      // requests, we can add this address into whitelist.
      if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
        PendingSTUNRequest pending_req(*remote_addr, msg->id);
        std::set<PendingSTUNRequest>::iterator it = pending_requests_.find(pending_req);
        if (it != pending_requests_.end()) {
          pending_requests_.erase(it);
          response_allowed_.erase(pending_req);
          white_list_.insert(*remote_addr);
        }
      } else {
        // If it is a STUN message, but not a response message, we add it into response
        // allowed list and allow outgoing filter to send a response back.
        response_allowed_.insert(PendingSTUNRequest(*remote_addr, msg->id));
      }
    }
    return true;
  }

  return false;
}
int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) {
  int r;

  if (port_mappings_.empty()) {
    r = internal_socket_->read(buf, maxlen, len);
  } else {
    MOZ_ASSERT(port_mappings_.size() == 1);
    r = port_mappings_.front()->external_socket_->read(buf, maxlen, len);
    if (!r && nat_->refresh_on_ingress_) {
      port_mappings_.front()->last_used_ = PR_IntervalNow();
    }
  }

  if (r) {
    return r;
  }

  if (nat_->block_tcp_ && !tls_) {
    // Should cause this socket to be abandoned
    return R_INTERNAL;
  }

  UCHAR *cbuf = static_cast<UCHAR*>(const_cast<void*>(buf));
  if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) {
    // Should cause this socket to be abandoned
    return R_INTERNAL;
  }

  return r;
}
Example #3
0
int
nr_is_stun_indication_message(UCHAR *buf, int len)
{
   UINT2 type;

   if (sizeof(nr_stun_message_header) > len)
       return 0;

   if (!nr_is_stun_message(buf, len))
       return 0;

   memcpy(&type, buf, 2);
   type = ntohs(type);

   return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_INDICATION;
}
Example #4
0
int
nr_is_stun_request_message(UCHAR *buf, int len)
{
   UINT2 type;

   if (sizeof(nr_stun_message_header) > len)
       return 0;

   if (!nr_is_stun_message(buf, len))
       return 0;

   memcpy(&type, buf, 2);
   type = ntohs(type);

   return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_REQUEST;
}
Example #5
0
int
nr_is_stun_response_message(UCHAR *buf, int len)
{
   UINT2 type;

   if (sizeof(nr_stun_message_header) > len)
       return 0;

   if (!nr_is_stun_message(buf, len))
       return 0;

   memcpy(&type, buf, 2);
   type = ntohs(type);

   return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_RESPONSE
       || NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_ERROR_RESPONSE;
}
int TestNrSocket::write(const void *msg, size_t len, size_t *written) {
  UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg));
  if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
    // Should cause this socket to be abandoned
    r_log(LOG_GENERIC, LOG_DEBUG,
          "TestNrSocket %s dropping outgoing TCP "
          "because it is configured to drop STUN",
          my_addr().as_string);
    return R_INTERNAL;
  }

  if (nat_->block_tcp_ && !tls_) {
    // Should cause this socket to be abandoned
    r_log(LOG_GENERIC, LOG_DEBUG,
          "TestNrSocket %s dropping outgoing TCP "
          "because it is configured to drop TCP",
          my_addr().as_string);
    return R_INTERNAL;
  }

  if (port_mappings_.empty()) {
    // The no-nat case, just pass call through.
    r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing",
          my_addr().as_string);

    return internal_socket_->write(msg, len, written);
  }
  destroy_stale_port_mappings();
  if (port_mappings_.empty()) {
    r_log(LOG_GENERIC, LOG_DEBUG,
          "TestNrSocket %s dropping outgoing TCP "
          "because the port mapping was stale",
          my_addr().as_string);
    return R_INTERNAL;
  }
  // This is TCP only
  MOZ_ASSERT(port_mappings_.size() == 1);
  r_log(LOG_GENERIC, LOG_DEBUG,
        "PortMapping %s -> %s writing",
        port_mappings_.front()->external_socket_->my_addr().as_string,
        port_mappings_.front()->remote_address_.as_string);
  port_mappings_.front()->last_used_ = PR_IntervalNow();
  return port_mappings_.front()->external_socket_->write(msg, len, written);
}
static void s_cb(int s, int how, void *cb_arg)
  {
#if 0
    nr_socket *sock = (nr_socket*)cb_arg;
    UCHAR buf2[4096];
    size_t len;
    nr_transport_addr addr2;
    int r;
    StunMessage req;
    StunMessage res;
    //StunAddress4 from;

    fprintf(stderr,"TURN_SERVER_UTIL: SERVER CB\n");

    if(r=nr_socket_recvfrom(sock,buf2,sizeof(buf2),&len,0,&addr2)){
      fprintf(stderr,"Error in recvfrom\n");
      exit(1);
    }

    memset(&req, 0, sizeof(req));
    memset(&res, 0, sizeof(res));
    //memset(&from, 0, sizeof(from));
    //from.addr = ntohl(addr2.u.addr4.sin_addr.s_addr);
    //from.port = ntohs(addr2.u.addr4.sin_port);

    if ((r=nr_stun_parse_message((char*)buf2, len, &req))) {
      fprintf(stderr,"Error in nr_stun_parse_message\n");
      exit(1);
    }

    if ((r=nr_stun_process_request(&req, buf2, len, &addr2, 0, 0, &res))) {
        /* failure is expected because the server code doesn't support TURN */
        if (r!=R_FAILED) {
            fprintf(stderr,"Failed to process message!\n");
            exit(1);
        }
        fprintf(stderr,"TURN_SERVER_UTIL: No problem with parse failure ... process TURN in test server code\n");
    }

    if (!nr_is_stun_message((char*)buf2, len)) {
        fprintf(stderr,"TURN_SERVER_UTIL: not a STUN/TURN message\n");
        /* instead of sending to remoteAddress, just echo the content back
         * for the purposes of our tests */
        goto send;
    }

    res.hasFingerprint = 1;

    switch (req.msgHdr.msgType) {
    case AllocateRequestMsg:
       if (! req.hasNonce) {
           fprintf(stderr, "Received AllocateRequestMsg #1\n");
           assert(!req.hasMessageIntegrity);
/* TODO: what about username: does it go into the req or not? spec not clear */
           res.msgHdr.msgType = AllocateErrorResponseMsg;
           res.hasUsername = 0;
           res.hasNonce = 1;
           strcpy(res.nonce, nonce);
           res.hasRealm = 1;
           strcpy(res.realm, "REALM");
           res.hasErrorCode = 1;
           res.errorCode.errorClass = 4;
           res.errorCode.number = 35;
       }
       else {
           fprintf(stderr, "Received AllocateRequestMsg #2\n");
           assert(req.hasUsername);
           assert(req.hasRealm);
           assert(req.hasNonce);
           assert(req.hasMessageIntegrity);
           res.msgHdr.msgType = AllocateResponseMsg;
           res.hasUsername = 1;
           res.hasMessageIntegrity = 1;
           strcpy(res.username, req.username);
#if 0
//TODO: this is totally broken, but TURN is changing and so
//TODO: we'll need to take another pass at all this code
//TODO: anyhow, so leave it broken for now
           res.hasRelayAddress = 1;
           res.relayAddress.family = IPv4Family;
           res.relayAddress.ipv4.addr = from.addr;
           res.relayAddress.ipv4.port = from.port;
           res.hasXorMappedAddress = 1;
           res.xorMappedAddress.family = IPv4Family;
           res.xorMappedAddress.ipv4.addr = from.addr;
           res.xorMappedAddress.ipv4.port = from.port;
#endif
       }
       break;
    case SendIndicationMsg:
       fprintf(stderr, "Received SendIndicationMsg\n");
       assert(req.hasRemoteAddress);

       /* pretend to send empty UDP packet to remoteAddress per the
        * TURN spec and to wait for a DataIndication resonse */
       fprintf(stderr, "Sending UDP packet to REMOTE-ADDRESS...\n");
       fprintf(stderr, "Waiting for response from REMOTE-ADDRESS...\n");

       /* ok, this is an indication, so formally there is no "response",
        * but we're going to send a data indication to it, so just pretend
        * that it's a formal response */
       res.msgHdr.msgType = DataIndicationMsg;
       res.hasRemoteAddress = 1;
       nr_transport_addr_copy(&res.remoteAddress, &req.remoteAddress);

       break;
    case SetActiveDestRequestMsg:
       fprintf(stderr, "Received SetActiveDestRequestMsg\n");
       assert(req.hasRemoteAddress);
 
       res.msgHdr.msgType = SetActiveDestResponseMsg;
       break;
    default:
       assert(0);
       break;
    }

   memset(&buf2, 0, sizeof(buf2));

   if ((r=nr_stun_encode_message(STUN_MODE_STUN, &res, (char*)buf2, STUN_MAX_MESSAGE_SIZE, &pass, (unsigned int*)&len))) {
      fprintf(stderr,"Error encoding TURN response\n");
      exit(1);
    }

 send:
   fprintf(stderr,"Sending response to %s\n", addr2.as_string);
   if(r=nr_socket_sendto(sock,buf2,len,0,&addr2)) {
      fprintf(stderr,"Error sending TURN response\n");
      exit(1);
    }

    NR_ASYNC_WAIT(s, how, s_cb, cb_arg);
#else
UNIMPLEMENTED;
#endif
  }
int TestNrSocket::sendto(const void *msg, size_t len,
                         int flags, nr_transport_addr *to) {
  MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);

  UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg));
  if (nat_->block_stun_ &&
      nr_is_stun_message(buf, len)) {
    return 0;
  }

  /* TODO: improve the functionality of this in bug 1253657 */
  if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
    if (nat_->delay_stun_resp_ms_ &&
        nr_is_stun_response_message(buf, len)) {
      NR_ASYNC_TIMER_SET(nat_->delay_stun_resp_ms_,
                         process_delayed_cb,
                         new DeferredPacket(this, msg, len, flags, to,
                                            internal_socket_),
                         &timer_handle_);
      return 0;
    }
    return internal_socket_->sendto(msg, len, flags, to);
  }

  destroy_stale_port_mappings();

  if (to->protocol == IPPROTO_UDP && nat_->block_udp_) {
    // Silently eat the packet
    return 0;
  }

  // Choose our port mapping based on our most selective criteria
  PortMapping *port_mapping = get_port_mapping(*to,
                                               std::max(nat_->filtering_type_,
                                                        nat_->mapping_type_));

  if (!port_mapping) {
    // See if we have already made the external socket we need to use.
    PortMapping *similar_port_mapping =
      get_port_mapping(*to, nat_->mapping_type_);
    RefPtr<NrSocketBase> external_socket;

    if (similar_port_mapping) {
      external_socket = similar_port_mapping->external_socket_;
    } else {
      external_socket = create_external_socket(*to);
      if (!external_socket) {
        MOZ_ASSERT(false);
        return R_INTERNAL;
      }
    }

    port_mapping = create_port_mapping(*to, external_socket);
    port_mappings_.push_back(port_mapping);

    if (poll_flags() & PR_POLL_READ) {
      // Make sure the new port mapping is ready to receive traffic if the
      // TestNrSocket is already waiting.
      port_mapping->async_wait(NR_ASYNC_WAIT_READ,
                              socket_readable_callback,
                              this,
                              (char*)__FUNCTION__,
                              __LINE__);
    }
  }

  // We probably don't want to propagate the flags, since this is a simulated
  // external IP address.
  return port_mapping->sendto(msg, len, *to);
}