smcp_status_t smcp_outbound_send() { smcp_status_t ret = SMCP_STATUS_FAILURE; smcp_t const self = smcp_get_current_instance(); coap_size_t header_len = (coap_size_t)(smcp_outbound_get_content_ptr(NULL)-(char*)self->outbound.packet); // Remove the start-of-payload marker if we have no payload. if (!smcp_get_current_instance()->outbound.content_len) { header_len--; } #if DEBUG { DEBUG_PRINTF("Outbound packet size: %d, %d remaining",header_len+smcp_get_current_instance()->outbound.content_len, smcp_outbound_get_space_remaining()); assert(header_len+smcp_get_current_instance()->outbound.content_len<=self->outbound.max_packet_len); assert(coap_verify_packet((char*)self->outbound.packet,header_len+smcp_get_current_instance()->outbound.content_len)); } #endif // DEBUG if (self->current_transaction) { self->current_transaction->sent_code = self->outbound.packet->code; self->current_transaction->sockaddr_remote = *smcp_plat_get_remote_sockaddr(); self->current_transaction->multicast = SMCP_IS_ADDR_MULTICAST(&self->current_transaction->sockaddr_remote.smcp_addr); } #if defined(SMCP_DEBUG_OUTBOUND_DROP_PERCENT) if(SMCP_DEBUG_OUTBOUND_DROP_PERCENT*SMCP_RANDOM_MAX>SMCP_FUNC_RANDOM_UINT32()) { DEBUG_PRINTF("Dropping outbound packet for debugging!"); if(smcp_get_current_instance()->is_responding) smcp_get_current_instance()->did_respond = true; smcp_get_current_instance()->is_responding = false; ret = SMCP_STATUS_OK; goto bail; } #endif if ((self->outbound.packet->tt == COAP_TRANS_TYPE_ACK) && smcp_session_type_is_reliable(smcp_plat_get_session_type()) ) { // If the session type is reliable, we don't bother // sending acks. } else { ret = smcp_plat_outbound_finish( self, (const uint8_t*)self->outbound.packet, header_len + self->outbound.content_len, 0 // FLAGS ); } require(ret == SMCP_STATUS_OK, bail); if (self->is_responding) { self->did_respond = true; } self->is_responding = false; ret = SMCP_STATUS_OK; bail: return ret; }
ssize_t sendtofrom( int fd, const void *data, size_t len, int flags, const struct sockaddr * saddr_to, socklen_t socklen_to, const struct sockaddr * saddr_from, socklen_t socklen_from ) { ssize_t ret = -1; if (SMCP_IS_ADDR_MULTICAST(&((smcp_sockaddr_t*)saddr_from)->smcp_addr)) { saddr_from = NULL; socklen_from = 0; } if ((socklen_from == 0) || (saddr_from == NULL) || (saddr_from->sa_family != saddr_to->sa_family) ) { ret = sendto( fd, data, len, 0, (struct sockaddr *)saddr_to, socklen_to ); check(ret>0); } else { struct iovec iov = { (void *)data, len }; uint8_t cmbuf[CMSG_SPACE(sizeof (struct in6_pktinfo))]; struct cmsghdr *scmsgp; struct msghdr msg = { .msg_name = (void*)saddr_to, .msg_namelen = socklen_to, .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmbuf, .msg_controllen = sizeof(cmbuf), }; #if defined(AF_INET6) if (saddr_to->sa_family == AF_INET6) { struct in6_pktinfo *pktinfo; scmsgp = CMSG_FIRSTHDR(&msg); scmsgp->cmsg_level = IPPROTO_IPV6; scmsgp->cmsg_type = IPV6_PKTINFO; scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp)); pktinfo->ipi6_addr = ((struct sockaddr_in6*)saddr_from)->sin6_addr; pktinfo->ipi6_ifindex = ((struct sockaddr_in6*)saddr_from)->sin6_scope_id; } else #endif if (saddr_to->sa_family == AF_INET) { struct in_pktinfo *pktinfo; scmsgp = CMSG_FIRSTHDR(&msg); scmsgp->cmsg_level = IPPROTO_IP; scmsgp->cmsg_type = IP_PKTINFO; scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); pktinfo = (struct in_pktinfo *)(CMSG_DATA(scmsgp)); pktinfo->ipi_spec_dst = ((struct sockaddr_in*)saddr_to)->sin_addr; pktinfo->ipi_addr = ((struct sockaddr_in*)saddr_from)->sin_addr; pktinfo->ipi_ifindex = 0; } ret = sendmsg(fd, &msg, flags); check(ret > 0); check_string(ret >= 0, strerror(errno)); } return ret; } smcp_status_t smcp_plat_set_remote_hostname_and_port(const char* hostname, uint16_t port) { smcp_status_t ret; SMCP_NON_RECURSIVE smcp_sockaddr_t saddr; DEBUG_PRINTF("Outbound: Dest host [%s]:%d",hostname,port); #if SMCP_DTLS smcp_plat_ssl_set_remote_hostname(hostname); #endif // Check to see if this host is a group we know about. if (strcasecmp(hostname, COAP_MULTICAST_STR_ALLDEVICES) == 0) { hostname = SMCP_COAP_MULTICAST_ALLDEVICES_ADDR; } ret = smcp_plat_lookup_hostname(hostname, &saddr, SMCP_LOOKUP_HOSTNAME_FLAG_DEFAULT); require_noerr(ret, bail); saddr.smcp_port = htons(port); smcp_plat_set_remote_sockaddr(&saddr); bail: return ret; }
smcp_status_t smcp_plat_process( smcp_t self ) { SMCP_EMBEDDED_SELF_HOOK; smcp_status_t ret = 0; int tmp; struct pollfd polls[4]; int poll_count; poll_count = smcp_plat_update_pollfds(self, polls, sizeof(polls)/sizeof(polls[0])); if (poll_count > (int)(sizeof(polls)/sizeof(*polls))) { poll_count = sizeof(polls)/sizeof(*polls); } errno = 0; tmp = poll(polls, poll_count, 0); // Ensure that poll did not fail with an error. require_action_string( errno == 0, bail, ret = SMCP_STATUS_ERRNO, strerror(errno) ); if(tmp > 0) { for (tmp = 0; tmp < poll_count; tmp++) { if (!polls[tmp].revents) { continue; } else { char packet[SMCP_MAX_PACKET_LENGTH+1]; smcp_sockaddr_t remote_saddr = {}; smcp_sockaddr_t local_saddr = {}; ssize_t packet_len = 0; char cmbuf[0x100]; struct iovec iov = { packet, SMCP_MAX_PACKET_LENGTH }; struct msghdr msg = { .msg_name = &remote_saddr, .msg_namelen = sizeof(remote_saddr), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmbuf, .msg_controllen = sizeof(cmbuf), }; struct cmsghdr *cmsg; packet_len = recvmsg(polls[tmp].fd, &msg, 0); require_action(packet_len > 0, bail, ret = SMCP_STATUS_ERRNO); packet[packet_len] = 0; for ( cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg) ) { if (cmsg->cmsg_level != SMCP_IPPROTO || cmsg->cmsg_type != SMCP_PKTINFO ) { continue; } // Preinitialize some of the fields. local_saddr = remote_saddr; #if SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET6 struct in6_pktinfo *pi = (struct in6_pktinfo *)CMSG_DATA(cmsg); local_saddr.smcp_addr = pi->ipi6_addr; local_saddr.sin6_scope_id = pi->ipi6_ifindex; #elif SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET struct in_pktinfo *pi = (struct in_pktinfo *)CMSG_DATA(cmsg); local_saddr.smcp_addr = pi->ipi_addr; #endif local_saddr.smcp_port = htons(get_port_for_fd(polls[tmp].fd)); self->plat.pktinfo = *pi; } smcp_set_current_instance(self); smcp_plat_set_remote_sockaddr(&remote_saddr); smcp_plat_set_local_sockaddr(&local_saddr); if (self->plat.fd_udp == polls[tmp].fd) { smcp_plat_set_session_type(SMCP_SESSION_TYPE_UDP); ret = smcp_inbound_packet_process(self, packet, (coap_size_t)packet_len, 0); require_noerr(ret, bail); #if SMCP_DTLS } else if (self->plat.fd_dtls == polls[tmp].fd) { smcp_plat_set_session_type(SMCP_SESSION_TYPE_DTLS); smcp_plat_ssl_inbound_packet_process( self, packet, (coap_size_t)packet_len ); #endif } } } } smcp_handle_timers(self); bail: smcp_set_current_instance(NULL); self->is_responding = false; return ret; } smcp_status_t smcp_plat_lookup_hostname(const char* hostname, smcp_sockaddr_t* saddr, int flags) { smcp_status_t ret; struct addrinfo hint = { .ai_flags = AI_ADDRCONFIG, .ai_family = AF_UNSPEC, }; struct addrinfo *results = NULL; struct addrinfo *iter = NULL; #if SMCP_BSD_SOCKETS_NET_FAMILY != AF_INET6 hint.ai_family = SMCP_BSD_SOCKETS_NET_FAMILY; #endif if ((flags & (SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY|SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY)) == (SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY|SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY)) { ret = SMCP_STATUS_INVALID_ARGUMENT; goto bail; } else if ((flags & SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY) == SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY) { hint.ai_family = AF_INET; } else if ((flags & SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY) == SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY) { hint.ai_family = AF_INET6; } memset(saddr, 0, sizeof(*saddr)); saddr->___smcp_family = SMCP_BSD_SOCKETS_NET_FAMILY; #if SOCKADDR_HAS_LENGTH_FIELD saddr->___smcp_len = sizeof(*saddr); #endif int error = getaddrinfo(hostname, NULL, &hint, &results); #if SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET6 if(error && (inet_addr(hostname) != INADDR_NONE)) { char addr_v4mapped_str[8 + strlen(hostname)]; hint.ai_family = AF_INET6; hint.ai_flags = AI_ALL | AI_V4MAPPED, strcpy(addr_v4mapped_str,"::ffff:"); strcat(addr_v4mapped_str,hostname); error = getaddrinfo(addr_v4mapped_str, NULL, &hint, &results ); } #endif if (EAI_AGAIN == error) { ret = SMCP_STATUS_WAIT_FOR_DNS; goto bail; } #ifdef TM_EWOULDBLOCK if (TM_EWOULDBLOCK == error) { ret = SMCP_STATUS_WAIT_FOR_DNS; goto bail; } #endif require_action_string( !error, bail, ret = SMCP_STATUS_HOST_LOOKUP_FAILURE, gai_strerror(error) ); // Move to the first recognized result for(iter = results;iter && (iter->ai_family!=AF_INET6 && iter->ai_family!=AF_INET);iter=iter->ai_next); require_action( iter, bail, ret = SMCP_STATUS_HOST_LOOKUP_FAILURE ); #if SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET6 if(iter->ai_family == AF_INET) { struct sockaddr_in *v4addr = (void*)iter->ai_addr; saddr->sin6_addr.s6_addr[10] = 0xFF; saddr->sin6_addr.s6_addr[11] = 0xFF; memcpy(&saddr->sin6_addr.s6_addr[12], &v4addr->sin_addr.s_addr, 4); } else #endif if(iter->ai_family == SMCP_BSD_SOCKETS_NET_FAMILY) { memcpy(saddr, iter->ai_addr, iter->ai_addrlen); } if(SMCP_IS_ADDR_MULTICAST(&saddr->smcp_addr)) { smcp_t const self = smcp_get_current_instance(); check(self->outbound.packet->tt != COAP_TRANS_TYPE_CONFIRMABLE); if(self->outbound.packet->tt == COAP_TRANS_TYPE_CONFIRMABLE) { self->outbound.packet->tt = COAP_TRANS_TYPE_NONCONFIRMABLE; } } ret = SMCP_STATUS_OK; bail: if(results) freeaddrinfo(results); return ret; }