smcp_status_t smcp_set_remote_sockaddr_from_host_and_port(const char* addr_str,uint16_t toport) { smcp_status_t ret; SMCP_NON_RECURSIVE smcp_sockaddr_t saddr; DEBUG_PRINTF("Outbound: Dest host [%s]:%d",addr_str,toport); // Check to see if this host is a group we know about. if (strcasecmp(addr_str, COAP_MULTICAST_STR_ALLDEVICES) == 0) { addr_str = SMCP_COAP_MULTICAST_ALLDEVICES_ADDR; } ret = smcp_plat_lookup_hostname(addr_str, &saddr); require_noerr(ret, bail); saddr.smcp_port = htons(toport); smcp_plat_set_remote_sockaddr(&saddr); bail: return ret; }
smcp_status_t smcp_outbound_begin( smcp_t self, coap_code_t code, coap_transaction_type_t tt ) { smcp_status_t ret = SMCP_STATUS_FAILURE; SMCP_EMBEDDED_SELF_HOOK; check(!smcp_get_current_instance() || smcp_get_current_instance()==self); smcp_set_current_instance(self); #if SMCP_USE_CASCADE_COUNT require_action(self->cascade_count != 1, bail, ret = SMCP_STATUS_CASCADE_LOOP); #endif if (!self->is_processing_message) { smcp_plat_set_remote_sockaddr(NULL); smcp_plat_set_local_sockaddr(NULL); } self->outbound.max_packet_len = SMCP_MAX_PACKET_LENGTH; require_noerr((ret=smcp_plat_outbound_start(self,(uint8_t**)&self->outbound.packet,&self->outbound.max_packet_len)), bail); assert(NULL != self->outbound.packet); self->outbound.packet->tt = tt; self->outbound.packet->msg_id = self->outbound.next_tid; self->outbound.packet->code = code; self->outbound.packet->version = COAP_VERSION; // Set the token. if ( self->is_processing_message && self->inbound.packet != NULL && self->inbound.packet->token_len != 0 && code != COAP_CODE_EMPTY ) { self->outbound.packet->token_len = self->inbound.packet->token_len; memcpy(self->outbound.packet->token,self->inbound.packet->token,self->outbound.packet->token_len); } else if (code && (code < COAP_RESULT_100) && self->current_transaction) { // For sending a request. self->outbound.packet->token_len = sizeof(self->current_transaction->token); memcpy(self->outbound.packet->token,(void*)&self->current_transaction->token,self->outbound.packet->token_len); } else { self->outbound.packet->token_len = 0; } self->outbound.last_option_key = 0; self->outbound.content_ptr = (char*)self->outbound.packet->token + self->outbound.packet->token_len; *self->outbound.content_ptr++ = 0xFF; // start-of-content marker self->outbound.content_len = 0; self->force_current_outbound_code = false; 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; }