Beispiel #1
0
int overlay_mdp_service_stun_req(overlay_mdp_frame *mdp)
{
  if (config.debug.overlayrouting)
    DEBUGF("Processing STUN request from %s", alloca_tohex_sid(mdp->out.src.sid));

  struct overlay_buffer *payload = ob_static(mdp->out.payload, mdp->out.payload_length);
  ob_limitsize(payload, mdp->out.payload_length);
  
  overlay_mdp_frame reply;
  bzero(&reply, sizeof(reply));
  reply.packetTypeAndFlags=MDP_TX;
  
  bcopy(mdp->out.src.sid, reply.out.dst.sid, SID_SIZE);
  bcopy(mdp->out.dst.sid, reply.out.src.sid, SID_SIZE);
  reply.out.src.port=MDP_PORT_STUNREQ;
  reply.out.dst.port=MDP_PORT_STUN;
  reply.out.queue=OQ_MESH_MANAGEMENT;
  
  struct overlay_buffer *replypayload = ob_static(reply.out.payload, sizeof(reply.out.payload));
  
  ob_checkpoint(replypayload);
  while(ob_remaining(payload)>0){
    struct subscriber *subscriber=NULL;
    
    if (overlay_address_parse(NULL, payload, &subscriber))
      break;
    
    if (!subscriber){
      if (config.debug.overlayrouting)
	DEBUGF("Unknown subscriber");
      continue;
    }
    
    if (overlay_append_unicast_address(subscriber, replypayload))
      break;
  }
  
  ob_rewind(replypayload);
  reply.out.payload_length=ob_position(replypayload);
  
  if (reply.out.payload_length){
    if (config.debug.overlayrouting)
      DEBUGF("Sending reply");
    overlay_mdp_dispatch(&reply,0 /* system generated */, NULL,0);
  }
  ob_free(replypayload);
  ob_free(payload);
  return 0;
}
Beispiel #2
0
int overlay_mdp_dnalookup_reply(const sockaddr_mdp *dstaddr, const unsigned char *resolved_sid, const char *uri, const char *did, const char *name)
{
  overlay_mdp_frame mdpreply;
  bzero(&mdpreply, sizeof mdpreply);
  mdpreply.packetTypeAndFlags = MDP_TX; // outgoing MDP message
  memcpy(mdpreply.out.src.sid, resolved_sid, SID_SIZE);
  mdpreply.out.src.port = MDP_PORT_DNALOOKUP;
  bcopy(dstaddr, &mdpreply.out.dst, sizeof(sockaddr_mdp));
  /* build reply as TOKEN|URI|DID|NAME|<NUL> */
  strbuf b = strbuf_local((char *)mdpreply.out.payload, sizeof mdpreply.out.payload);
  strbuf_tohex(b, resolved_sid, SID_SIZE);
  strbuf_sprintf(b, "|%s|%s|%s|", uri, did, name);
  if (strbuf_overrun(b))
    return WHY("MDP payload overrun");
  mdpreply.out.payload_length = strbuf_len(b) + 1;
  /* deliver reply */
  return overlay_mdp_dispatch(&mdpreply, 0 /* system generated */, NULL, 0);
}
Beispiel #3
0
// send a registration packet
static void directory_send(struct subscriber *directory_service, const unsigned char *sid, const char *did, const char *name){
  overlay_mdp_frame request;
  
  memset(&request, 0, sizeof(overlay_mdp_frame));
  
  request.packetTypeAndFlags = MDP_TX;
  
  bcopy(sid, request.out.src.sid, SID_SIZE);
  request.out.src.port=MDP_PORT_NOREPLY;
  
  bcopy(directory_service->sid, request.out.dst.sid, SID_SIZE);
  request.out.dst.port=MDP_PORT_DIRECTORY;
  request.out.payload_length = snprintf((char *)request.out.payload, sizeof(request.out.payload), 
					"%s|%s", did, name);
  // Used by tests
  INFOF("Sending directory registration for %s, %s, %s to %s", 
	alloca_tohex(sid,7), did, name, alloca_tohex(directory_service->sid, 7));
  overlay_mdp_dispatch(&request, 0, NULL, 0);
}
Beispiel #4
0
int overlay_send_stun_request(struct subscriber *server, struct subscriber *request){
  if ((!server) || (!request))
    return -1;
  if (!(subscriber_is_reachable(server)&REACHABLE))
    return -1;
  // don't bother with a stun request if the peer is already reachable directly
  // TODO link timeouts
  if (subscriber_is_reachable(request)&REACHABLE_DIRECT)
    return -1;
  
  time_ms_t now = gettime_ms();
  if (request->last_stun_request +1000 > now)
    return -1;
  
  request->last_stun_request=now;
  
  overlay_mdp_frame mdp;
  bzero(&mdp, sizeof(mdp));
  mdp.packetTypeAndFlags=MDP_TX;
  
  bcopy(my_subscriber->sid, mdp.out.src.sid, SID_SIZE);
  bcopy(server->sid, mdp.out.dst.sid, SID_SIZE);
  mdp.out.src.port=MDP_PORT_STUN;
  mdp.out.dst.port=MDP_PORT_STUNREQ;
  mdp.out.queue=OQ_MESH_MANAGEMENT;
  
  struct overlay_buffer *payload = ob_static(mdp.out.payload, sizeof(mdp.out.payload));
  overlay_address_append(NULL, payload, request);
  mdp.out.payload_length=ob_position(payload);
  if (config.debug.overlayrouting)
    DEBUGF("Sending STUN request to %s", alloca_tohex_sid(server->sid));
  overlay_mdp_dispatch(&mdp,0 /* system generated */,
		       NULL,0);
  ob_free(payload);
  return 0;
}
Beispiel #5
0
void overlay_mdp_poll(struct sched_ent *alarm)
{
  if (alarm->poll.revents & POLLIN) {
    unsigned char buffer[16384];
    int ttl;
    unsigned char recvaddrbuffer[1024];
    struct sockaddr *recvaddr=(struct sockaddr *)&recvaddrbuffer[0];
    socklen_t recvaddrlen=sizeof(recvaddrbuffer);
    struct sockaddr_un *recvaddr_un=NULL;

    ttl=-1;
    bzero((void *)recvaddrbuffer,sizeof(recvaddrbuffer));
    
    ssize_t len = recvwithttl(alarm->poll.fd,buffer,sizeof(buffer),&ttl, recvaddr, &recvaddrlen);
    recvaddr_un=(struct sockaddr_un *)recvaddr;

    if (len>0) {
      /* Look at overlay_mdp_frame we have received */
      overlay_mdp_frame *mdp=(overlay_mdp_frame *)&buffer[0];      
      unsigned int mdp_type = mdp->packetTypeAndFlags & MDP_TYPE_MASK;

      switch (mdp_type) {
      case MDP_GOODBYE:
	if (debug & DEBUG_MDPREQUESTS) DEBUG("MDP_GOODBYE");
	overlay_mdp_releasebindings(recvaddr_un,recvaddrlen);
	return;
      case MDP_NODEINFO:
	if (debug & DEBUG_MDPREQUESTS) DEBUG("MDP_NODEINFO");
	overlay_route_node_info(mdp,recvaddr_un,recvaddrlen);
	return;
      case MDP_GETADDRS:
	if (debug & DEBUG_MDPREQUESTS)
	  DEBUGF("MDP_GETADDRS first_sid=%u last_sid=%u frame_sid_count=%u mode=%d",
	      mdp->addrlist.first_sid,
	      mdp->addrlist.last_sid,
	      mdp->addrlist.frame_sid_count,
	      mdp->addrlist.mode
	    );
	{
	  overlay_mdp_frame mdpreply;
	  
	  /* Work out which SIDs to get ... */
	  int sid_num=mdp->addrlist.first_sid;
	  int max_sid=mdp->addrlist.last_sid;
	  int max_sids=mdp->addrlist.frame_sid_count;
	  /* ... and constrain list for sanity */
	  if (sid_num<0) sid_num=0;
	  if (max_sids>MDP_MAX_SID_REQUEST) max_sids=MDP_MAX_SID_REQUEST;
	  if (max_sids<0) max_sids=0;
	  
	  /* Prepare reply packet */
	  mdpreply.packetTypeAndFlags = MDP_ADDRLIST;
	  mdpreply.addrlist.mode = mdp->addrlist.mode;
	  mdpreply.addrlist.first_sid = sid_num;
	  mdpreply.addrlist.last_sid = max_sid;
	  mdpreply.addrlist.frame_sid_count = max_sids;
	  
	  /* Populate with SIDs */
	  struct search_state state={
	    .mdp=mdp,
	    .mdpreply=&mdpreply,
	    .first=sid_num,
	    .max=max_sid,
	  };
	  
	  enum_subscribers(NULL, search_subscribers, &state);
	  
	  mdpreply.addrlist.frame_sid_count = state.index;
	  mdpreply.addrlist.last_sid = sid_num + state.index - 1;
	  mdpreply.addrlist.server_sid_count = state.count;

	  if (debug & DEBUG_MDPREQUESTS)
	    DEBUGF("reply MDP_ADDRLIST first_sid=%u last_sid=%u frame_sid_count=%u server_sid_count=%u",
		mdpreply.addrlist.first_sid,
		mdpreply.addrlist.last_sid,
		mdpreply.addrlist.frame_sid_count,
		mdpreply.addrlist.server_sid_count
	      );

	  /* Send back to caller */
	  overlay_mdp_reply(alarm->poll.fd,
			    (struct sockaddr_un *)recvaddr,recvaddrlen,
			    &mdpreply);
	  return;
	}
	break;
	  
      case MDP_TX: /* Send payload (and don't treat it as system privileged) */
	if (debug & DEBUG_MDPREQUESTS) DEBUG("MDP_TX");
	overlay_mdp_dispatch(mdp,1,(struct sockaddr_un*)recvaddr,recvaddrlen);
	return;
	break;
	  
      case MDP_BIND: /* Bind to port */
	{
	  if (debug & DEBUG_MDPREQUESTS) DEBUG("MDP_BIND");
	  
	  struct subscriber *subscriber=NULL;
	  /* Make sure source address is either all zeros (listen on all), or a valid
	   local address */
	  
	  if (!is_sid_any(mdp->bind.sid)){
	    subscriber = find_subscriber(mdp->bind.sid, SID_SIZE, 0);
	    if ((!subscriber) || subscriber->reachable != REACHABLE_SELF){
	      WHYF("Invalid bind request for sid=%s", alloca_tohex_sid(mdp->bind.sid));
	      /* Source address is invalid */
	      overlay_mdp_reply_error(alarm->poll.fd, recvaddr_un, recvaddrlen, 7,
					     "Bind address is not valid (must be a local MDP address, or all zeroes).");
	      return;
	    }
	    
	  }
	  if (overlay_mdp_process_bind_request(alarm->poll.fd, subscriber, mdp->bind.port,
					       mdp->packetTypeAndFlags, recvaddr_un, recvaddrlen))
	    overlay_mdp_reply_error(alarm->poll.fd,recvaddr_un,recvaddrlen,3, "Port already in use");
	  else
	    overlay_mdp_reply_ok(alarm->poll.fd,recvaddr_un,recvaddrlen,"Port bound");
	  return;
	}
	break;
	  
      default:
	/* Client is not allowed to send any other frame type */
	WARNF("Unsupported MDP frame type: %d", mdp_type);
	mdp->packetTypeAndFlags=MDP_ERROR;
	mdp->error.error=2;
	snprintf(mdp->error.message,128,"Illegal request type.  Clients may use only MDP_TX or MDP_BIND.");
	int len=4+4+strlen(mdp->error.message)+1;
	errno=0;
	/* We ignore the result of the following, because it is just sending an
	   error message back to the client.  If this fails, where would we report
	   the error to? My point exactly. */
	sendto(alarm->poll.fd,mdp,len,0,(struct sockaddr *)recvaddr,recvaddrlen);
      }
    }
Beispiel #6
0
int overlay_saw_mdp_frame(overlay_mdp_frame *mdp, time_ms_t now)
{
  IN();
  int i;
  int match=-1;

  switch(mdp->packetTypeAndFlags&MDP_TYPE_MASK) {
  case MDP_TX: 
    /* Regular MDP frame addressed to us.  Look for matching port binding,
       and if available, push to client.  Else do nothing, or if we feel nice
       send back a connection refused type message? Silence is probably the
       more prudent path.
    */

    if (debug & DEBUG_MDPREQUESTS) 
      DEBUGF("Received packet with listener (MDP ports: src=%s*:%d, dst=%d)",
	   alloca_tohex(mdp->out.src.sid, 7),
	   mdp->out.src.port,mdp->out.dst.port);

    // TODO pass in dest subscriber as an argument, we should know it by now
    struct subscriber *destination = NULL;
    if (!is_broadcast(mdp->out.dst.sid)){
      destination = find_subscriber(mdp->out.dst.sid, SID_SIZE, 1);
    }
    
    for(i=0;i<MDP_MAX_BINDINGS;i++)
      {
	if (mdp_bindings[i].port!=mdp->out.dst.port)
	  continue;
	
	if ((!destination) || mdp_bindings[i].subscriber == destination){
	  /* exact match, so stop searching */
	  match=i;
	  break;
	}else if (!mdp_bindings[i].subscriber){
	  /* If we find an "ANY" binding, remember it. But we will prefer an exact match if we find one */
	  match=i;
	}
      }
    
    if (match>-1) {
      struct sockaddr_un addr;

      bcopy(mdp_bindings[match].socket_name,addr.sun_path,mdp_bindings[match].name_len);
      addr.sun_family=AF_UNIX;
      errno=0;
      int len=overlay_mdp_relevant_bytes(mdp);
      int r=sendto(mdp_named.poll.fd,mdp,len,0,(struct sockaddr*)&addr,sizeof(addr));
      if (r==overlay_mdp_relevant_bytes(mdp)) {	
	RETURN(0);
      }
      WHY("didn't send mdp packet");
      if (errno==ENOENT) {
	/* far-end of socket has died, so drop binding */
	INFOF("Closing dead MDP client '%s'",mdp_bindings[match].socket_name);
	overlay_mdp_releasebindings(&addr,mdp_bindings[match].name_len);
      }
      WHY_perror("sendto(e)");
      RETURN(WHY("Failed to pass received MDP frame to client"));
    } else {
      /* No socket is bound, ignore the packet ... except for magic sockets */
      switch(mdp->out.dst.port) {
      case MDP_PORT_VOMP:
	RETURN(vomp_mdp_received(mdp));
      case MDP_PORT_KEYMAPREQUEST:
	/* Either respond with the appropriate SAS, or record this one if it
	   verifies out okay. */
	if (debug & DEBUG_MDPREQUESTS)
	  DEBUG("MDP_PORT_KEYMAPREQUEST");
	RETURN(keyring_mapping_request(keyring,mdp));
      case MDP_PORT_DNALOOKUP: /* attempt to resolve DID to SID */
	{
	  int cn=0,in=0,kp=0;
	  char did[64+1];
	  int pll=mdp->out.payload_length;
	  if (pll>64) pll=64;
	  /* get did from the packet */
	  if (mdp->out.payload_length<1) {
	    RETURN(WHY("Empty DID in DNA resolution request")); }
	  bcopy(&mdp->out.payload[0],&did[0],pll);
	  did[pll]=0;
	  
	  if (debug & DEBUG_MDPREQUESTS)
	    DEBUG("MDP_PORT_DNALOOKUP");
	  
	  int results=0;
	  while(keyring_find_did(keyring,&cn,&in,&kp,did))
	    {
	      /* package DID and Name into reply (we include the DID because
		 it could be a wild-card DID search, but the SID is implied 
		 in the source address of our reply). */
	      if (keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key_len > DID_MAXSIZE) 
		/* skip excessively long DID records */
		continue;
	      const unsigned char *packedSid = keyring->contexts[cn]->identities[in]->keypairs[0]->public_key;
	      const char *unpackedDid = (const char *) keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
	      const char *name = (const char *)keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key;
	      // URI is sid://SIDHEX/DID
	      strbuf b = strbuf_alloca(SID_STRLEN + DID_MAXSIZE + 10);
	      strbuf_puts(b, "sid://");
	      strbuf_tohex(b, packedSid, SID_SIZE);
	      strbuf_puts(b, "/local/");
	      strbuf_puts(b, unpackedDid);
	      overlay_mdp_dnalookup_reply(&mdp->out.src, packedSid, strbuf_str(b), unpackedDid, name);
	      kp++;
	      results++;
	    }
	  if (!results) {
	    /* No local results, so see if servald has been configured to use
	       a DNA-helper that can provide additional mappings.  This provides
	       a generalised interface for resolving telephone numbers into URIs.
	       The first use will be for resolving DIDs to SIP addresses for
	       OpenBTS boxes run by the OTI/Commotion project. 

	       The helper is run asynchronously, and the replies will be delivered
	       when results become available, so this function will return
	       immediately, so as not to cause blockages and delays in servald.
	    */
	    dna_helper_enqueue(mdp, did, mdp->out.src.sid);
	    monitor_tell_formatted(MONITOR_DNAHELPER, "LOOKUP:%s:%d:%s\n", alloca_tohex_sid(mdp->out.src.sid), mdp->out.src.port, did);
	  }
	  RETURN(0);
	}
	break;
      case MDP_PORT_ECHO: /* well known ECHO port for TCP/UDP and now MDP */
	{
	  /* Echo is easy: we swap the sender and receiver addresses (and thus port
	     numbers) and send the frame back. */

	  /* Swap addresses */
	  overlay_mdp_swap_src_dst(mdp);

	  /* Prevent echo:echo connections and the resulting denial of service from triggering endless pongs. */
	  if (mdp->out.dst.port==MDP_PORT_ECHO) {
	    RETURN(WHY("echo loop averted"));
	  }
	  /* If the packet was sent to broadcast, then replace broadcast address
	     with our local address. For now just responds with first local address */
	  if (is_broadcast(mdp->out.src.sid))
	    {
	      if (my_subscriber)		  
		bcopy(my_subscriber->sid,
		      mdp->out.src.sid,SID_SIZE);
	      else
		/* No local addresses, so put all zeroes */
		bzero(mdp->out.src.sid,SID_SIZE);
	    }

	  /* queue frame for delivery */	  
	  overlay_mdp_dispatch(mdp,0 /* system generated */,
			       NULL,0);
	  
	  /* and switch addresses back around in case the caller was planning on
	     using MDP structure again (this happens if there is a loop-back reply
	     and the frame needs sending on, as happens with broadcasts.  MDP ping
	     is a simple application where this occurs). */
	  overlay_mdp_swap_src_dst(mdp);
	  
	}
	break;
      default:
	/* Unbound socket.  We won't be sending ICMP style connection refused
	   messages, partly because they are a waste of bandwidth. */
	RETURN(WHYF("Received packet for which no listening process exists (MDP ports: src=%d, dst=%d",
		    mdp->out.src.port,mdp->out.dst.port));
      }
    }
    break;
  default:
    RETURN(WHYF("We should only see MDP_TX frames here (MDP message type = 0x%x)",
		mdp->packetTypeAndFlags));
  }

  RETURN(0);
}