void handle_reply_line(const char *bufp, size_t len) { if (!dna_helper_started) { if (len == 8 && strncmp(bufp, "STARTED\n", 8) == 0) { if (config.debug.dnahelper) DEBUGF("DNAHELPER got STARTED ACK"); dna_helper_started = 1; // Start sending request if there is one pending. if (request_bufptr) { sched_requests.poll.fd = dna_helper_stdin; watch(&sched_requests); } } else { WHYF("DNAHELPER malformed start ACK %s", alloca_toprint(-1, bufp, len)); dna_helper_kill(); } } else if (awaiting_reply) { if (len == 5 && strncmp(bufp, "DONE\n", 5) == 0) { if (config.debug.dnahelper) DEBUG("DNAHELPER reply DONE"); unschedule(&sched_timeout); awaiting_reply = 0; } else { char sidhex[SID_STRLEN + 1]; char did[DID_MAXSIZE + 1]; char name[64]; char uri[512]; const char *replyend = NULL; if (!parseDnaReply(bufp, len, sidhex, did, name, uri, &replyend)) WHYF("DNAHELPER reply %s invalid -- ignored", alloca_toprint(-1, bufp, len)); else if (uri[0] == '\0') WHYF("DNAHELPER reply %s contains empty URI -- ignored", alloca_toprint(-1, bufp, len)); else if (!str_is_uri(uri)) WHYF("DNAHELPER reply %s contains invalid URI -- ignored", alloca_toprint(-1, bufp, len)); else if (sidhex[0] == '\0') WHYF("DNAHELPER reply %s contains empty token -- ignored", alloca_toprint(-1, bufp, len)); else if (!str_is_subscriber_id(sidhex)) WHYF("DNAHELPER reply %s contains invalid token -- ignored", alloca_toprint(-1, bufp, len)); else if (strncmp(sidhex, request_buffer, SID_STRLEN) != 0) WHYF("DNAHELPER reply %s contains mismatched token -- ignored", alloca_toprint(-1, bufp, len)); else if (did[0] == '\0') WHYF("DNAHELPER reply %s contains empty DID -- ignored", alloca_toprint(-1, bufp, len)); else if (!str_is_did(did)) WHYF("DNAHELPER reply %s contains invalid DID -- ignored", alloca_toprint(-1, bufp, len)); else if (strcmp(did, request_did) != 0) WHYF("DNAHELPER reply %s contains mismatched DID -- ignored", alloca_toprint(-1, bufp, len)); else if (*replyend != '\n') WHYF("DNAHELPER reply %s contains spurious trailing chars -- ignored", alloca_toprint(-1, bufp, len)); else { if (config.debug.dnahelper) DEBUGF("DNAHELPER reply %s", alloca_toprint(-1, bufp, len)); overlay_mdp_dnalookup_reply(request_source, request_port, my_subscriber, uri, did, name); } } } else { WARNF("DNAHELPER spurious output %s -- ignored", alloca_toprint(-1, bufp, len)); } }
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); }