int overlay_mdp_recv(overlay_mdp_frame *mdp, int port, int *ttl) { char mdp_socket_name[101]; unsigned char recvaddrbuffer[1024]; struct sockaddr *recvaddr=(struct sockaddr *)recvaddrbuffer; unsigned int recvaddrlen=sizeof(recvaddrbuffer); struct sockaddr_un *recvaddr_un; if (!FORM_SERVAL_INSTANCE_PATH(mdp_socket_name, "mdp.socket")) return WHY("Could not find mdp socket"); mdp->packetTypeAndFlags=0; /* Check if reply available */ set_nonblock(mdp_client_socket); ssize_t len = recvwithttl(mdp_client_socket,(unsigned char *)mdp, sizeof(overlay_mdp_frame),ttl,recvaddr,&recvaddrlen); set_block(mdp_client_socket); recvaddr_un=(struct sockaddr_un *)recvaddr; /* Null terminate received address so that the stat() call below can succeed */ if (recvaddrlen<1024) recvaddrbuffer[recvaddrlen]=0; if (len>0) { /* Make sure recvaddr matches who we sent it to */ if (strncmp(mdp_socket_name, recvaddr_un->sun_path, sizeof(recvaddr_un->sun_path))) { /* Okay, reply was PROBABLY not from the server, but on OSX if the path has a symlink in it, it is resolved in the reply path, but might not be in the request path (mdp_socket_name), thus we need to stat() and compare inode numbers etc */ struct stat sb1,sb2; if (stat(mdp_socket_name,&sb1)) return WHY("stat(mdp_socket_name) failed, so could not verify that reply came from MDP server"); if (stat(recvaddr_un->sun_path,&sb2)) return WHY("stat(ra->sun_path) failed, so could not verify that reply came from MDP server"); if ((sb1.st_ino!=sb2.st_ino)||(sb1.st_dev!=sb2.st_dev)) return WHY("Reply did not come from server"); } // silently drop incoming packets for the wrong port number if (port>0 && port != mdp->in.dst.port){ WARNF("Ignoring packet for port %d",mdp->in.dst.port); return -1; } int expected_len = overlay_mdp_relevant_bytes(mdp); if (len < expected_len){ return WHYF("Expected packet length of %d, received only %lld bytes", expected_len, (long long) len); } /* Valid packet received */ return 0; } else /* no packet received */ return -1; }
int overlay_mdp_reply(int sock,struct sockaddr_un *recvaddr,int recvaddrlen, overlay_mdp_frame *mdpreply) { int replylen; if (!recvaddr) return 0; replylen=overlay_mdp_relevant_bytes(mdpreply); if (replylen<0) return WHY("Invalid MDP frame (could not compute length)"); errno=0; int r=sendto(sock,(char *)mdpreply,replylen,0, (struct sockaddr *)recvaddr,recvaddrlen); if (r<replylen) { WHY_perror("sendto(d)"); return WHYF("sendto() failed when sending MDP reply, sock=%d, r=%d", sock, r); } else if (0) DEBUGF("reply of %d bytes sent",r); return 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); }
static int overlay_saw_mdp_frame(struct overlay_frame *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 (config.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 (frame) destination = frame->destination; else if (!is_sid_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 */ RETURN(overlay_mdp_try_interal_services(frame, mdp)); } break; default: RETURN(WHYF("We should only see MDP_TX frames here (MDP message type = 0x%x)", mdp->packetTypeAndFlags)); } RETURN(0); OUT(); }
int overlay_mdp_send(overlay_mdp_frame *mdp,int flags,int timeout_ms) { int len=4; if (mdp_client_socket==-1) if (overlay_mdp_client_init() != 0) return -1; /* Minimise frame length to save work and prevent accidental disclosure of memory contents. */ len=overlay_mdp_relevant_bytes(mdp); if (len<0) return WHY("MDP frame invalid (could not compute length)"); /* Construct name of socket to send to. */ struct sockaddr_un name; name.sun_family = AF_UNIX; if (!FORM_SERVAL_INSTANCE_PATH(name.sun_path, "mdp.socket")) return -1; set_nonblock(mdp_client_socket); int result=sendto(mdp_client_socket, mdp, len, 0, (struct sockaddr *)&name, sizeof(struct sockaddr_un)); set_block(mdp_client_socket); if (result<0) { mdp->packetTypeAndFlags=MDP_ERROR; mdp->error.error=1; snprintf(mdp->error.message,128,"Error sending frame to MDP server."); return WHY_perror("sendto(f)"); } else { if (!(flags&MDP_AWAITREPLY)) { return 0; } } int port=0; if ((mdp->packetTypeAndFlags&MDP_TYPE_MASK) == MDP_TX) port = mdp->out.src.port; time_ms_t started = gettime_ms(); while(timeout_ms>=0 && overlay_mdp_client_poll(timeout_ms)>0){ int ttl=-1; if (!overlay_mdp_recv(mdp, port, &ttl)) { /* If all is well, examine result and return error code provided */ if ((mdp->packetTypeAndFlags&MDP_TYPE_MASK)==MDP_ERROR) return mdp->error.error; else /* Something other than an error has been returned */ return 0; } // work out how much longer we can wait for a valid response time_ms_t now = gettime_ms(); timeout_ms -= (now - started); } /* Timeout */ mdp->packetTypeAndFlags=MDP_ERROR; mdp->error.error=1; snprintf(mdp->error.message,128,"Timeout waiting for reply to MDP packet (packet was successfully sent)."); return -1; /* WHY("Timeout waiting for server response"); */ }